def __init__(self):
     self._layout = KeyboardLayout()
class KeyboardEmulation(object):

    RAW_PRESS, STRING_PRESS = range(2)

    def __init__(self):
        self._layout = KeyboardLayout()

    @staticmethod
    def send_backspaces(number_of_backspaces):
        for _ in range(number_of_backspaces):
            backspace_down = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, BACK_SPACE, True)
            backspace_up = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, BACK_SPACE, False)
            CGEventPost(kCGSessionEventTap, backspace_down)
            CGEventPost(kCGSessionEventTap, backspace_up)

    def send_string(self, s):
        """

        Args:
            s: The string to emulate.

        We can send keys by keycodes or by SetUnicodeString.
        Setting the string is less ideal, but necessary for things like emoji.
        We want to try to group modifier presses, where convenient.
        So, a string like 'THIS dog [dog emoji]' might be processed like:
            'Raw: Shift down t h i s shift up,
            Raw: space d o g space,
            String: [dog emoji]'
        There are 3 groups, the shifted group, the spaces and dog string,
        and the emoji.
        """
        # Key plan will store the type of output
        # (raw keycodes versus setting string)
        # and the list of keycodes or the goal character.
        key_plan = []

        # apply_raw's properties are used to store
        # the current keycode sequence,
        # and add the sequence to the key plan when called as a function.
        def apply_raw():
            if hasattr(apply_raw, "sequence") and len(apply_raw.sequence) is not 0:
                apply_raw.sequence.extend(apply_raw.release_modifiers)
                key_plan.append((self.RAW_PRESS, apply_raw.sequence))
            apply_raw.sequence = []
            apply_raw.release_modifiers = []

        apply_raw()

        last_modifier = None
        for c in characters(s):
            for keycode, modifier in self._layout.char_to_key_sequence(c):
                if keycode is not None:
                    if modifier is not last_modifier:
                        # Flush on modifier change.
                        apply_raw()
                        last_modifier = modifier
                        modifier_keycodes = self._modifier_to_keycodes(modifier)
                        apply_raw.sequence.extend(down(modifier_keycodes))
                        apply_raw.release_modifiers.extend(up(modifier_keycodes))
                    apply_raw.sequence.extend(down_up([keycode]))
                else:
                    # Flush on type change.
                    apply_raw()
                    key_plan.append((self.STRING_PRESS, c))
        # Flush after string is complete.
        apply_raw()

        # We have a key plan for the whole string, grouping modifiers.
        for press_type, sequence in key_plan:
            if press_type is self.STRING_PRESS:
                self._send_string_press(sequence)
            elif press_type is self.RAW_PRESS:
                self._send_sequence(sequence)

    @staticmethod
    def _send_string_press(c):
        event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, True)
        KeyboardEmulation._set_event_string(event, c)
        CGEventPost(kCGSessionEventTap, event)
        event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, False)
        KeyboardEmulation._set_event_string(event, c)
        CGEventPost(kCGSessionEventTap, event)

    def send_key_combination(self, combo_string):
        """Emulate a sequence of key combinations.

        Args:
            combo_string: A string representing a sequence of key
                combinations. Keys are represented by their names in the
                Xlib.XK module, without the 'XK_' prefix. For example, the
                left Alt key is represented by 'Alt_L'. Keys are either
                separated by a space or a left or right parenthesis.
                Parentheses must be properly formed in pairs and may be
                nested. A key immediately followed by a parenthetical
                indicates that the key is pressed down while all keys enclosed
                in the parenthetical are pressed and released in turn. For
                example, Alt_L(Tab) means to hold the left Alt key down, press
                and release the Tab key, and then release the left Alt key.

        """

        def name_to_code(name):
            # Static key codes
            code = KEYNAME_TO_KEYCODE.get(name)
            if code is not None:
                pass
            # Dead keys
            elif name.startswith("dead_"):
                code, mod = self._layout.deadkey_symbol_to_key_sequence(DEADKEY_SYMBOLS.get(name))[0]
            # Normal keys
            else:
                char = KEYNAME_TO_CHAR.get(name, name)
                code, mods = self._layout.char_to_key_sequence(char)[0]
            return code

        # Parse and validate combo.
        key_events = parse_key_combo(combo_string, name_to_code)
        # Send events...
        self._send_sequence(key_events)

    @staticmethod
    def _modifier_to_keycodes(modifier):
        keycodes = []
        if modifier & 16:
            keycodes.append(KEYNAME_TO_KEYCODE["control_r"])
        if modifier & 8:
            keycodes.append(KEYNAME_TO_KEYCODE["alt_r"])
        if modifier & 2:
            keycodes.append(KEYNAME_TO_KEYCODE["shift_r"])
        if modifier & 1:
            keycodes.append(KEYNAME_TO_KEYCODE["super_r"])
        return keycodes

    @staticmethod
    def _set_event_string(event, s):
        nb_utf16_codepoints = len(s.encode("utf-16-le")) // 2
        CGEventKeyboardSetUnicodeString(event, nb_utf16_codepoints, s)

    MODS_MASK = (
        kCGEventFlagMaskAlternate
        | kCGEventFlagMaskControl
        | kCGEventFlagMaskShift
        | kCGEventFlagMaskCommand
        | kCGEventFlagMaskSecondaryFn
    )

    @staticmethod
    def _get_media_event(key_id, key_down):
        # Credit: https://gist.github.com/fredrikw/4078034
        flags = 0xA00 if key_down else 0xB00
        return NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(  # nopep8: We can't make this line shorter!
            NSSystemDefined, (0, 0), flags, 0, 0, 0, 8, (key_id << 16) | flags, -1
        ).CGEvent()

    @staticmethod
    def _send_sequence(sequence):
        # There is a bug in the event system that seems to cause inconsistent
        # modifiers on key events:
        # http://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events
        # My solution is to manage the state myself.
        # I'm not sure how to deal with caps lock.
        # If mods_flags is not zero at the end then bad things might happen.
        mods_flags = 0

        for keycode, key_down in sequence:
            if keycode >= NX_KEY_OFFSET:
                # Handle media (NX) key.
                event = KeyboardEmulation._get_media_event(keycode - NX_KEY_OFFSET, key_down)
            else:
                # Handle regular keycode.
                if not key_down and keycode in MODIFIER_KEYS_TO_MASKS:
                    mods_flags &= ~MODIFIER_KEYS_TO_MASKS[keycode]

                event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, keycode, key_down)

                if key_down and keycode not in MODIFIER_KEYS_TO_MASKS:
                    event_flags = CGEventGetFlags(event)
                    # Add wanted flags, remove unwanted flags.
                    goal_flags = (event_flags & ~KeyboardEmulation.MODS_MASK) | mods_flags
                    if event_flags != goal_flags:
                        CGEventSetFlags(event, goal_flags)

                    # Half millisecond pause after key down.
                    sleep(0.0005)
                if key_down and keycode in MODIFIER_KEYS_TO_MASKS:
                    mods_flags |= MODIFIER_KEYS_TO_MASKS[keycode]

            CGEventPost(kCGSessionEventTap, event)
