def test_hold(self): history = [] code_a = 100 code_b = 101 code_c = 102 system_mapping.clear() system_mapping._set('a', code_a) system_mapping._set('b', code_b) system_mapping._set('c', code_c) macro_mapping = { ((EV_KEY, 1, 1), ): parse('k(a).h(k(b)).k(c)', self.mapping) } def handler(*args): history.append(args) macro_mapping[((EV_KEY, 1, 1), )].set_handler(handler) """start macro""" handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 1), None) loop = asyncio.get_event_loop() # let the mainloop run for some time so that the macro does its stuff sleeptime = 500 keystroke_sleep = config.get('macros.keystroke_sleep_ms', 10) loop.run_until_complete(asyncio.sleep(sleeptime / 1000)) self.assertTrue(active_macros[(EV_KEY, 1)].holding) self.assertTrue(active_macros[(EV_KEY, 1)].running) """stop macro""" handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 0), None) loop.run_until_complete(asyncio.sleep(keystroke_sleep * 10 / 1000)) events = calculate_event_number(sleeptime, 1, 1) self.assertGreater(len(history), events * 0.9) self.assertLess(len(history), events * 1.1) self.assertIn((code_a, 1), history) self.assertIn((code_a, 0), history) self.assertIn((code_b, 1), history) self.assertIn((code_b, 0), history) self.assertIn((code_c, 1), history) self.assertIn((code_c, 0), history) self.assertGreater(history.count((code_b, 1)), 1) self.assertGreater(history.count((code_b, 0)), 1) # it's stopped and won't write stuff anymore count_before = len(history) loop.run_until_complete(asyncio.sleep(0.2)) count_after = len(history) self.assertEqual(count_before, count_after) self.assertFalse(active_macros[(EV_KEY, 1)].holding) self.assertFalse(active_macros[(EV_KEY, 1)].running)
def test_handle_keycode_macro(self): history = [] code_a = 100 code_b = 101 system_mapping.clear() system_mapping._set('a', code_a) system_mapping._set('b', code_b) macro_mapping = { ((EV_KEY, 1, 1), ): parse('k(a)', self.mapping), ((EV_KEY, 2, 1), ): parse('r(5, k(b))', self.mapping) } macro_mapping[((EV_KEY, 1, 1), )].set_handler(lambda *args: history.append(args)) macro_mapping[((EV_KEY, 2, 1), )].set_handler(lambda *args: history.append(args)) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 1), None) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 2, 1), None) loop = asyncio.get_event_loop() sleeptime = config.get('macros.keystroke_sleep_ms', 10) * 12 # let the mainloop run for some time so that the macro does its stuff loop.run_until_complete(asyncio.sleep(sleeptime / 1000 + 0.1)) # 6 keycodes written, with down and up events self.assertEqual(len(history), 12) self.assertIn((code_a, 1), history) self.assertIn((code_a, 0), history) self.assertIn((code_b, 1), history) self.assertIn((code_b, 0), history)
def test_filter_trigger_spam(self): # test_filter_duplicates trigger = (EV_KEY, BTN_TL) _key_to_code = {((*trigger, 1), ): 51, ((*trigger, -1), ): 52} uinput = UInput() """positive""" for _ in range(1, 20): handle_keycode(_key_to_code, {}, InputEvent(*trigger, 1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*trigger, 0), uinput) self.assertEqual(len(uinput_write_history), 2) """negative""" for _ in range(1, 20): handle_keycode(_key_to_code, {}, InputEvent(*trigger, -1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*trigger, 0), uinput) self.assertEqual(len(uinput_write_history), 4) self.assertEqual(uinput_write_history[0].t, (EV_KEY, 51, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 51, 0)) self.assertEqual(uinput_write_history[2].t, (EV_KEY, 52, 1)) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 52, 0))
def test_d_pad_combination(self): ev_1 = (EV_ABS, ABS_HAT0X, 1) ev_2 = (EV_ABS, ABS_HAT0Y, -1) ev_3 = (EV_ABS, ABS_HAT0X, 0) ev_4 = (EV_ABS, ABS_HAT0Y, 0) _key_to_code = { (ev_1, ev_2): 51, (ev_2, ): 52, } uinput = UInput() # a bunch of d-pad key down events at once handle_keycode(_key_to_code, {}, InputEvent(*ev_1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_2), uinput) # (what_will_be_released, what_caused_the_key_down) self.assertEqual(unreleased.get(ev_1[:2]), ((EV_ABS, ABS_HAT0X), ev_1)) self.assertEqual(unreleased.get(ev_2[:2]), ((EV_KEY, 51), ev_2)) self.assertEqual(len(unreleased), 2) # ev_1 is unmapped and the other is the triggered combination self.assertEqual(len(uinput_write_history), 2) self.assertEqual(uinput_write_history[0].t, ev_1) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 51, 1)) # release all of them handle_keycode(_key_to_code, {}, InputEvent(*ev_3), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_4), uinput) self.assertEqual(len(unreleased), 0) self.assertEqual(len(uinput_write_history), 4) self.assertEqual(uinput_write_history[2].t, ev_3) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 51, 0))
def test_two_d_pad_macros(self): # executing two macros that stop automatically at the same time code_1 = 61 code_2 = 62 system_mapping.clear() system_mapping._set('1', code_1) system_mapping._set('2', code_2) # try two concurrent macros with D-Pad events because they are # more difficult to manage, since their only difference is their # value, and one of them is negative. down_1 = (EV_ABS, ABS_HAT0X, 1) down_2 = (EV_ABS, ABS_HAT0X, -1) repeats = 10 macro_mapping = { (down_1, ): parse(f'r({repeats}, k(1))', self.mapping), (down_2, ): parse(f'r({repeats}, k(2))', self.mapping) } history = [] def handler(*args): history.append(args) macro_mapping[(down_1, )].set_handler(handler) macro_mapping[(down_2, )].set_handler(handler) handle_keycode({}, macro_mapping, InputEvent(*down_1), None) handle_keycode({}, macro_mapping, InputEvent(*down_2), None) loop = asyncio.get_event_loop() sleeptime = config.get('macros.keystroke_sleep_ms') / 1000 loop.run_until_complete(asyncio.sleep(1.1 * repeats * 2 * sleeptime)) self.assertEqual(len(history), repeats * 4) self.assertEqual(history.count((code_1, 1)), 10) self.assertEqual(history.count((code_1, 0)), 10) self.assertEqual(history.count((code_2, 1)), 10) self.assertEqual(history.count((code_2, 0)), 10)
async def _event_consumer(self, macros, source, uinput, abs_to_rel): """Reads input events to inject keycodes or talk to the ev_abs_mapper. Can be stopped by stopping the asyncio loop. Parameters ---------- macros : int: _Macro macro with a handler that writes to the provided uinput source : evdev.InputDevice where to read keycodes from uinput : evdev.UInput where to write keycodes to abs_to_rel : bool if joystick events should be mapped to mouse movements """ logger.debug('Started injecting into %s, fd %s', uinput.device.path, uinput.fd) async for event in source.async_read_loop(): if utils.should_map_event_as_btn(source, event, self.mapping): handle_keycode(self._key_to_code, macros, event, uinput) continue is_joystick = event.type == EV_ABS and event.code in utils.JOYSTICK if abs_to_rel and is_joystick: # talks to the ev_abs_mapper via the abs_state array if event.code == evdev.ecodes.ABS_X: self.abs_state[0] = event.value elif event.code == evdev.ecodes.ABS_Y: self.abs_state[1] = event.value elif event.code == evdev.ecodes.ABS_RX: self.abs_state[2] = event.value elif event.code == evdev.ecodes.ABS_RY: self.abs_state[3] = event.value continue # forward the rest uinput.write(event.type, event.code, event.value) # this already includes SYN events, so need to syn here again logger.error('The injector for "%s" stopped early', uinput.device.path)
def test_handle_keycode(self): _key_to_code = {((EV_KEY, 1, 1), ): 101, ((EV_KEY, 2, 1), ): 102} uinput = UInput() handle_keycode(_key_to_code, {}, InputEvent(EV_KEY, 1, 1), uinput) handle_keycode(_key_to_code, {}, InputEvent(EV_KEY, 3, 1), uinput) handle_keycode(_key_to_code, {}, InputEvent(EV_KEY, 2, 1), uinput) self.assertEqual(len(uinput_write_history), 3) self.assertEqual(uinput_write_history[0].t, (EV_KEY, 101, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 3, 1)) self.assertEqual(uinput_write_history[2].t, (EV_KEY, 102, 1))
def test_hold_3(self): # test irregular input patterns code_a = 100 code_b = 101 code_c = 102 system_mapping.clear() system_mapping._set('a', code_a) system_mapping._set('b', code_b) system_mapping._set('c', code_c) macro_mapping = { ((EV_KEY, 1, 1), ): parse('k(a).h(k(b)).k(c)', self.mapping), } history = [] def handler(*args): history.append(args) macro_mapping[((EV_KEY, 1, 1), )].set_handler(handler) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 1), None) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.sleep(0.1)) for _ in range(5): self.assertTrue(active_macros[(EV_KEY, 1)].holding) self.assertTrue(active_macros[(EV_KEY, 1)].running) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 1), None) loop.run_until_complete(asyncio.sleep(0.05)) # duplicate key down events don't do anything self.assertEqual(history.count((code_a, 1)), 1) self.assertEqual(history.count((code_a, 0)), 1) self.assertEqual(history.count((code_c, 1)), 0) self.assertEqual(history.count((code_c, 0)), 0) # stop handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 0), None) loop.run_until_complete(asyncio.sleep(0.1)) self.assertEqual(history.count((code_a, 1)), 1) self.assertEqual(history.count((code_a, 0)), 1) self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) self.assertFalse(active_macros[(EV_KEY, 1)].holding) self.assertFalse(active_macros[(EV_KEY, 1)].running) # it's stopped and won't write stuff anymore count_before = len(history) loop.run_until_complete(asyncio.sleep(0.1)) count_after = len(history) self.assertEqual(count_before, count_after)
def test_ignore_hold(self): # hold as in event-value 2, not in macro-hold. # linux will generate events with value 2 after key-mapper injected # the key-press, so key-mapper doesn't need to forward them. key = (EV_KEY, KEY_A) ev_1 = (*key, 1) ev_2 = (*key, 2) ev_3 = (*key, 0) _key_to_code = { ((*key, 1), ): 21, } uinput = UInput() handle_keycode(_key_to_code, {}, InputEvent(*ev_1), uinput) for _ in range(10): handle_keycode(_key_to_code, {}, InputEvent(*ev_2), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_3), uinput) self.assertEqual(len(uinput_write_history), 2) self.assertEqual(uinput_write_history[0].t, (EV_KEY, 21, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 21, 0))
def test_ignore_disabled(self): ev_1 = (EV_ABS, ABS_HAT0Y, 1) ev_2 = (EV_ABS, ABS_HAT0Y, 0) ev_3 = (EV_ABS, ABS_HAT0X, 1) ev_4 = (EV_ABS, ABS_HAT0X, 0) ev_5 = (EV_KEY, KEY_A, 1) ev_6 = (EV_KEY, KEY_A, 0) combi_1 = (ev_5, ev_3) combi_2 = (ev_3, ev_5) _key_to_code = { (ev_1, ): 61, (ev_3, ): DISABLE_CODE, combi_1: 62, combi_2: 63 } uinput = UInput() """single keys""" # down handle_keycode(_key_to_code, {}, InputEvent(*ev_1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_3), uinput) # up handle_keycode(_key_to_code, {}, InputEvent(*ev_2), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_4), uinput) self.assertEqual(len(uinput_write_history), 2) self.assertEqual(uinput_write_history[0].t, (EV_KEY, 61, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 61, 0)) """a combination that ends in a disabled key""" # ev_5 should be forwarded and the combination triggered handle_keycode(_key_to_code, {}, InputEvent(*combi_1[0]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combi_1[1]), uinput) self.assertEqual(len(uinput_write_history), 4) self.assertEqual(uinput_write_history[2].t, (EV_KEY, KEY_A, 1)) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 62, 1)) # release the last key of the combi first, it should # release what the combination maps to event = InputEvent(combi_1[1][0], combi_1[1][1], 0) handle_keycode(_key_to_code, {}, event, uinput) self.assertEqual(len(uinput_write_history), 5) self.assertEqual(uinput_write_history[-1].t, (EV_KEY, 62, 0)) event = InputEvent(combi_1[0][0], combi_1[0][1], 0) handle_keycode(_key_to_code, {}, event, uinput) self.assertEqual(len(uinput_write_history), 6) self.assertEqual(uinput_write_history[-1].t, (EV_KEY, KEY_A, 0)) """a combination that starts with a disabled key""" # only the combination should get triggered handle_keycode(_key_to_code, {}, InputEvent(*combi_2[0]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combi_2[1]), uinput) self.assertEqual(len(uinput_write_history), 7) self.assertEqual(uinput_write_history[-1].t, (EV_KEY, 63, 1)) # release the last key of the combi first, it should # release what the combination maps to event = InputEvent(combi_2[1][0], combi_2[1][1], 0) handle_keycode(_key_to_code, {}, event, uinput) self.assertEqual(len(uinput_write_history), 8) self.assertEqual(uinput_write_history[-1].t, (EV_KEY, 63, 0)) # the first key of combi_2 is disabled, so it won't write another # key-up event event = InputEvent(combi_2[0][0], combi_2[0][1], 0) handle_keycode(_key_to_code, {}, event, uinput) self.assertEqual(len(uinput_write_history), 8)
def test_hold_two(self): # holding two macros at the same time, # the first one is triggered by a combination history = [] code_1 = 100 code_2 = 101 code_3 = 102 code_a = 103 code_b = 104 code_c = 105 system_mapping.clear() system_mapping._set('1', code_1) system_mapping._set('2', code_2) system_mapping._set('3', code_3) system_mapping._set('a', code_a) system_mapping._set('b', code_b) system_mapping._set('c', code_c) key_0 = (EV_KEY, 10) key_1 = (EV_KEY, 11) key_2 = (EV_ABS, ABS_HAT0X) down_0 = (*key_0, 1) down_1 = (*key_1, 1) down_2 = (*key_2, -1) up_0 = (*key_0, 0) up_1 = (*key_1, 0) up_2 = (*key_2, 0) macro_mapping = { (down_0, down_1): parse('k(1).h(k(2)).k(3)', self.mapping), (down_2, ): parse('k(a).h(k(b)).k(c)', self.mapping) } def handler(*args): history.append(args) macro_mapping[(down_0, down_1)].set_handler(handler) macro_mapping[(down_2, )].set_handler(handler) loop = asyncio.get_event_loop() macros_uinput = UInput() keys_uinput = UInput() # key up won't do anything handle_keycode({}, macro_mapping, InputEvent(*up_0), macros_uinput) handle_keycode({}, macro_mapping, InputEvent(*up_1), macros_uinput) handle_keycode({}, macro_mapping, InputEvent(*up_2), macros_uinput) loop.run_until_complete(asyncio.sleep(0.1)) self.assertEqual(len(active_macros), 0) """start macros""" handle_keycode({}, macro_mapping, InputEvent(*down_0), keys_uinput) self.assertEqual(keys_uinput.write_count, 1) handle_keycode({}, macro_mapping, InputEvent(*down_1), keys_uinput) handle_keycode({}, macro_mapping, InputEvent(*down_2), keys_uinput) self.assertEqual(keys_uinput.write_count, 1) # let the mainloop run for some time so that the macro does its stuff sleeptime = 500 keystroke_sleep = config.get('macros.keystroke_sleep_ms', 10) loop.run_until_complete(asyncio.sleep(sleeptime / 1000)) self.assertEqual(len(active_macros), 2) self.assertTrue(active_macros[key_1].holding) self.assertTrue(active_macros[key_1].running) self.assertTrue(active_macros[key_2].holding) self.assertTrue(active_macros[key_2].running) """stop macros""" # releasing the last key of a combination releases the whole macro handle_keycode({}, macro_mapping, InputEvent(*up_1), None) handle_keycode({}, macro_mapping, InputEvent(*up_2), None) loop.run_until_complete(asyncio.sleep(keystroke_sleep * 10 / 1000)) self.assertFalse(active_macros[key_1].holding) self.assertFalse(active_macros[key_1].running) self.assertFalse(active_macros[key_2].holding) self.assertFalse(active_macros[key_2].running) events = calculate_event_number(sleeptime, 1, 1) * 2 self.assertGreater(len(history), events * 0.9) self.assertLess(len(history), events * 1.1) self.assertIn((code_a, 1), history) self.assertIn((code_a, 0), history) self.assertIn((code_b, 1), history) self.assertIn((code_b, 0), history) self.assertIn((code_c, 1), history) self.assertIn((code_c, 0), history) self.assertIn((code_1, 1), history) self.assertIn((code_1, 0), history) self.assertIn((code_2, 1), history) self.assertIn((code_2, 0), history) self.assertIn((code_3, 1), history) self.assertIn((code_3, 0), history) self.assertGreater(history.count((code_b, 1)), 1) self.assertGreater(history.count((code_b, 0)), 1) self.assertGreater(history.count((code_2, 1)), 1) self.assertGreater(history.count((code_2, 0)), 1) # it's stopped and won't write stuff anymore count_before = len(history) loop.run_until_complete(asyncio.sleep(0.2)) count_after = len(history) self.assertEqual(count_before, count_after)
def test_hold_2(self): # test irregular input patterns code_a = 100 code_b = 101 code_c = 102 code_d = 103 system_mapping.clear() system_mapping._set('a', code_a) system_mapping._set('b', code_b) system_mapping._set('c', code_c) system_mapping._set('d', code_d) macro_mapping = { ((EV_KEY, 1, 1), ): parse('h(k(b))', self.mapping), ((EV_KEY, 2, 1), ): parse('k(c).r(1, r(1, r(1, h(k(a))))).k(d)', self.mapping), ((EV_KEY, 3, 1), ): parse('h(k(b))', self.mapping) } history = [] def handler(*args): history.append(args) macro_mapping[((EV_KEY, 1, 1), )].set_handler(handler) macro_mapping[((EV_KEY, 2, 1), )].set_handler(handler) macro_mapping[((EV_KEY, 3, 1), )].set_handler(handler) """start macro 2""" handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 2, 1), None) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.sleep(0.1)) # starting code_c written self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) # spam garbage events for _ in range(5): handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 1), None) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 3, 1), None) loop.run_until_complete(asyncio.sleep(0.05)) self.assertTrue(active_macros[(EV_KEY, 1)].holding) self.assertTrue(active_macros[(EV_KEY, 1)].running) self.assertTrue(active_macros[(EV_KEY, 2)].holding) self.assertTrue(active_macros[(EV_KEY, 2)].running) self.assertTrue(active_macros[(EV_KEY, 3)].holding) self.assertTrue(active_macros[(EV_KEY, 3)].running) # there should only be one code_c in the events, because no key # up event was ever done so the hold just continued self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) # without an key up event on 2, it won't write code_d self.assertNotIn((code_d, 1), history) self.assertNotIn((code_d, 0), history) # stop macro 2 handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 2, 0), None) loop.run_until_complete(asyncio.sleep(0.1)) # it stopped and didn't restart, so the count stays at 1 self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) # and the trailing d was written self.assertEqual(history.count((code_d, 1)), 1) self.assertEqual(history.count((code_d, 0)), 1) # it's stopped and won't write stuff anymore count_before = history.count((code_a, 1)) self.assertGreater(count_before, 1) loop.run_until_complete(asyncio.sleep(0.1)) count_after = history.count((code_a, 1)) self.assertEqual(count_before, count_after) """restart macro 2""" history = [] handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 2, 1), None) loop.run_until_complete(asyncio.sleep(0.1)) self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) # spam garbage events again, this time key-up events on all other # macros for _ in range(5): handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 0), None) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 3, 0), None) loop.run_until_complete(asyncio.sleep(0.05)) self.assertFalse(active_macros[(EV_KEY, 1)].holding) self.assertFalse(active_macros[(EV_KEY, 1)].running) self.assertTrue(active_macros[(EV_KEY, 2)].holding) self.assertTrue(active_macros[(EV_KEY, 2)].running) self.assertFalse(active_macros[(EV_KEY, 3)].holding) self.assertFalse(active_macros[(EV_KEY, 3)].running) # stop macro 2 handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 2, 0), None) loop.run_until_complete(asyncio.sleep(0.1)) # was started only once self.assertEqual(history.count((code_c, 1)), 1) self.assertEqual(history.count((code_c, 0)), 1) # and the trailing d was also written only once self.assertEqual(history.count((code_d, 1)), 1) self.assertEqual(history.count((code_d, 0)), 1) # stop all macros handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 1, 0), None) handle_keycode({}, macro_mapping, InputEvent(EV_KEY, 3, 0), None) loop.run_until_complete(asyncio.sleep(0.1)) # it's stopped and won't write stuff anymore count_before = len(history) loop.run_until_complete(asyncio.sleep(0.1)) count_after = len(history) self.assertEqual(count_before, count_after) self.assertFalse(active_macros[(EV_KEY, 1)].holding) self.assertFalse(active_macros[(EV_KEY, 1)].running) self.assertFalse(active_macros[(EV_KEY, 2)].holding) self.assertFalse(active_macros[(EV_KEY, 2)].running) self.assertFalse(active_macros[(EV_KEY, 3)].holding) self.assertFalse(active_macros[(EV_KEY, 3)].running)
def test_combination_keycode_2(self): combination_1 = ((EV_KEY, 1, 1), (EV_KEY, 2, 1), (EV_KEY, 3, 1), (EV_KEY, 4, 1)) combination_2 = ( # should not be triggered, combination_1 should be prioritized # when all of its keys are down (EV_KEY, 2, 1), (EV_KEY, 3, 1), (EV_KEY, 4, 1)) down_5 = (EV_KEY, 5, 1) up_5 = (EV_KEY, 5, 0) up_4 = (EV_KEY, 4, 0) _key_to_code = { combination_1: 101, combination_2: 102, (down_5, ): 103 } uinput = UInput() # 10 and 11: more key-down events than needed handle_keycode(_key_to_code, {}, InputEvent(EV_KEY, 10, 1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination_1[0]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination_1[1]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination_1[2]), uinput) handle_keycode(_key_to_code, {}, InputEvent(EV_KEY, 11, 1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination_1[3]), uinput) self.assertEqual(len(uinput_write_history), 6) # the first event is written and then the triggered combination self.assertEqual(uinput_write_history[1].t, (EV_KEY, 1, 1)) self.assertEqual(uinput_write_history[2].t, (EV_KEY, 2, 1)) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 3, 1)) self.assertEqual(uinput_write_history[5].t, (EV_KEY, 101, 1)) # while the combination is down, another unrelated key can be used handle_keycode(_key_to_code, {}, InputEvent(*down_5), uinput) # the keycode_mapper searches for subsets of the current held-down # keys to activate combinations, down_5 should not trigger them # again. self.assertEqual(len(uinput_write_history), 7) self.assertEqual(uinput_write_history[6].t, (EV_KEY, 103, 1)) # release the combination by releasing the last key, and release # the unrelated key handle_keycode(_key_to_code, {}, InputEvent(*up_4), uinput) handle_keycode(_key_to_code, {}, InputEvent(*up_5), uinput) self.assertEqual(len(uinput_write_history), 9) self.assertEqual(uinput_write_history[7].t, (EV_KEY, 101, 0)) self.assertEqual(uinput_write_history[8].t, (EV_KEY, 103, 0))
def test_combination_keycode(self): combination = ((EV_KEY, 1, 1), (EV_KEY, 2, 1)) _key_to_code = {combination: 101} uinput = UInput() handle_keycode(_key_to_code, {}, InputEvent(*combination[0]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination[1]), uinput) self.assertEqual(len(uinput_write_history), 2) # the first event is written and then the triggered combination self.assertEqual(uinput_write_history[0].t, (EV_KEY, 1, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 101, 1)) # release them handle_keycode(_key_to_code, {}, InputEvent(*combination[0][:2], 0), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination[1][:2], 0), uinput) # the first key writes its release event. The second key is hidden # behind the executed combination. The result of the combination is # also released, because it acts like a key. self.assertEqual(len(uinput_write_history), 4) self.assertEqual(uinput_write_history[2].t, (EV_KEY, 1, 0)) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 101, 0)) # press them in the wrong order (the wrong key at the end, the order # of all other keys won't matter). no combination should be triggered handle_keycode(_key_to_code, {}, InputEvent(*combination[1]), uinput) handle_keycode(_key_to_code, {}, InputEvent(*combination[0]), uinput) self.assertEqual(len(uinput_write_history), 6) self.assertEqual(uinput_write_history[4].t, (EV_KEY, 2, 1)) self.assertEqual(uinput_write_history[5].t, (EV_KEY, 1, 1))
def test_d_pad(self): ev_1 = (EV_ABS, ABS_HAT0X, 1) ev_2 = (EV_ABS, ABS_HAT0X, -1) ev_3 = (EV_ABS, ABS_HAT0X, 0) ev_4 = (EV_ABS, ABS_HAT0Y, 1) ev_5 = (EV_ABS, ABS_HAT0Y, -1) ev_6 = (EV_ABS, ABS_HAT0Y, 0) _key_to_code = { (ev_1, ): 51, (ev_2, ): 52, (ev_4, ): 54, (ev_5, ): 55, } uinput = UInput() # a bunch of d-pad key down events at once handle_keycode(_key_to_code, {}, InputEvent(*ev_1), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_4), uinput) self.assertEqual(len(unreleased), 2) self.assertEqual(unreleased.get(ev_1[:2]), ((EV_KEY, _key_to_code[(ev_1, )]), ev_1)) self.assertEqual(unreleased.get(ev_4[:2]), ((EV_KEY, _key_to_code[(ev_4, )]), ev_4)) # release all of them handle_keycode(_key_to_code, {}, InputEvent(*ev_3), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_6), uinput) self.assertEqual(len(unreleased), 0) # repeat with other values handle_keycode(_key_to_code, {}, InputEvent(*ev_2), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_5), uinput) self.assertEqual(len(unreleased), 2) self.assertEqual(unreleased.get(ev_2[:2]), ((EV_KEY, _key_to_code[(ev_2, )]), ev_2)) self.assertEqual(unreleased.get(ev_5[:2]), ((EV_KEY, _key_to_code[(ev_5, )]), ev_5)) # release all of them again handle_keycode(_key_to_code, {}, InputEvent(*ev_3), uinput) handle_keycode(_key_to_code, {}, InputEvent(*ev_6), uinput) self.assertEqual(len(unreleased), 0) self.assertEqual(len(uinput_write_history), 8) self.assertEqual(uinput_write_history[0].t, (EV_KEY, 51, 1)) self.assertEqual(uinput_write_history[1].t, (EV_KEY, 54, 1)) self.assertEqual(uinput_write_history[2].t, (EV_KEY, 51, 0)) self.assertEqual(uinput_write_history[3].t, (EV_KEY, 54, 0)) self.assertEqual(uinput_write_history[4].t, (EV_KEY, 52, 1)) self.assertEqual(uinput_write_history[5].t, (EV_KEY, 55, 1)) self.assertEqual(uinput_write_history[6].t, (EV_KEY, 52, 0)) self.assertEqual(uinput_write_history[7].t, (EV_KEY, 55, 0))