benilla's layer speaks the 1.12 API alone: handlers take this and no arguments, GetBinding answers the 1.12 list, the Key Bindings page captures through its window, /errors chains the error handler, and the host hooks leave the VM (#569)

benilla's layer now uses only the 1.12 API and the 1.12 Lua forms, and the host hooks it used are gone from the VM.

- **The VM calls handlers as 1.12.1 does.** A handler body is compiled as the chunk itself, so `self` in it is an ordinary global (`0x704c70`). A handler is called with no arguments (`0x705199`). The loader reads no `function=` attribute.
- **`GetBinding`/`GetNumBindings` answer the 1.12 list** (`0x4b7f60`): `HEADER_*` section rows, plus every key bound to a command, with no category column. The stock `KeyBindingFrame` now shows its section headers and both key columns.
- **The Key Bindings page captures keys through its own window**, as `Blizzard_BindingUI` does: the frame's `OnKeyDown` for keys, clicks for mouse buttons, and the wheel. It takes the `Benilla` prefix, so loading the stock binding addon no longer breaks it. The UI pointer feed now hands frames mouse buttons 4 and 5.
- **`/errors` keeps its log by chaining the Lua error handler**, and the stock ScriptErrors dialog is unchanged. Load failures that never raise now print one chat line each at world entry.
- **Slash commands:** `/reload` and `/errors` are `SlashCmdList` rows in the layer. `/convertraid` is gone; 1.12.1 has no such command. The dev commands are host rows in dev builds only, so a player build answers them with the unknown-command line once.
- **Naming:** every global the layer defines is `Benilla`-prefixed. `BenillaBindCapture`, `KeyBindings_OnHostKey`, the three script-error globals and `SubmitChatInput` are removed.

Verified:
- New tests for each rule and each removed surface; 16 mutations each fail a test.
- Region dumps of the Options window (general page, Key Bindings, armed) are identical before and after.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run bound a key, a Shift chord and the middle button through the page, and opened `/errors`.

Built on macOS; no platform seam touched.

Fixes #539
Fixes #544
Fixes #407
Part of #171

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-09-29 00:12:27 +07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 0f36374445
commit 05e204de10
48 changed files with 1966 additions and 1495 deletions
+39 -5
View File
@@ -47,6 +47,18 @@ impl PointerFeed<'_> {
}
}
/// The buttons the pointer feed hands the UI, by the names its handlers read (`arg1` of `OnClick`,
/// `OnMouseDown` and `OnMouseUp`, and the ones `RegisterForClicks` takes,
/// `Blizzard_BindingUI.xml:10`); buttons 4 and 5 on the physical buttons the binding chords call
/// BUTTON4 and BUTTON5 (`bindings::chord`).
const UI_MOUSE_BUTTONS: [(MouseButton, &str); 5] = [
(MouseButton::Left, "LeftButton"),
(MouseButton::Right, "RightButton"),
(MouseButton::Middle, "MiddleButton"),
(MouseButton::Forward, "Button4"),
(MouseButton::Back, "Button5"),
];
/// One `OnMouseWheel` call per whole notch, `arg1 = ±1`, the fraction carried in `notches`: a
/// trackpad gesture arrives as a `Pixel` trickle, and the stock handlers act on the sign alone
/// (`ScrollFrameTemplate_OnMouseWheel` scrolls half a pane per call, `UIPanelTemplates.lua:150`).
@@ -162,11 +174,7 @@ pub(super) fn feed_ui_input(
);
}
}
for (btn, name) in [
(MouseButton::Left, "LeftButton"),
(MouseButton::Right, "RightButton"),
(MouseButton::Middle, "MiddleButton"),
] {
for (btn, name) in UI_MOUSE_BUTTONS {
if buttons.just_pressed(btn) {
// A left press that would drop into the world (`world_drop_click`: any payload over
// terrain or nothing) is consumed now: the drop fires on the release, but the world
@@ -390,6 +398,32 @@ mod tests {
assert!(sum <= 1.0, "…and they may move at most one notch: {sum}");
}
/// Every button 1.12's frames take reaches the UI, under the name its binding chord agrees
/// with, so the Key Bindings page binds mouse 4 and 5 from a click.
#[test]
fn the_ui_takes_all_five_buttons_under_the_chords_names() {
let names: Vec<&str> = UI_MOUSE_BUTTONS.iter().map(|(_, n)| *n).collect();
assert_eq!(
names,
[
"LeftButton",
"RightButton",
"MiddleButton",
"Button4",
"Button5"
]
);
for (i, (button, _)) in UI_MOUSE_BUTTONS.iter().enumerate() {
let chord = crate::bindings::chord::Chord::parse(&format!("BUTTON{}", i + 1));
assert_eq!(
chord.map(|c| c.key),
Some(crate::bindings::chord::BindKey::Mouse(*button)),
"BUTTON{} is {button:?}",
i + 1
);
}
}
#[test]
fn a_line_notch_fires_once() {
let mut s = wheel_pane();