horizontal joystick scrolling

This commit is contained in:
sezanzeb 2020-12-27 19:06:17 +01:00
parent 4a745eb2e9
commit 03ccb935fe
8 changed files with 56 additions and 51 deletions

View File

@ -51,6 +51,8 @@ INITIAL_CONFIG = {
'pointer_speed': 80,
'left_purpose': MOUSE,
'right_purpose': WHEEL,
'x_scroll_speed': 2,
'y_scroll_speed': 0.5
},
}
}

View File

@ -41,7 +41,7 @@ JOYSTICK = [
]
# miniscule movements on the joystick should not trigger a mouse wheel event
WHEEL_THRESHOLD = 0.3
WHEEL_THRESHOLD = 0.15
def _write(device, ev_type, keycode, value):
@ -146,6 +146,8 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device, mapping):
non_linearity = mapping.get('gamepad.joystick.non_linearity')
left_purpose = mapping.get('gamepad.joystick.left_purpose')
right_purpose = mapping.get('gamepad.joystick.right_purpose')
x_scroll_speed = mapping.get('gamepad.joystick.x_scroll_speed')
y_scroll_speed = mapping.get('gamepad.joystick.y_scroll_speed')
logger.info(
'Left joystick as %s, right joystick as %s',
@ -161,15 +163,15 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device, mapping):
right_purpose
)
if non_linearity != 1:
# to make small movements smaller for more precision
speed = (mouse_x ** 2 + mouse_y ** 2) ** 0.5
factor = (speed / max_speed) ** non_linearity
else:
factor = 1
# mouse movements
if abs(mouse_x) > 0 or abs(mouse_y) > 0:
if non_linearity != 1:
# to make small movements smaller for more precision
speed = (mouse_x ** 2 + mouse_y ** 2) ** 0.5
factor = (speed / max_speed) ** non_linearity
else:
factor = 1
rel_x = mouse_x * factor * pointer_speed / max_value
rel_y = mouse_y * factor * pointer_speed / max_value
pending_x_rel, rel_x = accumulate(pending_x_rel, rel_x)
@ -181,15 +183,15 @@ async def ev_abs_mapper(abs_state, input_device, keymapper_device, mapping):
# wheel movements
if abs(wheel_x) > 0:
float_rel_rx = wheel_x / max_value
float_rel_rx = wheel_x * x_scroll_speed / 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)
if abs(float_rel_rx) > WHEEL_THRESHOLD * x_scroll_speed:
_write(keymapper_device, EV_REL, REL_HWHEEL, rel_rx)
if abs(wheel_y) > 0:
float_rel_ry = wheel_y / max_value
float_rel_ry = wheel_y * y_scroll_speed / max_value
pending_ry_rel, rel_ry = accumulate(pending_ry_rel, float_rel_ry)
if abs(float_rel_ry) > WHEEL_THRESHOLD:
if abs(float_rel_ry) > WHEEL_THRESHOLD * y_scroll_speed:
_write(keymapper_device, EV_REL, REL_WHEEL, -rel_ry)
# try to do this as close to 60hz as possible

View File

@ -247,13 +247,13 @@ class KeycodeInjector:
capabilities[EV_KEY] += list(macro.get_capabilities())
if abs_to_rel:
# 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.
# REL_WHEEL was also required to recognize the gamepad
# as mouse, even if no joystick is used as wheel.
capabilities[EV_REL] = [
evdev.ecodes.REL_X,
evdev.ecodes.REL_Y,
evdev.ecodes.REL_WHEEL,
evdev.ecodes.REL_HWHEEL,
]
keys = capabilities.get(EV_KEY)
if keys is None:

View File

@ -97,7 +97,6 @@ def can_read_devices():
plugdev_check = check_group('plugdev')
# ubuntu. funnily, individual devices in /dev/input/ have write permitted.
print(is_service_running(), check_injection_rights())
if not is_service_running():
can_write = check_injection_rights()
else:

View File

@ -56,7 +56,6 @@ class Mapping(ConfigBase):
def set(self, *args):
"""Set a config value. See `ConfigBase.set`."""
print('set', args)
self.changed = True
return super().set(*args)

View File

@ -365,6 +365,9 @@ _fixture_copy = copy.deepcopy(fixtures)
def cleanup():
"""Reset the applications state."""
for task in asyncio.Task.all_tasks():
task.cancel()
os.system('pkill -f key-mapper-service')
if os.path.exists(tmp):
shutil.rmtree(tmp)
@ -374,6 +377,7 @@ def cleanup():
system_mapping.populate()
custom_mapping.empty()
custom_mapping.clear_config()
clear_write_history()
@ -388,6 +392,7 @@ def cleanup():
for path in list(_fixture_copy.keys()):
if path not in fixtures:
fixtures[path] = _fixture_copy[path]
refresh_devices()

