Skip to content

Input hints: Display logical keys#2347

Merged
manuq merged 1 commit into
mainfrom
wjt/rewrite-input-hud-again
Jun 11, 2026
Merged

Input hints: Display logical keys#2347
manuq merged 1 commit into
mainfrom
wjt/rewrite-input-hud-again

Conversation

@wjt

@wjt wjt commented Jun 10, 2026

Copy link
Copy Markdown
Member

Our keybindings are, correctly, configured to use the physical location
of keys on the keyboard. This is because WASD are chosen to be the same
shape as the arrow keys, not because "W" means up; and so other bindings
such as Z and X must also be physical to avoid clashing.

However this means the labels are wrong for QWERTZ and AZERTY keyboards:

Action QWERTY QWERTZ AZERTY
Up W W Z
Left A A Q
Down S S S
Right D D D
Throw X X X
Repel Z Y W

We don't actually show WASD in the UI, but we do show Z for Repel (for
example).

Rather than hardcoding that the glyph for Repel is Z on a keyboard and
L1 on a gamepad, instead look these up dynamically.

Update to Kenney Input Prompts 1.5, carrying forward the change from
commit c17ffac to remove the solid grey
region in the top-right of the keyboard-and-mouse atlas. Apply the same
change to the joypad spritesheets: the Switch stick glyphs would have
the same problem, but we've never tested them!

Among other things, this update introduces a glyph for the "Pause" key
on a keyboard, which we do actually have a binding for (though it's not
the primary so is not shown). Only check in the five atlases sheets that
we actually use – keyboard, switch, steamdeck, xbox, playstation – along
with the accompanying XML file.

Change the button resources to be based on the Key/JoyButton/etc.
enums. Rewrite the importer accordingly to generate just one resource
per device type (which contains all the necessary AtlasTextures)
rather than hundreds of separate AtlasTexture resources. (It was my
idea to do the latter; I was wrong!)

Then in the input hint controls, determine which key/button the event is
bound to, and display that.

In theory this will mean that we never have to manually look up the
glyphs for a new action again: interact_input.gd will do that for us.

On the web, DisplayServer.keyboard_get_keycode_from_physical() is not
implemented: it is not possible to ask the browser for the logical
keycode corresponding to a physical keycode. So on the web platform we
always show 'Z' even on an AZERTY keyboard where it should be 'W'. A bit
sad but not a regression.

There is a web API for this, but it is not widely supported, is blocked
in some browsers for anti-fingerprinting reasons, and Godot doesn't
(yet) know how to use it.

https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap

I considered working around this limitation, either by calling
getLayoutMap directly, or by inferring the keyboard layout based on
the events we actually receive: i.e. if we receive an InputEventKey
whose keycode is 'z' but physical keycode is 'w', we can infer that the
layout is almost certainly AZERTY. This does work – which makes me think
that Brave blocking getLayoutMap() is probably not improving user
privacy! – but I don't think it's worth the complexity.

There are some other annoying edge cases to handle, including:

  • R2 is analogue so is represented in Godot as an axis that can never be
    negative, but we present it as a button.

  • The arrow keys want to be treated like a joystick in the UI.

  • We show mouse + keyboard controls for some actions, so we need to keep
    the keyboard vs mouse branching.

  • Sony helpfully relabelled the Start/Select buttons from PS3 (and
    older) to something else in PS4 and something else again in PS5. I
    chose the PS5 glyphs because they don't have any text on them. If we
    wanted to fix this in future, we could ask InputHelper for the
    "granular" identifier for the joypad and handle the three PlayStation
    generations separately.

Resolves #2185

@wjt

wjt commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Needs rebase; conflicts due to #2331.

Also needs testing on the web platform.

@wjt wjt changed the title Input hints: Display logical keys [1/1] Input hints: Display logical keys Jun 10, 2026
@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch 2 times, most recently from e6b4629 to f59fb44 Compare June 10, 2026 21:27
@wjt wjt changed the title [1/1] Input hints: Display logical keys Input hints: Display logical keys Jun 10, 2026
@github-actions

Copy link
Copy Markdown

Play this branch at https://play.threadbare.game/branches/endlessm/wjt/rewrite-input-hud-again/.

(This launches the game from the start, not directly at the change(s) in this pull request.)

@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch from f59fb44 to f26ff0e Compare June 10, 2026 21:30
Comment thread scenes/game_elements/props/hint/input_key/interact_input.gd Outdated
@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch from f26ff0e to 694c8a5 Compare June 10, 2026 21:38
@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch from 694c8a5 to e8f795b Compare June 11, 2026 12:18
@wjt wjt changed the title Input hints: Display logical keys [1/1] Input hints: Display logical keys Jun 11, 2026
@wjt wjt changed the title [1/1] Input hints: Display logical keys Input hints: Display logical keys Jun 11, 2026
@wjt wjt marked this pull request as ready for review June 11, 2026 12:24
@wjt wjt requested review from a team as code owners June 11, 2026 12:24
@wjt

wjt commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Play this branch at https://play.threadbare.game/branches/endlessm/wjt/rewrite-input-hud-again/.

Note that as explained in the commit message, the physical-to-logical mapping does not work on the web. So on the web we can only test for regressions.

On desktop, one must reconfigure one's keyboard to e.g. QWERTZ, AZERTY, Dvorak. I will attach some screenshots of the latter...

@wjt

wjt commented Jun 11, 2026

Copy link
Copy Markdown
Member Author
image

Well I'm glad I took a screenshot - that's a nasty black line in the mouse button glyph!

@wjt

wjt commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Sokoban on Dvorak:
image

