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. That # would cause duplicate events of those values otherwise. 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() context = Context(self.mapping) context.uinput = uinput context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, self.source, uinput) keycode_mapper.handle_keycode(new_event(*ev_1)) for _ in range(10): keycode_mapper.handle_keycode(new_event(*ev_2)) self.assertIn(key, unreleased) keycode_mapper.handle_keycode(new_event(*ev_3)) self.assertNotIn(key, unreleased) 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_gamepad_purpose_none_2(self): # forward abs joystick events for the left joystick only custom_mapping.set('gamepad.joystick.left_purpose', NONE) config.set('gamepad.joystick.right_purpose', MOUSE) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event30' device = self.injector._grab_device(path) # the right joystick maps as mouse, so it is grabbed # even with an empty mapping self.assertIsNotNone(device) gamepad = classify(device) == GAMEPAD self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities) self.assertIn(EV_REL, capabilities) custom_mapping.change(Key(EV_KEY, BTN_A, 1), 'a') device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD self.assertIsNotNone(device) self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities) self.assertIn(EV_REL, capabilities) self.assertIn(EV_KEY, capabilities)
def test_handle_keycode(self): code_2 = 2 # this also makes sure that the keycode_mapper doesn't get confused # when input and output codes are the same (because it at some point # screwed it up because of that) _key_to_code = { ((EV_KEY, 1, 1),): 101, ((EV_KEY, code_2, 1),): code_2 } uinput_mapped = UInput() uinput_forwarded = UInput() context = Context(self.mapping) context.uinput = uinput_mapped context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, self.source, uinput_forwarded) keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 1)) keycode_mapper.handle_keycode(new_event(EV_KEY, 3, 1)) keycode_mapper.handle_keycode(new_event(EV_KEY, code_2, 1)) keycode_mapper.handle_keycode(new_event(EV_KEY, code_2, 0)) self.assertEqual(len(uinput_write_history), 4) self.assertEqual(uinput_mapped.write_history[0].t, (EV_KEY, 101, 1)) self.assertEqual(uinput_mapped.write_history[1].t, (EV_KEY, code_2, 1)) self.assertEqual(uinput_mapped.write_history[2].t, (EV_KEY, code_2, 0)) self.assertEqual(uinput_forwarded.write_history[0].t, (EV_KEY, 3, 1))
def test_dont_filter_unmapped(self): # if an event is not used at all, it should be written but not # furthermore modified. For example wheel events # keep reporting events of the same value without a release inbetween, # they should be forwarded. down = (EV_KEY, 91, 1) up = (EV_KEY, 91, 0) uinput = UInput() forward_to = UInput() context = Context(self.mapping) context.uinput = uinput keycode_mapper = KeycodeMapper(context, self.source, forward_to) for _ in range(10): # don't filter duplicate events if not mapped keycode_mapper.handle_keycode(new_event(*down)) self.assertEqual(unreleased[(EV_KEY, 91)].input_event_tuple, down) self.assertEqual(unreleased[(EV_KEY, 91)].target_type_code, down[:2]) self.assertEqual(len(unreleased), 1) self.assertEqual(forward_to.write_count, 10) self.assertEqual(uinput.write_count, 0) keycode_mapper.handle_keycode(new_event(*up)) self.assertEqual(len(unreleased), 0) self.assertEqual(forward_to.write_count, 11) self.assertEqual(uinput.write_count, 0)
def test_release_joystick_button(self): # with the left joystick mapped as button, it will release the mapped # key when it goes back to close to its resting position ev_1 = (3, 0, MAX_ABS // 10) # release ev_3 = (3, 0, MIN_ABS) # press uinput = UInput() _key_to_code = { ((3, 0, -1),): 73 } self.mapping.set('gamepad.joystick.left_purpose', BUTTONS) # something with gamepad capabilities source = InputDevice('/dev/input/event30') context = Context(self.mapping) context.uinput = uinput context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, source, uinput) keycode_mapper.handle_keycode(new_event(*ev_3)) keycode_mapper.handle_keycode(new_event(*ev_1)) # array of 3-tuples history = [a.t for a in uinput_write_history] self.assertIn((EV_KEY, 73, 1), history) self.assertEqual(history.count((EV_KEY, 73, 1)), 1) self.assertIn((EV_KEY, 73, 0), history) self.assertEqual(history.count((EV_KEY, 73, 0)), 1)
def test_grab_device_non_existing(self): custom_mapping.change(Key(EV_ABS, ABS_HAT0X, 1), 'a') self.injector = Injector('foobar', custom_mapping) self.injector.context = Context(custom_mapping) _grab_device = self.injector._grab_device self.assertIsNone(_grab_device('/dev/input/event1234'))
def setUp(self): self.mapping = Mapping() self.mapping.set('gamepad.joystick.left_purpose', WHEEL) self.mapping.set('gamepad.joystick.right_purpose', WHEEL) self.mapping.change(Key(1, 31, 1), 'k(a)') self.mapping.change(Key(1, 32, 1), 'b') self.mapping.change(Key((1, 33, 1), (1, 34, 1), (1, 35, 1)), 'c') self.context = Context(self.mapping)
def test_grab_device_1(self): # according to the fixtures, /dev/input/event30 can do ABS_HAT0X custom_mapping.change(Key(EV_ABS, ABS_HAT0X, 1), 'a') self.injector = Injector('foobar', custom_mapping) self.injector.context = Context(custom_mapping) _grab_device = self.injector._grab_device self.assertIsNone(_grab_device('/dev/input/event10')) self.assertIsNotNone(_grab_device('/dev/input/event30'))
def test_skip_unused_device(self): # skips a device because its capabilities are not used in the mapping custom_mapping.change(Key(EV_KEY, 10, 1), 'a') self.injector = Injector('device 1', custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event11' device = self.injector._grab_device(path) self.assertIsNone(device) self.assertEqual(self.failed, 0)
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) context = Context(self.mapping) context.macros = macro_mapping keycode_mapper = KeycodeMapper(context, self.source, None) keycode_mapper.macro_write = handler keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 1)) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.sleep(0.1)) for _ in range(5): self.assertTrue(active_macros[(EV_KEY, 1)].is_holding()) self.assertTrue(active_macros[(EV_KEY, 1)].running) keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 1)) loop.run_until_complete(asyncio.sleep(0.05)) # duplicate key down events don't do anything self.assertEqual(history.count((EV_KEY, code_a, 1)), 1) self.assertEqual(history.count((EV_KEY, code_a, 0)), 1) self.assertEqual(history.count((EV_KEY, code_c, 1)), 0) self.assertEqual(history.count((EV_KEY, code_c, 0)), 0) # stop keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 0)) loop.run_until_complete(asyncio.sleep(0.1)) self.assertEqual(history.count((EV_KEY, code_a, 1)), 1) self.assertEqual(history.count((EV_KEY, code_a, 0)), 1) self.assertEqual(history.count((EV_KEY, code_c, 1)), 1) self.assertEqual(history.count((EV_KEY, code_c, 0)), 1) self.assertFalse(active_macros[(EV_KEY, 1)].is_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_skip_unknown_device(self): custom_mapping.change(Key(EV_KEY, 10, 1), 'a') # skips a device because its capabilities are not used in the mapping self.injector = Injector('device 1', custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event11' device = self.injector._grab_device(path) # skips the device alltogether, so no grab attempts fail self.assertEqual(self.failed, 0) self.assertIsNone(device)
def test_grab_device_1(self): custom_mapping.change(Key(EV_ABS, ABS_HAT0X, 1), 'a') self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.context = Context(custom_mapping) _grab_device = self.injector._grab_device # doesn't have the required capability self.assertIsNone(_grab_device('/dev/input/event10')) # according to the fixtures, /dev/input/event30 can do ABS_HAT0X self.assertIsNotNone(_grab_device('/dev/input/event30')) # this doesn't exist self.assertIsNone(_grab_device('/dev/input/event1234'))
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. right = (EV_ABS, ABS_HAT0X, 1) release = (EV_ABS, ABS_HAT0X, 0) left = (EV_ABS, ABS_HAT0X, -1) repeats = 10 macro_mapping = { (right,): parse(f'r({repeats}, k(1))', self.mapping), (left,): parse(f'r({repeats}, k(2))', self.mapping) } history = [] def handler(*args): history.append(args) context = Context(self.mapping) context.macros = macro_mapping keycode_mapper = KeycodeMapper(context, self.source, None) keycode_mapper.macro_write = handler keycode_mapper.macro_write = handler keycode_mapper.handle_keycode(new_event(*right)) self.assertIn((EV_ABS, ABS_HAT0X), unreleased) keycode_mapper.handle_keycode(new_event(*release)) self.assertNotIn((EV_ABS, ABS_HAT0X), unreleased) keycode_mapper.handle_keycode(new_event(*left)) self.assertIn((EV_ABS, ABS_HAT0X), unreleased) 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(history.count((EV_KEY, code_1, 1)), 10) self.assertEqual(history.count((EV_KEY, code_1, 0)), 10) self.assertEqual(history.count((EV_KEY, code_2, 1)), 10) self.assertEqual(history.count((EV_KEY, code_2, 0)), 10) self.assertEqual(len(history), repeats * 4)
def test_adds_ev_key(self): # for some reason, having any EV_KEY capability is needed to # be able to control the mouse. it probably wants the mouse click. custom_mapping.change(Key(EV_KEY, BTN_A, 1), 'a') self.injector = Injector(groups.find(name='gamepad'), custom_mapping) custom_mapping.set('gamepad.joystick.left_purpose', MOUSE) self.injector.context = Context(custom_mapping) """ABS device without any key capability""" path = self.new_gamepad_path gamepad_template = copy.deepcopy(fixtures['/dev/input/event30']) fixtures[path] = { 'name': 'qux 2', 'phys': 'abcd', 'info': '1234', 'capabilities': gamepad_template['capabilities'] } del fixtures[path]['capabilities'][EV_KEY] device = self.injector._grab_device(path) # no reason to grab, BTN_A capability is missing in the device self.assertIsNone(device) """ABS device with a btn_mouse capability""" path = self.new_gamepad_path gamepad_template = copy.deepcopy(fixtures['/dev/input/event30']) fixtures[path] = { 'name': 'qux 3', 'phys': 'abcd', 'info': '1234', 'capabilities': gamepad_template['capabilities'] } fixtures[path]['capabilities'][EV_KEY].append(BTN_LEFT) fixtures[path]['capabilities'][EV_KEY].append(KEY_A) device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD capabilities = self.injector._construct_capabilities(gamepad) self.assertIn(EV_KEY, capabilities) self.assertIn(evdev.ecodes.BTN_MOUSE, capabilities[EV_KEY]) self.assertIn(evdev.ecodes.KEY_A, capabilities[EV_KEY]) """a gamepad""" path = '/dev/input/event30' device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD self.assertIn(EV_KEY, device.capabilities()) self.assertNotIn(evdev.ecodes.BTN_MOUSE, device.capabilities()[EV_KEY]) capabilities = self.injector._construct_capabilities(gamepad) self.assertIn(EV_KEY, capabilities) self.assertGreater(len(capabilities), 1) self.assertIn(evdev.ecodes.BTN_MOUSE, capabilities[EV_KEY])
def test_grab(self): # path is from the fixtures custom_mapping.change(Key(EV_KEY, 10, 1), 'a') self.injector = Injector('device 1', custom_mapping) path = '/dev/input/event10' # this test needs to pass around all other constraints of # _grab_device self.injector.context = Context(custom_mapping) device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD self.assertFalse(gamepad) self.assertEqual(self.failed, 2) # success on the third try device.name = fixtures[path]['name']
def test_construct_capabilities_buttons_buttons(self): self.mapping.change(Key(EV_KEY, 60, 1), self.macro.code) # those settings shouldn't have an effect with gamepad=False config.set('gamepad.joystick.left_purpose', BUTTONS) self.mapping.set('gamepad.joystick.right_purpose', BUTTONS) self.injector = Injector(groups.find(name='foo'), self.mapping) self.injector.context = Context(self.mapping) capabilities = self.injector._construct_capabilities(gamepad=False) self.check_keys(capabilities) self.assertNotIn(EV_ABS, capabilities) self.assertNotIn(EV_REL, capabilities)
def test_construct_capabilities_gamepad_none_none(self): self.mapping.change(Key(EV_KEY, 60, 1), self.macro.code) config.set('gamepad.joystick.left_purpose', NONE) self.mapping.set('gamepad.joystick.right_purpose', NONE) self.injector = Injector(groups.find(name='foo'), self.mapping) self.injector.context = Context(self.mapping) self.assertFalse(self.injector.context.maps_joystick()) self.assertFalse(self.injector.context.joystick_as_mouse()) self.assertFalse(self.injector.context.joystick_as_dpad()) capabilities = self.injector._construct_capabilities(gamepad=True) self.assertNotIn(EV_ABS, capabilities) self.check_keys(capabilities)
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) } context = Context(self.mapping) context.macros = macro_mapping keycode_mapper = KeycodeMapper(context, self.source, None) keycode_mapper.macro_write = lambda *args: history.append(args) keycode_mapper.macro_write = lambda *args: history.append(args) keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 1)) keycode_mapper.handle_keycode(new_event(EV_KEY, 2, 1)) 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((EV_KEY, code_a, 1), history) self.assertIn((EV_KEY, code_a, 0), history) self.assertIn((EV_KEY, code_b, 1), history) self.assertIn((EV_KEY, code_b, 0), history) # releasing stuff self.assertIn((EV_KEY, 1), unreleased) self.assertIn((EV_KEY, 2), unreleased) keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 0)) keycode_mapper.handle_keycode(new_event(EV_KEY, 2, 0)) self.assertNotIn((EV_KEY, 1), unreleased) self.assertNotIn((EV_KEY, 2), unreleased) loop.run_until_complete(asyncio.sleep(0.1)) self.assertEqual(len(history), 12)
def test_construct_capabilities_gamepad_buttons_buttons(self): self.mapping.change(Key((EV_KEY, 60, 1)), self.macro.code) config.set('gamepad.joystick.left_purpose', BUTTONS) self.mapping.set('gamepad.joystick.right_purpose', BUTTONS) self.injector = Injector('foo', self.mapping) self.injector.context = Context(self.mapping) self.assertTrue(self.injector.context.maps_joystick()) self.assertFalse(self.injector.context.joystick_as_mouse()) self.assertTrue(self.injector.context.joystick_as_dpad()) capabilities = self.injector._construct_capabilities(gamepad=True) self.check_keys(capabilities) self.assertNotIn(EV_ABS, capabilities) self.assertNotIn(EV_REL, capabilities)
def setUp(self): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) self.mapping = Mapping() self.context = Context(self.mapping) uinput = UInput() self.context.uinput = uinput device = InputDevice('/dev/input/event30') self.event_producer = EventProducer(self.context) self.event_producer.set_abs_range_from(device) asyncio.ensure_future(self.event_producer.run()) config.set('gamepad.joystick.x_scroll_speed', 1) config.set('gamepad.joystick.y_scroll_speed', 1)
def test_construct_capabilities(self): self.mapping.change(Key(EV_KEY, 60, 1), self.macro.code) self.injector = Injector(groups.find(name='foo'), self.mapping) self.injector.context = Context(self.mapping) capabilities = self.injector._construct_capabilities(gamepad=False) self.assertNotIn(EV_ABS, capabilities) self.check_keys(capabilities) keys = capabilities[EV_KEY] # mouse capabilities were not present in the fake_device and are # still not needed self.assertNotIn(self.left, keys) self.assertNotIn(evdev.ecodes.EV_SYN, capabilities) self.assertNotIn(evdev.ecodes.EV_FF, capabilities) self.assertNotIn(EV_REL, capabilities)
def test_not_forward(self): down = (EV_KEY, 91, 1) up = (EV_KEY, 91, 0) uinput = UInput() context = Context(self.mapping) context.uinput = uinput keycode_mapper = KeycodeMapper(context, self.source, uinput) keycode_mapper.handle_keycode(new_event(*down), forward=False) self.assertEqual(unreleased[(EV_KEY, 91)].input_event_tuple, down) self.assertEqual(unreleased[(EV_KEY, 91)].target_type_code, down[:2]) self.assertEqual(len(unreleased), 1) self.assertEqual(uinput.write_count, 0) keycode_mapper.handle_keycode(new_event(*up), forward=False) self.assertEqual(len(unreleased), 0) self.assertEqual(uinput.write_count, 0)
def test_gamepad_purpose_none(self): # forward abs joystick events custom_mapping.set('gamepad.joystick.left_purpose', NONE) config.set('gamepad.joystick.right_purpose', NONE) self.injector = Injector(groups.find(name='gamepad'), custom_mapping) self.injector.context = Context(custom_mapping) path = '/dev/input/event30' device = self.injector._grab_device(path) self.assertIsNone(device) # no capability is used, so it won't grab custom_mapping.change(Key(EV_KEY, BTN_A, 1), 'a') device = self.injector._grab_device(path) self.assertIsNotNone(device) gamepad = classify(device) == GAMEPAD self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities)
def test_fail_grab(self): self.make_it_fail = 10 custom_mapping.change(Key(EV_KEY, 10, 1), 'a') self.injector = Injector('device 1', custom_mapping) path = '/dev/input/event10' self.injector.context = Context(custom_mapping) device = self.injector._grab_device(path) self.assertIsNone(device) self.assertGreaterEqual(self.failed, 1) self.assertEqual(self.injector.get_state(), UNKNOWN) self.injector.start() self.assertEqual(self.injector.get_state(), STARTING) # since none can be grabbed, the process will terminate. But that # actually takes quite some time. time.sleep(1.5) self.assertFalse(self.injector.is_alive()) self.assertEqual(self.injector.get_state(), NO_GRAB)
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() context = Context(self.mapping) context.uinput = uinput context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, self.source, uinput) # a bunch of d-pad key down events at once keycode_mapper.handle_keycode(new_event(*ev_1)) keycode_mapper.handle_keycode(new_event(*ev_2)) # (what_will_be_released, what_caused_the_key_down) self.assertEqual(unreleased.get(ev_1[:2]).target_type_code, (EV_ABS, ABS_HAT0X)) self.assertEqual(unreleased.get(ev_1[:2]).input_event_tuple, ev_1) self.assertEqual(unreleased.get(ev_2[:2]).target_type_code, (EV_KEY, 51)) self.assertEqual(unreleased.get(ev_2[:2]).input_event_tuple, 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 keycode_mapper.handle_keycode(new_event(*ev_3)) keycode_mapper.handle_keycode(new_event(*ev_4)) 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_filter_trigger_spam(self): # test_filter_duplicates trigger = (EV_KEY, BTN_TL) _key_to_code = { ((*trigger, 1),): 51, ((*trigger, -1),): 52 } uinput = UInput() context = Context(self.mapping) context.uinput = uinput context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, self.source, uinput) """positive""" for _ in range(1, 20): keycode_mapper.handle_keycode(new_event(*trigger, 1)) self.assertIn(trigger, unreleased) keycode_mapper.handle_keycode(new_event(*trigger, 0)) self.assertNotIn(trigger, unreleased) self.assertEqual(len(uinput_write_history), 2) """negative""" for _ in range(1, 20): keycode_mapper.handle_keycode(new_event(*trigger, -1)) self.assertIn(trigger, unreleased) keycode_mapper.handle_keycode(new_event(*trigger, 0)) self.assertNotIn(trigger, unreleased) 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_construct_capabilities_gamepad(self): self.mapping.change(Key((EV_KEY, 60, 1)), self.macro.code) config.set('gamepad.joystick.left_purpose', MOUSE) self.mapping.set('gamepad.joystick.right_purpose', WHEEL) self.injector = Injector(groups.find(name='foo'), self.mapping) self.injector.context = Context(self.mapping) self.assertTrue(self.injector.context.maps_joystick()) self.assertTrue(self.injector.context.joystick_as_mouse()) self.assertFalse(self.injector.context.joystick_as_dpad()) capabilities = self.injector._construct_capabilities(gamepad=True) self.assertNotIn(EV_ABS, capabilities) self.check_keys(capabilities) keys = capabilities[EV_KEY] # now that it is told that it is a gamepad, btn_left is inserted # to ensure the operating system interprets it as mouse. self.assertIn(self.left, keys)
def test_macro_writes_to_context_uinput(self): macro_mapping = { ((EV_KEY, 1, 1),): parse('k(a)', self.mapping) } context = Context(self.mapping) context.macros = macro_mapping context.uinput = UInput() forward_to = UInput() keycode_mapper = KeycodeMapper(context, self.source, forward_to) keycode_mapper.handle_keycode(new_event(EV_KEY, 1, 1)) loop = asyncio.get_event_loop() sleeptime = config.get('macros.keystroke_sleep_ms', 10) * 12 loop.run_until_complete(asyncio.sleep(sleeptime / 1000 + 0.1)) self.assertEqual(context.uinput.write_count, 2) # down and up self.assertEqual(forward_to.write_count, 0) keycode_mapper.handle_keycode(new_event(EV_KEY, 2, 1)) self.assertEqual(forward_to.write_count, 1)
def test_combination_keycode(self): combination = ((EV_KEY, 1, 1), (EV_KEY, 2, 1)) _key_to_code = { combination: 101 } uinput = UInput() context = Context(self.mapping) context.uinput = uinput context.key_to_code = _key_to_code keycode_mapper = KeycodeMapper(context, self.source, uinput) keycode_mapper.handle_keycode(new_event(*combination[0])) keycode_mapper.handle_keycode(new_event(*combination[1])) 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 keycode_mapper.handle_keycode(new_event(*combination[0][:2], 0)) keycode_mapper.handle_keycode(new_event(*combination[1][:2], 0)) # 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 keycode_mapper.handle_keycode(new_event(*combination[1])) keycode_mapper.handle_keycode(new_event(*combination[0])) 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_gamepad_capabilities(self): self.injector = Injector(groups.find(name='gamepad'), custom_mapping) # give the injector a reason to grab the device custom_mapping.set('gamepad.joystick.left_purpose', MOUSE) self.injector.context = Context(custom_mapping) path = '/dev/input/event30' device = self.injector._grab_device(path) gamepad = classify(device) == GAMEPAD self.assertIsNotNone(device) self.assertTrue(gamepad) capabilities = self.injector._construct_capabilities(gamepad) self.assertNotIn(EV_ABS, capabilities) self.assertIn(EV_REL, capabilities) self.assertIn(evdev.ecodes.REL_X, capabilities.get(EV_REL)) self.assertIn(evdev.ecodes.REL_Y, capabilities.get(EV_REL)) self.assertIn(evdev.ecodes.REL_WHEEL, capabilities.get(EV_REL)) self.assertIn(evdev.ecodes.REL_HWHEEL, capabilities.get(EV_REL)) self.assertIn(EV_KEY, capabilities) self.assertIn(evdev.ecodes.BTN_LEFT, capabilities[EV_KEY])