View File

@ -30,7 +30,7 @@ from keymapper.mapping import Mapping
from keymapper.dev.ev_abs_mapper import MOUSE, WHEEL
from tests.test import InputDevice, UInput, MAX_ABS, clear_write_history, \
uinput_write_history
uinput_write_history, cleanup
abs_state = [0, 0, 0, 0]
@ -53,21 +53,11 @@ class TestEvAbsMapper(unittest.TestCase):
self.mapping
))
config.set('gamepad.joystick.x_scroll_speed', 1)
config.set('gamepad.joystick.y_scroll_speed', 1)
def tearDown(self):
config.clear_config()
self.mapping.clear_config()
loop = asyncio.get_event_loop()
try:
for task in asyncio.Task.all_tasks():
task.cancel()
loop.stop()
loop.close()
except RuntimeError:
pass
clear_write_history()
cleanup()
def do(self, a, b, c, d, expectation):
"""Present fake values to the loop and observe the outcome."""
@ -97,9 +87,9 @@ class TestEvAbsMapper(unittest.TestCase):
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_Y, speed))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_Y, -speed))
# wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_HWHEEL, -1))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_HWHEEL, 1))
# vertical wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_HWHEEL, 1))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_HWHEEL, -1))
self.do(0, 0, 0, MAX_ABS, (EV_REL, REL_WHEEL, -1))
self.do(0, 0, 0, -MAX_ABS, (EV_REL, REL_WHEEL, 1))
@ -109,13 +99,15 @@ class TestEvAbsMapper(unittest.TestCase):
config.set('gamepad.joystick.pointer_speed', speed)
config.set('gamepad.joystick.left_purpose', WHEEL)
config.set('gamepad.joystick.right_purpose', MOUSE)
config.set('gamepad.joystick.x_scroll_speed', 1)
config.set('gamepad.joystick.y_scroll_speed', 2)
self.do(MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, -1))
self.do(-MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, 1))
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, -1))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, 1))
# vertical wheel event values are negative
self.do(MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, 1))
self.do(-MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, -1))
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, -2))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, 2))
# wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_X, speed))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_X, -speed))
self.do(0, 0, 0, MAX_ABS, (EV_REL, REL_Y, speed))
@ -133,7 +125,6 @@ class TestEvAbsMapper(unittest.TestCase):
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_Y, speed))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_Y, -speed))
# wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_X, speed))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_X, -speed))
self.do(0, 0, 0, MAX_ABS, (EV_REL, REL_Y, speed))
@ -142,17 +133,19 @@ class TestEvAbsMapper(unittest.TestCase):
def test_joystick_purpose_4(self):
config.set('gamepad.joystick.left_purpose', WHEEL)
config.set('gamepad.joystick.right_purpose', WHEEL)
self.mapping.set('gamepad.joystick.x_scroll_speed', 2)
self.mapping.set('gamepad.joystick.y_scroll_speed', 3)
self.do(MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, -1))
self.do(-MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, 1))
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, -1))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, 1))
self.do(MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, 2))
self.do(-MAX_ABS, 0, 0, 0, (EV_REL, REL_HWHEEL, -2))
self.do(0, MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, -3))
self.do(0, -MAX_ABS, 0, 0, (EV_REL, REL_WHEEL, 3))
# wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_HWHEEL, -1))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_HWHEEL, 1))
self.do(0, 0, 0, MAX_ABS, (EV_REL, REL_WHEEL, -1))
self.do(0, 0, 0, -MAX_ABS, (EV_REL, REL_WHEEL, 1))
# vertical wheel event values are negative
self.do(0, 0, MAX_ABS, 0, (EV_REL, REL_HWHEEL, 2))
self.do(0, 0, -MAX_ABS, 0, (EV_REL, REL_HWHEEL, -2))
self.do(0, 0, 0, MAX_ABS, (EV_REL, REL_WHEEL, -3))
self.do(0, 0, 0, -MAX_ABS, (EV_REL, REL_WHEEL, 3))
if __name__ == "__main__":

View File

@ -174,6 +174,11 @@ class TestInjector(unittest.TestCase):
self.assertNotIn(evdev.ecodes.EV_ABS, capabilities)
self.assertIn(evdev.ecodes.EV_REL, capabilities)
self.assertIn(evdev.ecodes.REL_X, capabilities.get(EV_REL))
self.assertIn(evdev.ecodes.REL_Y, capabilities.get(EV_REL))
self.assertIn(evdev.ecodes.REL_WHEEL, capabilities.get(EV_REL))
self.assertIn(evdev.ecodes.REL_HWHEEL, capabilities.get(EV_REL))
self.assertIn(evdev.ecodes.EV_KEY, capabilities)
self.assertEqual(len(capabilities[evdev.ecodes.EV_KEY]), 1)