diff --git a/keymapper/dev/ev_abs_mapper.py b/keymapper/dev/ev_abs_mapper.py index bd977617..ebcaae8e 100644 --- a/keymapper/dev/ev_abs_mapper.py +++ b/keymapper/dev/ev_abs_mapper.py @@ -26,7 +26,7 @@ import asyncio import time import evdev -from evdev.ecodes import EV_ABS, EV_REL +from evdev.ecodes import EV_ABS, EV_REL, REL_X, REL_Y, REL_WHEEL, REL_HWHEEL from keymapper.logger import logger from keymapper.config import config @@ -40,6 +40,9 @@ JOYSTICK = [ evdev.ecodes.ABS_RY, ] +# miniscule movements on the joystick should not trigger a mouse wheel event +WHEEL_THRESHOLD = 0.3 + def _write(device, ev_type, keycode, value): """Inject.""" @@ -47,6 +50,19 @@ def _write(device, ev_type, keycode, value): device.syn() +def accumulate(pending, current): + """Since devices can't do float values, stuff has to be accumulated. + + If pending is 0.6 and current is 0.5, return 0.1 and 1. + Because 1 may move 1px, and 0.1px is rememberd for the next value in + pending. + """ + pending += current + current = int(pending) + pending -= current + return pending, current + + async def ev_abs_mapper(abs_state, input_device, keymapper_device): """Keep writing mouse movements based on the gamepad stick position. @@ -59,8 +75,11 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): """ # events only take ints, so a movement of 0.3 needs to add # up to 1.2 to affect the cursor. + # pending_x_rel = 0 pending_y_rel = 0 + pending_rx_rel = 0 + pending_ry_rel = 0 logger.info('Mapping gamepad to mouse movements') max_value = input_device.absinfo(EV_ABS).max @@ -71,7 +90,7 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): while True: start = time.time() - abs_x, abs_y = abs_state + abs_x, abs_y, abs_rx, abs_ry = abs_state if non_linearity != 1: # to make small movements smaller for more precision @@ -80,31 +99,26 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device): else: factor = 1 + # mouse movements rel_x = abs_x * factor * pointer_speed / max_value rel_y = abs_y * factor * pointer_speed / max_value - - pending_x_rel += rel_x - pending_y_rel += rel_y - rel_x = int(pending_x_rel) - rel_y = int(pending_y_rel) - pending_x_rel -= rel_x - pending_y_rel -= rel_y - - if rel_y != 0: - _write( - keymapper_device, - EV_REL, - evdev.ecodes.ABS_Y, - rel_y - ) - + pending_x_rel, rel_x = accumulate(pending_x_rel, rel_x) + pending_y_rel, rel_y = accumulate(pending_y_rel, rel_y) if rel_x != 0: - _write( - keymapper_device, - EV_REL, - evdev.ecodes.ABS_X, - rel_x - ) + _write(keymapper_device, EV_REL, REL_X, rel_x) + if rel_y != 0: + _write(keymapper_device, EV_REL, REL_Y, rel_y) + + # wheel movements + float_rel_rx = abs_rx / max_value + pending_rx_rel, rel_rx = accumulate(pending_rx_rel, float_rel_rx) + if abs(float_rel_rx) > WHEEL_THRESHOLD: + _write(keymapper_device, EV_REL, REL_HWHEEL, -rel_rx) + + float_rel_ry = abs_ry / max_value + pending_ry_rel, rel_ry = accumulate(pending_ry_rel, float_rel_ry) + if abs(float_rel_ry) > WHEEL_THRESHOLD: + _write(keymapper_device, EV_REL, REL_WHEEL, -rel_ry) # try to do this as close to 60hz as possible time_taken = time.time() - start diff --git a/keymapper/dev/injector.py b/keymapper/dev/injector.py index 8461ec4e..52306d21 100644 --- a/keymapper/dev/injector.py +++ b/keymapper/dev/injector.py @@ -29,7 +29,7 @@ import subprocess import multiprocessing import evdev -from evdev.ecodes import EV_KEY, EV_ABS +from evdev.ecodes import EV_KEY, EV_ABS, EV_REL from keymapper.logger import logger from keymapper.getdevices import get_devices @@ -103,7 +103,7 @@ class KeycodeInjector: self._msg_pipe = multiprocessing.Pipe() # some EV_ABS mapping stuff - self.abs_state = [0, 0] + self.abs_state = [0, 0, 0, 0] def start_injecting(self): """Start injecting keycodes.""" @@ -204,7 +204,7 @@ class KeycodeInjector: # those are the requirements to recognize it as mouse # on my system. REL_X and REL_Y are of course required to # accept the events that the mouse-movement-mapper writes. - capabilities[ecodes.EV_REL] = [ + capabilities[EV_REL] = [ evdev.ecodes.REL_X, evdev.ecodes.REL_Y, evdev.ecodes.REL_WHEEL, @@ -352,8 +352,12 @@ class KeycodeInjector: if abs_to_rel and event.type == EV_ABS and event.code in JOYSTICK: if event.code == evdev.ecodes.ABS_X: self.abs_state[0] = event.value - if event.code == evdev.ecodes.ABS_Y: + elif event.code == evdev.ecodes.ABS_Y: self.abs_state[1] = event.value + elif event.code == evdev.ecodes.ABS_RX: + self.abs_state[2] = event.value + elif event.code == evdev.ecodes.ABS_RY: + self.abs_state[3] = event.value continue if should_map_event_as_btn(event.type, event.code):