def test_autoload_other_path(self): devices = ['device 1', 'device 2'] presets = ['bar123', 'bar2'] config_dir = os.path.join(tmp, 'qux', 'quux') paths = [ os.path.join(config_dir, 'presets', devices[0], presets[0] + '.json'), os.path.join(config_dir, 'presets', devices[1], presets[1] + '.json') ] Mapping().save(paths[0]) Mapping().save(paths[1]) daemon = Daemon() start_history = [] daemon.start_injecting = lambda *args: start_history.append(args) config.path = os.path.join(config_dir, 'config.json') config.load_config() config.set_autoload_preset(devices[0], presets[0]) config.set_autoload_preset(devices[1], presets[1]) config.save_config() control( options('autoload', config_dir, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 2) self.assertEqual(start_history[0], (devices[0], presets[0])) self.assertEqual(start_history[1], (devices[1], presets[1]))
def test_autoload_other_path(self): device_names = ['Foo Device', 'Bar Device'] groups_ = [groups.find(name=name) for name in device_names] presets = ['bar123', 'bar2'] config_dir = os.path.join(tmp, 'qux', 'quux') paths = [ os.path.join(config_dir, 'presets', device_names[0], presets[0] + '.json'), os.path.join(config_dir, 'presets', device_names[1], presets[1] + '.json') ] Mapping().save(paths[0]) Mapping().save(paths[1]) daemon = Daemon() start_history = [] daemon.start_injecting = lambda *args: start_history.append(args) config.path = os.path.join(config_dir, 'config.json') config.load_config() config.set_autoload_preset(device_names[0], presets[0]) config.set_autoload_preset(device_names[1], presets[1]) config.save_config() communicate( options('autoload', config_dir, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 2) self.assertEqual(start_history[0], (groups_[0].key, presets[0])) self.assertEqual(start_history[1], (groups_[1].key, presets[1]))
def test_autoload(self): devices = ['device 1234', 'device 2345'] presets = ['preset', 'bar'] paths = [ get_preset_path(devices[0], presets[0]), get_preset_path(devices[1], presets[1]) ] xmodmap = 'a/xmodmap.json' Mapping().save(paths[0]) Mapping().save(paths[1]) daemon = Daemon() start_history = [] stop_history = [] daemon.start_injecting = lambda *args: start_history.append(args) daemon.stop = lambda *args: stop_history.append(args) config.set_autoload_preset(devices[0], presets[0]) config.set_autoload_preset(devices[1], presets[1]) control(options('autoload', None, None, False, False), daemon, xmodmap) self.assertEqual(len(start_history), 2) self.assertEqual(len(stop_history), 1) self.assertEqual(start_history[0], (devices[0], os.path.expanduser(paths[0]), xmodmap)) self.assertEqual(start_history[1], (devices[1], os.path.abspath(paths[1]), xmodmap))
def test_store_permutations_for_macros(self): mapping = Mapping() ev_1 = (EV_KEY, 41, 1) ev_2 = (EV_KEY, 42, 1) ev_3 = (EV_KEY, 43, 1) # a combination mapping.change(Key(ev_1, ev_2, ev_3), 'k(a)') self.injector = Injector(groups.find(key='Foo Device 2'), mapping) history = [] class Stop(Exception): pass def _construct_capabilities(*args): history.append(args) # avoid going into any mainloop raise Stop() with mock.patch.object(self.injector, '_construct_capabilities', _construct_capabilities): try: self.injector.run() except Stop: pass # one call self.assertEqual(len(history), 1) # first argument of the first call macros = self.injector.context.macros self.assertEqual(len(macros), 2) self.assertEqual(macros[(ev_1, ev_2, ev_3)].code, 'k(a)') self.assertEqual(macros[(ev_2, ev_1, ev_3)].code, 'k(a)')
def test_modify_capabilities(self): class FakeDevice: def capabilities(self, absinfo=True): assert absinfo is False return { evdev.ecodes.EV_SYN: [1, 2, 3], evdev.ecodes.EV_FF: [1, 2, 3], evdev.ecodes.EV_ABS: [1, 2, 3] } mapping = Mapping() mapping.change(Key(EV_KEY, 80, 1), 'a') mapping.change(Key(EV_KEY, 81, 1), DISABLE_NAME) macro_code = 'r(2, m(sHiFt_l, r(2, k(1).k(2))))' macro = parse(macro_code, mapping) mapping.change(Key(EV_KEY, 60, 111), macro_code) # going to be ignored, because EV_REL cannot be mapped, that's # mouse movements. mapping.change(Key(EV_REL, 1234, 3), 'b') a = system_mapping.get('a') shift_l = system_mapping.get('ShIfT_L') one = system_mapping.get(1) two = system_mapping.get('2') btn_left = system_mapping.get('BtN_lEfT') self.injector = KeycodeInjector('foo', mapping) fake_device = FakeDevice() capabilities_1 = self.injector._modify_capabilities({60: macro}, fake_device, abs_to_rel=False) self.assertIn(EV_KEY, capabilities_1) keys = capabilities_1[EV_KEY] self.assertIn(a, keys) self.assertIn(one, keys) self.assertIn(two, keys) self.assertIn(shift_l, keys) self.assertNotIn(DISABLE_CODE, keys) # abs_to_rel is false, so mouse capabilities are not needed self.assertNotIn(btn_left, keys) self.assertNotIn(evdev.ecodes.EV_SYN, capabilities_1) self.assertNotIn(evdev.ecodes.EV_FF, capabilities_1) self.assertNotIn(evdev.ecodes.EV_REL, capabilities_1) self.assertNotIn(evdev.ecodes.EV_ABS, capabilities_1) # abs_to_rel makes sure that BTN_LEFT is present capabilities_2 = self.injector._modify_capabilities({60: macro}, fake_device, abs_to_rel=True) keys = capabilities_2[EV_KEY] self.assertIn(a, keys) self.assertIn(one, keys) self.assertIn(two, keys) self.assertIn(shift_l, keys) self.assertIn(btn_left, keys)
def test_save_load_2(self): # loads mappings with only (type, code) as the key by using 1 as value, # loads combinations chained with + path = os.path.join(tmp, 'presets', 'device 1', 'test.json') os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'w') as file: json.dump( { 'mapping': { f'{EV_KEY},3': 'a', f'{EV_ABS},{ABS_HAT0X},-1': 'b', f'{EV_ABS},1,1+{EV_ABS},2,-1+{EV_ABS},3,1': 'c', # ignored because broken f'3,1,1,2': 'e', f'3': 'e', f',,+3,1,2': 'g', f'': 'h', } }, file) loaded = Mapping() self.assertEqual(loaded.num_saved_keys, 0) loaded.load(get_preset_path('device 1', 'test')) self.assertEqual(len(loaded), 3) self.assertEqual(loaded.num_saved_keys, 3) self.assertEqual(loaded.get_character(Key(EV_KEY, 3, 1)), 'a') self.assertEqual(loaded.get_character(Key(EV_ABS, ABS_HAT0X, -1)), 'b') self.assertEqual( loaded.get_character( Key((EV_ABS, 1, 1), (EV_ABS, 2, -1), Key(EV_ABS, 3, 1))), 'c')
def test_store_permutations_for_macros(self): mapping = Mapping() ev_1 = (EV_KEY, 41, 1) ev_2 = (EV_KEY, 42, 1) ev_3 = (EV_KEY, 43, 1) # a combination mapping.change(Key(ev_1, ev_2, ev_3), 'k(a)') self.injector = KeycodeInjector('device 1', mapping) history = [] class Stop(Exception): pass def _modify_capabilities(*args): history.append(args) # avoid going into any mainloop raise Stop() self.injector._modify_capabilities = _modify_capabilities try: self.injector._start_injecting() except Stop: pass # one call self.assertEqual(len(history), 1) # first argument of the first call self.assertEqual(len(history[0][0]), 2) self.assertEqual(history[0][0][(ev_1, ev_2, ev_3)].code, 'k(a)') self.assertEqual(history[0][0][(ev_2, ev_1, ev_3)].code, 'k(a)')
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_autoload(self): preset = 'preset7' group = groups.find(key='Foo Device 2') daemon = Daemon() self.daemon = daemon self.daemon.set_config_dir(get_config_path()) mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(group.get_preset_path(preset)) # no autoloading is configured yet self.daemon._autoload(group.key) self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) config.set_autoload_preset(group.key, preset) config.save_config() self.daemon.set_config_dir(get_config_path()) len_before = len(self.daemon.autoload_history._autoload_history) # now autoloading is configured, so it will autoload self.daemon._autoload(group.key) len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual( daemon.autoload_history._autoload_history[group.key][1], preset) self.assertFalse( daemon.autoload_history.may_autoload(group.key, preset)) injector = daemon.injectors[group.key] self.assertEqual(len_before + 1, len_after) # calling duplicate _autoload does nothing self.daemon._autoload(group.key) self.assertEqual( daemon.autoload_history._autoload_history[group.key][1], preset) self.assertEqual(injector, daemon.injectors[group.key]) self.assertFalse( daemon.autoload_history.may_autoload(group.key, preset)) # explicit start_injecting clears the autoload history self.daemon.start_injecting(group.key, preset) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) # calling autoload for (yet) unknown devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon._autoload('unknown-key-1234') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after) # autoloading key-mapper devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon.autoload_single('Bar Device') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after)
def start_injecting(self, device, path, xmodmap_path=None): """Start injecting the preset for the device. Returns True on success. Parameters ---------- device : string The name of the device path : string Path to the preset. The daemon, if started via systemctl, has no knowledge of the user and their home path, so the complete absolute path needs to be provided here. xmodmap_path : string, None Path to a dump of the xkb mappings, to provide more human readable keys in the correct keyboard layout to the service. The service cannot use `xmodmap -pke` because it's running via systemd. """ if device not in get_devices(): logger.debug('Devices possibly outdated, refreshing') refresh_devices() # reload the config, since it may have been changed config.load_config() if self.injectors.get(device) is not None: self.injectors[device].stop_injecting() mapping = Mapping() try: mapping.load(path) except FileNotFoundError as error: logger.error(str(error)) return False if xmodmap_path is not None: try: with open(xmodmap_path, 'r') as file: xmodmap = json.load(file) logger.debug('Using keycodes from "%s"', xmodmap_path) system_mapping.update(xmodmap) # the service now has process wide knowledge of xmodmap # keys of the users session except FileNotFoundError: logger.error('Could not find "%s"', xmodmap_path) try: injector = KeycodeInjector(device, mapping) injector.start_injecting() self.injectors[device] = injector except OSError: return False return True
def setUp(self): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) self.mapping = Mapping() device = InputDevice('/dev/input/event30') uinput = UInput() asyncio.ensure_future( ev_abs_mapper(abs_state, device, uinput, self.mapping)) config.set('gamepad.joystick.x_scroll_speed', 1) config.set('gamepad.joystick.y_scroll_speed', 1)
def test_autoload(self): device = 'device 1' preset = 'preset7' path = '/dev/input/event11' daemon = Daemon() self.daemon = daemon self.daemon.set_config_dir(get_config_path()) mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(get_preset_path(device, preset)) # no autoloading is configured yet self.daemon._autoload(device) self.daemon._autoload(path) self.assertNotIn(device, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload(device, preset)) config.set_autoload_preset(device, preset) config.save_config() self.daemon.set_config_dir(get_config_path()) len_before = len(self.daemon.autoload_history._autoload_history) self.daemon._autoload(path) len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(daemon.autoload_history._autoload_history[device][1], preset) self.assertFalse(daemon.autoload_history.may_autoload(device, preset)) injector = daemon.injectors[device] self.assertEqual(len_before + 1, len_after) # calling duplicate _autoload does nothing self.daemon._autoload(path) self.assertEqual(daemon.autoload_history._autoload_history[device][1], preset) self.assertEqual(injector, daemon.injectors[device]) self.assertFalse(daemon.autoload_history.may_autoload(device, preset)) # explicit start_injecting clears the autoload history self.daemon.start_injecting(device, preset) self.assertTrue(daemon.autoload_history.may_autoload(device, preset)) # calling autoload for (yet) unknown devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon._autoload('/dev/input/qux') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after) # autoloading key-mapper devices does nothing len_before = len(self.daemon.autoload_history._autoload_history) self.daemon.autoload_single('/dev/input/event40') len_after = len(self.daemon.autoload_history._autoload_history) self.assertEqual(len_before, len_after)
def setUp(self): class FakeDevice: def __init__(self): self._capabilities = { evdev.ecodes.EV_SYN: [1, 2, 3], evdev.ecodes.EV_FF: [1, 2, 3], EV_ABS: [(1, evdev.AbsInfo(value=None, min=None, max=1234, fuzz=None, flat=None, resolution=None)), (2, evdev.AbsInfo(value=None, min=50, max=2345, fuzz=None, flat=None, resolution=None)), 3] } def capabilities(self, absinfo=False): assert absinfo is True return self._capabilities mapping = Mapping() mapping.change(Key(EV_KEY, 80, 1), 'a') mapping.change(Key(EV_KEY, 81, 1), DISABLE_NAME) macro_code = 'r(2, m(sHiFt_l, r(2, k(1).k(2))))' macro = parse(macro_code, mapping) mapping.change(Key(EV_KEY, 60, 111), macro_code) # going to be ignored, because EV_REL cannot be mapped, that's # mouse movements. mapping.change(Key(EV_REL, 1234, 3), 'b') self.a = system_mapping.get('a') self.shift_l = system_mapping.get('ShIfT_L') self.one = system_mapping.get(1) self.two = system_mapping.get('2') self.left = system_mapping.get('BtN_lEfT') self.fake_device = FakeDevice() self.mapping = mapping self.macro = macro
def test_clone(self): ev_1 = Key(EV_KEY, 1, 1) ev_2 = Key(EV_KEY, 2, 0) mapping1 = Mapping() mapping1.change(ev_1, ' a') mapping2 = mapping1.clone() mapping1.change(ev_2, 'b ') self.assertEqual(mapping1.get_character(ev_1), 'a') self.assertEqual(mapping1.get_character(ev_2), 'b') self.assertEqual(mapping2.get_character(ev_1), 'a') self.assertIsNone(mapping2.get_character(ev_2)) self.assertIsNone(mapping2.get_character(Key(EV_KEY, 2, 3))) self.assertIsNone(mapping2.get_character(Key(EV_KEY, 1, 3)))
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_key_to_code(self): mapping = Mapping() ev_1 = (EV_KEY, 41, 1) ev_2 = (EV_KEY, 42, 1) ev_3 = (EV_KEY, 43, 1) ev_4 = (EV_KEY, 44, 1) mapping.change(Key(ev_1), 'a') # a combination mapping.change(Key(ev_2, ev_3, ev_4), 'b') self.assertEqual(mapping.get_character(Key(ev_2, ev_3, ev_4)), 'b') system_mapping.clear() system_mapping._set('a', 51) system_mapping._set('b', 52) injector = KeycodeInjector('device 1', mapping) self.assertEqual(injector._key_to_code.get((ev_1, )), 51) # permutations to make matching combinations easier self.assertEqual(injector._key_to_code.get((ev_2, ev_3, ev_4)), 52) self.assertEqual(injector._key_to_code.get((ev_3, ev_2, ev_4)), 52) self.assertEqual(len(injector._key_to_code), 3)
def test_save_load(self): one = Key(EV_KEY, 10, 1) two = Key(EV_KEY, 11, 1) three = Key(EV_KEY, 12, 1) self.mapping.change(one, '1') self.mapping.change(two, '2') self.mapping.change(Key(two, three), '3') self.mapping._config['foo'] = 'bar' self.mapping.save(get_preset_path('device 1', 'test')) path = os.path.join(tmp, 'presets', 'device 1', 'test.json') self.assertTrue(os.path.exists(path)) loaded = Mapping() self.assertEqual(len(loaded), 0) loaded.load(get_preset_path('device 1', 'test')) self.assertEqual(len(loaded), 3) self.assertEqual(loaded.get_character(one), '1') self.assertEqual(loaded.get_character(two), '2') self.assertEqual(loaded.get_character(Key(two, three)), '3') self.assertEqual(loaded._config['foo'], 'bar')
def test_key_to_code(self): mapping = Mapping() ev_1 = (EV_KEY, 41, 1) ev_2 = (EV_KEY, 42, 1) ev_3 = (EV_KEY, 43, 1) ev_4 = (EV_KEY, 44, 1) mapping.change(Key(ev_1), 'a') # a combination mapping.change(Key(ev_2, ev_3, ev_4), 'b') self.assertEqual(mapping.get_symbol(Key(ev_2, ev_3, ev_4)), 'b') system_mapping.clear() system_mapping._set('a', 51) system_mapping._set('b', 52) injector = Injector(groups.find(key='Foo Device 2'), mapping) injector.context = Context(mapping) self.assertEqual(injector.context.key_to_code.get((ev_1, )), 51) # permutations to make matching combinations easier self.assertEqual(injector.context.key_to_code.get((ev_2, ev_3, ev_4)), 52) self.assertEqual(injector.context.key_to_code.get((ev_3, ev_2, ev_4)), 52) self.assertEqual(len(injector.context.key_to_code), 3)
def test_autoload_3(self): # based on a bug preset = 'preset7' group = groups.find(key='Foo Device 2') mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(group.get_preset_path(preset)) config.set_autoload_preset(group.key, preset) config.save_config() self.daemon = Daemon() self.daemon.set_config_dir(get_config_path()) groups.set_groups([]) # caused the bug self.assertIsNone(groups.find(key='Foo Device 2')) self.daemon.autoload() # it should try to refresh the groups because all the # group_keys are unknown at the moment history = self.daemon.autoload_history._autoload_history self.assertEqual(history[group.key][1], preset) self.assertEqual(self.daemon.get_state(group.key), STARTING) self.assertIsNotNone(groups.find(key='Foo Device 2'))
def test_autoload_2(self): self.daemon = Daemon() history = self.daemon.autoload_history._autoload_history # existing device preset = 'preset7' group = groups.find(key='Foo Device 2') mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(group.get_preset_path(preset)) config.set_autoload_preset(group.key, preset) # ignored, won't cause problems: config.set_autoload_preset('non-existant-key', 'foo') # daemon is missing the config directory yet self.daemon.autoload() self.assertEqual(len(history), 0) config.save_config() self.daemon.set_config_dir(get_config_path()) self.daemon.autoload() self.assertEqual(len(history), 1) self.assertEqual(history[group.key][1], preset)
def test_autoload(self): devices = ['device 1', 'device 2'] presets = ['bar0', 'bar', 'bar2'] paths = [ get_preset_path(devices[0], presets[0]), get_preset_path(devices[1], presets[1]), get_preset_path(devices[1], presets[2]) ] Mapping().save(paths[0]) Mapping().save(paths[1]) Mapping().save(paths[2]) daemon = Daemon() start_history = [] stop_counter = 0 # using an actual injector is not within the scope of this test class Injector: def stop_injecting(self, *args, **kwargs): nonlocal stop_counter stop_counter += 1 def start_injecting(device, preset): print(f'\033[90mstart_injecting\033[0m') start_history.append((device, preset)) daemon.injectors[device] = Injector() daemon.start_injecting = start_injecting config.set_autoload_preset(devices[0], presets[0]) config.set_autoload_preset(devices[1], presets[1]) config.save_config() control(options('autoload', None, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 2) self.assertEqual(start_history[0], (devices[0], presets[0])) self.assertEqual(start_history[1], (devices[1], presets[1])) self.assertIn(devices[0], daemon.injectors) self.assertIn(devices[1], daemon.injectors) self.assertFalse( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[1])) # calling autoload again doesn't load redundantly control(options('autoload', None, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 2) self.assertEqual(stop_counter, 0) self.assertFalse( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[1])) # unless the injection in question ist stopped control(options('stop', None, None, devices[0], False, False, False), daemon) self.assertEqual(stop_counter, 1) self.assertTrue( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[1])) control(options('autoload', None, None, None, False, False, False), daemon) self.assertEqual(len(start_history), 3) self.assertEqual(start_history[2], (devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[1])) # if a device name is passed, will only start injecting for that one control(options('stop-all', None, None, None, False, False, False), daemon) self.assertTrue( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertTrue( daemon.autoload_history.may_autoload(devices[1], presets[1])) self.assertEqual(stop_counter, 3) config.set_autoload_preset(devices[1], presets[2]) config.save_config() control( options('autoload', None, None, devices[1], False, False, False), daemon) self.assertEqual(len(start_history), 4) self.assertEqual(start_history[3], (devices[1], presets[2])) self.assertTrue( daemon.autoload_history.may_autoload(devices[0], presets[0])) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[2])) # autoloading for the same device again redundantly will not autoload # again control( options('autoload', None, None, devices[1], False, False, False), daemon) self.assertEqual(len(start_history), 4) self.assertEqual(stop_counter, 3) self.assertFalse( daemon.autoload_history.may_autoload(devices[1], presets[2])) # any other arbitrary preset may be autoloaded self.assertTrue( daemon.autoload_history.may_autoload(devices[1], 'quuuux')) # after 15 seconds it may be autoloaded again daemon.autoload_history._autoload_history[devices[1]] = (time.time() - 16, presets[2]) self.assertTrue( daemon.autoload_history.may_autoload(devices[1], presets[2]))
Parameters ---------- mapping : dict maps from name to code. Make sure your keys are lowercase. """ self._mapping.update(mapping) def _set(self, name, code): """Map name to code.""" self._mapping[str(name).lower()] = code def get(self, name): """Return the code mapped to the key.""" return self._mapping.get(str(name).lower()) def clear(self): """Remove all mapped keys. Only needed for tests.""" keys = list(self._mapping.keys()) for key in keys: del self._mapping[key] # one mapping object for the GUI application custom_mapping = Mapping() # this mapping represents the xmodmap output, which stays constant system_mapping = SystemMapping() # permissions for files created in /usr _PERMISSIONS = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IROTH
def test_writes_keys(self): self.assertTrue(self.context.writes_keys()) self.assertFalse(Context(Mapping()).writes_keys())
def test_start_stop(self): group = groups.find(key='Foo Device 2') preset = 'preset8' daemon = Daemon() self.daemon = daemon mapping = Mapping() mapping.change(Key(3, 2, 1), 'a') mapping.save(group.get_preset_path(preset)) # the daemon needs set_config_dir first before doing anything daemon.start_injecting(group.key, preset) self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertNotIn(group.key, daemon.injectors) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) # start config.save_config() daemon.set_config_dir(get_config_path()) daemon.start_injecting(group.key, preset) # explicit start, not autoload, so the history stays empty self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) # path got translated to the device name self.assertIn(group.key, daemon.injectors) # start again previous_injector = daemon.injectors[group.key] self.assertNotEqual(previous_injector.get_state(), STOPPED) daemon.start_injecting(group.key, preset) self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) self.assertIn(group.key, daemon.injectors) self.assertEqual(previous_injector.get_state(), STOPPED) # a different injetor is now running self.assertNotEqual(previous_injector, daemon.injectors[group.key]) self.assertNotEqual(daemon.injectors[group.key].get_state(), STOPPED) # trying to inject a non existing preset keeps the previous inejction # alive injector = daemon.injectors[group.key] daemon.start_injecting(group.key, 'qux') self.assertEqual(injector, daemon.injectors[group.key]) self.assertNotEqual(daemon.injectors[group.key].get_state(), STOPPED) # trying to start injecting for an unknown device also just does # nothing daemon.start_injecting('quux', 'qux') self.assertNotEqual(daemon.injectors[group.key].get_state(), STOPPED) # after all that stuff autoload_history is still unharmed self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset)) # stop daemon.stop_injecting(group.key) self.assertNotIn(group.key, daemon.autoload_history._autoload_history) self.assertEqual(daemon.injectors[group.key].get_state(), STOPPED) self.assertTrue(daemon.autoload_history.may_autoload( group.key, preset))
def test_should_map_as_btn(self): mapping = Mapping() def do(gamepad, event): return utils.should_map_as_btn(event, mapping, gamepad) """D-Pad""" self.assertTrue(do(1, new_event(EV_ABS, ABS_HAT0X, 1))) self.assertTrue(do(0, new_event(EV_ABS, ABS_HAT0X, -1))) """Mouse movements""" self.assertTrue(do(1, new_event(EV_REL, REL_WHEEL, 1))) self.assertTrue(do(0, new_event(EV_REL, REL_WHEEL, -1))) self.assertTrue(do(1, new_event(EV_REL, REL_HWHEEL, 1))) self.assertTrue(do(0, new_event(EV_REL, REL_HWHEEL, -1))) self.assertFalse(do(1, new_event(EV_REL, REL_X, -1))) """regular keys and buttons""" self.assertTrue(do(1, new_event(EV_KEY, KEY_A, 1))) self.assertTrue(do(0, new_event(EV_KEY, KEY_A, 1))) self.assertTrue(do(1, new_event(EV_ABS, ABS_HAT0X, -1))) self.assertTrue(do(0, new_event(EV_ABS, ABS_HAT0X, -1))) """mousepad events""" self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_SLOT, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_SLOT, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_POSITION_X, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_POSITION_X, 1))) self.assertFalse(do(1, new_event(EV_KEY, ecodes.BTN_TOUCH, 1))) self.assertFalse(do(0, new_event(EV_KEY, ecodes.BTN_TOUCH, 1))) """stylus movements""" self.assertFalse(do(0, new_event(EV_KEY, ecodes.BTN_DIGI, 1))) self.assertFalse(do(1, new_event(EV_KEY, ecodes.BTN_DIGI, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_TILT_X, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_TILT_X, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_TILT_Y, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_TILT_Y, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_DISTANCE, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_DISTANCE, 1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_PRESSURE, 1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_PRESSURE, 1))) """joysticks""" # without a purpose of BUTTONS it won't map any button, even for # gamepads self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RX, 1234))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_RX, 1234))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_Y, -1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_Y, -1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RY, -1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_RY, -1))) mapping.set('gamepad.joystick.right_purpose', BUTTONS) config.set('gamepad.joystick.left_purpose', BUTTONS) # but only for gamepads self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_Y, -1))) self.assertTrue(do(1, new_event(EV_ABS, ecodes.ABS_Y, -1))) self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RY, -1))) self.assertTrue(do(1, new_event(EV_ABS, ecodes.ABS_RY, -1))) """weird events""" self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MISC, -1))) self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MISC, -1)))
def test_should_map_event_as_btn(self): device = InputDevice('/dev/input/event30') mapping = Mapping() # the function name is so horribly long do = utils.should_map_event_as_btn """D-Pad""" self.assertTrue(do(device, InputEvent(EV_ABS, ABS_HAT0X, 1), mapping)) self.assertTrue(do(device, InputEvent(EV_ABS, ABS_HAT0X, -1), mapping)) """Mouse movements""" self.assertFalse(do(device, InputEvent(EV_REL, REL_WHEEL, 1), mapping)) self.assertFalse(do(device, InputEvent(EV_REL, REL_WHEEL, -1), mapping)) self.assertFalse(do(device, InputEvent(EV_REL, REL_HWHEEL, 1), mapping)) self.assertFalse( do(device, InputEvent(EV_REL, REL_HWHEEL, -1), mapping)) self.assertFalse(do(device, InputEvent(EV_REL, REL_X, -1), mapping)) """regular keys and buttons""" self.assertTrue(do(device, InputEvent(EV_KEY, KEY_A, 1), mapping)) self.assertTrue(do(device, InputEvent(EV_ABS, ABS_HAT0X, -1), mapping)) """mousepad events""" self.assertFalse( do(device, InputEvent(EV_ABS, ecodes.ABS_MT_SLOT, 1), mapping)) self.assertFalse( do(device, InputEvent(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1), mapping)) self.assertFalse( do(device, InputEvent(EV_ABS, ecodes.ABS_MT_POSITION_X, 1), mapping)) """joysticks""" self.assertFalse( do(device, InputEvent(EV_ABS, ecodes.ABS_RX, 1234), mapping)) self.assertFalse( do(device, InputEvent(EV_ABS, ecodes.ABS_Y, -1), mapping)) mapping.set('gamepad.joystick.left_purpose', BUTTONS) event = InputEvent(EV_ABS, ecodes.ABS_RX, MAX_ABS) self.assertFalse(do(device, event, mapping)) self.assertEqual(event.value, MAX_ABS) event = InputEvent(EV_ABS, ecodes.ABS_Y, -MAX_ABS) self.assertTrue(do(device, event, mapping)) self.assertEqual(event.value, -1) event = InputEvent(EV_ABS, ecodes.ABS_X, -MAX_ABS / 4) self.assertTrue(do(device, event, mapping)) self.assertEqual(event.value, 0) config.set('gamepad.joystick.right_purpose', BUTTONS) event = InputEvent(EV_ABS, ecodes.ABS_RX, MAX_ABS) self.assertTrue(do(device, event, mapping)) self.assertEqual(event.value, 1) event = InputEvent(EV_ABS, ecodes.ABS_Y, MAX_ABS) self.assertTrue(do(device, event, mapping)) self.assertEqual(event.value, 1) event = InputEvent(EV_ABS, ecodes.ABS_X, MAX_ABS / 4) self.assertTrue(do(device, event, mapping)) self.assertEqual(event.value, 0)
def start_injecting(self, group_key, preset): """Start injecting the preset for the device. Returns True on success. If an injection is already ongoing for the specified device it will stop it automatically first. Parameters ---------- group_key : string The unique key of the group preset : string The name of the preset """ self.refresh(group_key) if self.config_dir is None: logger.error( 'Tried to start an injection without configuring the daemon ' 'first via set_config_dir.') return False group = groups.find(key=group_key) if group is None: logger.error('Could not find group "%s"', group_key) return False preset_path = os.path.join(self.config_dir, 'presets', group.name, f'{preset}.json') mapping = Mapping() try: mapping.load(preset_path) except FileNotFoundError as error: logger.error(str(error)) return False if self.injectors.get(group_key) is not None: self.stop_injecting(group_key) # Path to a dump of the xkb mappings, to provide more human # readable keys in the correct keyboard layout to the service. # The service cannot use `xmodmap -pke` because it's running via # systemd. xmodmap_path = os.path.join(self.config_dir, 'xmodmap.json') try: with open(xmodmap_path, 'r') as file: # do this for each injection to make sure it is up to # date when the system layout changes. xmodmap = json.load(file) logger.debug('Using keycodes from "%s"', xmodmap_path) system_mapping.update(xmodmap) # the service now has process wide knowledge of xmodmap # keys of the users session except FileNotFoundError: logger.error('Could not find "%s"', xmodmap_path) try: injector = Injector(group, mapping) injector.start() self.injectors[group.key] = injector except OSError: # I think this will never happen, probably leftover from # some earlier version return False return True
def setUp(self): self.result = [] self.loop = asyncio.get_event_loop() self.mapping = Mapping()
def setUp(self): self.mapping = Mapping()
def setUp(self): self.mapping = Mapping() self.assertFalse(self.mapping.changed)