diff --git a/.context/patterns.md b/.context/patterns.md index 6c0a05d..03b23c0 100644 --- a/.context/patterns.md +++ b/.context/patterns.md @@ -12,3 +12,7 @@ Prefer thin domain modules ending in a façade that re-exports the public `CM.*` Canonical rules live in **`.cursor/rules/combatmode-lua-safety.mdc`** (Secret values and instance taint). Summary: never `==`/`` on possibly secret UnitGUID/cast GUID/reaction/health/name/width; use `issecretvalue` first, `PublicBool` / `PublicNumber`, `UnitExists`/`UnitIsUnit`/frame identity, color curves / `EvaluateColorFromBoolean`, and combat-deferred binding flushes. Reference Crosshair, FocusNameplateMarker, CastProgress, InteractionHUD Target, PartyRadial HealthBars/RoleIcons/Visual. + +## macOS stuck-cursor freeze + +A `MouselookStop()` issued while the window is backgrounded (a watched frame auto-unlocks Mouse Look during an alt-tab, e.g. bags/LFG) leaves the OS mouse capture stuck — frozen cursor/camera on return while `IsMouselooking()` lies. There is no window-focus event and the background keeps rendering, so it can't be detected from `OnUpdate`. Recovery is the manual `Combat Mode - Reset Mouse Look` keybind (`CombatMode_ResetMouseLook`, `Core/FreeLook/FreeLookController.lua`): re-grab + release through `CM.LockFreeLook` / `CM.UnlockFreeLook` (keeps UI in sync). The config keybind (`UI/Options/Tabs/TabGeneral.lua`) is shown macOS-only via `IsMacClient()`. diff --git a/CombatMode/Bindings.xml b/CombatMode/Bindings.xml index 2f963ff..ab8057f 100644 --- a/CombatMode/Bindings.xml +++ b/CombatMode/Bindings.xml @@ -4,6 +4,9 @@ CombatMode_CursorModeKey(keystate) end + + CombatMode_ResetMouseLook() + if keystate == "down" or keystate == "up" then CombatMode_PartyRadialKey(keystate) diff --git a/CombatMode/Core/FreeLook/FreeLookController.lua b/CombatMode/Core/FreeLook/FreeLookController.lua index 70b02e1..6e944d1 100644 --- a/CombatMode/Core/FreeLook/FreeLookController.lua +++ b/CombatMode/Core/FreeLook/FreeLookController.lua @@ -435,3 +435,21 @@ function _G.CombatMode_CursorModeKey(keystate) FreeLookOverride = false end end + +-- Manual recovery for the macOS stuck-cursor freeze (a MouselookStop issued while the +-- window was backgrounded leaves the OS capture stuck). Re-grab and release through the +-- free-look state machine so the crosshair/tooltip/CVar side effects stay in sync rather +-- than trusting IsMouselooking(); OnUpdate re-locks afterward if free look should be active. +function _G.CombatMode_ResetMouseLook() + CM.LockFreeLook() + local function settle() + if CM.ShouldFreeLookBeOff() then + CM.UnlockFreeLook() + end + end + if C_Timer and C_Timer.After then + C_Timer.After(0, settle) + else + settle() + end +end diff --git a/CombatMode/UI/Options/Tabs/TabGeneral.lua b/CombatMode/UI/Options/Tabs/TabGeneral.lua index cbd79f7..d473b54 100644 --- a/CombatMode/UI/Options/Tabs/TabGeneral.lua +++ b/CombatMode/UI/Options/Tabs/TabGeneral.lua @@ -25,6 +25,7 @@ local _G = _G -- WoW API local GetBindingKey = _G.GetBindingKey local GetCurrentBindingSet = _G.GetCurrentBindingSet +local IsMacClient = _G.IsMacClient local ReloadUI = _G.ReloadUI local SaveBindings = _G.SaveBindings local SetBinding = _G.SetBinding @@ -110,6 +111,22 @@ UI.Options.AddTab({ end) end, }) + -- macOS only: on other clients the stuck-cursor freeze does not occur, so this + -- recovery keybind is hidden to avoid clutter. + if IsMacClient() then + ctx:Keybind({ + label = "Reset Mouse Look", + desc = "Recover a stuck cursor after switching windows on macOS. Re-grabs and releases Mouse Look to clear a capture the game left stuck while running in the background.", + get = function() + return (GetBindingKey("Combat Mode - Reset Mouse Look")) + end, + set = function(key) + CM.TryApplyBindingChange("reset mouse look keybinding", function() + CM.AssignNamedKeybind("Combat Mode - Reset Mouse Look", key) + end) + end, + }) + end ctx:Toggle({ label = "Pulse Cursor on Unlock", desc = "Flash the cursor when Mouse Look ends.",