@wjt

wjt commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Ah I remember now - in commit c17ffac the keyboard and mouse atlas was edited to remove the solid grey region directly above that mouse glyph.

I'm going to see if I can fix it in the shader. Update: I couldn't. It's easier to carry forward the change to the atlas!

@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch from e8f795b to d95ca0e Compare June 11, 2026 12:40
@wjt wjt changed the title Input hints: Display logical keys [1/1] Input hints: Display logical keys Jun 11, 2026
@wjt wjt changed the title [1/1] Input hints: Display logical keys Input hints: Display logical keys Jun 11, 2026
@wjt

wjt commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Dvorak pause menu:

image

Our keybindings are, correctly, configured to use the physical location
of keys on the keyboard. This is because WASD are chosen to be the same
shape as the arrow keys, not because "W" means up; and so other bindings
such as Z and X must also be physical to avoid clashing.

However this means the labels are wrong for QWERTZ and AZERTY keyboards:

| Action | QWERTY | QWERTZ | AZERTY |
| ------ | ------ | ------ | ------ |
| Up     | W      | W      | Z      |
| Left   | A      | A      | Q      |
| Down   | S      | S      | S      |
| Right  | D      | D      | D      |
| Throw  | X      | X      | X      |
| Repel  | Z      | Y      | W      |

We don't actually show WASD in the UI, but we do show Z for Repel (for
example).

Rather than hardcoding that the glyph for Repel is Z on a keyboard and
L1 on a gamepad, instead look these up dynamically.

Update to Kenney Input Prompts 1.5, carrying forward the change from
commit c17ffac to remove the solid grey
region in the top-right of the keyboard-and-mouse atlas. Apply the same
change to the joypad spritesheets: the Switch stick glyphs would have
the same problem, but we've never tested them!

Among other things, this update introduces a glyph for the "Pause" key
on a keyboard, which we do actually have a binding for (though it's not
the primary so is not shown). Only check in the five atlases sheets that
we actually use – keyboard, switch, steamdeck, xbox, playstation – along
with the accompanying XML file.

Change the button resources to be based on the `Key`/`JoyButton`/etc.
enums. Rewrite the importer accordingly to generate just one resource
per device type (which contains all the necessary `AtlasTexture`s)
rather than hundreds of separate `AtlasTexture` resources. (It was my
idea to do the latter; I was wrong!)

Then in the input hint controls, determine which key/button the event is
bound to, and display that.

In theory this will mean that we never have to manually look up the
glyphs for a new action again: interact_input.gd will do that for us.

On the web, `DisplayServer.keyboard_get_keycode_from_physical()` is not
implemented: it is not possible to ask the browser for the logical
keycode corresponding to a physical keycode. So on the web platform we
always show 'Z' even on an AZERTY keyboard where it should be 'W'. A bit
sad but not a regression.

There is a web API for this, but it is not widely supported, is blocked
in some browsers for anti-fingerprinting reasons, and Godot doesn't
(yet) know how to use it.

https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap

I considered working around this limitation, either by calling
`getLayoutMap` directly, or by inferring the keyboard layout based on
the events we actually receive: i.e. if we receive an InputEventKey
whose keycode is 'z' but physical keycode is 'w', we can infer that the
layout is almost certainly AZERTY. This does work – which makes me think
that Brave blocking `getLayoutMap()` is probably not improving user
privacy! – but I don't think it's worth the complexity.

There are some other annoying edge cases to handle, including:

- R2 is analogue so is represented in Godot as an axis that can never be
  negative, but we present it as a button.

- The arrow keys want to be treated like a joystick in the UI.

- We show mouse + keyboard controls for some actions, so we need to keep
  the keyboard vs mouse branching.

- Sony helpfully relabelled the Start/Select buttons from PS3 (and
  older) to something else in PS4 and something else again in PS5. I
  chose the PS5 glyphs because they don't have any text on them. If we
  wanted to fix this in future, we could ask InputHelper for the
  "granular" identifier for the joypad and handle the three PlayStation
  generations separately.

Resolves #2185
@wjt wjt force-pushed the wjt/rewrite-input-hud-again branch from d95ca0e to 9ce36fd Compare June 11, 2026 12:55
@wjt wjt changed the title Input hints: Display logical keys [1/1] Input hints: Display logical keys Jun 11, 2026
@wjt wjt changed the title [1/1] Input hints: Display logical keys Input hints: Display logical keys Jun 11, 2026

@manuq manuq left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is impressive, how you managed to overcome the corner cases. I thought this needed more work on the addon side, but I only see nathanhoad/godot_input_helper#88 upstreamed.

Unfortunately I don't have a keyboard to test it, but I can confirm that it doesn't regress in my case. The screen captures you've posted are amazing.

I don't have anything to comment this time on the scripting side.

Comment on lines +58 to +61
# Try to show the logical label for physical mappings; i.e. on AZERTY
# show W when the physical binding is for Z. As of Godot 4.6, this API
# is only implemented on X11/Wayland/Mac/Windows; notably it is not
# available on the Web port.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍

Comment on lines +6 to +8
@export var neutral: Texture2D
@export var negative: Texture2D
@export var positive: Texture2D

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍

Comment on lines +14 to +15
## L2/R2 (aka LT/RT or ZL/ZR) are technically axes, but we treat them as buttons.
@export var triggers: Dictionary[JoyAxis, Texture2D]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍

@manuq manuq merged commit c6b6a43 into main Jun 11, 2026
7 checks passed
@manuq manuq deleted the wjt/rewrite-input-hud-again branch June 11, 2026 15:01
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.

Show logical labels for keyboard keys

2 participants