Skip to content

fix(prompts): find the prompts' font on native Windows, kept from its stream's start - #2583

Merged
NotRexed merged 1 commit into
NotRexed:mainfrom
polackangry:fix/prompts-font-at-stream-start
Nov 12, 2026
Merged

NotRexed merged 1 commit into
NotRexed:mainfrom
polackangry:fix/prompts-font-at-stream-start

Conversation

@polackangry

Copy link
Copy Markdown
Contributor

On native Windows the live button prompts (#2366) never find the font, so a change of face or of controls waits for a restart (seen in #2435's testing). The game keeps the font there from its stream's start with the stream's size the file's, and hos_prompts_glyph looked 8 bytes in, for a header that isn't there.

What it does

hos_prompts_glyph (host/prompts.c) takes a stream of the served font's size and looks for the font's head where it looked before (behind the 8-byte header when the size is the file's), then at the stream's start. Whichever holds the head is font.at. A face whose bytes aren't readable yet is looked at again, as before; one with neither head is another font, as before.

Why

A diagnostic build on native Windows 11 (RTX 5060 Ti, the v131072 game) logged each face FreeType draws from, its stream's base and size:

prompts-diag: face 0x107e504c00 stream 0x107e4f9370 base 0x7fd37800e8 size 55088 (want 55088 or 55080)
prompts-diag: face 0x107e504c00 has the size but not the head (00030030 vs 4f54544f)

The size is the served file's (55088), so the code read from base + 8. The bytes there, 00 03 00 30, are the OpenType header's own entrySelector (3) and rangeShift (48): the font starts at base, with no header in front. #2435's description and comment guessed a size mismatch; the size matches and the start is the difference.

Testing

In game, native Windows 11, RTX 5060 Ti, keyboard and mouse, driven with the control bridge (headless), a save in the Fire Temple:

Case Before After
prompts: the font at ... never logged prompts: the font at 0x7fd37800e8: the icons in texts follow at once
Keyboard → Xbox while the game runs (#2435's branch) held for a restart now the xbox icons in texts; the pause menu reopened shows LB/RB, A, B
Xbox → Keyboard while the game runs held for a restart now the keyboard icons in texts; Q/R, E, Shift again

Tests: test_prompts_live, test_prompt_font_bind, test_menu_hooks pass. The fix was tested on top of #2435's branch, the same function as on main.

🤖 Generated with Claude Code

… stream's start

On native Windows the face's stream has the served file's size but holds
the font from its start, with no 8-byte header in front. hos_prompts_glyph
read from base + 8, never matched the font's head, and the live button
prompts (NotRexed#2366) waited for a restart. Look for the head at both places.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NotRexed pushed a commit that referenced this pull request Nov 12, 2026
…texts, hints and menus (#2435)

Players on the keyboard and mouse see a controller's buttons in every
prompt (the Switch's when no controller is connected), so they have to
work out which key each one means. This adds a **Keyboard** face to the
Button prompts system: the game's texts, hints and menus show the key or
mouse button each action is bound to.

Part of #1007 (keyboard button prompts, from #2028).

## What it does

- **Button prompts → Keyboard** (a seventh value). **Auto** picks it
when no controller is connected, through SDL or XInput; with one
connected, Auto picks that controller's face as before.
- Each Switch button shows the first key bound to it on the Controls
page, or the second when the first slot is clear. Mouse buttons show as
a mouse with that button lit, and keys named by words (Ctrl, Alt, Esc,
Home, PgUp, Num1...) as wider keys set in the game's own bold font.
Kenney's versions of those are too small to read at prompt size.
- The sticks' icons (Move, Change View and their direction variants)
show **WASD** (or W/A/S/D, AD, WS) and the mouse moving.
- It follows the live system from #2366: changing a key, or switching to
or from Keyboard, shows at once, like a pad rebind.
- Kept as the Switch's: the textures (ocarina notes, the letters on
black, the pause menu's controller picture) and the Talk ring's places.
The ocarina hint is spaced by the keys' icon widths.

## How

- `nx2/prompt_keys.py` (new, data only, standard library): which icon
each key and stick shows, and the code points.
- `nx2/prompt_draw.py`: the keyboard face's glyphs. They are Kenney's
Input Prompts **Keyboard & Mouse** font (CC0, the same 1.5 release as
the controller faces), in its own folder
`third_party/kenney_input_keyboard` with a `PROVENANCE.md` and the
release's `License.txt`; the AppImage copies it. The word keys and the
mice are drawn as non-crossing contours, so they fill the same under
either winding rule.
- `nx2/prompt_assets.py`: `faces_font` builds the keyboard face (the
Switch's pad icons, plus each key's icon at `KEY_BASE + key code`, past
every face's code points), and writes its `hint.bin` per key code.
- `host/prompts.c`:
- The bind arrays carry each button's first key after its pad input
(`BINDS`), so key changes go through the same live path.
  - `cmap4_bind` points a button at its key's icon on the keyboard face.
- `text_code` gives a text a code of its own for each (button, key) pair
as it first appears (`key_slot`), and points it at the key's icon where
the game keeps the font.
- Code points: the per-pair codes come from 320 codes at U+EEC0–U+EFFF,
and the sticks' codes are at U+F8C0–U+F8DF. I checked every font in the
romfs: none uses either range. The CJK fonts use most of the private use
area, including parts of U+F700–U+F7BF.
- `host/nn_hid.c`: `hos_xinput_pads()`, so Auto doesn't pick Keyboard
for an XInput-only controller.
- `gpu/vk/vk_menu.cpp`: the Keyboard value and its hint; `pad_name`
names pad inputs by place on the keyboard face.

## Testing

**In game, a build of this branch (81a5445, the same code before the
rebases onto #2448 and #1990)** on Windows 11 (native, RTX 4060),
keyboard and mouse, no controller. Driven with the control bridge
(`HOS_CONTROL`, headless, `--user` on a copy of the saves), into Hyrule
Field from a save, then the pause menu:

| Case | Result |
|---|---|
| Auto, no controller | ✅ `prompts: auto: controller "(none)":
keyboard`, `keyboard icons, 16 buttons on these keys`. The pause menu
shows Q/R on the tabs, the mouse for Rotate, E for Select, Shift for
Back; the HUD's item buttons show R and X. |
| Keyboard picked, a key rebound (A from E to F) | ✅ Select reads F. |
| Xbox picked (a pad face, unchanged by this PR) | ✅ LB/RB, Select on B
and Back on A (the pad layout in use); the ocarina hint spread for
Xbox's icons. |
| A change while the game runs (Keyboard ↔ Xbox) | ✅ with #2583: on
native Windows #2366's live path never found the font (it read it 8
bytes past its start), with or without the keyboard face. @polackangry
found it and fixed it in #2583, and with that on top of this branch,
Keyboard → Xbox → Keyboard while the game runs applies at once (`now the
xbox icons in texts` / `now the keyboard icons in texts`), the pause
menu reopened showing each face's icons. Without #2583 the row says
"(restart)" on native Windows, as for every face. |
| A pad plugged in and out, a key rebound with a hint on screen | ⚠️ Not
tested: no controller here, and both go through the live path. |

Also:
- `tests/test_prompt_font_bind.py` is extended. On the keyboard face it
checks: keys bound in the first or second slot, a key with no icon, the
sticks, and text codes for each (button, key) pair. The other faces are
unchanged. It passes, as do `prompts_live`, `appimage_prompt_fonts` and
`menu_hooks`.
- `tests/run_all.py`: the same results with and without the PR on this
machine (its other failures are missing .NET and MSVC-target C++
headers).

## Screenshots

From the build of this branch, 1280×720 (the bridge's shots: the game's
own picture).

Auto with no controller, the pause menu: Q/R tabs, the mouse for Rotate,
E for Select, Shift for Back.

![Auto, no controller: the pause menu with keyboard
prompts](https://raw.githubusercontent.com/Needicla/Hookshot/pr-2435-screenshots/screenshots/branch-auto-pause.png)

Keyboard with A rebound to F: Select reads F.

![Keyboard face, A on F: Select reads
F](https://raw.githubusercontent.com/Needicla/Hookshot/pr-2435-screenshots/screenshots/branch-keyboard-rebound-pause.png)

Xbox, for comparison (a pad face, as before this PR).

![Xbox face: LB/RB, B and
A](https://raw.githubusercontent.com/Needicla/Hookshot/pr-2435-screenshots/screenshots/branch-xbox-pause.png)

The field: the HUD's item buttons on R and X.

![Hyrule Field: the item buttons on R and
X](https://raw.githubusercontent.com/Needicla/Hookshot/pr-2435-screenshots/screenshots/branch-field-hud.png)

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

---------

Co-authored-by: Needicla <226049938+Needicla@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@NotRexed
NotRexed merged commit 7bd6076 into NotRexed:main Nov 12, 2026
1 check passed
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