Skip to content

Insert menu: free the mouse while it's open, and make the menu clickable - #946

Merged
hckrman101 merged 2 commits into
NotRexed:mainfrom
hckrman101:menu-mouse
Nov 9, 2026
Merged

hckrman101 merged 2 commits into
NotRexed:mainfrom
hckrman101:menu-mouse

Conversation

@hckrman101

@hckrman101 hckrman101 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

While the in-game menu (Insert / F1) is open, the mouse stayed in the game's capture: hidden, relative, and still turning the camera. The menu could only be used with the keyboard or a gamepad.

With this change the mouse is released while the menu is open, and the menu can be used with it. When the menu closes, the capture goes back to what it was before (captured for mouse camera, or free if it was free).

The change

Mouse release (host/display.c on Windows, host/posix/display_sdl.c on Linux)

  • The menu tells the window when it opens or closes (WM_APP + 8, the same channel the Fullscreen row already uses with WM_APP + 7).
  • On open, the window remembers whether the mouse was captured, then releases it. On Linux that turns off SDL relative mode. On Windows it removes the ClipCursor and shows the cursor again.
  • While the menu is open, a click does not capture the mouse. Mouse motion and buttons never reach the game. hos_mouse_take reports the mouse as not captured, and nn_hid.c already ignores keys and mouse buttons while the menu is open.
  • On close, the capture comes back if the mouse was captured before and the window still has the focus.

Clicks (gpu/vk/vk_menu.cpp, new gpu/vk/vk_menu_hit.h)

  • A new host function, MkwVkHost.pointer, gives the renderer the free mouse: the cursor position in window pixels and the window's pixel size, the buttons held, the presses since the last read and the wheel notches. MKWVK_API_VERSION is bumped to 5.
    • SDL reports positions in window units. They are scaled to pixels with the window's pixel size, so HiDPI and fractional scaling work.
    • On Windows the window thread is already DPI-aware, so client coordinates are pixels.
  • The menu maps the cursor to the swapchain image, then to its own centred bitmap. That accounts for the menu's integer scale (1 at 720 lines, 2 at 1440).
  • vk_menu_hit.h holds the layout constants paint_menu now draws with and a pure hit-test, so the picture and the hit areas can't drift apart.
  • What the mouse does:
    • Hover: the row under the cursor becomes the selected row, so the help text follows. Hovered tabs and the [Q / LB] / [E / RB] hints get a lighter highlight. A cursor resting over the menu when it opens does not move the keyboard selection.
    • Tabs: clicking a tab shows it. Clicking [Q / LB] or [E / RB] goes to the previous or next tab.
    • Rows:
      • Clicking the left half of a value (the < side) steps it down. The right half (the > side) steps it up.
      • Toggles flip wherever they are clicked.
      • Clicking a row's name acts like Enter.
      • Clicking "Controls" opens its page.
    • Sliders: Sharpness and Mouse sensitivity now draw a small track with a knob between the name and the value. Click or drag on it to set the value. A drag is saved when the button is released.
    • Wheel:
      • Over a value or slider, it changes the value (a slider stops at its ends).
      • Over the tab strip, it changes tabs.
      • Anywhere else on the menu, it moves the selection.
    • Right click: acts as Esc (closes the menu, or goes back from the Controls page).
    • Controls page: hovering selects a row and column. Clicking a gamepad or keyboard cell starts listening for the new binding, like Enter. Clicking "Reset to defaults" resets the bindings.
  • Keyboard and gamepad input is unchanged. The value-stepping code moved into one set_row function that both use.

SDL wrapper (host/hos_sdl.c/.h): mouse button up, wheel and enter events, the button and position on button events, the absolute position on motion events (SDL3 and SDL2 layouts), and SDL_GetWindowSize.

Tests

  • New tests/test_menu_hit.py (registered in tests/run_all.py with --cxx g++). It compiles tests/test_menu_hit.cpp and checks the hit-test:

    • tabs, rows, value halves, slider positions and values, and the Controls page cells;
    • at scale 1 and scale 2;
    • through a 1280x720 window, a 1920x1080 window, and a window whose pixel size differs from the swapchain's.

    It also checks the sources: the menu hit-tests with the shared layout, posts open/close to the window, and both window procedures release the mouse, give it back and don't capture on a click while the menu is open.

  • python -I -B tests/run_all.py: 106 passed, 0 failed, 6 skipped (Linux).

  • Linux build (SDL3, Wayland): the game and hookshot_vk.so build with this change, and the ABI check passes (no undefined mkwvk symbols). The game started and presented normally at 60 fps.

  • A live mouse run (a headless compositor output, with clicks injected through the compositor's seat cursor) did not reach the menu. The injected keypress never opened the menu, and a later run was cut short. Since then it has been tried by hand on Linux (SDL3, Wayland) with a real mouse, in the merged build with the Cheats pages: the cursor appears when the menu opens, and hover, clicks, the sliders, the wheel and the capture coming back on close all work.

Not tested

  • Windows: the Win32 path in host/display.c is not built or run, because only Linux was available. It covers WM_MOUSEMOVE / WM_MOUSELEAVE tracking, button and wheel messages while the menu is open, SetCapture during a drag, and the release and restore of ClipCursor and ShowCursor. It is kept simple, but it needs a build and a try on Windows before merging.
  • SDL2: the SDL2 event offsets follow SDL2's struct layouts but were not run (the test machine loads SDL3).

Notes

…: hover selects, tabs, value halves, toggles, sliders (click or drag), the wheel; the capture comes back on close
@hckrman101
hckrman101 marked this pull request as ready for review November 9, 2026 02:43
@NotRexed

NotRexed commented Nov 9, 2026

Copy link
Copy Markdown
Owner

Thanks! This is a draft that hasn't been tried with a real mouse yet. Closing to keep the queue clear; please reopen once it has had that test.

@NotRexed NotRexed closed this Nov 9, 2026
@hckrman101 hckrman101 reopened this Nov 9, 2026
@hckrman101

Copy link
Copy Markdown
Collaborator Author

Reopened: this has now had the live test with a real mouse on Linux (hover, clicks, sliders, wheel, and the capture returning on close all work), and it's up to date with main. Windows still needs a try; the Win32 side is described under Not tested.

@hckrman101
hckrman101 merged commit d458f05 into NotRexed:main Nov 9, 2026
15 of 18 checks passed
AR51AurA added a commit to AR51AurA/Hookshot that referenced this pull request Nov 9, 2026
Conflicts with the Insert menu's mouse (NotRexed#946): the host table takes pointer, then mic_count and mic_name, in
the same order in host/rx_backend.c, MKWVK_API_VERSION 8; the menu's row changes go through NotRexed#946's set_row,
which keeps the Microphone row out of the quality preset as before.

Assisted-by: Claude Opus 5.5 (Anthropic)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NotRexed pushed a commit that referenced this pull request Nov 9, 2026
…m update list (#939); screensaver fixture follows #946

#939's precompile pool started building without ub_thread_off(), unlike every other background shader thread, so its
builds could race the replay's uniform updates (tests/test_ub_threads.py caught it). The screensaver fixture mocks the
mouse handling #946 added to the window procedure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants