diff --git a/keymapper/dev/ev_abs_mapper.py b/keymapper/dev/ev_abs_mapper.py index ebcaae8e..00342985 100644 --- a/keymapper/dev/ev_abs_mapper.py +++ b/keymapper/dev/ev_abs_mapper.py @@ -73,6 +73,13 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): input_device : evdev.InputDevice keymapper_device : evdev.UInput """ + max_value = input_device.absinfo(EV_ABS).max + + if max_value == 0: + return + + max_speed = ((max_value ** 2) * 2) ** 0.5 + # events only take ints, so a movement of 0.3 needs to add # up to 1.2 to affect the cursor. # @@ -81,13 +88,11 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): pending_rx_rel = 0 pending_ry_rel = 0 - logger.info('Mapping gamepad to mouse movements') - max_value = input_device.absinfo(EV_ABS).max - max_speed = ((max_value ** 2) * 2) ** 0.5 - pointer_speed = config.get('gamepad.joystick.pointer_speed') non_linearity = config.get('gamepad.joystick.non_linearity') + logger.info('Mapping gamepad to mouse movements') + while True: start = time.time() abs_x, abs_y, abs_rx, abs_ry = abs_state diff --git a/keymapper/dev/keycode_mapper.py b/keymapper/dev/keycode_mapper.py index 20c039d7..24801645 100644 --- a/keymapper/dev/keycode_mapper.py +++ b/keymapper/dev/keycode_mapper.py @@ -25,6 +25,7 @@ import asyncio import evdev +from evdev.ecodes import EV_KEY, EV_ABS, ABS_MISC from keymapper.logger import logger from keymapper.dev.ev_abs_mapper import JOYSTICK @@ -48,15 +49,32 @@ def should_map_event_as_btn(ev_type, code): code : int linux keycode """ - if ev_type == evdev.events.EV_KEY: + if ev_type == EV_KEY: return True - if ev_type == evdev.events.EV_ABS and code not in JOYSTICK: + if ev_type == EV_ABS and code not in JOYSTICK + [ABS_MISC]: + # wacom intuos 5 reports ABS_MISC for every event right after the + # actual event return True return False +def is_key_down(event): + """Is this event a key press.""" + if event.type == EV_KEY: + # might be 2 for hold + return event.value == 1 + + # for all other event types, just fire for anything that is not 0 + return event.value != 0 + + +def is_key_up(event): + """Is this event a key release.""" + return event.value == 0 + + def handle_keycode(code_to_code, macros, event, uinput): """Write the mapped keycode or forward unmapped ones. @@ -77,16 +95,14 @@ def handle_keycode(code_to_code, macros, event, uinput): input_type = event.type if input_keycode in macros: - if event.value == 0: - # key-release event. Tell the macro for that keycode - # that the key is released and let it decide what to with that - # information. + if is_key_up(event): + # Tell the macro for that keycode that the key is released and + # let it decide what to with that information. macro = active_macros.get(input_keycode) if macro is not None and macro.holding: macro.release_key() - if event.value != 1: - # only key-down events trigger macros + if not is_key_down(event): return existing_macro = active_macros.get(input_keycode) @@ -94,6 +110,8 @@ def handle_keycode(code_to_code, macros, event, uinput): # make sure that a duplicate key-down event won't make a # macro with a hold function run forever. there should always # be only one active. + # Furthermore, don't stop and rerun the macro because gamepad + # triggers report events all the time just by releasing the key. if existing_macro.running: return @@ -111,7 +129,7 @@ def handle_keycode(code_to_code, macros, event, uinput): if input_keycode in code_to_code: target_keycode = code_to_code[input_keycode] - target_type = evdev.events.EV_KEY + target_type = EV_KEY logger.spam( 'got code:%s value:%s event:%s, maps to EV_KEY:%s', input_keycode, diff --git a/keymapper/dev/reader.py b/keymapper/dev/reader.py index b80acef6..c394e217 100644 --- a/keymapper/dev/reader.py +++ b/keymapper/dev/reader.py @@ -117,7 +117,7 @@ class _KeycodeReader: event.value, evdev.ecodes.EV[event.type] ) - self._pipe[1].send((event.type, event.code)) + self._pipe[1].send(event) def _read_worker(self): """Process that reads keycodes and buffers them into a pipe.""" @@ -149,7 +149,11 @@ class _KeycodeReader: del rlist[fd] def read(self): - """Get the newest tuple of event type, keycode or None.""" + """Get the newest tuple of event type, keycode or None. + + If the timing of two recent events is very close, prioritize + key events over abs events. + """ if self._pipe is None: self.fail_counter += 1 if self.fail_counter % 10 == 0: @@ -157,11 +161,14 @@ class _KeycodeReader: logger.debug('No pipe available to read from') return None, None - newest_event = (None, None) + newest_event = None while self._pipe[0].poll(): newest_event = self._pipe[0].recv() - return newest_event + return ( + (None, None) if newest_event is None + else (newest_event.type, newest_event.code) + ) keycode_reader = _KeycodeReader() diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py index 619e5e19..cf65bae9 100644 --- a/tests/testcases/test_integration.py +++ b/tests/testcases/test_integration.py @@ -258,7 +258,7 @@ class TestIntegration(unittest.TestCase): if code: # modifies the keycode in the row not by writing into the input, # but by sending an event - keycode_reader._pipe[1].send((EV_KEY, code)) + keycode_reader._pipe[1].send(InputEvent(EV_KEY, code, 1)) time.sleep(0.1) gtk_iteration() if success: