Skip to content

Instantly share code, notes, and snippets.

@franklindyer
Created November 11, 2024 22:29
Show Gist options
  • Save franklindyer/49308a07f5bd79f01769cc16021e2cc1 to your computer and use it in GitHub Desktop.
Save franklindyer/49308a07f5bd79f01769cc16021e2cc1 to your computer and use it in GitHub Desktop.
PyObjC keyboard event sniffer for MacOS
# A simple PyObjC keyboard event sniffer.
# You don't want to import everything from Quartz - this slows things down significantly
from Quartz import \
CGEventTapCreate, \
kCGHIDEventTap, \
kCGHeadInsertEventTap, \
kCGEventTapOptionListenOnly, \
kCGEventKeyDown, \
CFMachPortCreateRunLoopSource, \
CFRunLoopAddSource, \
CFRunLoopGetCurrent, \
kCFRunLoopDefaultMode, \
CGEventTapEnable, \
CFRunLoopRun, \
CGEventMaskBit, \
CGEventGetTimestamp, \
CGEventKeyboardGetUnicodeString,\
CGEventGetIntegerValueField, \
CGEventGetFlags, \
kCGEventKeyUp, \
kCGEventFlagsChanged
def handleKeyEvent(p, t, ev, c):
timestamp = CGEventGetTimestamp(ev)
target_pid = CGEventGetIntegerValueField(ev, 40)
kb_ident = CGEventGetIntegerValueField(ev, 10)
flags = CGEventGetFlags(ev)
keycode = CGEventGetIntegerValueField(ev, 9)
if t == 10 or t == 11:
# Keyup and Keydown events
if t == 10:
ev_type = "KeyDown"
else:
ev_type = "KeyUp"
uni_string = CGEventKeyboardGetUnicodeString(ev, 10, None, None)[1]
is_repeat = CGEventGetIntegerValueField(ev, 8)
print(f"{ev_type},{timestamp},{keycode},{kb_ident},{is_repeat},{format(flags,'08x')},{uni_string}")
elif t == 12:
# Flag change events
ev_type = "FlagsChanged"
keycode = CGEventGetIntegerValueField(ev, 9)
print(f"{ev_type},{timestamp},{keycode},{kb_ident},{format(flags,'08x')}")
tap = CGEventTapCreate(
kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionListenOnly,
CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged),
handleKeyEvent,
None
)
runLoopSource = CFMachPortCreateRunLoopSource(None, tap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
CGEventTapEnable(tap, True);
CFRunLoopRun();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment