fix(win32): restore mouse moves, add enter and leave, capture drags, and close and destroy windows cleanly
Win32
- WM_MOUSEMOVE is handled again: c9ebd44 put the WM_CHAR case where it
was, so currentMousePos stopped following the mouse and onMouseMove
never fired. Positions are read signed (GET_X_LPARAM), since under
capture they run past the client area.
- onMouseEnter and onMouseLeave. The first move over the client area
after a leave is the enter, and arms the TME_LEAVE whose WM_MOUSELEAVE
is the leave. An enter counts only once that is armed, a leave only
after an enter, and a move past the client area is a move only.
- A press captures the mouse, so a drag that leaves the window still
sends its moves and its release, as Wayland's implicit grab does.
Losing the capture releases what is held, and a button-up without its
press fires nothing.
- WM_CLOSE fires onClose and clears open, as xdg_toplevel.close does,
instead of destroying the HWND under the swapchain. WM_DESTROY no
longer posts WM_QUIT, which nothing read.
- ~Window detaches the HWND and destroys it. It stayed on screen, and its
messages went to the freed Window. WndProc hands messages without a
Window to DefWindowProc, and looks the Window up again after an event
that more work follows, in case a listener destroyed it.
- The constructor's message pump leaves input queued for StartSync: the
WM_MOUSEMOVE sent when a window appears under the cursor made an
onMouseEnter nobody could hear yet. The mouse state is reset before
the platform code, no longer after it.
- A window after the first finds its class registered, which showed an
error box.
- The wheel remainder is the window's, no longer a function static that
every window shared, and a leave drops it.
Window
- PointerEnter, PointerMove, PointerLeave and PointerScroll hold the rules
both native backends report the pointer by. An enter puts the entry
position in currentMousePos before onMouseEnter fires, and is followed
by a move, on Wayland too now. scrollRemainder replaces
Device::scrollDetentRemainder, and mouseInside is shared.
Tests
- PointerEnterLeave: enter, move and leave through the wl_pointer
callbacks.
- PointerWin32: posts a real HWND the messages above and pumps them as
StartSync does. Built for x86_64-w64-mingw32 it runs under Wine, and is
skipped where Wine is not on PATH.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
397ff2db51
commit
c62109caf3
7 changed files with 504 additions and 62 deletions
|
|
@ -493,10 +493,7 @@ void Device::PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, std
|
|||
return;
|
||||
}
|
||||
Vector<float, 2> pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
|
||||
//Device::focusedWindow->lastMousePos = Device::focusedWindow->currentMousePos;
|
||||
Device::focusedWindow->currentMousePos = pos * Device::focusedWindow->scale;
|
||||
//Device::focusedWindow->mouseDelta = {Device::focusedWindow->currentMousePos.x-Device::focusedWindow->lastMousePos.x, Device::focusedWindow->currentMousePos.y-Device::focusedWindow->lastMousePos.y};
|
||||
Device::focusedWindow->onMouseMove.Invoke();
|
||||
Device::focusedWindow->PointerMove(pos * Device::focusedWindow->scale);
|
||||
}
|
||||
|
||||
void Device::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std::uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
|
|
@ -509,7 +506,12 @@ void Device::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std:
|
|||
window->cursorHotspotX_, window->cursorHotspotY_);
|
||||
}
|
||||
focusedWindow = window;
|
||||
window->onMouseEnter.Invoke();
|
||||
const Vector<float, 2> pos = Vector<float, 2>(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)) * window->scale;
|
||||
window->PointerEnter(pos);
|
||||
// ~Window clears focusedWindow, should a listener destroy it.
|
||||
if (focusedWindow == window) {
|
||||
window->PointerMove(pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -519,12 +521,9 @@ void Device::PointerListenerHandleLeave(void* data, wl_pointer*, std::uint32_t,
|
|||
// Sway has been observed to send a second leave after focus is already
|
||||
// gone (input-device hot-plug) — don't crash on it.
|
||||
if (focusedWindow != nullptr) {
|
||||
focusedWindow->onMouseLeave.Invoke();
|
||||
focusedWindow->PointerLeave();
|
||||
focusedWindow = nullptr;
|
||||
}
|
||||
// Drop any sub-detent scroll remainder so it can't leak into whatever
|
||||
// window the pointer enters next.
|
||||
scrollDetentRemainder = 0.0;
|
||||
}
|
||||
|
||||
void Device::PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t axis, wl_fixed_t value) {
|
||||
|
|
@ -535,18 +534,11 @@ void Device::PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::u
|
|||
// libinput convention (followed by wlroots, Mutter and KWin): one wheel
|
||||
// detent = 15 axis units, positive = wheel down / toward the user — the
|
||||
// same sign as DOM deltaY. Window::onMouseScroll speaks whole detents
|
||||
// (±1 per notch on every backend), so divide and accumulate the
|
||||
// remainder: smooth-scroll devices (touchpads) deliver many sub-detent
|
||||
// events that must add up instead of each truncating to zero.
|
||||
// (±1 per notch on every backend), so divide; smooth-scroll devices
|
||||
// (touchpads) deliver many sub-detent events, which PointerScroll adds
|
||||
// up instead of each truncating to zero.
|
||||
constexpr double axisUnitsPerDetent = 15.0;
|
||||
scrollDetentRemainder += wl_fixed_to_double(value) / axisUnitsPerDetent;
|
||||
const double whole = std::trunc(scrollDetentRemainder);
|
||||
if (whole != 0.0) {
|
||||
scrollDetentRemainder -= whole;
|
||||
// Event payload is uint32 — preserve sign via two's complement.
|
||||
focusedWindow->onMouseScroll.Invoke(
|
||||
static_cast<std::uint32_t>(static_cast<std::int32_t>(whole)));
|
||||
}
|
||||
focusedWindow->PointerScroll(wl_fixed_to_double(value) / axisUnitsPerDetent);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue