Commit graph Crafter.Graphics/tests/PointerWin32/main.cpp
Author SHA1 Message Date
578a3c6dc4
fix(window): set the title on Win32 through a Unicode window class, and send only the view's bytes on Wayland
Win32
- SetTitle had a body only for Wayland, so the title passed to Window's
  constructor and every later SetTitle left a Win32 window untitled. It
  converts the title from UTF-8 and hands it to SetWindowTextW.
- The window class is registered with RegisterClassW, the window made
  with CreateWindowExW, and WndProc hands what it does not handle to
  DefWindowProcW. An ANSI class has the text of every message converted
  to the process code page on its way to WndProc, so SetWindowTextW
  alone would not do: under Wine, in CP1252, 日本 in a title came back
  as ??. WM_CHAR, whose handler takes wParam for a UTF-16 code unit, got
  € as its CP1252 byte 0x80 and passed it on as U+0080, and 日 and both
  halves of a surrogate pair as ?.
- Both message pumps use PeekMessageW and DispatchMessageW. PeekMessageA
  converts WM_CHAR to the code page whatever the window's class: on the
  Unicode class, an ANSI pump still turned 日 and the surrogates into ?.

Wayland
- SetTitle handed title.data() to xdg_toplevel_set_title, which reads up
  to a NUL: a view into a longer string sent the whole string, and an
  empty view's null data() made libwayland fail the display with EINVAL.
  It copies the view into a std::string, as SetAppId does.

Tests
- TextWin32: the constructor's title, SetTitle with characters outside
  the code page, with a view into a longer string and with an empty
  title, read back with GetWindowTextW; and é, €, 日 and a surrogate
  pair posted as WM_CHAR and delivered by StartSync's own loop, which
  returns on the WM_CLOSE posted after them, so a StartSync pumping with
  the A functions fails it. The window is disabled, so a real keystroke
  can't reach the check. Without the fix every check but the empty title
  fails. Like PointerWin32, built on Linux it runs under Wine.
- TitleWayland: SetTitle's set_title requests, read off a wl_display
  connected to a socketpair, for a view into a longer string and for an
  empty view. Both checks fail without the fix. No compositor.
- PointerWin32 pumps with the W functions, as StartSync now does.

Docs
- AGENTS.md names TextWin32 beside PointerWin32 as the Win32 tests, says
  what their Wine runs needed (the vulkan-1 import library, -luuid, which
  the link flags lack, #184, and a __libc_single_threaded definition),
  and lists TitleWayland among the tests that need no GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:28:16 +01:00
c62109caf3
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>
2026-10-31 03:29:50 +01:00