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 ) })
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)
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)
def do2(event): return utils.normalize_value(event, (MIN_ABS, MAX_ABS))
def do(event): return utils.normalize_value(event, (0, MAX_ABS))