RX and RY movements as mouse wheel

This commit is contained in:
sezanzeb 2020-12-03 21:36:15 +01:00 committed by sezanzeb
parent 1e10f0bd35
commit 28854888a3
2 changed files with 46 additions and 28 deletions

View File

@ -26,7 +26,7 @@ import asyncio
import time import time
import evdev 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.logger import logger
from keymapper.config import config from keymapper.config import config
@ -40,6 +40,9 @@ JOYSTICK = [
evdev.ecodes.ABS_RY, 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): def _write(device, ev_type, keycode, value):
"""Inject.""" """Inject."""
@ -47,6 +50,19 @@ def _write(device, ev_type, keycode, value):
device.syn() 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): async def ev_abs_mapper(abs_state, input_device, keymapper_device):
"""Keep writing mouse movements based on the gamepad stick position. """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 # events only take ints, so a movement of 0.3 needs to add
# up to 1.2 to affect the cursor. # up to 1.2 to affect the cursor.
#
pending_x_rel = 0 pending_x_rel = 0
pending_y_rel = 0 pending_y_rel = 0
pending_rx_rel = 0
pending_ry_rel = 0
logger.info('Mapping gamepad to mouse movements') logger.info('Mapping gamepad to mouse movements')
max_value = input_device.absinfo(EV_ABS).max 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: while True:
start = time.time() start = time.time()
abs_x, abs_y = abs_state abs_x, abs_y, abs_rx, abs_ry = abs_state
if non_linearity != 1: if non_linearity != 1:
# to make small movements smaller for more precision # to make small movements smaller for more precision
@ -80,31 +99,26 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device):
else: else:
factor = 1 factor = 1
# mouse movements
rel_x = abs_x * factor * pointer_speed / max_value rel_x = abs_x * factor * pointer_speed / max_value
rel_y = abs_y * factor * pointer_speed / max_value rel_y = abs_y * factor * pointer_speed / max_value
pending_x_rel, rel_x = accumulate(pending_x_rel, rel_x)
pending_x_rel += rel_x pending_y_rel, rel_y = accumulate(pending_y_rel, rel_y)
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
)
if rel_x != 0: if rel_x != 0:
_write( _write(keymapper_device, EV_REL, REL_X, rel_x)
keymapper_device, if rel_y != 0:
EV_REL, _write(keymapper_device, EV_REL, REL_Y, rel_y)
evdev.ecodes.ABS_X,
rel_x # 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 # try to do this as close to 60hz as possible
time_taken = time.time() - start time_taken = time.time() - start

View File

@ -29,7 +29,7 @@ import subprocess
import multiprocessing import multiprocessing
import evdev 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.logger import logger
from keymapper.getdevices import get_devices from keymapper.getdevices import get_devices
@ -103,7 +103,7 @@ class KeycodeInjector:
self._msg_pipe = multiprocessing.Pipe() self._msg_pipe = multiprocessing.Pipe()
# some EV_ABS mapping stuff # some EV_ABS mapping stuff
self.abs_state = [0, 0] self.abs_state = [0, 0, 0, 0]
def start_injecting(self): def start_injecting(self):
"""Start injecting keycodes.""" """Start injecting keycodes."""
@ -204,7 +204,7 @@ class KeycodeInjector:
# those are the requirements to recognize it as mouse # those are the requirements to recognize it as mouse
# on my system. REL_X and REL_Y are of course required to # on my system. REL_X and REL_Y are of course required to
# accept the events that the mouse-movement-mapper writes. # accept the events that the mouse-movement-mapper writes.
capabilities[ecodes.EV_REL] = [ capabilities[EV_REL] = [
evdev.ecodes.REL_X, evdev.ecodes.REL_X,
evdev.ecodes.REL_Y, evdev.ecodes.REL_Y,
evdev.ecodes.REL_WHEEL, 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 abs_to_rel and event.type == EV_ABS and event.code in JOYSTICK:
if event.code == evdev.ecodes.ABS_X: if event.code == evdev.ecodes.ABS_X:
self.abs_state[0] = event.value 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 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 continue
if should_map_event_as_btn(event.type, event.code): if should_map_event_as_btn(event.type, event.code):