Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions core/key_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ def execute_action(action_id):
# ==================================================================

elif sys.platform == "darwin":
_INJECTED_EVENT_MARKER = 0x4D4F5554
import ctypes

try:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
11 changes: 11 additions & 0 deletions core/mouse_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Loading