From edf71e82e03325b935054695771ec174d51e5c4c Mon Sep 17 00:00:00 2001 From: sezanzeb Date: Wed, 2 Dec 2020 19:33:31 +0100 Subject: [PATCH] add event type to mapping key --- keymapper/config.py | 48 ++++++++++++----- keymapper/dev/ev_abs_mapper.py | 4 +- keymapper/dev/injector.py | 20 ++++--- keymapper/dev/macros.py | 2 +- keymapper/getdevices.py | 1 + keymapper/gtk/row.py | 10 ++-- keymapper/gtk/window.py | 2 +- keymapper/mapping.py | 53 +++++++++++++----- tests/testcases/test_config.py | 8 ++- tests/testcases/test_daemon.py | 5 +- tests/testcases/test_injector.py | 18 +++---- tests/testcases/test_integration.py | 35 ++++++------ tests/testcases/test_macros.py | 6 +-- tests/testcases/test_mapping.py | 84 +++++++++++++++-------------- 14 files changed, 180 insertions(+), 116 deletions(-) diff --git a/keymapper/config.py b/keymapper/config.py index b055a14b..19777f7f 100644 --- a/keymapper/config.py +++ b/keymapper/config.py @@ -41,8 +41,13 @@ INITIAL_CONFIG = { 'keystroke_sleep_ms': 10 }, 'gamepad': { - 'non_linearity': 4, - 'pointer_speed': 80 + 'joystick': { + 'non_linearity': 4, + 'pointer_speed': 80, + }, + 'triggers': { + 'button_threshold': 0.5 + }, } } @@ -52,10 +57,15 @@ class _Config: self._config = {} self.load_config() - def _resolve(self, path, func): + def _resolve(self, path, func, config=None): """Call func for the given config value.""" chunks = path.split('.') - child = self._config + + if config is None: + child = self._config + else: + child = config + while True: chunk = chunks.pop(0) parent = child @@ -97,19 +107,27 @@ class _Config: self._resolve(path, do) - def get(self, path, default=None): - """Get a config value. + def get(self, path, log_unknown=True): + """Get a config value. If not set, return the default Parameters ---------- path : string For example 'macros.keystroke_sleep_ms' - default : any - If the configured value is not available or None, return this - instead + log_unknown : bool + If True, write an error. """ - resolved = self._resolve(path, lambda parent, child, chunk: child) - return resolved if resolved is not None else default + def do(parent, child, chunk): + return child + + resolved = self._resolve(path, do) + if resolved is None: + resolved = self._resolve(path, do, INITIAL_CONFIG) + + if resolved is None and log_unknown: + logger.error('Unknown config key "%s"', path) + + return resolved def set_autoload_preset(self, device, preset): """Set a preset to be automatically applied on start. @@ -118,7 +136,7 @@ class _Config: ---------- device : string preset : string or None - if None, don't autoload something for this device + if None, don't autoload something for this device. """ if preset is not None: self.set(f'autoload.{device}', preset) @@ -131,7 +149,7 @@ class _Config: def is_autoloaded(self, device, preset): """Should this preset be loaded automatically?""" - return self.get(f'autoload.{device}') == preset + return self.get(f'autoload.{device}', '') == preset def load_config(self): """Load the config from the file system.""" @@ -139,7 +157,9 @@ class _Config: if not os.path.exists(CONFIG_PATH): # treated like an empty config - logger.debug('Config file "%s" doesn\'t exist', CONFIG_PATH) + logger.debug('Config "%s" doesn\'t exist yet', CONFIG_PATH) + self.clear_config() + self.save_config() return with open(CONFIG_PATH, 'r') as file: diff --git a/keymapper/dev/ev_abs_mapper.py b/keymapper/dev/ev_abs_mapper.py index b2a88400..8a2ee088 100644 --- a/keymapper/dev/ev_abs_mapper.py +++ b/keymapper/dev/ev_abs_mapper.py @@ -57,8 +57,8 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): max_value = input_device.absinfo(EV_ABS).max max_speed = ((max_value ** 2) * 2) ** 0.5 - pointer_speed = config.get('gamepad.pointer_speed', 80) - non_linearity = config.get('gamepad.non_linearity', 4) + pointer_speed = config.get('gamepad.joystick.pointer_speed') + non_linearity = config.get('gamepad.joystick.non_linearity') while True: start = time.time() diff --git a/keymapper/dev/injector.py b/keymapper/dev/injector.py index e07952ec..c5f3e941 100644 --- a/keymapper/dev/injector.py +++ b/keymapper/dev/injector.py @@ -119,6 +119,13 @@ class KeycodeInjector: self._process = multiprocessing.Process(target=self._start_injecting) self._process.start() + def map_ev_to_abs(self, capabilities): + """Check if joystick movements can and should be mapped.""" + # mapping buttons only works without ABS events in the capabilities, + # possibly due to some intentional constraints in the os. So always + # do this without the option to configure, if it is possible. + return evdev.ecodes.ABS_X in capabilities.get(EV_ABS, []) + def _prepare_device(self, path): """Try to grab the device, return if not needed/possible. @@ -135,12 +142,13 @@ class KeycodeInjector: needed = False if capabilities.get(EV_KEY) is not None: - for keycode, _ in self.mapping: - if keycode - KEYCODE_OFFSET in capabilities[EV_KEY]: + for (ev_type, keycode), _ in self.mapping: + # TEST ev_type + if keycode - KEYCODE_OFFSET in capabilities.get(ev_type, []): needed = True break - map_ev_abs = evdev.ecodes.ABS_X in capabilities.get(EV_ABS, []) + map_ev_abs = self.map_ev_to_abs(capabilities) if map_ev_abs: needed = True @@ -194,10 +202,10 @@ class KeycodeInjector: if len(self.mapping) > 0 and capabilities.get(ecodes.EV_KEY) is None: capabilities[ecodes.EV_KEY] = [] - for _, character in self.mapping: + for (ev_type, _), character in self.mapping: keycode = system_mapping.get(character) if keycode is not None: - capabilities[ecodes.EV_KEY].append(keycode - KEYCODE_OFFSET) + capabilities[ev_type].append(keycode - KEYCODE_OFFSET) if map_ev_abs: del capabilities[ecodes.EV_ABS] @@ -320,7 +328,7 @@ class KeycodeInjector: # Parse all macros beforehand logger.debug('Parsing macros') macros = {} - for keycode, output in self.mapping: + for (ev_type, keycode), output in self.mapping: keycode -= KEYCODE_OFFSET if '(' in output and ')' in output and len(output) >= 4: diff --git a/keymapper/dev/macros.py b/keymapper/dev/macros.py index 9b41ce57..4336b111 100644 --- a/keymapper/dev/macros.py +++ b/keymapper/dev/macros.py @@ -114,7 +114,7 @@ class _Macro: def add_keycode_pause(self): """To add a pause between keystrokes.""" - sleeptime = config.get('macros.keystroke_sleep_ms', 10) / 1000 + sleeptime = config.get('macros.keystroke_sleep_ms') / 1000 async def sleep(): await asyncio.sleep(sleeptime) diff --git a/keymapper/getdevices.py b/keymapper/getdevices.py index e3898639..bc1e7bfd 100644 --- a/keymapper/getdevices.py +++ b/keymapper/getdevices.py @@ -83,6 +83,7 @@ class _GetDevices(threading.Thread): # https://www.kernel.org/doc/html/latest/input/event-codes.html capabilities = device.capabilities().keys() if EV_KEY not in capabilities and EV_ABS not in capabilities: + # or gamepads, because they can be mapped like a keyboard continue usb = device.phys.split('/')[0] diff --git a/keymapper/gtk/row.py b/keymapper/gtk/row.py index 3ce82de9..73e36678 100644 --- a/keymapper/gtk/row.py +++ b/keymapper/gtk/row.py @@ -27,6 +27,8 @@ gi.require_version('Gtk', '3.0') gi.require_version('GLib', '2.0') from gi.repository import Gtk, GLib +from evdev.ecodes import EV_KEY + from keymapper.state import custom_mapping from keymapper.logger import logger @@ -76,7 +78,7 @@ class Row(Gtk.ListBoxRow): return # keycode is already set by some other row - if custom_mapping.get_character(new_keycode) is not None: + if custom_mapping.get_character(EV_KEY, new_keycode) is not None: msg = f'Keycode {new_keycode} is already mapped' logger.info(msg) self.window.get('status_bar').push(CTX_KEYCODE, msg) @@ -98,7 +100,7 @@ class Row(Gtk.ListBoxRow): return # else, the keycode has changed, the character is set, all good - custom_mapping.change(new_keycode, character, previous_keycode) + custom_mapping.change(EV_KEY, new_keycode, character, previous_keycode) def highlight(self): """Mark this row as changed.""" @@ -116,7 +118,7 @@ class Row(Gtk.ListBoxRow): self.highlight() if keycode is not None: - custom_mapping.change( + custom_mapping.change(EV_KEY, previous_keycode=None, new_keycode=keycode, character=character @@ -178,7 +180,7 @@ class Row(Gtk.ListBoxRow): """Destroy the row and remove it from the config.""" keycode = self.get_keycode() if keycode is not None: - custom_mapping.clear(keycode) + custom_mapping.clear(EV_KEY, keycode) self.character_input.set_text('') self.keycode_input.set_label('') self.delete_callback(self) diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py index 350fb7d8..a20c605e 100755 --- a/keymapper/gtk/window.py +++ b/keymapper/gtk/window.py @@ -374,7 +374,7 @@ class Window: custom_mapping.load(self.selected_device, self.selected_preset) key_list = self.get('key_list') - for keycode, output in custom_mapping: + for (_, keycode), output in custom_mapping: single_key_mapping = Row( window=self, delete_callback=self.on_row_removed, diff --git a/keymapper/mapping.py b/keymapper/mapping.py index 24f62662..191eaf3f 100644 --- a/keymapper/mapping.py +++ b/keymapper/mapping.py @@ -25,6 +25,7 @@ import os import json import copy +import evdev from keymapper.logger import logger from keymapper.paths import get_config_path, touch @@ -50,16 +51,18 @@ class Mapping: def __len__(self): return len(self._mapping) - def change(self, new_keycode, character, previous_keycode=None): + def change(self, ev_type, new_keycode, character, previous_keycode=None): """Replace the mapping of a keycode with a different one. Return True on success. Parameters ---------- + ev_type : int + one of evdev.events. The original event new_keycode : int The source keycode, what the mouse would report without any - modification. + modification. xkb keycode. character : string or string[] A single character known to xkb, Examples: KP_1, Shift_L, a, B. Can also be an array, which is used for reading the xkbmap output @@ -67,7 +70,7 @@ class Mapping: previous_keycode : int or None If None, will not remove any previous mapping. If you recently used 10 for new_keycode and want to overwrite that with 11, - provide 5 here. + provide 5 here. xkb keycode. """ try: new_keycode = int(new_keycode) @@ -78,25 +81,28 @@ class Mapping: return False if new_keycode and character: - self._mapping[new_keycode] = character + self._mapping[(ev_type, new_keycode)] = character if new_keycode != previous_keycode: # clear previous mapping of that code, because the line # representing that one will now represent a different one. - self.clear(previous_keycode) + self.clear(ev_type, previous_keycode) self.changed = True return True return False - def clear(self, keycode): + def clear(self, ev_type, keycode): """Remove a keycode from the mapping. Parameters ---------- keycode : int + the xkb keycode + ev_type : int + one of evdev.events """ - if self._mapping.get(keycode) is not None: - del self._mapping[keycode] + if self._mapping.get((ev_type, keycode)) is not None: + del self._mapping[(ev_type, keycode)] self.changed = True def empty(self): @@ -119,13 +125,23 @@ class Mapping: logger.error('Invalid preset config at "%s"', path) return - for keycode, character in preset_dict['mapping'].items(): + for key, character in preset_dict['mapping'].items(): + if ',' not in key: + logger.error('Found invalid key: "%s"', key) + continue + + ev_type, keycode = key.split(',') try: keycode = int(keycode) except ValueError: - logger.error('Found non-int keycode: %s', keycode) + logger.error('Found non-int keycode: "%s"', keycode) continue - self._mapping[keycode] = character + try: + ev_type = int(ev_type) + except ValueError: + logger.error('Found non-int ev_type: "%s"', ev_type) + continue + self._mapping[(ev_type, keycode)] = character # add any metadata of the mapping for key in preset_dict: @@ -152,18 +168,27 @@ class Mapping: with open(path, 'w') as file: # make sure to keep the option to add metadata if ever needed, # so put the mapping into a special key - preset_dict = {'mapping': self._mapping} + json_ready_mapping = {} + # tuple keys are not possible in json + for key, value in self._mapping.items(): + new_key = f'{key[0]},{key[1]}' + json_ready_mapping[new_key] = value + + preset_dict = {'mapping': json_ready_mapping} preset_dict.update(self.config) json.dump(preset_dict, file, indent=4) file.write('\n') self.changed = False - def get_character(self, keycode): + def get_character(self, ev_type, keycode): """Read the character that is mapped to this keycode. Parameters ---------- keycode : int + ev_type : int + one of evdev.events. codes may be the same for various + event types. """ - return self._mapping.get(keycode) + return self._mapping.get((ev_type, keycode)) diff --git a/tests/testcases/test_config.py b/tests/testcases/test_config.py index dae75c22..18f419af 100644 --- a/tests/testcases/test_config.py +++ b/tests/testcases/test_config.py @@ -30,9 +30,15 @@ class TestConfig(unittest.TestCase): self.assertEqual(len(config.iterate_autoload_presets()), 0) config.save_config() + 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) + def test_basic(self): self.assertEqual(config.get('a'), None) - self.assertEqual(config.get('a', 'foo'), 'foo') config.set('a', 1) self.assertEqual(config.get('a'), 1) diff --git a/tests/testcases/test_daemon.py b/tests/testcases/test_daemon.py index d5b88776..e9e159bf 100644 --- a/tests/testcases/test_daemon.py +++ b/tests/testcases/test_daemon.py @@ -25,6 +25,7 @@ import unittest import time import evdev +from evdev.ecodes import EV_KEY import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk @@ -84,8 +85,8 @@ class TestDaemon(unittest.TestCase): keycode_from_2 = 12 keycode_to_2 = 100 - custom_mapping.change(keycode_from_1, 'a') - custom_mapping.change(keycode_from_2, 'b') + custom_mapping.change(EV_KEY, keycode_from_1, 'a') + custom_mapping.change(EV_KEY, keycode_from_2, 'b') clear_system_mapping() system_mapping['a'] = keycode_to_1 system_mapping['b'] = keycode_to_2 diff --git a/tests/testcases/test_injector.py b/tests/testcases/test_injector.py index f061bc79..06773317 100644 --- a/tests/testcases/test_injector.py +++ b/tests/testcases/test_injector.py @@ -76,7 +76,7 @@ class TestInjector(unittest.TestCase): } mapping = Mapping() - mapping.change( + mapping.change(EV_KEY, new_keycode=80, character='a' ) @@ -99,7 +99,7 @@ class TestInjector(unittest.TestCase): def test_grab(self): # path is from the fixtures - custom_mapping.change(10, 'a') + custom_mapping.change(EV_KEY, 10, 'a') self.injector = KeycodeInjector('device 1', custom_mapping) path = '/dev/input/event10' @@ -124,7 +124,7 @@ class TestInjector(unittest.TestCase): def test_skip_unused_device(self): # skips a device because its capabilities are not used in the mapping - custom_mapping.change(10, 'a') + custom_mapping.change(EV_KEY, 10, 'a') self.injector = KeycodeInjector('device 1', custom_mapping) path = '/dev/input/event11' device, map_ev_abs = self.injector._prepare_device(path) @@ -166,11 +166,9 @@ class TestInjector(unittest.TestCase): def test_abs_to_rel(self): # maps gamepad joystick events to mouse events - # TODO enable this somewhere so that map_ev_abs returns true - # in the .json file of the mapping. - config.set('gamepad.non_linearity', 1) + config.set('gamepad.joystick.non_linearity', 1) pointer_speed = 80 - config.set('gamepad.pointer_speed', pointer_speed) + config.set('gamepad.joystick.pointer_speed', pointer_speed) # same for ABS, 0 for x, 1 for y rel_x = evdev.ecodes.REL_X @@ -286,11 +284,11 @@ class TestInjector(unittest.TestCase): self.assertIn(('b', 0), history) def test_injector(self): - custom_mapping.change(8, 'k(KEY_Q).k(w)') - custom_mapping.change(9, 'a') + custom_mapping.change(EV_KEY, 8, 'k(KEY_Q).k(w)') + custom_mapping.change(EV_KEY, 9, 'a') # one mapping that is unknown in the system_mapping on purpose input_b = 10 - custom_mapping.change(input_b, 'b') + custom_mapping.change(EV_KEY, input_b, 'b') clear_system_mapping() code_a = 100 diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py index 3955e1c0..a62e722e 100644 --- a/tests/testcases/test_integration.py +++ b/tests/testcases/test_integration.py @@ -24,6 +24,7 @@ import time import os import unittest import evdev +from evdev.events import EV_KEY import json from unittest.mock import patch from importlib.util import spec_from_loader, module_from_spec @@ -156,9 +157,9 @@ class TestIntegration(unittest.TestCase): def test_select_device(self): # creates a new empty preset when no preset exists for the device self.window.on_select_device(FakeDropdown('device 1')) - custom_mapping.change(50, 'q') - custom_mapping.change(51, 'u') - custom_mapping.change(52, 'x') + custom_mapping.change(EV_KEY, 50, 'q') + custom_mapping.change(EV_KEY, 51, 'u') + custom_mapping.change(EV_KEY, 52, 'x') self.assertEqual(len(custom_mapping), 3) self.window.on_select_device(FakeDropdown('device 2')) self.assertEqual(len(custom_mapping), 0) @@ -202,7 +203,7 @@ class TestIntegration(unittest.TestCase): gtk_iteration() self.assertEqual(len(self.window.get('key_list').get_children()), 2) - self.assertEqual(custom_mapping.get_character(30), 'Shift_L') + self.assertEqual(custom_mapping.get_character(EV_KEY, 30), 'Shift_L') self.assertEqual(row.get_character(), 'Shift_L') self.assertEqual(row.get_keycode(), 30) @@ -270,8 +271,8 @@ class TestIntegration(unittest.TestCase): time.sleep(0.1) self.assertEqual(len(self.get_rows()), num_rows_target) - self.assertEqual(custom_mapping.get_character(10), 'a') - self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)') + self.assertEqual(custom_mapping.get_character(EV_KEY, 10), 'a') + self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)') self.assertTrue(custom_mapping.changed) self.window.on_save_preset_clicked(None) @@ -293,13 +294,13 @@ class TestIntegration(unittest.TestCase): row.get_style_context().list_classes() ) - self.assertEqual(custom_mapping.get_character(10), 'c') - self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)') + self.assertEqual(custom_mapping.get_character(EV_KEY, 10), 'c') + self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)') self.assertTrue(custom_mapping.changed) # try to add a duplicate keycode, it should be ignored self.change_empty_row(11, 'd', success=False) - self.assertEqual(custom_mapping.get_character(11), 'k(b).k(c)') + self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'k(b).k(c)') # and the number of rows shouldn't change self.assertEqual(len(self.get_rows()), num_rows_target) @@ -316,11 +317,11 @@ class TestIntegration(unittest.TestCase): gtk_iteration() self.assertEqual(len(self.get_rows()), 3) - self.assertEqual(custom_mapping.get_character(11), 'b') + self.assertEqual(custom_mapping.get_character(EV_KEY, 11), 'b') def remove(row, code, char, num_rows_after): if code is not None and char is not None: - self.assertEqual(custom_mapping.get_character(code), char) + self.assertEqual(custom_mapping.get_character(EV_KEY, code), char) self.assertEqual(row.get_character(), char) self.assertEqual(row.get_keycode(), code) row.on_delete_button_clicked() @@ -328,7 +329,7 @@ class TestIntegration(unittest.TestCase): gtk_iteration() self.assertIsNone(row.get_keycode()) self.assertIsNone(row.get_character()) - self.assertIsNone(custom_mapping.get_character(code)) + self.assertIsNone(custom_mapping.get_character(EV_KEY, code)) self.assertEqual(len(self.get_rows()), num_rows_after) remove(row_1, 10, 'a', 2) @@ -339,17 +340,17 @@ class TestIntegration(unittest.TestCase): remove(row_3, None, 'c', 1) def test_rename_and_save(self): - custom_mapping.change(14, 'a', None) + custom_mapping.change(EV_KEY, 14, 'a', None) self.assertEqual(self.window.selected_preset, 'new preset') self.window.on_save_preset_clicked(None) - self.assertEqual(custom_mapping.get_character(14), 'a') + self.assertEqual(custom_mapping.get_character(EV_KEY, 14), 'a') - custom_mapping.change(14, 'b', None) + custom_mapping.change(EV_KEY, 14, 'b', None) self.window.get('preset_name_input').set_text('asdf') self.window.on_save_preset_clicked(None) self.assertEqual(self.window.selected_preset, 'asdf') self.assertTrue(os.path.exists(f'{CONFIG}/device 1/asdf.json')) - self.assertEqual(custom_mapping.get_character(14), 'b') + self.assertEqual(custom_mapping.get_character(EV_KEY, 14), 'b') def test_select_device_and_preset(self): # created on start because the first device is selected and some empty @@ -379,7 +380,7 @@ class TestIntegration(unittest.TestCase): gtk_iteration() self.assertEqual(self.window.selected_preset, 'new preset') self.assertFalse(os.path.exists(f'{CONFIG}/device 1/abc 123.json')) - custom_mapping.change(10, '1', None) + custom_mapping.change(EV_KEY, 10, '1', None) self.window.on_save_preset_clicked(None) gtk_iteration() self.assertEqual(self.window.selected_preset, 'abc 123') diff --git a/tests/testcases/test_macros.py b/tests/testcases/test_macros.py index 4c02ffc8..7a6bcb2f 100644 --- a/tests/testcases/test_macros.py +++ b/tests/testcases/test_macros.py @@ -57,7 +57,7 @@ class TestMacros(unittest.TestCase): repeats = 20 macro = f'r({repeats}, k(k))' self.loop.run_until_complete(parse(macro, self.handler).run()) - keystroke_sleep = config.get('macros.keystroke_sleep_ms', 10) + keystroke_sleep = config.get('macros.keystroke_sleep_ms') sleep_time = 2 * repeats * keystroke_sleep / 1000 self.assertGreater(time.time() - start, sleep_time * 0.9) self.assertLess(time.time() - start, sleep_time * 1.1) @@ -68,7 +68,7 @@ class TestMacros(unittest.TestCase): macro = 'r(3, k(m).w(100))' self.loop.run_until_complete(parse(macro, self.handler).run()) - keystroke_time = 6 * config.get('macros.keystroke_sleep_ms', 10) + keystroke_time = 6 * config.get('macros.keystroke_sleep_ms') total_time = keystroke_time + 300 total_time /= 1000 @@ -97,7 +97,7 @@ class TestMacros(unittest.TestCase): self.loop.run_until_complete(parse(macro, self.handler).run()) num_pauses = 8 + 6 + 4 - keystroke_time = num_pauses * config.get('macros.keystroke_sleep_ms', 10) + keystroke_time = num_pauses * config.get('macros.keystroke_sleep_ms') wait_time = 220 total_time = (keystroke_time + wait_time) / 1000 diff --git a/tests/testcases/test_mapping.py b/tests/testcases/test_mapping.py index 5a0eb9cd..39fdf03f 100644 --- a/tests/testcases/test_mapping.py +++ b/tests/testcases/test_mapping.py @@ -20,6 +20,7 @@ import unittest +from evdev.events import EV_KEY from keymapper.mapping import Mapping from keymapper.state import populate_system_mapping @@ -31,6 +32,7 @@ class TestMapping(unittest.TestCase): self.assertFalse(self.mapping.changed) def test_populate_system_mapping(self): + # not actually a mapping object, just a dict mapping = populate_system_mapping() self.assertGreater(len(mapping), 100) # xkb keycode 10 is typically mapped to '1' @@ -41,20 +43,20 @@ class TestMapping(unittest.TestCase): def test_clone(self): mapping1 = Mapping() - mapping1.change(1, 'a') + mapping1.change(EV_KEY, 1, 'a') mapping2 = mapping1.clone() - mapping1.change(2, 'b') + mapping1.change(EV_KEY, 2, 'b') - self.assertEqual(mapping1.get_character(1), 'a') - self.assertEqual(mapping1.get_character(2), 'b') + self.assertEqual(mapping1.get_character(EV_KEY, 1), 'a') + self.assertEqual(mapping1.get_character(EV_KEY, 2), 'b') - self.assertEqual(mapping2.get_character(1), 'a') - self.assertIsNone(mapping2.get_character(2)) + self.assertEqual(mapping2.get_character(EV_KEY, 1), 'a') + self.assertIsNone(mapping2.get_character(EV_KEY, 2)) def test_save_load(self): - self.mapping.change(10, '1') - self.mapping.change(11, '2') - self.mapping.change(12, '3') + self.mapping.change(EV_KEY, 10, '1') + self.mapping.change(EV_KEY, 11, '2') + self.mapping.change(EV_KEY, 12, '3') self.mapping.config['foo'] = 'bar' self.mapping.save('device 1', 'test') @@ -63,77 +65,77 @@ class TestMapping(unittest.TestCase): loaded.load('device 1', 'test') self.assertEqual(len(loaded), 3) - self.assertEqual(loaded.get_character(10), '1') - self.assertEqual(loaded.get_character(11), '2') - self.assertEqual(loaded.get_character(12), '3') + self.assertEqual(loaded.get_character(EV_KEY, 10), '1') + self.assertEqual(loaded.get_character(EV_KEY, 11), '2') + self.assertEqual(loaded.get_character(EV_KEY, 12), '3') self.assertEqual(loaded.config['foo'], 'bar') def test_change(self): # 1 is not assigned yet, ignore it - self.mapping.change(2, 'a', 1) + self.mapping.change(EV_KEY, 2, 'a', 1) self.assertTrue(self.mapping.changed) - self.assertIsNone(self.mapping.get_character(1)) - self.assertEqual(self.mapping.get_character(2), 'a') + self.assertIsNone(self.mapping.get_character(EV_KEY, 1)) + self.assertEqual(self.mapping.get_character(EV_KEY, 2), 'a') self.assertEqual(len(self.mapping), 1) # change 2 to 3 and change a to b - self.mapping.change(3, 'b', 2) - self.assertIsNone(self.mapping.get_character(2)) - self.assertEqual(self.mapping.get_character(3), 'b') + self.mapping.change(EV_KEY, 3, 'b', 2) + self.assertIsNone(self.mapping.get_character(EV_KEY, 2)) + self.assertEqual(self.mapping.get_character(EV_KEY, 3), 'b') self.assertEqual(len(self.mapping), 1) # add 4 - self.mapping.change(4, 'c', None) - self.assertEqual(self.mapping.get_character(3), 'b') - self.assertEqual(self.mapping.get_character(4), 'c') + self.mapping.change(EV_KEY, 4, 'c', None) + self.assertEqual(self.mapping.get_character(EV_KEY, 3), 'b') + self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'c') self.assertEqual(len(self.mapping), 2) # change the mapping of 4 to d - self.mapping.change(4, 'd', None) - self.assertEqual(self.mapping.get_character(4), 'd') + self.mapping.change(EV_KEY, 4, 'd', None) + self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'd') self.assertEqual(len(self.mapping), 2) # this also works in the same way - self.mapping.change(4, 'e', 4) - self.assertEqual(self.mapping.get_character(4), 'e') + self.mapping.change(EV_KEY, 4, 'e', 4) + self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'e') self.assertEqual(len(self.mapping), 2) # and this - self.mapping.change('4', 'f', '4') - self.assertEqual(self.mapping.get_character(4), 'f') + self.mapping.change(EV_KEY, '4', 'f', '4') + self.assertEqual(self.mapping.get_character(EV_KEY, 4), 'f') self.assertEqual(len(self.mapping), 2) # non-int keycodes are ignored - self.mapping.change('b', 'c', 'a') + self.mapping.change(EV_KEY, 'b', 'c', 'a') self.assertEqual(len(self.mapping), 2) def test_clear(self): # does nothing - self.mapping.clear(40) + self.mapping.clear(EV_KEY, 40) self.assertFalse(self.mapping.changed) self.assertEqual(len(self.mapping), 0) - self.mapping._mapping[40] = 'b' + self.mapping._mapping[(EV_KEY, 40)] = 'b' self.assertEqual(len(self.mapping), 1) - self.mapping.clear(40) + self.mapping.clear(EV_KEY, 40) self.assertEqual(len(self.mapping), 0) self.assertTrue(self.mapping.changed) - self.mapping.change(10, 'KP_1', None) + self.mapping.change(EV_KEY, 10, 'KP_1', None) self.assertTrue(self.mapping.changed) - self.mapping.change(20, 'KP_2', None) - self.mapping.change(30, 'KP_3', None) + self.mapping.change(EV_KEY, 20, 'KP_2', None) + self.mapping.change(EV_KEY, 30, 'KP_3', None) self.assertEqual(len(self.mapping), 3) - self.mapping.clear(20) + self.mapping.clear(EV_KEY, 20) self.assertEqual(len(self.mapping), 2) - self.assertEqual(self.mapping.get_character(10), 'KP_1') - self.assertIsNone(self.mapping.get_character(20)) - self.assertEqual(self.mapping.get_character(30), 'KP_3') + self.assertEqual(self.mapping.get_character(EV_KEY, 10), 'KP_1') + self.assertIsNone(self.mapping.get_character(EV_KEY, 20)) + self.assertEqual(self.mapping.get_character(EV_KEY, 30), 'KP_3') def test_empty(self): - self.mapping.change(10, '1') - self.mapping.change(11, '2') - self.mapping.change(12, '3') + self.mapping.change(EV_KEY, 10, '1') + self.mapping.change(EV_KEY, 11, '2') + self.mapping.change(EV_KEY, 12, '3') self.assertEqual(len(self.mapping), 3) self.mapping.empty() self.assertEqual(len(self.mapping), 0)