feat(menu): the Aspect ratio row (was Ultrawide), at the monitor's shape by default #2119

Merged
pmwork-personal merged 6 commits from feat/aspect-monitor-default into main 2026-11-11 22:21:06 +00:00
pmwork-personal commented 2026-11-11 03:45:37 +00:00 (Migrated from github.com)

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. Not in this PR: skipping the move in runs with HOS_ASPECT set (Joey's point)
    came after the merge and follows in its own PR.
  • 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.

For the release notes (this changes a default for everyone, and only a 21:9 PC ran it):

The Graphics tab's Ultrawide row is now Aspect ratio, and it follows your monitor's shape by default: wider on an ultrawide, narrower on 16:10 (the Steam Deck) or 4:3. On a screen narrower than 16:9, an options file from an earlier version also moves from 16:9 to the monitor's shape; set Aspect ratio to 16:9 to get the old picture back. On an ultrawide, an options file from an earlier version keeps the shape it saved.

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 06c30bdc (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 2a74ec4d) 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

First start on the 21:9 monitor: Kokiri Forest at the monitor's shape, the HUD in the middle 16:9

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

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. Not in this PR: skipping the move in runs with `HOS_ASPECT` set (Joey's point) came after the merge and follows in its own PR. - 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. **For the release notes** (this changes a default for everyone, and only a 21:9 PC ran it): > The Graphics tab's **Ultrawide** row is now **Aspect ratio**, and it follows your monitor's shape by default: wider on an ultrawide, narrower on 16:10 (the Steam Deck) or 4:3. On a screen narrower than 16:9, an options file from an earlier version also moves from 16:9 to the monitor's shape; set Aspect ratio to 16:9 to get the old picture back. On an ultrawide, an options file from an earlier version keeps the shape it saved. **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 06c30bdc (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 2a74ec4d) 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)
Joey-Magnus (Migrated from github.com) approved these changes 2026-11-11 20:06:38 +00:00
Joey-Magnus (Migrated from github.com) left a comment

This is a good change, and it's well-contained. The host and the menu share one rule (hos_aspect_on), so they can't disagree. The saved key stays ultrawide=, so existing files keep working. The migration only moves the old default where 16:9 can only add bars. host->aspect_ratio was already in MkwVkHost, so no API bump is needed. On Linux, GetSystemMetrics gets SDL's screen size, which display_sdl.c sets before game_main, so the host side works there too. The merge with main is clean. The one red CI job (Windows part 2/2) is tests/test_native_astc_texture_count.py, which fails the same way on other PRs today and isn't related.

should fix: screenshots. The Graphics tab's row is renamed and the default picture changes shape on non-16:9 screens. CONTRIBUTING asks for screenshots when a change shows on screen. A shot of the renamed row, and the first-start picture on the 21:9 screen you tested, would cover it.

minor gpu/vk/vk_menu.cpp:2959-2962 (at the PR's head): menu_init infers "the host started this older file at the monitor's shape" from host->aspect_ratio() alone. That value is also set when HOS_ASPECT is given (host/hos_main.c uses it instead of the file). With an older file holding ultrawide=0, running once with, say, HOS_ASPECT=1.6 flips the row to Monitor's shape, and the next save() writes ultrawide=1 with options_version=5. That stores a choice the player never made. It's rare (a debugging switch), but an easy fix is to skip the move when getenv("HOS_ASPECT") is set, which is what hos_main.c checks.

minor: as you note, #2197 and #2110 also claim options_version=5. Whichever lands second has to bump the number and its version < N checks (here in hos_aspect_on and menu_init). It might be worth a line in the save() comment so the next person sees all three migrations. Steam Deck players with an existing options file will see the picture change to 16:10 after this update without choosing it. That is the intent, but it should go in the changelog/release notes, since the Deck wasn't run.

This is a good change, and it's well-contained. The host and the menu share one rule (`hos_aspect_on`), so they can't disagree. The saved key stays `ultrawide=`, so existing files keep working. The migration only moves the old default where 16:9 can only add bars. `host->aspect_ratio` was already in `MkwVkHost`, so no API bump is needed. On Linux, `GetSystemMetrics` gets SDL's screen size, which `display_sdl.c` sets before `game_main`, so the host side works there too. The merge with main is clean. The one red CI job (Windows part 2/2) is `tests/test_native_astc_texture_count.py`, which fails the same way on other PRs today and isn't related. **should fix**: screenshots. The Graphics tab's row is renamed and the default picture changes shape on non-16:9 screens. CONTRIBUTING asks for screenshots when a change shows on screen. A shot of the renamed row, and the first-start picture on the 21:9 screen you tested, would cover it. **minor** `gpu/vk/vk_menu.cpp:2959-2962` (at the PR's head): `menu_init` infers "the host started this older file at the monitor's shape" from `host->aspect_ratio()` alone. That value is also set when `HOS_ASPECT` is given (`host/hos_main.c` uses it instead of the file). With an older file holding `ultrawide=0`, running once with, say, `HOS_ASPECT=1.6` flips the row to Monitor's shape, and the next `save()` writes `ultrawide=1` with `options_version=5`. That stores a choice the player never made. It's rare (a debugging switch), but an easy fix is to skip the move when `getenv("HOS_ASPECT")` is set, which is what `hos_main.c` checks. **minor**: as you note, #2197 and #2110 also claim `options_version=5`. Whichever lands second has to bump the number and its `version < N` checks (here in `hos_aspect_on` and `menu_init`). It might be worth a line in the `save()` comment so the next person sees all three migrations. Steam Deck players with an existing options file will see the picture change to 16:10 after this update without choosing it. That is the intent, but it should go in the changelog/release notes, since the Deck wasn't run.
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:26:02 +00:00
Joey-Magnus (Migrated from github.com) left a comment
  • This changes a default for everyone: new players, and Steam Deck or 16:10 players with an older options file, get the monitor's shape (hos_aspect_on). It's only tested on one 21:9 machine, so it belongs in the release notes.
  • The earlier HOS_ASPECT point is still open: the migration at vk_menu.cpp:3121 trusts host->aspect_ratio() even when HOS_ASPECT set it, so a debug run can save ultrawide=1 with options_version=5.
  • Screenshots are still missing.
  • options_version=5 is also used by #2197 and #2110, and this PR conflicts with #2177 over the Ultrawide row. Suggested order: #2197, then #2177, then this rebased.
- This changes a default for everyone: new players, and Steam Deck or 16:10 players with an older options file, get the monitor's shape (`hos_aspect_on`). It's only tested on one 21:9 machine, so it belongs in the release notes. - The earlier `HOS_ASPECT` point is still open: the migration at `vk_menu.cpp:3121` trusts `host->aspect_ratio()` even when `HOS_ASPECT` set it, so a debug run can save `ultrawide=1` with `options_version=5`. - Screenshots are still missing. - `options_version=5` is also used by #2197 and #2110, and this PR conflicts with #2177 over the Ultrawide row. Suggested order: #2197, then #2177, then this rebased.
hckrman101 (Migrated from github.com) approved these changes 2026-11-11 22:18:24 +00:00
hckrman101 (Migrated from github.com) left a comment

Looks good to me. The default flips to the monitor's shape, the saved key stays ultrawide=, and the older-file move is one function (hos_aspect_on) that the host and the menu both follow, so they can't disagree. I read the diff and the tests: test_aspect_default.py covers the shapes (16:10, 4:3, 5:4, 1366x768, ultrawides, portrait, no screen size), test_menu_keys.py now reads the old-shortcut version from menu_init so the version 6 bump doesn't move a chosen Insert/F1 again, and the ultrawide stubs in the other two tests match what ini_key now reads. save() writes 6, so a 16:9 chosen after the update stays. All CI jobs are green on this head.

Since Joey-Magnus's earlier reviews: the screenshots he asked for are in the description now (both links load), and the merge with main is clean. Still open, both small and neither a blocker:

  • nit: Joey's HOS_ASPECT point (vk_menu.cpp, the version < 6 block in menu_init) is not fixed. host->aspect_ratio() is also set by HOS_ASPECT and by a custom output wider than 32:9, so a run with one of those on an older ultrawide=0 file on a 16:10 screen can save ultrawide=1 the player did not pick. It's a debug switch and a rare setup, so fine to follow up, but skipping the move when getenv("HOS_ASPECT") is set would close it.
  • options_version=6 and the ordering with #2110 and #2177 are covered in the description; whichever lands later takes the next number. Because the default changes for everyone (Steam Deck and 16:10 players included, untested on a real 16:10 screen), it belongs in the release notes.

The decision to merge is yours (and NotRexed's, as maintainer); nothing here objects.

Looks good to me. The default flips to the monitor's shape, the saved key stays `ultrawide=`, and the older-file move is one function (`hos_aspect_on`) that the host and the menu both follow, so they can't disagree. I read the diff and the tests: `test_aspect_default.py` covers the shapes (16:10, 4:3, 5:4, 1366x768, ultrawides, portrait, no screen size), `test_menu_keys.py` now reads the old-shortcut version from `menu_init` so the version 6 bump doesn't move a chosen Insert/F1 again, and the `ultrawide` stubs in the other two tests match what `ini_key` now reads. `save()` writes 6, so a 16:9 chosen after the update stays. All CI jobs are green on this head. Since Joey-Magnus's earlier reviews: the screenshots he asked for are in the description now (both links load), and the merge with main is clean. Still open, both small and neither a blocker: - nit: Joey's `HOS_ASPECT` point (`vk_menu.cpp`, the `version < 6` block in `menu_init`) is not fixed. `host->aspect_ratio()` is also set by `HOS_ASPECT` and by a custom output wider than 32:9, so a run with one of those on an older `ultrawide=0` file on a 16:10 screen can save `ultrawide=1` the player did not pick. It's a debug switch and a rare setup, so fine to follow up, but skipping the move when `getenv("HOS_ASPECT")` is set would close it. - `options_version=6` and the ordering with #2110 and #2177 are covered in the description; whichever lands later takes the next number. Because the default changes for everyone (Steam Deck and 16:10 players included, untested on a real 16:10 screen), it belongs in the release notes. The decision to merge is yours (and NotRexed's, as maintainer); nothing here objects.
pmwork-personal commented 2026-11-11 22:40:53 +00:00 (Migrated from github.com)

Thanks for the reviews and the merge. The HOS_ASPECT guard landed on the branch right after the merge, so it is its own PR now: #2541. menu_init skips the move when HOS_ASPECT is set, as hos_main.c does, and test_aspect_default.py pins it.

The description now has the screenshots (the renamed row, and a first start on the 21:9 monitor) and a suggested release-notes line for the default change, Steam Deck and 16:10 included.

Thanks for the reviews and the merge. The `HOS_ASPECT` guard landed on the branch right after the merge, so it is its own PR now: #2541. `menu_init` skips the move when `HOS_ASPECT` is set, as `hos_main.c` does, and `test_aspect_default.py` pins it. The description now has the screenshots (the renamed row, and a first start on the 21:9 monitor) and a suggested release-notes line for the default change, Steam Deck and 16:10 included.
Commenting is not possible because the repository is archived.
No description provided.