Mac key citations point at the Mac client's published key input (#2912)

benilla's Mac key-handling comments cited the 1.12.1 Mac client as `0x5bf320` and `0x8a4aa`. The first resolved to an unrelated `WoW.exe` function, and nothing documented the second. benilla-re now publishes the Mac client's key input with `mac:` addresses (`systems/core/mackeys.md`), and every citation points there; `cited` finds all 15 backed.

Read against it:
- Caps Lock queues nothing on macOS, as the Mac client's modifier handler tests only Shift, Control and Option (`mac:08a06c`).
- F17-F20 are named by the OS layout there, so benilla's names are noted as its own.
- Deviation: Z keeps its capital name, where the Mac client's fill uppercases only a-y (`mac:08a3df`), because the fault leaves the stock sheathe binding unreachable.

Tested: Caps Lock is a key only off the Mac.

Fixes #2895

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

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-10-31 22:07:14 +07:00
committed by GitHub
parent 4cc50bc5ab
commit fe7148b043
5 changed files with 56 additions and 23 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
//!
//! Deviation: a press with Super (Cmd) held runs no binding because that way a system chord such as
//! Cmd+Tab or Cmd+Q never acts in game. The reference has no such modifier and runs the bare key's
//! binding: the Mac enqueues the key with Command held and skips only its text (`0x8a519`), and
//! binding: the Mac enqueues the key with Command held and skips only its text (`mac:08a51e`), and
//! Windows hands `VK_LWIN`/`VK_RWIN` to the layout arm (`0x42da39`) like any key.
//!
//! Persistence: `benilla-config/bindings/account.txt` and `<Realm>-<Char>.txt` ([`store`]); the
+39 -7
View File
@@ -107,6 +107,10 @@ impl Chord {
pub(crate) fn key_token(k: KeyCode, layout: &LayoutNames) -> Option<KeyName> {
match layout.name(k) {
// The lookup folds `a`-`z` up (`0x64b419`), so Turkish `VK_OEM_7`'s `i` is `I`'s key.
//
// Deviation: the Z key is named `Z` on a Mac too, because the Mac client uppercases only
// `a`-`y` (`mac:08a3df`) and so names it `z`, which no binding can press; that fault would
// leave a binding to `Z`, the stock `TOGGLESHEATH` key, unreachable there.
Some(LayoutName::Char(c)) => Some(KeyName::Char(c.to_ascii_uppercase())),
Some(LayoutName::Dropped) => None,
None => fixed_name(k),
@@ -119,7 +123,9 @@ pub(crate) fn key_token(k: KeyCode, layout: &LayoutNames) -> Option<KeyName> {
/// codes the table cannot name, `0x210` and `0x211` (ScrollLock and Pause, and F14 and F15 on a
/// Mac). Every other key without a name never reaches the walk: the Windows key table drops it
/// where `MapVirtualKeyA` answers 0 (`0x42da39`: the Windows and Menu keys, media keys, a key the
/// layout drops), the Mac client's table `0x5bf320` has -1 for Command and fn. Unlike
/// layout drops), the Mac client's table `mac:5bf320` has -1 for Command and fn, and its modifier handler queues
/// only Shift, Control and Option (`mac:08a06c`, `mac:08a0d3`, `mac:08a13a`), so Caps Lock queues
/// nothing. Unlike
/// [`key_token`], which is a binding's, a modifier still gets a down and an up.
pub(crate) fn walk_name(k: KeyCode, layout: &LayoutNames) -> Option<String> {
if let Some(name) = key_token(k, layout) {
@@ -157,23 +163,27 @@ fn fixed_name(k: KeyCode) -> Option<KeyName> {
F10 => Word("F10"),
F11 => Word("F11"),
F12 => Word("F12"),
// On a Mac F13 is print-screen: the Mac client's key table `0x5bf320` maps keycode
// `0x69` to `0x212`, `PRINTSCREEN`, and macOS has no PrintScreen keycode.
// On a Mac F13 is print-screen: the Mac client's key table `mac:5bf320` maps keycode
// `0x69` to `0x212`, which the namer calls `PRINTSCREEN` (`mac:1de30e`), and macOS has no PrintScreen keycode.
#[cfg(target_os = "macos")]
F13 => Word("PRINTSCREEN"),
// `IsValidBindingKeyString` accepts `F` + any digits (`0x846c04`) and the Mac table maps
// keycode `0x6A` to `F16` (`0x30d`). Deviation: the Windows key table stops at `VK_F12`
// keycode `0x6A` to `0x30d`, which the namer calls `F16` (`mac:1de48a`). Deviation: the Windows key table stops at `VK_F12`
// and drops later F-keys; they are named here because that way a stored `F13`-`F24` can be
// pressed.
#[cfg(not(target_os = "macos"))]
F13 => Word("F13"),
// On a Mac F14 and F15 are ScrollLock and Pause: keycodes `0x6B` and `0x71` map to
// `0x210` and `0x211`, which the namer calls `UNKNOWN`, so they stay unbindable there.
// `0x210` and `0x211`, which the namer calls `UNKNOWN` (`mac:5bf320`, `mac:1de334`), so they stay
// unbindable there.
#[cfg(not(target_os = "macos"))]
F14 => Word("F14"),
#[cfg(not(target_os = "macos"))]
F15 => Word("F15"),
F16 => Word("F16"),
// The Mac table leaves keycodes 0x40, 0x4f, 0x50 and 0x5a (F17-F20) at -1 (`mac:5bf320`),
// which `KeyTranslate` fills from the layout (`mac:08a3c3`), so the reference's name for
// them depends on the OS; these are benilla's.
F17 => Word("F17"),
F18 => Word("F18"),
F19 => Word("F19"),
@@ -218,6 +228,9 @@ fn fixed_name(k: KeyCode) -> Option<KeyName> {
PrintScreen => Word("PRINTSCREEN"),
// No ScrollLock or Pause: the namer (`0x4b66b0`) calls `0x210`/`0x211` `UNKNOWN`, and
// `IsValidBindingKeyString` (`0x4b7890`) has neither name.
// The Mac client queues no Caps Lock key (`mac:08a06c`): its modifier handler tests only
// Shift, Control and Option, though the table `mac:5bf320` maps keycode 0x39 to `0x20e`.
#[cfg(not(target_os = "macos"))]
CapsLock => Word("CAPSLOCK"),
_ => return None,
})
@@ -391,7 +404,6 @@ mod tests {
KeyCode::PageUp,
KeyCode::PageDown,
KeyCode::NumLock,
KeyCode::CapsLock,
KeyCode::PrintScreen,
KeyCode::Minus,
KeyCode::Equal,
@@ -462,13 +474,15 @@ mod tests {
"PAGEUP",
"PAGEDOWN",
"NUMLOCK",
"CAPSLOCK",
"PRINTSCREEN",
"MOUSEWHEELDOWN",
"MOUSEWHEELUP",
]
.map(str::to_string),
);
// The Mac client queues no Caps Lock key (`mac:08a06c`).
#[cfg(not(target_os = "macos"))]
tokens.push("CAPSLOCK".to_string());
tokens.extend(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=[]\\;',./`"
.chars()
@@ -488,6 +502,24 @@ mod tests {
}
}
/// Caps Lock is a key on Windows and Linux (`CAPSLOCK`); the Mac client's modifier handler
/// queues only Shift, Control and Option (`mac:08a06c`), so it names and binds nothing there.
#[test]
fn caps_lock_is_a_key_only_off_the_mac() {
let us = LayoutNames::default();
let named = key_token(KeyCode::CapsLock, &us);
let parsed = Chord::parse("CAPSLOCK");
if cfg!(target_os = "macos") {
assert_eq!((named, parsed), (None, None));
} else {
assert_eq!(named, Some(KeyName::Word("CAPSLOCK")));
assert_eq!(
parsed.map(|c| c.key),
Some(BindKey::Key(KeyName::Word("CAPSLOCK")))
);
}
}
#[test]
fn the_codec_round_trips_every_keyboard_token() {
let us = LayoutNames::default();
+8 -8
View File
@@ -4,8 +4,8 @@
//! outside its fixed table, `VK_A`-`VK_Z` and `VK_OEM_*` among them, to
//! `MapVirtualKeyA(vk, MAPVK_VK_TO_CHAR)` (`0x42da39`), which returns `VK_A`-`VK_Z`'s own letter
//! whatever the layout types there. The Mac client fills its letter and punctuation keys from the
//! live layout, `KeyTranslate` (`0x8a3c3`) on the current `KCHR` (re-read when it changes,
//! `0x8a345`), and fixes its digits by position (`0x5bf320`). Bevy's `KeyboardInput` carries the
//! live layout, `KeyTranslate` (`mac:08a3c3`) on the current `KCHR` (re-read when it changes,
//! `mac:08a345`), and fixes its digits by position (`mac:5bf320`). Bevy's `KeyboardInput` carries the
//! physical key and a `logical_key` with Shift applied, so each platform's name is read off the
//! raw winit key messages here, before any `Update` reader.
@@ -35,7 +35,7 @@ impl Position {
/// The keys a layout names: the letters, the digits, the punctuation, the ISO and JIS extras and
/// the keypad comma. Windows sends `VK_OEM_102`, `VK_ABNT_C2` and `VK_SEPARATOR` to the layout arm
/// like any `VK_OEM_*` key, and the Mac client's table leaves the ISO key `0x0A` and keypad comma
/// `0x5F` to `KeyTranslate` (`-1` at `0x5bf320`). winit 0.30 cannot deliver those on a Mac, as on
/// `0x5F` to `KeyTranslate` (`-1` at `mac:5bf320`). winit 0.30 cannot deliver those on a Mac, as on
/// main: it reports `0x0A` as `Backquote` (whose scancode it gives back as `0x32`) and leaves
/// `0x5E` and `0x5F` unidentified, so the `IntlBackslash`, `IntlRo` and `NumpadComma` arms never
/// fire there.
@@ -114,7 +114,7 @@ impl LayoutNames {
/// reported (a headless run) keeps its US name.
///
/// Deviation: on the Mac and Linux a key its platform reports no character for keeps its US
/// name too, where the Mac reference drops it (`0x8a4aa`), and a Linux dead key not yet pressed
/// name too, where the Mac reference drops it (`mac:08a4aa`), and a Linux dead key not yet pressed
/// bare stays bindable.
pub(crate) fn name(&self, key: KeyCode) -> Option<LayoutName> {
let position = position(key)?;
@@ -289,13 +289,13 @@ mod windows {
///
/// Deviation: a layout that is not ASCII-capable (Hebrew, Greek, Russian, Arabic, Thai) names its
/// keys through the ASCII-capable layout macOS pairs with it, as macOS names its own shortcuts,
/// because the reference reads a non-Roman `KCHR`'s byte as MacRoman (`0x8a40f`), which names no
/// because the reference reads a non-Roman `KCHR`'s byte as MacRoman (`mac:08a40f`), which names no
/// real key.
///
/// Deviation: a character above U+00FF (Czech `ě`, Polish `ł`, Turkish `ş`) names the key by itself
/// because the reference reads `KeyTranslate`'s byte as MacRoman (`0x8a40f`) and keeps it only if
/// it converts to Latin-1 (`CFStringGetBytes`, `0x8a46b`, stored at `0x8a482`), else leaves the
/// slot `-1`, which `0x8a4aa` drops, so such a key is mislabelled or dropped; named by what it
/// because the reference reads `KeyTranslate`'s byte as MacRoman (`mac:08a40f`) and keeps it only if
/// it converts to Latin-1 (`CFStringGetBytes`, `mac:08a46b`, stored at `mac:08a482`), else leaves the
/// slot `-1`, which `mac:08a4aa` drops, so such a key is mislabelled or dropped; named by what it
/// types, it stays bindable and readable.
#[cfg(any(target_os = "macos", test))]
fn mac_name(
+1 -1
View File
@@ -9,7 +9,7 @@
//! Off macOS, each keyboard message whose logical key is a named key other than its physical one
//! takes that named key's code, before Bevy's input collection, so every reader (the bindings,
//! the text boxes, the glue screens, `ButtonInput<KeyCode>`) sees the layout's key. The Mac client
//! reads its non-character keys off a fixed table on the physical keycode (`0x5bf320`), so macOS
//! reads its non-character keys off a fixed table on the physical keycode (`mac:5bf320`), so macOS
//! keeps them physical. A letter, digit or punctuation key keeps its code too: 1.12 names it by
//! the virtual key the layout makes it, a digit as itself (`0x42d81c`) and a letter or punctuation
//! key through `MapVirtualKeyA` (`0x42da39`), which the game's key namer reads, not by a named
+7 -6
View File
@@ -29,7 +29,7 @@ A setting boots at the reference's default unless it is listed here (`docs/METHO
Each entry is the first sentence of a `Deviation:` comment, linking to where it lives.
- [benilla-app](#benilla-app): 254
- [benilla-app](#benilla-app): 255
- [benilla-assets](#benilla-assets): 6
- [benilla-blp](#benilla-blp): 1
- [benilla-formats](#benilla-formats): 24
@@ -74,14 +74,15 @@ Each entry is the first sentence of a `Deviation:` comment, linking to where it
#### bindings
- A press with Super (Cmd) held runs no binding because that way a system chord such as Cmd+Tab or Cmd+Q never acts in game. The reference has no such modifier and runs the bare key's binding: the Mac enqueues the key with Command held and skips only its text (`0x8a519`), and Windows hands `VK_LWIN`/`VK_RWIN` to the layout arm (`0x42da39`) like any key. ([`src/bindings.rs:19`](../crates/benilla-app/src/bindings.rs#L19))
- A press with Super (Cmd) held runs no binding because that way a system chord such as Cmd+Tab or Cmd+Q never acts in game. The reference has no such modifier and runs the bare key's binding: the Mac enqueues the key with Command held and skips only its text (`mac:08a51e`), and Windows hands `VK_LWIN`/`VK_RWIN` to the layout arm (`0x42da39`) like any key. ([`src/bindings.rs:19`](../crates/benilla-app/src/bindings.rs#L19))
- A trackpad's `Pixel` deltas are carried until they make a line, and step at most once per event (`crate::ui_script::Record::Wheel`). The reference steps on every `WM_MOUSEWHEEL`, whatever its delta (`0x76c1a9`); the carry is kept because a carried trackpad does not zoom or scroll at its event rate. ([`src/bindings.rs:637`](../crates/benilla-app/src/bindings.rs#L637))
- Latches die with the VM that made them, because a latch names a command of that VM's table and releasing it against a new one would run another load's up-half. The reference keeps a held key running through `ReloadUI`; here it re-latches on its next press. The held bits die with it, as the key's Stop would have cleared them. ([`src/bindings.rs:682`](../crates/benilla-app/src/bindings.rs#L682))
- The Windows key table stops at `VK_F12` and drops later F-keys; they are named here because that way a stored `F13`-`F24` can be pressed. ([`src/bindings/chord.rs:165`](../crates/benilla-app/src/bindings/chord.rs#L165))
- On the Mac and Linux a key its platform reports no character for keeps its US name too, where the Mac reference drops it (`0x8a4aa`), and a Linux dead key not yet pressed bare stays bindable. ([`src/bindings/layout.rs:116`](../crates/benilla-app/src/bindings/layout.rs#L116))
- The Z key is named `Z` on a Mac too, because the Mac client uppercases only `a`-`y` (`mac:08a3df`) and so names it `z`, which no binding can press; that fault would leave a binding to `Z`, the stock `TOGGLESHEATH` key, unreachable there. ([`src/bindings/chord.rs:111`](../crates/benilla-app/src/bindings/chord.rs#L111))
- The Windows key table stops at `VK_F12` and drops later F-keys; they are named here because that way a stored `F13`-`F24` can be pressed. ([`src/bindings/chord.rs:171`](../crates/benilla-app/src/bindings/chord.rs#L171))
- On the Mac and Linux a key its platform reports no character for keeps its US name too, where the Mac reference drops it (`mac:08a4aa`), and a Linux dead key not yet pressed bare stays bindable. ([`src/bindings/layout.rs:116`](../crates/benilla-app/src/bindings/layout.rs#L116))
- A character above U+00FF names the key by itself because the reference's `MapVirtualKeyA` returns an ANSI code-page byte, which `0x42da4b` drops past `0xff` and the namer's UTF-8 encoder (`0x41abb0`) otherwise reads as Latin-1, so such a key is mislabelled or dropped; named by what it types, it stays bindable and readable. ([`src/bindings/layout.rs:216`](../crates/benilla-app/src/bindings/layout.rs#L216))
- A layout that is not ASCII-capable (Hebrew, Greek, Russian, Arabic, Thai) names its keys through the ASCII-capable layout macOS pairs with it, as macOS names its own shortcuts, because the reference reads a non-Roman `KCHR`'s byte as MacRoman (`0x8a40f`), which names no real key. ([`src/bindings/layout.rs:290`](../crates/benilla-app/src/bindings/layout.rs#L290))
- A character above U+00FF (Czech `ě`, Polish `ł`, Turkish `ş`) names the key by itself because the reference reads `KeyTranslate`'s byte as MacRoman (`0x8a40f`) and keeps it only if it converts to Latin-1 (`CFStringGetBytes`, `0x8a46b`, stored at `0x8a482`), else leaves the slot `-1`, which `0x8a4aa` drops, so such a key is mislabelled or dropped; named by what it types, it stays bindable and readable. ([`src/bindings/layout.rs:295`](../crates/benilla-app/src/bindings/layout.rs#L295))
- A layout that is not ASCII-capable (Hebrew, Greek, Russian, Arabic, Thai) names its keys through the ASCII-capable layout macOS pairs with it, as macOS names its own shortcuts, because the reference reads a non-Roman `KCHR`'s byte as MacRoman (`mac:08a40f`), which names no real key. ([`src/bindings/layout.rs:290`](../crates/benilla-app/src/bindings/layout.rs#L290))
- A character above U+00FF (Czech `ě`, Polish `ł`, Turkish `ş`) names the key by itself because the reference reads `KeyTranslate`'s byte as MacRoman (`mac:08a40f`) and keeps it only if it converts to Latin-1 (`CFStringGetBytes`, `mac:08a46b`, stored at `mac:08a482`), else leaves the slot `-1`, which `mac:08a4aa` drops, so such a key is mislabelled or dropped; named by what it types, it stays bindable and readable. ([`src/bindings/layout.rs:295`](../crates/benilla-app/src/bindings/layout.rs#L295))
- The reference saves a full snapshot (`bindings-cache.wtf`); a diff is stored because a full snapshot would leave every command added later unbound for a saved file. ([`src/bindings/store.rs:13`](../crates/benilla-app/src/bindings/store.rs#L13))
#### blob_shadow