fix(cdm): mirror presses from click-routed keybinds - #1231
Open
dfrisone wants to merge 2 commits into
Open
Conversation
The CDM press mirror hangs off ActionButtonDown and MultiActionButtonDown, which are the NATIVE binding commands. Any key the action bars route through the button with SetOverrideBindingClick never reaches them, so the mirror never saw it. That is not just Bars 9 and 10, which have no native command at all: useClick is also true for every empower spell on any bar, for any bar with user-configured paging, and for MainBar when form or skyriding paging is opted out. An empowered spell on bar 1 had the same missing press flash with no extra bar involved. Rather than hook UseAction, which sits upstream of the protected UseContainerItem/UseInventoryItem/SpellTargetItem calls that OnActionButtonClick makes on the spell-targets-item path, ride the PostClick hook the action bars module already installs on every button for its own press-time GCD paint. PostClick runs after Blizzard's click handler has fully returned, so this keeps the same taint posture the two native hooks already have. The action bars module publishes the press through a global, matching _EAB_UpdateKeybinds, so it costs one nil check when the CDM module is off. The subscriber needs the binding command back from the button to poll IsKeyDown for the release, so the keybind cache now also records slot -> command as it walks the bars. Built in that same loop deliberately: it shares the page resolution and the bar priority order, and cannot drift from the keybind cache the way the bar list itself had drifted. Two gates keep this to the existing feature's semantics. It acts on the down edge only, because buttons register for both edges and the key is already released by the up click in key-up mode. And it requires one of the slot's binding keys to be held, because by PostClick a click-routed keypress and a real mouse click are indistinguishable, as Blizzard's own comment in SecureTemplates says. Without that second gate, mouse clicks would start mirroring, which is a behaviour change no one asked for.
…eel binds Two defects from a review of the previous commit. The slot to binding-command map was both ambiguous and unnecessary. Bar 9 IS action page 2, so its slots collide with MainBar's whenever MainBar is paged there, and a slot-keyed lookup then hands back the wrong bar's command. It was also stale for the 0.5s debounce after any page change. The action bars module already knows the exact command at hook-install time, and that command is a property of the BUTTON rather than of the slot it currently shows, so it stays correct across every page swap. Capture it there and pass it through. This deletes the map, and with it the only change to EllesmereUICooldownManager.lua. The keyboard-versus-mouse gate dropped mousewheel binds entirely. IsKeyDown is never true for MOUSEWHEELUP/DOWN, so requiring a held key suppressed the mirror for every wheel-bound button, where the native path still shows it and simply releases on the MIN_VISIBLE floor. A held key still proves keyboard; the cursor-rect test now covers the binds it cannot see. The pair only misses a wheel bind pressed while the cursor happens to rest on that same button.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The CDM press mirror hangs off
ActionButtonDownandMultiActionButtonDown, which are the native binding commands. Any key the action bars route through the button withSetOverrideBindingClicknever reaches them, so the mirror never saw the press.Per the
useClickdecision inEllesmereUIActionBars.lua, that is not just Bars 9 and 10, which have no native command at all:info.customPage ~= nil)So an empowered spell sitting on bar 1 had the same missing press flash, with no extra bar involved. Found while fixing the Bar 9 keybind report in #1230, not separately reported.
Why not hook UseAction
The tempting fix is
hooksecurefunc("UseAction", ...), which catches every route at once:SECURE_ACTIONS.actioncalls the global from Lua atSecureTemplates.lua:349.It is not safe here.
OnActionButtonClickkeeps going after that handler returns and calls protectedC_Container.UseContainerItem,UseInventoryItem, andSpellTargetItemon the spell-targets-item path, which is what runs when you apply an oil, enchant, or poison. A hook onUseActionsits upstream of those. The two existing native hooks are safe precisely becauseActionButtonDowncallsTryUseActionButton, which completes that whole protected block before the post-hook runs.What it does instead
The action bars module already hooks
PostClickon every button for its own press-time GCD paint, added for this exact reason ("keybinds arrive as clicks too").PostClickruns after Blizzard's click handler has fully returned, so it keeps the same taint posture the native hooks already have. The bars module publishes the press through a global, matching_EAB_UpdateKeybinds, so it costs one nil check when the CDM module is off.The binding command is captured at hook-install time from
BINDING_MAP[info.key] .. index. It is a property of the button rather than of the slot the button currently shows, so it stays correct across every page swap. Deriving it from the live action slot instead would be ambiguous exactly where it matters, since Bar 9 is action page 2 and its slots collide with MainBar's whenever MainBar is paged there.Keeping the existing semantics
Two gates, so nothing changes for anyone not on a click-routed key.
It acts on the down edge only, because buttons register for both edges and the key is already released by the up click in key-up mode.
It requires evidence the press was from the keyboard, because by
PostClicka click-routed keypress and a real mouse click are indistinguishable, asSecureActionButton_OnClick's own comment says. That needs two tests rather than one: a held binding key proves keyboard, butIsKeyDownis never true for mousewheel binds, and gating on it alone would silently drop every wheel-bound button that the native path still mirrors. The cursor-rect test covers those. Together they only miss a wheel bind pressed while the cursor happens to rest on that same button.Notes
Two files, 56 lines. Read-only plus Show/Hide on our own overlay textures. No new events, no new strings, no options changes, no saved-variable changes. Zero cost when the mirror is off, which is an O(1) early-out on an already-cached flag.
Independent of #1230 despite being found alongside it: no shared files.
Tested in game: a bar 2 key flashes exactly once (native-routed presses do not reach
PostClick, sinceTryUseActionButtoncallsSecureActionButton_OnClickas a plain Lua call rather than a widget click), Bars 9 and 10 now mirror, and empower spells now mirror where they previously did not.