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 do_stuff(): if self.injector is not None: # discard the previous injector self.injector.stop_injecting() time.sleep(0.1) while uinput_write_history_pipe[0].poll(): uinput_write_history_pipe[0].recv() pending_events['gamepad'] = [ InputEvent(*w_down), InputEvent(*d_down), InputEvent(*w_up), InputEvent(*d_up), ] self.injector = KeycodeInjector('gamepad', custom_mapping) # the injector will otherwise skip the device because # the capabilities don't contain EV_TYPE input = InputDevice('/dev/input/event30') self.injector._prepare_device = lambda *args: (input, False) self.injector.start_injecting() uinput_write_history_pipe[0].poll(timeout=1) time.sleep(EVENT_READ_TIMEOUT * 10) return read_write_history_pipe()
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_wrong_device(self): pending_events['device 1'] = [ InputEvent(EV_KEY, CODE_1, 1), InputEvent(EV_KEY, CODE_2, 1), InputEvent(EV_KEY, CODE_3, 1) ] keycode_reader.start_reading('device 2') time.sleep(EVENT_READ_TIMEOUT * 5) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 0)
def test_reading_ignore_up(self): pending_events['device 1'] = [ InputEvent(EV_KEY, CODE_1, 0, 10), InputEvent(EV_KEY, CODE_2, 1, 11), InputEvent(EV_KEY, CODE_3, 0, 12), ] keycode_reader.start_reading('device 1') time.sleep(0.1) self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_2, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_ignore_value_2(self): # this is not a combination, because (EV_KEY CODE_3, 2) is ignored pending_events['device 1'] = [ InputEvent(EV_ABS, ABS_HAT0X, 1), InputEvent(EV_KEY, CODE_3, 2) ] keycode_reader.start_reading('device 1') wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_HAT0X, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_switch_device(self): pending_events['device 2'] = [InputEvent(EV_KEY, CODE_1, 1)] pending_events['device 1'] = [InputEvent(EV_KEY, CODE_3, 1)] keycode_reader.start_reading('device 2') time.sleep(EVENT_READ_TIMEOUT * 5) keycode_reader.start_reading('device 1') time.sleep(EVENT_READ_TIMEOUT * 5) self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3, 1)) self.assertEqual(keycode_reader.read(), None)
def test_abs_to_rel(self): # maps gamepad joystick events to mouse events config.set('gamepad.joystick.non_linearity', 1) pointer_speed = 80 config.set('gamepad.joystick.pointer_speed', pointer_speed) rel_x = evdev.ecodes.REL_X rel_y = evdev.ecodes.REL_Y # they need to sum up before something is written divisor = 10 x = MAX_ABS / pointer_speed / divisor y = MAX_ABS / pointer_speed / divisor pending_events['gamepad'] = [ InputEvent(EV_ABS, rel_x, x), InputEvent(EV_ABS, rel_y, y), InputEvent(EV_ABS, rel_x, -x), InputEvent(EV_ABS, rel_y, -y), ] self.injector = KeycodeInjector('gamepad', custom_mapping) self.injector.start_injecting() # wait for the injector to start sending, at most 1s uinput_write_history_pipe[0].poll(1) # wait a bit more for it to sum up sleep = 0.5 time.sleep(sleep) # convert the write history to some easier to manage list history = [] while uinput_write_history_pipe[0].poll(): event = uinput_write_history_pipe[0].recv() history.append((event.type, event.code, event.value)) if history[0][0] == EV_ABS: raise AssertionError( 'The injector probably just forwarded them unchanged') # movement is written at 60hz and it takes `divisor` steps to # move 1px. take it times 2 for both x and y events. self.assertGreater(len(history), 60 * sleep * 0.9 * 2 / divisor) self.assertLess(len(history), 60 * sleep * 1.1 * 2 / divisor) # those may be in arbitrary order, the injector happens to write # y first self.assertEqual(history[-1][0], EV_REL) self.assertEqual(history[-1][1], rel_x) self.assertAlmostEqual(history[-1][2], -1) self.assertEqual(history[-2][0], EV_REL) self.assertEqual(history[-2][1], rel_y) self.assertAlmostEqual(history[-2][2], -1)
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_prioritizing_3_normalize(self): # take the sign of -1234, just like in test_prioritizing_2_normalize pending_events['device 1'] = [ InputEvent(EV_ABS, ABS_HAT0X, -1234, 1234.0000), InputEvent(EV_ABS, ABS_HAT0Y, 0, 1234.0030) # ignored # this time don't release anything as well, but it's not # a combination because only one event is accepted ] keycode_reader.start_reading('device 1') wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_HAT0X, -1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_reading_ignore_duplicate_down(self): pipe = multiprocessing.Pipe() pipe[1].send(InputEvent(EV_ABS, ABS_Z, 1, 10)) keycode_reader._pipe = pipe self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_Z, 1)) self.assertEqual(keycode_reader.read(), None) pipe[1].send(InputEvent(EV_ABS, ABS_Z, 1, 10)) # still none self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_keymapper_devices(self): # Don't read from keymapper devices, their keycodes are not # representative for the original key. As long as this is not # intentionally programmed it won't even do that. But it was at some # point. pending_events['key-mapper device 2'] = [ InputEvent(EV_KEY, CODE_1, 1), InputEvent(EV_KEY, CODE_2, 1), InputEvent(EV_KEY, CODE_3, 1) ] keycode_reader.start_reading('device 2') time.sleep(EVENT_READ_TIMEOUT * 5) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 0)
def test_ignore_btn_left(self): # click events are ignored because overwriting them would render the # mouse useless, but a mouse is needed to stop the injection # comfortably. Furthermore, reading mouse events breaks clicking # around in the table. It can still be changed in the config files. pending_events['device 1'] = [ InputEvent(EV_KEY, BTN_LEFT, 1), InputEvent(EV_KEY, CODE_2, 1), InputEvent(EV_KEY, BTN_TOOL_DOUBLETAP, 1), ] keycode_reader.start_reading('device 1') time.sleep(0.1) self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_2, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 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))
def test_xmodmap_file(self): from_keycode = evdev.ecodes.KEY_A to_name = 'qux' to_keycode = 100 event = (EV_KEY, from_keycode, 1) device = 'device 2' preset = 'foo' path = get_preset_path(device, preset) custom_mapping.change(Key(event), to_name) custom_mapping.save(path) system_mapping.clear() config.set_autoload_preset(device, preset) pending_events[device] = [InputEvent(*event)] xmodmap_path = os.path.join(tmp, 'foobar.json') with open(xmodmap_path, 'w') as file: file.write(f'{{"{to_name}":{to_keycode}}}') self.daemon = Daemon() self.daemon.start_injecting(device, path, xmodmap_path) event = uinput_write_history_pipe[0].recv() self.assertEqual(event.type, EV_KEY) self.assertEqual(event.code, to_keycode) self.assertEqual(event.value, 1)
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_reads_joysticks(self): # if their purpose is "buttons" custom_mapping.set('gamepad.joystick.left_purpose', BUTTONS) pending_events['gamepad'] = [InputEvent(EV_ABS, ABS_Y, MAX_ABS)] keycode_reader.start_reading('gamepad') wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_Y, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1) keycode_reader._unreleased = {} custom_mapping.set('gamepad.joystick.left_purpose', MOUSE) pending_events['gamepad'] = [InputEvent(EV_ABS, ABS_Y, MAX_ABS)] keycode_reader.start_reading('gamepad') time.sleep(0.1) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 0)
def test_reading_1(self): # a single event pending_events['device 1'] = [InputEvent(EV_ABS, ABS_HAT0X, 1)] keycode_reader.start_reading('device 1') wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_HAT0X, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_reading_2(self): # a combination of events pending_events['device 1'] = [ InputEvent(EV_KEY, CODE_1, 1, 10000.1234), InputEvent(EV_KEY, CODE_3, 1, 10001.1234), InputEvent(EV_ABS, ABS_HAT0X, -1, 10002.1234) ] keycode_reader.start_reading('device 1') # sending anything arbitrary does not stop the pipe keycode_reader._pipe[0].send((EV_KEY, 1234)) wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), ((EV_KEY, CODE_1, 1), (EV_KEY, CODE_3, 1), (EV_ABS, ABS_HAT0X, -1))) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 3)
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)
def test_prioritizing_2_normalize(self): # a value of 1234 becomes 1 in the reader in order to properly map # it. Value like that are usually some sort of continuous trigger # value and normal for some ev_abs events. custom_mapping.set('gamepad.joystick.left_purpose', BUTTONS) pending_events['gamepad'] = [ InputEvent(EV_ABS, ABS_HAT0X, 1, 1234.0000), InputEvent(EV_ABS, ABS_MISC, 1, 1235.0000), # ignored InputEvent(EV_ABS, ABS_Y, MAX_ABS, 1235.0010), InputEvent(EV_ABS, ABS_MISC, 1, 1235.0020), # ignored InputEvent(EV_ABS, ABS_MISC, 1, 1235.0030) # ignored # this time, don't release anything. the combination should # ignore stuff as well. ] keycode_reader.start_reading('gamepad') time.sleep(0.5) wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), ((EV_ABS, ABS_HAT0X, 1), (EV_ABS, ABS_Y, 1))) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 2)
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_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_start_injecting(self): keycode_from = 9 keycode_to = 200 self.change_empty_row(Key(EV_KEY, keycode_from, 1), 'a') system_mapping.clear() system_mapping._set('a', keycode_to) pending_events['device 2'] = [ InputEvent(evdev.events.EV_KEY, keycode_from, 1), InputEvent(evdev.events.EV_KEY, keycode_from, 0) ] custom_mapping.save(get_preset_path('device 2', 'foo preset')) # use only the manipulated system_mapping os.remove(os.path.join(tmp, XMODMAP_FILENAME)) self.window.selected_device = 'device 2' self.window.selected_preset = 'foo preset' self.window.on_apply_preset_clicked(None) # the integration tests will cause the injection to be started as # processes, as intended. Luckily, recv will block until the events # are handled and pushed. # Note, that pushing events to pending_events won't work anymore # from here on because the injector processes memory cannot be # modified from here. event = uinput_write_history_pipe[0].recv() self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.code, keycode_to) self.assertEqual(event.value, 1) event = uinput_write_history_pipe[0].recv() self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.code, keycode_to) self.assertEqual(event.value, 0)
def test_prioritizing_1(self): # filter the ABS_MISC events of the wacom intuos 5 out that come # with every button press. Or more general, prioritize them # based on the event type pending_events['device 1'] = [ InputEvent(EV_ABS, ABS_HAT0X, 1, 1234.0000), InputEvent(EV_ABS, ABS_HAT0X, 0, 1234.0001), InputEvent(EV_ABS, ABS_HAT0X, 1, 1235.0000), # ignored InputEvent(EV_ABS, ABS_HAT0X, 0, 1235.0001), InputEvent(EV_KEY, KEY_COMMA, 1, 1235.0010), InputEvent(EV_KEY, KEY_COMMA, 0, 1235.0011), InputEvent(EV_ABS, ABS_HAT0X, 1, 1235.0020), # ignored InputEvent(EV_ABS, ABS_HAT0X, 0, 1235.0021), # ignored InputEvent(EV_ABS, ABS_HAT0X, 1, 1236.0000) ] keycode_reader.start_reading('device 1') wait(keycode_reader._pipe[0].poll, 0.5) self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_HAT0X, 1)) self.assertEqual(keycode_reader.read(), None) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_stop_injecting(self): keycode_from = 16 keycode_to = 90 self.change_empty_row(Key(EV_KEY, keycode_from, 1), 't') system_mapping.clear() system_mapping._set('t', keycode_to) # not all of those events should be processed, since that takes some # time due to time.sleep in the fakes and the injection is stopped. pending_events['device 2'] = [InputEvent(1, keycode_from, 1)] * 100 custom_mapping.save(get_preset_path('device 2', 'foo preset')) self.window.selected_device = 'device 2' self.window.selected_preset = 'foo preset' self.window.on_apply_preset_clicked(None) pipe = uinput_write_history_pipe[0] # block until the first event is available, indicating that # the injector is ready write_history = [pipe.recv()] # stop self.window.on_apply_system_layout_clicked(None) # try to receive a few of the events time.sleep(0.2) while pipe.poll(): write_history.append(pipe.recv()) len_before = len(write_history) self.assertLess(len(write_history), 50) # since the injector should not be running anymore, no more events # should be received after waiting even more time time.sleep(0.2) while pipe.poll(): write_history.append(pipe.recv()) self.assertEqual(len(write_history), len_before)
def test_refresh_devices_on_start(self): ev = (EV_KEY, 9) keycode_to = 100 device = '9876 name' # this test only makes sense if this device is unknown yet self.assertIsNone(get_devices().get(device)) custom_mapping.change(Key(*ev, 1), 'a') system_mapping.clear() system_mapping._set('a', keycode_to) preset = 'foo' custom_mapping.save(get_preset_path(device, preset)) config.set_autoload_preset(device, preset) pending_events[device] = [InputEvent(*ev, 1)] self.daemon = Daemon() preset_path = get_preset_path(device, preset) # make sure the devices are populated get_devices() fixtures[self.new_fixture] = { 'capabilities': { evdev.ecodes.EV_KEY: [ev[1]] }, 'phys': '9876 phys', 'info': 'abcd', 'name': device } self.daemon.start_injecting(device, preset_path) # test if the injector called refresh_devices successfully self.assertIsNotNone(get_devices().get(device)) event = uinput_write_history_pipe[0].recv() self.assertEqual(event.type, EV_KEY) self.assertEqual(event.code, keycode_to) self.assertEqual(event.value, 1) self.daemon.stop_injecting(device) self.assertFalse(self.daemon.is_injecting(device))