def callback(proxy, event_type, event, reference): SUPPRESS_EVENT = None PASS_EVENT_THROUGH = event # Don't pass on meta events meant for this event tap. is_unexpected_event = event_type not in self._KEYBOARD_EVENTS if is_unexpected_event: if event_type == kCGEventTapDisabledByTimeout: # Re-enable the tap and hope we act faster next time CGEventTapEnable(self._tap, True) plover.log.warning( "Keystrokes may have been missed. " + "Keyboard event tap has been re-enabled. ") return SUPPRESS_EVENT # Don't intercept the event if it has modifiers, allow # Fn and Numeric flags so we can suppress the arrow and # extended (home, end, etc...) keys. suppressible_modifiers = (kCGEventFlagMaskNumericPad | kCGEventFlagMaskSecondaryFn | kCGEventFlagMaskNonCoalesced) has_nonsupressible_modifiers = \ CGEventGetFlags(event) & ~suppressible_modifiers if has_nonsupressible_modifiers: return PASS_EVENT_THROUGH keycode = CGEventGetIntegerValueField( event, kCGKeyboardEventKeycode) key = KEYCODE_TO_KEY.get(keycode) self._async_dispatch(key, event_type) if key in self._suppressed_keys: return SUPPRESS_EVENT return PASS_EVENT_THROUGH
def run(self): source = CFMachPortCreateRunLoopSource(None, self._tap, 0) handler_thread = threading.Thread(target=self._event_handler, name="KeyEventDispatcher") handler_thread.start() self._loop = CFRunLoopGetCurrent() CFRunLoopAddSource(self._loop, source, kCFRunLoopCommonModes) CGEventTapEnable(self._tap, True) CFRunLoopRun() # Wake up event handler. self._event_queue.put_nowait(None) handler_thread.join() CFMachPortInvalidate(self._tap) CFRelease(self._tap) CFRunLoopSourceInvalidate(source)
def __init__(self): threading.Thread.__init__(self) self._running_thread = None self._suppressed_keys = set() self.key_down = lambda key: None self.key_up = lambda key: None # Returning the event means that it is passed on for further processing by others. # Returning None means that the event is intercepted. def callback(proxy, event_type, event, reference): # Don't pass on meta events meant for this event tap. if event_type not in self._KEYBOARD_EVENTS: return None # Don't intercept the event if it has modifiers, allow # Fn and Numeric flags so we can suppress the arrow and # extended (home, end, etc...) keys. if CGEventGetFlags(event) & ~(kCGEventFlagMaskNumericPad | kCGEventFlagMaskSecondaryFn | kCGEventFlagMaskNonCoalesced): return event keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) key = KEYCODE_TO_KEY.get(keycode) if key is not None: handler = self.key_up if event_type == kCGEventKeyUp else self.key_down handler(key) if key in self._suppressed_keys: # Suppress event. event = None return event self._tap = CGEventTapCreate( kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp), callback, None) if self._tap is None: # Todo(hesky): See if there is a nice way to show the user what's # needed (or do it for them). raise Exception("Enable access for assistive devices.") self._source = CFMachPortCreateRunLoopSource(None, self._tap, 0) CGEventTapEnable(self._tap, False)
def __init__(self, suppressed_keys): threading.Thread.__init__(self) self._running_thread = None self.suppress_keyboard(True) self.suppressed_keys = suppressed_keys # Returning the event means that it is passed on for further processing by others. # Returning None means that the event is intercepted. def callback(proxy, event_type, event, reference): # Don't pass on meta events meant for this event tap. if event_type not in self._KEYBOARD_EVENTS: return None # Don't intercept events from this module. if (CGEventGetIntegerValueField( event, kCGEventSourceStateID) == MY_EVENT_SOURCE_ID): return event # Don't intercept the event if it has modifiers. if CGEventGetFlags(event) & ~kCGEventFlagMaskNonCoalesced: return event keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) key = KEYCODE_TO_KEY.get(keycode, None) if key in self.suppressed_keys: handler_name = 'key_up' if event_type == kCGEventKeyUp else 'key_down' handler = getattr(self, handler_name, lambda event: None) handler(key) if self.is_keyboard_suppressed(): # Supress event. event = None return event self._tap = CGEventTapCreate( kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp), callback, None) if self._tap is None: # Todo(hesky): See if there is a nice way to show the user what's # needed (or do it for them). raise Exception("Enable access for assistive devices.") self._source = CFMachPortCreateRunLoopSource(None, self._tap, 0) CGEventTapEnable(self._tap, False)
def run_mac(self): from Quartz import ( CGEventTapCreate, CFMachPortCreateRunLoopSource, CFRunLoopAddSource, CFRunLoopGetCurrent, CGEventTapEnable, CGEventMaskBit, CFRunLoopRun, CGEventGetIntegerValueField, CGEventPostToPid, kCGEventKeyDown, kCGEventKeyUp, kCGEventFlagsChanged, kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTargetUnixProcessID, kCFAllocatorDefault, kCFRunLoopDefaultMode, ) pid = QCoreApplication.applicationPid() def callback(proxy, type, event, refcon): if self.pid == CGEventGetIntegerValueField( event, kCGEventTargetUnixProcessID): CGEventPostToPid(pid, event) return event tap = CGEventTapCreate( kCGSessionEventTap, kCGHeadInsertEventTap, 0, CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged), callback, None) if tap: source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0) self.runLoop = CFRunLoopGetCurrent() CFRunLoopAddSource(self.runLoop, source, kCFRunLoopDefaultMode) CGEventTapEnable(tap, True) CFRunLoopRun()
def cancel(self): CGEventTapEnable(self._tap, False) CFRunLoopStop(self._running_thread)
def run(self): self._running_thread = CFRunLoopGetCurrent() CFRunLoopAddSource(self._running_thread, self._source, kCFRunLoopCommonModes) CGEventTapEnable(self._tap, True) CFRunLoopRun()
def __init__(self): threading.Thread.__init__(self, name="KeyboardEventTapThread") self._loop = None self._event_queue = Queue() # Drained by event handler thread. self._suppressed_keys = set() self.key_down = lambda key: None self.key_up = lambda key: None # Returning the event means that it is passed on # for further processing by others. # # Returning None means that the event is intercepted. # # Delaying too long in returning appears to cause the # system to ignore the tap forever after # (https://github.com/openstenoproject/plover/issues/484#issuecomment-214743466). # # This motivates pushing callbacks to the other side # of a queue of received events, so that we can return # from this callback as soon as possible. def callback(proxy, event_type, event, reference): SUPPRESS_EVENT = None PASS_EVENT_THROUGH = event # Don't pass on meta events meant for this event tap. is_unexpected_event = event_type not in self._KEYBOARD_EVENTS if is_unexpected_event: if event_type == kCGEventTapDisabledByTimeout: # Re-enable the tap and hope we act faster next time CGEventTapEnable(self._tap, True) plover.log.warning( "Keystrokes may have been missed. " + "Keyboard event tap has been re-enabled. ") return SUPPRESS_EVENT # Don't intercept the event if it has modifiers, allow # Fn and Numeric flags so we can suppress the arrow and # extended (home, end, etc...) keys. suppressible_modifiers = (kCGEventFlagMaskNumericPad | kCGEventFlagMaskSecondaryFn | kCGEventFlagMaskNonCoalesced) has_nonsupressible_modifiers = \ CGEventGetFlags(event) & ~suppressible_modifiers if has_nonsupressible_modifiers: return PASS_EVENT_THROUGH keycode = CGEventGetIntegerValueField( event, kCGKeyboardEventKeycode) key = KEYCODE_TO_KEY.get(keycode) self._async_dispatch(key, event_type) if key in self._suppressed_keys: return SUPPRESS_EVENT return PASS_EVENT_THROUGH self._tap = CGEventTapCreate( kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp), callback, None) if self._tap is None: # Todo(hesky): See if there is a nice way to show the user what's # needed (or do it for them). raise Exception("Enable access for assistive devices.") CGEventTapEnable(self._tap, False)