Skip to content

feat(menu): Esc opens the Insert menu by default (Menu shortcut "Insert, F1 or Esc") - #2197

Merged
hckrman101 merged 6 commits into
NotRexed:mainfrom
pmwork-personal:feat/menu-esc-default
Nov 11, 2026
Merged

hckrman101 merged 6 commits into
NotRexed:mainfrom
pmwork-personal:feat/menu-esc-default

Conversation

@pmwork-personal

Copy link
Copy Markdown
Contributor

On PC, Esc is the key that opens a game's menu, and players expect it: after #1998 added Esc as an opt-in Menu shortcut
choice, null asked in #dev-work why Esc closes the Insert menu but doesn't open it. This makes that choice the default,
for existing players too.

What changed

  • gpu/vk/vk_menu.h: menu_shortcut defaults to 2, Insert, F1 or Esc. On a closed menu Esc opens it; while it is
    open Esc still goes back a page or closes it. F1 always works, and the row still offers Insert or F1 and F1 only.
  • Existing options move too (options_version 5). null is an existing player, and so is nearly everyone who would
    notice this. The menu writes every row on save, so the old default, menu_shortcut=0 (Insert or F1), is in every
    options file whether the player chose it or not. A default that only new installs get wouldn't reach them. Like Game
    speed (version 2) and Descriptor reuse (version 4), menu_init now moves an older file's 0 to 2. Only 0 moves:
    • Insert and F1 still open the menu after the move, so nothing is taken away; Esc is added.
    • F1 only (1) can only have been chosen on purpose, so it stays. 2 stays.
    • save() writes options_version=5, so a player who picks Insert or F1 from now on keeps it.
    • feat(vk): AMD FSR 3.1 as the default Upscaler without matrix hardware #2110 (FSR 3.1 as the default Upscaler without matrix hardware) also takes version 5 for its own move. Whichever
      lands second takes 6; it's a one-line renumber plus its test, and I'll do it here if this one is second.
  • Play is unchanged. Esc still lets a captured mouse go first, and the menu it opens gives the capture back when it
    closes (esc_had_capture, feat(menu): Esc can open the Insert menu (Menu shortcut "Insert, F1 or Esc") #1998 and fix(menu): keep the mouse free after Escape then F1 #2035, unchanged here). No game control can be bound to Esc (bindable_keys,
    host/nn_hid.c defaults), and a gamepad's back button counts as Esc only while the menu is open.
  • readme.md: the menu keys paragraph (with the move of older options), and the Menu shortcut row's values and default
    in the options table.
  • tests/test_menu_keys.py:
    • It compiles menu_tick's Esc block with the real Options from vk_menu.h and the row's names
      (tx_menu_shortcut). Out of the box the shortcut is "Insert, F1 or Esc", and Esc opens a closed menu without the same
      press going back a page. With Insert or F1 or F1 only, Esc leaves it closed. Both polled and queued presses.
    • It also compiles menu_init's reading of hookshot_options.ini and loads real files through it. From before
      version 5 (versions 2 to 4, or no options_version line): 0 loads as 2, 1 and 2 stay, no line gets 2. At the
      version save() writes now, read from save() itself: 0, 1 and 2 all stay.
    • This replaces the text check that pinned the old default (int menu_shortcut = 0;).

Testing

  • tests/test_menu_keys.py passes, and each part fails without its change:
    • default put back to 0: the "Insert, F1 or Esc" check fails;
    • migration line removed: loaded_as(0, old) == 2 fails;
    • save() left writing options_version=4: loaded_as(mode, 4) == mode fails (a choice made after the change would
      be moved again).
  • The other tests that read the menu's sources, its options or the window's Esc handling pass: test_menu_env_on,
    test_audio_output_menu, test_framegen_preset, test_rumble_menu, test_vsync_row, test_audio_volume,
    test_menu_size, test_stick_dead_zone, test_menu_hit, test_menu_restart, test_menu_restart_slots,
    test_gpu_choice, test_display_pointer, test_screensaver.
  • tests/run_all.py on Windows (llvm-mingw), on main e19d969 with this change: 341 passed, 2 failed, 30 skipped.
    The two failures fail the same way on untouched main on this PC: test_precompile_compute_guard (this toolchain has no
    windows.h for its fixture) and test_recorder_sync (its exit case; its vk_menu.cpp source checks pass).

