From da75644b29baf1a6587b60e211e6d819ae8a38f7 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Sun, 12 Apr 2026 15:25:42 +0200 Subject: [PATCH 1/2] fix: macos prevent circular mapping --- core/key_simulator.py | 17 ++++++++++++++++- core/mouse_hook.py | 11 +++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/key_simulator.py b/core/key_simulator.py index 766369f..f84c36e 100644 --- a/core/key_simulator.py +++ b/core/key_simulator.py @@ -555,6 +555,7 @@ def execute_action(action_id): # ================================================================== elif sys.platform == "darwin": + _INJECTED_EVENT_MARKER = 0x4D4F5554 import ctypes try: @@ -628,6 +629,13 @@ def inject_scroll(flags, delta): else: event = Quartz.CGEventCreateScrollWheelEvent(None, 0, 2, 0, delta) if event: + try: + # Mark synthetic scroll events so the CGEventTap can ignore them + Quartz.CGEventSetIntegerValueField( + event, Quartz.kCGEventSourceUserData, _INJECTED_EVENT_MARKER + ) + except Exception: + pass Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) # Mouse button simulation @@ -668,6 +676,13 @@ def _inject_mac_mouse(action_id, is_down): loc = Quartz.CGEventGetLocation(Quartz.CGEventCreate(None)) ev = Quartz.CGEventCreateMouseEvent(None, evt_type, loc, entry["button"]) if ev: + try: + # Mark synthetic mouse events so the CGEventTap can ignore them + Quartz.CGEventSetIntegerValueField( + ev, Quartz.kCGEventSourceUserData, _INJECTED_EVENT_MARKER + ) + except Exception: + pass Quartz.CGEventPost(Quartz.kCGHIDEventTap, ev) def inject_mouse_down(action_id): @@ -1483,7 +1498,7 @@ def _linux_workspace_keys(direction: str): "f1": KEY_F1, "f2": KEY_F2, "f3": KEY_F3, "f4": KEY_F4, "f5": KEY_F5, "f6": KEY_F6, "f7": KEY_F7, "f8": KEY_F8, "f9": KEY_F9, "f10": KEY_F10, "f11": KEY_F11, "f12": KEY_F12, - "volumeup": KEY_VOLUMEUP, "volumedown": KEY_VOLUMEDOWN, + "volumeup": KEY_VOLUME_UP, "volumedown": KEY_VOLUMEDOWN, "mute": KEY_MUTE, "playpause": KEY_PLAYPAUSE, "nexttrack": KEY_NEXTSONG, "prevtrack": KEY_PREVIOUSSONG, } diff --git a/core/mouse_hook.py b/core/mouse_hook.py index 3acf0d8..825747c 100644 --- a/core/mouse_hook.py +++ b/core/mouse_hook.py @@ -990,6 +990,7 @@ def stop(self): _BTN_BACK = 3 _BTN_FORWARD = 4 _SCROLL_INVERT_MARKER = 0x4D4F5553 + _INJECTED_EVENT_MARKER = 0x4D4F5554 _kCGEventTapDisabledByTimeout = 0xFFFFFFFE _kCGEventTapDisabledByUserInput = 0xFFFFFFFF @@ -1379,6 +1380,16 @@ def _event_tap_callback(self, proxy, event_type, cg_event, refcon): self._first_event_logged = True print("[MouseHook] CGEventTap: first event received", flush=True) + # Ignore events we injected ourselves. The key_simulator marks + # synthetic events by setting kCGEventSourceUserData to _INJECTED_EVENT_MARKER. + try: + if Quartz.CGEventGetIntegerValueField( + cg_event, Quartz.kCGEventSourceUserData + ) == _INJECTED_EVENT_MARKER: + return cg_event + except Exception: + # If the field isn't available or the call fails, continue. + pass mouse_event = None should_block = False From fd3fad05fe891d9ce00b84f791585714867e7cc0 Mon Sep 17 00:00:00 2001 From: lucamene04 Date: Tue, 21 Apr 2026 16:20:23 +0200 Subject: [PATCH 2/2] fix: restore Linux volume key constant --- core/key_simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/key_simulator.py b/core/key_simulator.py index f84c36e..b582dce 100644 --- a/core/key_simulator.py +++ b/core/key_simulator.py @@ -1498,7 +1498,7 @@ def _linux_workspace_keys(direction: str): "f1": KEY_F1, "f2": KEY_F2, "f3": KEY_F3, "f4": KEY_F4, "f5": KEY_F5, "f6": KEY_F6, "f7": KEY_F7, "f8": KEY_F8, "f9": KEY_F9, "f10": KEY_F10, "f11": KEY_F11, "f12": KEY_F12, - "volumeup": KEY_VOLUME_UP, "volumedown": KEY_VOLUMEDOWN, + "volumeup": KEY_VOLUMEUP, "volumedown": KEY_VOLUMEDOWN, "mute": KEY_MUTE, "playpause": KEY_PLAYPAUSE, "nexttrack": KEY_NEXTSONG, "prevtrack": KEY_PREVIOUSSONG, }