Esempio n. 1
0
        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
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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()
Esempio n. 8
0
    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)