From 56b89451ee5863562214b02c2cb4fd04eb781212 Mon Sep 17 00:00:00 2001 From: sezanzeb Date: Sun, 27 Dec 2020 13:09:28 +0100 Subject: [PATCH] fixed bug for simultaneous injections, improved injection log --- keymapper/dev/injector.py | 8 +++++++- keymapper/dev/keycode_mapper.py | 6 ++++-- keymapper/dev/reader.py | 29 +++++++++++++++++++++-------- keymapper/gtk/window.py | 11 ----------- tests/testcases/test_integration.py | 7 ------- tests/testcases/test_reader.py | 18 +++++++++++++++++- 6 files changed, 49 insertions(+), 30 deletions(-) diff --git a/keymapper/dev/injector.py b/keymapper/dev/injector.py index 2c6adcdf..8f87a845 100644 --- a/keymapper/dev/injector.py +++ b/keymapper/dev/injector.py @@ -292,6 +292,13 @@ class KeycodeInjector: Stuff is non-blocking by using asyncio in order to do multiple things somewhat concurrently. """ + # create a new event loop, because somehow running an infinite loop + # that sleeps on iterations (ev_abs_mapper) in one process causes + # another injection process to screw up reading from the grabbed + # device. + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + numlock_state = is_numlock_on() loop = asyncio.get_event_loop() @@ -423,7 +430,6 @@ class KeycodeInjector: # forward the rest uinput.write(event.type, event.code, event.value) # this already includes SYN events, so need to syn here again - continue logger.error( 'The injector for "%s" stopped early', diff --git a/keymapper/dev/keycode_mapper.py b/keymapper/dev/keycode_mapper.py index 0d82483a..cab056a8 100644 --- a/keymapper/dev/keycode_mapper.py +++ b/keymapper/dev/keycode_mapper.py @@ -135,6 +135,7 @@ def handle_keycode(key_to_code, macros, event, uinput): # duplicate key-down. skip this event. Avoid writing millions of # key-down events when a continuous value is reported, for example # for gamepad triggers + logger.spam('%s, duplicate key down', key) return if is_key_up(event) and short in unreleased: @@ -142,17 +143,18 @@ def handle_keycode(key_to_code, macros, event, uinput): target_value = 0 target_code = unreleased[short] del unreleased[short] + logger.spam('%s, releasing %s', key, target_code) elif key in key_to_code and is_key_down(event): target_type = EV_KEY target_value = 1 target_code = key_to_code[key] unreleased[short] = target_code - logger.spam('got %s, maps to EV_KEY:%s', key, target_code) + logger.spam('%s, maps to %s', key, target_code) else: target_type = key[0] target_code = key[1] target_value = key[2] - logger.spam('got unmapped %s', key) + logger.spam('%s, unmapped', key) uinput.write(target_type, target_code, target_value) uinput.syn() diff --git a/keymapper/dev/reader.py b/keymapper/dev/reader.py index fae1da00..1a3f9c13 100644 --- a/keymapper/dev/reader.py +++ b/keymapper/dev/reader.py @@ -131,14 +131,27 @@ class _KeycodeReader: logger.debug('Pipe closed, reader stops.') sys.exit(0) - if should_map_event_as_btn(event.type, event.code): - logger.spam( - 'got (%s, %s, %s)', - event.type, - event.code, - event.value - ) - self._pipe[1].send(event) + click_events = [ + evdev.ecodes.BTN_LEFT, + evdev.ecodes.BTN_TOOL_DOUBLETAP + ] + + if event.type == EV_KEY and event.code in click_events: + # disable mapping the left mouse button because it would break + # the mouse. Also it is emitted right when focusing the row + # which breaks the current workflow. + return + + if not should_map_event_as_btn(event.type, event.code): + return + + logger.spam( + 'got (%s, %s, %s)', + event.type, + event.code, + event.value + ) + self._pipe[1].send(event) def _read_worker(self): """Process that reads keycodes and buffers them into a pipe.""" diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py index 17f4f59a..b536db2d 100755 --- a/keymapper/gtk/window.py +++ b/keymapper/gtk/window.py @@ -323,17 +323,6 @@ class Window: if key is None: return True - click_events = [ - evdev.ecodes.BTN_LEFT, - evdev.ecodes.BTN_TOOL_DOUBLETAP - ] - - if key[0] == EV_KEY and key[1] in click_events: - # disable mapping the left mouse button because it would break - # the mouse. Also it is emitted right when focusing the row - # which breaks the current workflow. - return True - self.get('keycode').set_text(to_string(*key)) # inform the currently selected row about the new keycode diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py index 3263a25b..924c0f89 100644 --- a/tests/testcases/test_integration.py +++ b/tests/testcases/test_integration.py @@ -317,13 +317,6 @@ class TestIntegration(unittest.TestCase): if key: # modifies the keycode in the row not by writing into the input, # but by sending an event - - # click events are ignored because it would render the mouse - # useless. It can still be changed in the config files. - keycode_reader._pipe[1].send(InputEvent(EV_KEY, BTN_LEFT, 1)) - time.sleep(0.1) - gtk_iteration() - keycode_reader._pipe[1].send(InputEvent(*key)) time.sleep(0.1) gtk_iteration() diff --git a/tests/testcases/test_reader.py b/tests/testcases/test_reader.py index cde91e31..b3a8bc87 100644 --- a/tests/testcases/test_reader.py +++ b/tests/testcases/test_reader.py @@ -81,14 +81,30 @@ class TestReader(unittest.TestCase): self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_HAT0X, 1)) self.assertEqual(keycode_reader.read(), None) + def test_ignore_btn_left(self): + # click events are ignored because overwriting them would render the + # mouse useless, but a mouse is needed to stop the injection + # comfortably. Furthermore, reading mouse events breaks clicking + # around in the table. It can still be changed in the config files. + pending_events['device 1'] = [ + InputEvent(EV_KEY, BTN_LEFT, 1), + InputEvent(EV_KEY, CODE_2, 1), + InputEvent(EV_KEY, BTN_TOOL_DOUBLETAP, 1), + ] + keycode_reader.start_reading('device 1') + time.sleep(0.1) + self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_2, 1)) + self.assertEqual(keycode_reader.read(), None) + def test_reading_ignore_up(self): pending_events['device 1'] = [ InputEvent(EV_KEY, CODE_1, 0, 10), - InputEvent(EV_KEY, CODE_2, 0, 11), + InputEvent(EV_KEY, CODE_2, 1, 11), InputEvent(EV_KEY, CODE_3, 0, 12), ] keycode_reader.start_reading('device 1') time.sleep(0.1) + self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_2, 1)) self.assertEqual(keycode_reader.read(), None) def test_wrong_device(self):