def test_save_load(self): self.assertEqual(len(config.iterate_autoload_presets()), 0) config.load_config() self.assertEqual(len(config.iterate_autoload_presets()), 0) config.set_autoload_preset('d1', 'a') config.set_autoload_preset('d2.foo', 'b') config.save_config() # ignored after load config.set_autoload_preset('d3', 'c') config.load_config() self.assertListEqual(list(config.iterate_autoload_presets()), [('d1', 'a'), ('d2.foo', 'b')]) config_2 = os.path.join(tmp, 'config_2.json') touch(config_2) with open(config_2, 'w') as f: f.write('{"a":"b"}') config.load_config(config_2) self.assertEqual(config.get("a"), "b") self.assertEqual(config.get(["a"]), "b")
def test_basic(self): self.assertEqual(config.get('a'), None) config.set('a', 1) self.assertEqual(config.get('a'), 1) config.remove('a') config.set('a.b', 2) self.assertEqual(config.get('a.b'), 2) self.assertEqual(config._config['a']['b'], 2) config.remove('a.b') config.set('a.b.c', 3) self.assertEqual(config.get('a.b.c'), 3) self.assertEqual(config._config['a']['b']['c'], 3)
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_autoload_config_dir(self): daemon = Daemon() path = os.path.join(tmp, 'foo') os.makedirs(path) with open(os.path.join(path, 'config.json'), 'w') as file: file.write('{"foo":"bar"}') self.assertIsNone(config.get('foo')) daemon.set_config_dir(path) # since daemon and this test share the same memory, the config # object that this test can access will be modified self.assertEqual(config.get('foo'), 'bar') # passing a path that doesn't exist or a path that doesn't contain # a config.json file won't do anything os.makedirs(os.path.join(tmp, 'bar')) daemon.set_config_dir(os.path.join(tmp, 'bar')) self.assertEqual(config.get('foo'), 'bar') daemon.set_config_dir(os.path.join(tmp, 'qux')) self.assertEqual(config.get('foo'), 'bar')
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_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_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 calculate_event_number(holdtime, before, after): """ Parameters ---------- holdtime : int in ms, how long was the key held down before : int how many extra k() calls are executed before h() after : int how many extra k() calls are executed after h() """ keystroke_sleep = config.get('macros.keystroke_sleep_ms', 10) # down and up: two sleeps per k # one initial k(a): events = before * 2 holdtime -= keystroke_sleep * 2 # hold events events += (holdtime / (keystroke_sleep * 2)) * 2 # one trailing k(c) events += after * 2 return events
def _autoload(self, device): """Check if autoloading is a good idea, and if so do it. Parameters ---------- device : str Device name. Expects a key that is present in get_devices(). Can also be a path starting with /dev/input/ """ self.refresh_devices(device) device = path_to_device_name(device) if device not in get_devices(): # even after refresh_devices, the device is not in # get_devices(), so it's either not relevant for key-mapper, # or not connected yet return preset = config.get(['autoload', device], log_unknown=False) if preset is None: # no autoloading is configured for this device return if not isinstance(preset, str): # might be broken due to a previous bug config.remove(['autoload', device]) config.save_config() return logger.info('Autoloading "%s"', device) if not self.autoload_history.may_autoload(device, preset): logger.info( 'Not autoloading the same preset "%s" again for device "%s"', preset, device) return self.start_injecting(device, preset) self.autoload_history.remember(device, preset)
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 _autoload(self, group_key): """Check if autoloading is a good idea, and if so do it. Parameters ---------- group_key : str unique identifier used by the groups object """ self.refresh(group_key) group = groups.find(key=group_key) if group is None: # even after groups.refresh, the device is unknown, so it's # either not relevant for key-mapper, or not connected yet return preset = config.get(['autoload', group.key], log_unknown=False) if preset is None: # no autoloading is configured for this device return if not isinstance(preset, str): # might be broken due to a previous bug config.remove(['autoload', group.key]) config.save_config() return logger.info('Autoloading for "%s"', group.key) if not self.autoload_history.may_autoload(group.key, preset): logger.info( 'Not autoloading the same preset "%s" again for group "%s"', preset, group.key) return self.start_injecting(group.key, preset) self.autoload_history.remember(group.key, preset)
def test_autoload(self): self.assertEqual(len(config.iterate_autoload_presets()), 0) self.assertFalse(config.is_autoloaded('d1', 'a')) self.assertFalse(config.is_autoloaded('d2.foo', 'b')) self.assertEqual(config.get(['autoload', 'd1']), None) self.assertEqual(config.get(['autoload', 'd2.foo']), None) config.set_autoload_preset('d1', 'a') self.assertEqual(len(config.iterate_autoload_presets()), 1) self.assertTrue(config.is_autoloaded('d1', 'a')) self.assertFalse(config.is_autoloaded('d2.foo', 'b')) config.set_autoload_preset('d2.foo', 'b') self.assertEqual(len(config.iterate_autoload_presets()), 2) self.assertTrue(config.is_autoloaded('d1', 'a')) self.assertTrue(config.is_autoloaded('d2.foo', 'b')) self.assertEqual(config.get(['autoload', 'd1']), 'a') self.assertEqual(config.get('autoload.d1'), 'a') self.assertEqual(config.get(['autoload', 'd2.foo']), 'b') config.set_autoload_preset('d2.foo', 'c') self.assertEqual(len(config.iterate_autoload_presets()), 2) self.assertTrue(config.is_autoloaded('d1', 'a')) self.assertFalse(config.is_autoloaded('d2.foo', 'b')) self.assertTrue(config.is_autoloaded('d2.foo', 'c')) self.assertEqual(config._config['autoload']['d2.foo'], 'c') self.assertListEqual(list(config.iterate_autoload_presets()), [('d1', 'a'), ('d2.foo', 'c')]) config.set_autoload_preset('d2.foo', None) self.assertTrue(config.is_autoloaded('d1', 'a')) self.assertFalse(config.is_autoloaded('d2.foo', 'b')) self.assertFalse(config.is_autoloaded('d2.foo', 'c')) self.assertListEqual(list(config.iterate_autoload_presets()), [('d1', 'a')]) self.assertEqual(config.get(['autoload', 'd1']), 'a')
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_get_default(self): config._config = {} self.assertEqual(config.get('gamepad.joystick.non_linearity'), 4) config.set('gamepad.joystick.non_linearity', 3) self.assertEqual(config.get('gamepad.joystick.non_linearity'), 3)