예제 #1
0
    def _send_event(self, event, device):
        """Write the event into the pipe to the main process.

        Parameters
        ----------
        event : evdev.InputEvent
        device : evdev.InputDevice
        """
        # value: 1 for down, 0 for up, 2 for hold.
        if event.type == EV_KEY and event.value == 2:
            # ignore hold-down events
            return

        blacklisted_keys = [
            evdev.ecodes.BTN_TOOL_DOUBLETAP
        ]

        if event.type == EV_KEY and event.code in blacklisted_keys:
            return

        if event.type == EV_ABS:
            abs_range = utils.get_abs_range(device, event.code)
            event.value = utils.normalize_value(event, abs_range)
        else:
            event.value = utils.normalize_value(event)

        self._results.send({
            'type': 'event',
            'message': (
                event.sec, event.usec,
                event.type, event.code, event.value
            )
        })
예제 #2
0
    def test_normalize_value(self):
        """"""
        """0 to MAX_ABS"""
        def do(event):
            return utils.normalize_value(event, (0, MAX_ABS))

        event = new_event(EV_ABS, ecodes.ABS_RX, MAX_ABS)
        self.assertEqual(do(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_Y, MAX_ABS)
        self.assertEqual(do(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_Y, 0)
        self.assertEqual(do(event), -1)
        event = new_event(EV_ABS, ecodes.ABS_X, MAX_ABS // 4)
        self.assertEqual(do(event), -1)
        event = new_event(EV_ABS, ecodes.ABS_X, MAX_ABS // 2)
        self.assertEqual(do(event), 0)
        """MIN_ABS to MAX_ABS"""

        def do2(event):
            return utils.normalize_value(event, (MIN_ABS, MAX_ABS))

        event = new_event(EV_ABS, ecodes.ABS_RX, MAX_ABS)
        self.assertEqual(do2(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_Y, MIN_ABS)
        self.assertEqual(do2(event), -1)
        event = new_event(EV_ABS, ecodes.ABS_X, MIN_ABS // 4)
        self.assertEqual(do2(event), 0)
        event = new_event(EV_ABS, ecodes.ABS_RX, MAX_ABS)
        self.assertEqual(do2(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_Y, MAX_ABS)
        self.assertEqual(do2(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_X, MAX_ABS // 4)
        self.assertEqual(do2(event), 0)
        """None"""

        # it just forwards the value
        event = new_event(EV_ABS, ecodes.ABS_RX, MAX_ABS)
        self.assertEqual(utils.normalize_value(event, None), MAX_ABS)
        """Not a joystick"""

        event = new_event(EV_ABS, ecodes.ABS_Z, 1234)
        self.assertEqual(do(event), 1)
        self.assertEqual(do2(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_Z, 0)
        self.assertEqual(do(event), 0)
        self.assertEqual(do2(event), 0)
        event = new_event(EV_ABS, ecodes.ABS_Z, -1234)
        self.assertEqual(do(event), -1)
        self.assertEqual(do2(event), -1)

        event = new_event(EV_KEY, ecodes.KEY_A, 1)
        self.assertEqual(do(event), 1)
        self.assertEqual(do2(event), 1)
        event = new_event(EV_ABS, ecodes.ABS_HAT0X, 0)
        self.assertEqual(do(event), 0)
        self.assertEqual(do2(event), 0)
        event = new_event(EV_ABS, ecodes.ABS_HAT0X, -1)
        self.assertEqual(do(event), -1)
        self.assertEqual(do2(event), -1)
예제 #3
0
    def handle_keycode(self, event, forward=True):
        """Write mapped keycodes, forward unmapped ones and manage macros.

        As long as the provided event is mapped it will handle it, it won't
        check any type, code or capability anymore. Otherwise it forwards
        it as it is.

        Parameters
        ----------
        event : evdev.InputEvent
        forward : bool
            if False, will not forward the event if it didn't trigger any
            mapping
        """
        if event.type == EV_KEY and event.value == 2:
            # button-hold event. Linux creates them on its own for the
            # injection-fake-device if the release event won't appear,
            # no need to forward or map them.
            return

        # normalize event numbers to one of -1, 0, +1. Otherwise
        # mapping trigger values that are between 1 and 255 is not
        # possible, because they might skip the 1 when pressed fast
        # enough.
        original_tuple = (event.type, event.code, event.value)
        event.value = utils.normalize_value(event, self.abs_range)

        # the tuple of the actual input event. Used to forward the event if
        # it is not mapped, and to index unreleased and active_macros. stays
        # constant
        event_tuple = (event.type, event.code, event.value)
        type_code = (event.type, event.code)
        active_macro = active_macros.get(type_code)

        key = self._get_key(event_tuple)
        is_mapped = self.context.is_mapped(key)
        """Releasing keys and macros"""

        if is_key_up(event.value):
            if active_macro is not None and active_macro.is_holding():
                # Tell the macro for that keycode that the key is released and
                # let it decide what to do with that information.
                active_macro.release_key()
                logger.key_spam(key, 'releasing macro')

            if type_code in unreleased:
                # figure out what this release event was for
                unreleased_entry = unreleased[type_code]
                target_type, target_code = unreleased_entry.target_type_code
                del unreleased[type_code]

                if target_code == DISABLE_CODE:
                    logger.key_spam(key, 'releasing disabled key')
                elif target_code is None:
                    logger.key_spam(key, 'releasing key')
                elif unreleased_entry.is_mapped():
                    # release what the input is mapped to
                    logger.key_spam(key, 'releasing %s', target_code)
                    self.write((target_type, target_code, 0))
                elif forward:
                    # forward the release event
                    logger.key_spam((original_tuple, ), 'forwarding release')
                    self.forward(original_tuple)
                else:
                    logger.key_spam(key, 'not forwarding release')
            elif event.type != EV_ABS:
                # ABS events might be spammed like crazy every time the
                # position slightly changes
                logger.key_spam(key, 'unexpected key up')

            # everything that can be released is released now
            return
        """Filtering duplicate key downs"""

        if is_mapped and is_key_down(event.value):
            # unmapped keys should not be filtered here, they should just
            # be forwarded to populate unreleased and then be written.

            if find_by_key(key) is not None:
                # this key/combination triggered stuff before.
                # duplicate key-down. skip this event. Avoid writing millions
                # of key-down events when a continuous value is reported, for
                # example for gamepad triggers or mouse-wheel-side buttons
                logger.key_spam(key, 'duplicate key down')
                return

            # it would start a macro usually
            in_macros = key in self.context.macros
            running = active_macro and active_macro.running
            if in_macros and running:
                # for key-down events and running macros, don't do anything.
                # This avoids spawning a second macro while the first one is
                # not finished, especially since gamepad-triggers report a ton
                # of events with a positive value.
                logger.key_spam(key, 'macro already running')
                return
        """starting new macros or injecting new keys"""

        if is_key_down(event.value):
            # also enter this for unmapped keys, as they might end up
            # triggering a combination, so they should be remembered in
            # unreleased

            if key in self.context.macros:
                macro = self.context.macros[key]
                active_macros[type_code] = macro
                Unreleased((None, None), event_tuple, key)
                macro.press_key()
                logger.key_spam(key, 'maps to macro %s', macro.code)
                asyncio.ensure_future(macro.run(self.macro_write))
                return

            if key in self.context.key_to_code:
                target_code = self.context.key_to_code[key]
                # remember the key that triggered this
                # (this combination or this single key)
                Unreleased((EV_KEY, target_code), event_tuple, key)

                if target_code == DISABLE_CODE:
                    logger.key_spam(key, 'disabled')
                    return

                logger.key_spam(key, 'maps to %s', target_code)
                self.write((EV_KEY, target_code, 1))
                return

            if forward:
                logger.key_spam((original_tuple, ), 'forwarding')
                self.forward(original_tuple)
            else:
                logger.key_spam((event_tuple, ), 'not forwarding')

            # unhandled events may still be important for triggering
            # combinations later, so remember them as well.
            Unreleased((event_tuple[:2]), event_tuple, None)
            return

        logger.error('%s unhandled', key)
예제 #4
0
 def do2(event):
     return utils.normalize_value(event, (MIN_ABS, MAX_ABS))
예제 #5
0
 def do(event):
     return utils.normalize_value(event, (0, MAX_ABS))