Beispiel #3
0
class KeyboardEmulation:

    RAW_PRESS, STRING_PRESS = range(2)

    def __init__(self):
        self._layout = KeyboardLayout()
        self._time_between_key_presses = 0

    def set_time_between_key_presses(self, ms):
        self._time_between_key_presses = ms

    def send_backspaces(self, number_of_backspaces):
        for _ in range(number_of_backspaces):
            backspace_down = CGEventCreateKeyboardEvent(
                OUTPUT_SOURCE, BACK_SPACE, True)
            backspace_up = CGEventCreateKeyboardEvent(
                OUTPUT_SOURCE, BACK_SPACE, False)
            CGEventPost(kCGSessionEventTap, backspace_down)
            CGEventPost(kCGSessionEventTap, backspace_up)
            if self._time_between_key_presses != 0:
                sleep(self._time_between_key_presses / 1000)

    def send_string(self, s):
        """

        Args:
            s: The string to emulate.

        We can send keys by keycodes or by SetUnicodeString.
        Setting the string is less ideal, but necessary for things like emoji.
        We want to try to group modifier presses, where convenient.
        So, a string like 'THIS dog [dog emoji]' might be processed like:
            'Raw: Shift down t h i s shift up,
            Raw: space d o g space,
            String: [dog emoji]'
        There are 3 groups, the shifted group, the spaces and dog string,
        and the emoji.
        """
        # Key plan will store the type of output
        # (raw keycodes versus setting string)
        # and the list of keycodes or the goal character.
        key_plan = []

        # apply_raw's properties are used to store
        # the current keycode sequence,
        # and add the sequence to the key plan when called as a function.
        def apply_raw():
            if hasattr(apply_raw, 'sequence') \
                    and len(apply_raw.sequence) is not 0:
                apply_raw.sequence.extend(apply_raw.release_modifiers)
                key_plan.append((self.RAW_PRESS, apply_raw.sequence))
            apply_raw.sequence = []
            apply_raw.release_modifiers = []
        apply_raw()

        last_modifier = None
        for c in s:
            for keycode, modifier in self._layout.char_to_key_sequence(c):
                if keycode is not None:
                    if modifier is not last_modifier:
                        # Flush on modifier change.
                        apply_raw()
                        last_modifier = modifier
                        modifier_keycodes = self._modifier_to_keycodes(
                            modifier)
                        apply_raw.sequence.extend(down(modifier_keycodes))
                        apply_raw.release_modifiers.extend(
                            up(modifier_keycodes))
                    apply_raw.sequence.extend(down_up([keycode]))
                else:
                    # Flush on type change.
                    last_modifier = None
                    apply_raw()
                    key_plan.append((self.STRING_PRESS, c))
        # Flush after string is complete.
        apply_raw()

        # We have a key plan for the whole string, grouping modifiers.
        for press_type, sequence in key_plan:
            if press_type is self.STRING_PRESS:
                self._send_string_press(sequence)
            elif press_type is self.RAW_PRESS:
                self._send_sequence(sequence)

    def _send_string_press(self, c):
        event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, True)
        KeyboardEmulation._set_event_string(event, c)
        CGEventPost(kCGSessionEventTap, event)
        event = CGEventCreateKeyboardEvent(OUTPUT_SOURCE, 0, False)
        KeyboardEmulation._set_event_string(event, c)
        CGEventPost(kCGSessionEventTap, event)
        if self._time_between_key_presses != 0:
            sleep(self._time_between_key_presses / 1000)

    def send_key_combination(self, combo_string):
        """Emulate a sequence of key combinations.

        Args:
            combo_string: A string representing a sequence of key
                combinations. Keys are represented by their names in the
                Xlib.XK module, without the 'XK_' prefix. For example, the
                left Alt key is represented by 'Alt_L'. Keys are either
                separated by a space or a left or right parenthesis.
                Parentheses must be properly formed in pairs and may be
                nested. A key immediately followed by a parenthetical
                indicates that the key is pressed down while all keys enclosed
                in the parenthetical are pressed and released in turn. For
                example, Alt_L(Tab) means to hold the left Alt key down, press
                and release the Tab key, and then release the left Alt key.

        """
        def name_to_code(name):
            # Static key codes
            code = KEYNAME_TO_KEYCODE.get(name)
            if code is not None:
                pass
            # Dead keys
            elif name.startswith('dead_'):
                code, mod = self._layout.deadkey_symbol_to_key_sequence(
                    DEADKEY_SYMBOLS.get(name)
                )[0]
            # Normal keys
            else:
                char = KEYNAME_TO_CHAR.get(name, name)
                code, mods = self._layout.char_to_key_sequence(char)[0]
            return code
        # Parse and validate combo.
        key_events = parse_key_combo(combo_string, name_to_code)
        # Send events...
        self._send_sequence(key_events)

    @staticmethod
    def _modifier_to_keycodes(modifier):
        keycodes = []
        if modifier & 16:
            keycodes.append(KEYNAME_TO_KEYCODE['control_r'])
        if modifier & 8:
            keycodes.append(KEYNAME_TO_KEYCODE['alt_r'])
        if modifier & 2:
            keycodes.append(KEYNAME_TO_KEYCODE['shift_r'])
        if modifier & 1:
            keycodes.append(KEYNAME_TO_KEYCODE['super_r'])
        return keycodes

    @staticmethod
    def _set_event_string(event, s):
        nb_utf16_codepoints = len(s.encode('utf-16-le')) // 2
        CGEventKeyboardSetUnicodeString(event, nb_utf16_codepoints, s)

    MODS_MASK = (
        kCGEventFlagMaskAlternate |
        kCGEventFlagMaskControl |
        kCGEventFlagMaskShift |
        kCGEventFlagMaskCommand |
        kCGEventFlagMaskSecondaryFn
    )

    @staticmethod
    def _get_media_event(key_id, key_down):
        # Credit: https://gist.github.com/fredrikw/4078034
        flags = 0xa00 if key_down else 0xb00
        return NSEvent.otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(  # nopep8: We can't make this line shorter!
            NSSystemDefined, (0, 0),
            flags,
            0, 0, 0, 8,
            (key_id << 16) | flags,
            -1
        ).CGEvent()

    def _send_sequence(self, sequence):
        # There is a bug in the event system that seems to cause inconsistent
        # modifiers on key events:
        # http://stackoverflow.com/questions/2008126/cgeventpost-possible-bug-when-simulating-keyboard-events
        # My solution is to manage the state myself.
        # I'm not sure how to deal with caps lock.
        # If mods_flags is not zero at the end then bad things might happen.
        mods_flags = 0

        for keycode, key_down in sequence:
            if keycode >= NX_KEY_OFFSET:
                # Handle media (NX) key.
                event = KeyboardEmulation._get_media_event(
                    keycode - NX_KEY_OFFSET, key_down)
            else:
                # Handle regular keycode.
                if not key_down and keycode in MODIFIER_KEYS_TO_MASKS:
                    mods_flags &= ~MODIFIER_KEYS_TO_MASKS[keycode]

                if key_down and keycode_needs_fn_mask(keycode):
                    mods_flags |= kCGEventFlagMaskSecondaryFn

                event = CGEventCreateKeyboardEvent(
                    OUTPUT_SOURCE, keycode, key_down)

                if key_down and keycode not in MODIFIER_KEYS_TO_MASKS:
                    event_flags = CGEventGetFlags(event)
                    # Add wanted flags, remove unwanted flags.
                    goal_flags = ((event_flags & ~KeyboardEmulation.MODS_MASK)
                                  | mods_flags)
                    if event_flags != goal_flags:
                        CGEventSetFlags(event, goal_flags)

                    # Half millisecond pause after key down.
                    sleep(0.0005)

                if key_down and keycode in MODIFIER_KEYS_TO_MASKS:
                    mods_flags |= MODIFIER_KEYS_TO_MASKS[keycode]

                if not key_down and keycode_needs_fn_mask(keycode):
                    mods_flags &= ~kCGEventFlagMaskSecondaryFn

            CGEventPost(kCGSessionEventTap, event)
            if self._time_between_key_presses != 0:
                sleep(self._time_between_key_presses / 1000)
Beispiel #4
0
 def __init__(self):
     self._layout = KeyboardLayout()
     self._time_between_key_presses = 0
Beispiel #5
0
 def __init__(self):
     self._layout = KeyboardLayout()