Not tested: in game (no build of this branch was run; the Esc behaviour itself is #1998's, which was played),
Linux.

Screenshots: nothing new on screen. The row and its values are #1998's; it now starts on "Insert, F1 or Esc".

🤖 Generated with Claude Code

…rt, F1 or Esc")

On PC, Esc is the key that opens a game's menu, and players expect it:
after NotRexed#1998 added Esc as an opt-in Menu shortcut choice, a player asked
in #dev-work why Esc closes the Insert menu but does not open it. The
opt-in choice is now the default, for existing players too.

- gpu/vk/vk_menu.h: menu_shortcut defaults to 2, "Insert, F1 or Esc".
- gpu/vk/vk_menu.cpp: options_version 5. The menu writes every row, so
  a file from before has menu_shortcut=0, the old default, whether it
  was chosen or not; menu_init moves that 0 to 2, as for Game speed (2)
  and Descriptor reuse (4). Insert and F1 still open the menu after the
  move, so nothing is taken away. F1 only (1) can only have been chosen
  and stays; 2 stays. A file saved from now on keeps any choice,
  Insert or F1 included.
- Esc still lets a captured mouse go first, and the menu it opens gives
  the capture back when it closes (NotRexed#1998, NotRexed#2035; unchanged). No control
  can be bound to Esc, so nothing in play changes.
- readme.md: the menu's keys, the move of older options, and the Menu
  shortcut row's values and default in the options table.
- tests/test_menu_keys.py: compiles menu_tick's Esc block with the real
  Options and the row's names. Out of the box the shortcut is "Insert,
  F1 or Esc" and Esc opens a closed menu without the same press going
  back a page. It also compiles menu_init's reading of the options file
  and loads files through it: before options_version 5, 0 loads as 2,
  1 and 2 stay; at the version save() writes now, every choice stays.
  Replaces the text check that pinned the old default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hckrman101

Copy link
Copy Markdown
Collaborator

I think someone is trying to get normal mouse/keyboard working, so Esc should probably open the in-game menu in the future

@hckrman101

Copy link
Copy Markdown
Collaborator

Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? Thanks!

@pmwork-personal

Copy link
Copy Markdown
Contributor Author

Merged main in to clear the conflict with #2181 (no force-push). The FPS counter rows from #2181 are kept as they are, and the Menu shortcut default is 2 again on top. tests/run_all.py on Windows: 342 passed, 30 skipped, 2 failed (test_recorder_sync and test_precompile_compute_guard, which also fail on untouched main here).

On Esc and keyboard play: agreed. Once the game takes keyboard and mouse input, Esc should open the game's own menu. Then the default can move again with the next options_version, the same way this PR moves it. Until then Esc does nothing in game, so opening the Insert menu with it is what players asked for.

(Still open: #2110 also writes options_version 5. Whichever lands second takes 6.)

pmwork-personal and others added 2 commits November 11, 2026 11:27
Windows CI's clang uses the MSVC CRT, which marks fopen deprecated; with
-Werror the fixture's options-file stub and the cut-out load did not build.
The same define the other source-reading tests and the game's own Windows
build pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. The migration is the right shape: only a pre-version-5 menu_shortcut=0 moves to 2 (gpu/vk/vk_menu.cpp:2956), F1 only is left alone, and save() writes options_version=5 (gpu/vk/vk_menu.cpp:1374), so a player who picks Insert or F1 again keeps it. I checked the places where a new default for Esc could leak into play:

  • The keyboard is polled only while our window has the focus (mine in menu_tick), so Esc pressed in another app doesn't open the menu.
  • The right mouse button and the gamepad's back button only count as Esc while the menu is open (press[5] from the pointer is read only under open_now, and pad_back likewise), so a right click or B in game doesn't open it.
  • esc_opens clears press[5], so the press that opens the menu doesn't also go back a page.

The rewritten tests/test_menu_keys.py is stronger than the text check it replaces: it compiles the real Esc block and menu_init's reader against real files. I ran it at the PR's head, together with test_menu_env_on, test_menu_restart, test_menu_restart_slots and test_display_pointer: all pass. The branch merges cleanly onto current main (still at options_version=4). The two red CI jobs are unrelated: test_native_astc_texture_count.py on Windows, fixed on main by #2397, and a Launchpad PPA timeout in appimage. A re-run or a merge of main should clear them.

One thing to keep in mind rather than change:

  • minor #2110 is still open and also claims options_version 5. Whichever merges second has to renumber both its save() line and its version < N check (and its test's expected version), or the second migration will never run for files the first one already rewrote. You've already said you'll take 6 if this one lands second; just flagging it so the maintainer merging the second one checks it.

pmwork-personal and others added 2 commits November 11, 2026 15:54
# Conflicts:
#	gpu/vk/vk_menu.h

vk_menu.h: menu_shortcut stays 2 beside main's new compact_menu_key (NotRexed#2378).
tests/test_menu_keys.py: the fixture stubs tune_mode and tune_down, which
menu_tick's Esc block now sets (NotRexed#2378), and has_vram_budget and
has_memory_budget, which menu_init's options loader now reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and doc fixes

A self-review of the PR's diff found that the test checked only versions
2 to 4: a later bump of save()'s version without menu_init's threshold
would still pass. It now loads every version below the one save() writes.

- tests/test_menu_keys.py: every older options_version, and Esc with the
  compact menu key held opens the compact bar, as Insert and F1 do (NotRexed#2378).
- readme.md: Esc goes back or closes with every Menu shortcut; the
  choices cell uses semicolons, as one name has a comma.
- docs/development.md: save() writes options_version=5.
- vk_menu.cpp: the Esc comment names RX_VK_MENU_SCRIPT's ESC and the
  compact bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pmwork-personal

Copy link
Copy Markdown
Contributor Author

Merged main: the vk_menu.h conflict with #2378's compact_menu_key is cleared; Menu shortcut stays "Insert, F1 or Esc" by default, with the options_version=5 move of older files.

@hckrman101
hckrman101 merged commit 06c30bd into NotRexed:main Nov 11, 2026
10 checks passed
pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
NotRexed#2197 (Esc opens the menu by default) took options_version 5 on main, so the
Aspect ratio's move of an older file's 16:9 is now version 6: save() writes 6,
menu_init and host/hos_wide_size.h hos_aspect_on move ultrawide=0 from files
before 6, and both moves are kept in menu_init.

tests/test_menu_keys.py (from NotRexed#2197) expected every version below the one
save() writes to move Insert or F1 to Insert, F1 or Esc, which would move a
version 5 file's chosen Insert or F1 again. It now reads the move's version
from menu_init and checks that files from it up to save()'s version keep their
choice; its host stub gains aspect_ratio, which menu_init's Aspect ratio move
asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conflict is one line (vk_menu.h, main's new compact_menu_key). After resolving it, the rewritten tests/test_menu_keys.py doesn't compile against main, because menu_tick/menu_init now use tune_mode, tune_down, has_memory_budget and has_vram_budget, which the fixture doesn't stub. The red checks are main's old ASTC failure and a PPA timeout.

options_version=5 is also used by #2119 and #2110. Suggested order: this one, then #2177, then #2119 rebased, with later ones renumbering their save() line, version < N check and tests.

hckrman101 added a commit that referenced this pull request Nov 11, 2026
…ape by default (#2119)

A player on an ultrawide, 16:10 or 4:3 screen starts with the game's
16:9 picture until they find the Graphics tab's **Ultrawide** row,
change it and restart. The name also hides that the same row shapes the
picture on screens narrower than 16:9. PC games usually follow the
monitor's shape from the first start, with 16:9 as a choice.

**What changed**
- The **Ultrawide** row is now **Aspect ratio**, and **Monitor's shape**
is its default (`gpu/vk/vk_menu.h`; `host/hos_main.c` reads an options
file with no `ultrawide=` line as Monitor's shape). 16:9 stays a choice.
On a 16:9 screen nothing changes.
- The saved key stays `ultrawide=` (`ini_key`), so a saved `ultrawide=0`
still means 16:9.
- **Older options files on a screen narrower than 16:9.** The game saves
every row, so a file from before this PR holds
`ultrawide=0`, the old default, whether chosen or not. On a screen
narrower than 16:9 (16:10 such as the Steam Deck's
1280x800, or 4:3), where 16:9 only adds bars above and below, that
file's 16:9 moves to Monitor's shape; an ultrawide keeps
its 16:9 (often a real choice there). The rule is one function,
`hos_aspect_on` in `host/hos_wide_size.h`: `host/hos_main.c`
starts the game with it, and the menu's row follows what the host
started with (`host->aspect_ratio()`, in `menu_init`
before `restart_init`), so the two agree on Linux too, where the menu
has no screen size of its own. `save()` writes
  `options_version=6`, so a 16:9 chosen after the update stays.
- The other value reads **16:9** again instead of #2078's "Off (16:9)".
#2078 added "Off" because "Ultrawide: 16:9" read as if ultrawide were
already on; under the name Aspect ratio the value is a shape, and
"Aspect ratio: Off (16:9)" would not read well. Happy to keep "Off
(16:9)" if you prefer it.
- `recomplauncher.json`: the same label and help, and its default is
`1`, so the launcher does not write 16:9 over the new default.
- New help text for the row, and for **Ultrawide menus**, whose text
named the old row. The readme's settings table and Output resolution
notes, `menu_restart.h` and the comments that named the row follow
(#2052's wording kept, with the new name).
- Tests: `test_menu_restart.py` and `test_menu_restart.cpp` use the new
name; `test_audio_output_menu.py` and `test_stick_dead_zone.py` give
their option stubs the `ultrawide` field that `ini_key` now reads. New
`test_aspect_default.py`: `hos_aspect_on` over 16:10, 4:3, 5:4, 16:9,
1366x768, 21:9, 32:9, portrait and no screen size, plus the host and
menu source checks.

**Tested**
- In game on Windows 11, RTX 5090 (driver 621.14), 5120x2160 (21:9)
monitor, in one build with the widescreen cap change from #2120: the
picture takes the monitor's shape, and Kokiri Forest and Castle Town
load. That run was before the merge with current main (#2052, #2041,
#2078). This PR only moves a default and renames a row; the code that
sizes the picture is unchanged.
- `tests/run_all.py` on Windows (llvm-mingw) after merging main at
06c30bd (with #2197): 392 passed, 31 skipped, 1 failed:
`test_vram_budget_row`, which fails the same way on main's own sources
on this PC (clang stops on the unused `kVramMinimumOnly`).
- A build of this branch compiled (`hos_main.c`, `vk_menu.cpp`) before
that merge; the merge itself only adds the version 6 move next to
#2197's. The move for older files was not run in game: this PC has a
21:9 screen, where an older 16:9 stays. The tests this PR touches pass,
and so do the others that build `ini_key` or the row:
`test_framegen_preset`, `test_menu_env_on`, `test_recomplauncher_json`,
`test_rumble_menu`, `test_upscaler_rows`, `test_vsync_row`,
`test_output_resolution`, `test_widescreen_shapes`.

**Not tested**
- 16:10, 4:3, 32:9 and 48:9 screens in game (`test_widescreen_shapes.py`
covers their sizes).
- Linux and the Steam Deck: SDL gives the screen's size there too, so a
Steam Deck (16:10) now starts with a 16:10 picture instead of 16:9.
- If #1864's translations name the row, they would need the new name.

**Screenshots**

From a headless run of this branch (head 2a74ec4) on Windows 11, RTX
5090, with a 5120x2160 (21:9) monitor and no options file, so a first
start (`HOS_HEADLESS=1`, `RX_VK_SHOTS` at 2560x1080; the save loaded
with `HOS_INPUT_SCRIPT` presses). The log: `widescreen: picture
3416x1440, scene 2560x1080, camera ratio 2.3722`.

![The Graphics tab: Aspect ratio at Monitor's shape, the default,
selected with its
help](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/aspect-2119/aspect-ratio-row.webp)

![First start on the 21:9 monitor: Kokiri Forest at the monitor's shape,
the HUD in the middle
16:9](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/aspect-2119/first-start-21x9-kokiri.webp)

_The menu shot is the middle 16:9 of the frame: behind it the title
movie stays 16:9 with black sides. Its build had one extra line so the
list scrolls headless (`ease_step = 1.0f` in `menu_record_image`), not
part of this PR._

The widescreen cap PR (#2120) edited the same readme paragraph; it has
merged, and the paragraph is merged with it here.

**`options_version`:** #2197 (merged) took 5 for the Esc default, so
this writes 6 and moves an older file's 16:9 from files
before 6 (`version < 6` in `hos_aspect_on` and `menu_init`). #2110 also
writes 5 today; whichever of the two lands later
takes the next number. #2197's `test_menu_keys.py` compiles
`menu_init`'s options read, which now also reads
`host->aspect_ratio`, so its `host` stub gains that member. Its check of
older files expected every version below the one
`save()` writes to move Insert or F1 to Insert, F1 or Esc, which would
move a version 5 file's chosen Insert or F1 again; it
now reads the move's version from `menu_init` and checks that files from
it up to `save()`'s version keep their choice.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: hckrman101 <239247847+hckrman101@users.noreply.github.com>
pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
…2119, NotRexed#2197, NotRexed#2481, NotRexed#2496)

NotRexed#2177 landed (squashed), so this takes main's sections as they shipped and
keeps this PR's change on top: the Screen, HDR and Overlays rows on the
Display tab. Main's newer rows go with them, word for word: NotRexed#2119's Aspect
ratio and Ultrawide menus text and NotRexed#2481's HDR tone mapper (last in Display's
HDR). Every one of main's 88 rows keeps main's definition; the only changes
are the moved rows' tab and the Overlays heading. draw_options keeps main's
Save my pipeline list line (NotRexed#2447) with the restart line on Display too. The
readme takes main's Menu shortcut text (NotRexed#2197) with six tabs, NotRexed#2177's VRAM
budget line, and main's HDR tone mapper and Field of view lines.
test_menu_sections.py pins Aspect ratio by name and HDR tone mapper in
Display's HDR; test_scene_tonemap_pass.py (NotRexed#2481) finds the tone mapper row on Display.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pmwork-personal added a commit to pmwork-personal/Hookshot-pr that referenced this pull request Nov 11, 2026
NotRexed#2197, NotRexed#2481, NotRexed#2496) into feat/menu-game-tab

Takes NotRexed#2178 as merged with main (cf90c14f) and keeps this PR's change on
top: the Game tab first, where the menu opens. Main's new Gameplay row,
Field of view, goes with the Game rows at the table's top, word for word,
after Game speed. Every one of main's 88 rows keeps main's definition; the
only changes are the moved rows' tab (Display) and the Overlays heading.
The readme takes main's Menu shortcut text (NotRexed#2197) with this PR's tab
sentence, and Field of view among Game's rows. The tab enum, the strip's
titles and the control bridge's names follow the same Game-first order.

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.

3 participants