mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-04 12:00:16 +00:00
only considering joystick events for buttons on gamepads
This commit is contained in:
parent
3344837845
commit
14bbd9f7fc
@ -2,8 +2,8 @@
|
||||
|
||||
disable=
|
||||
# that is the standard way to import GTK afaik
|
||||
wrong-import-position
|
||||
wrong-import-position,
|
||||
|
||||
# using """ for comments highlights them in green for me and makes it
|
||||
# a great way to separate stuff into multiple sections
|
||||
pointless-string-statement
|
||||
pointless-string-statement
|
||||
|
@ -23,9 +23,7 @@
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import copy
|
||||
|
||||
from keymapper.paths import CONFIG_PATH, USER, touch
|
||||
|
@ -28,8 +28,7 @@ import time
|
||||
import asyncio
|
||||
|
||||
import evdev
|
||||
from evdev.ecodes import EV_KEY, EV_ABS, KEY_CAMERA, EV_REL, ABS_PRESSURE, \
|
||||
BTN_STYLUS, BTN_A
|
||||
from evdev.ecodes import EV_KEY, EV_ABS, KEY_CAMERA, EV_REL, BTN_STYLUS, BTN_A
|
||||
|
||||
from keymapper.logger import logger
|
||||
|
||||
|
@ -34,7 +34,7 @@ from evdev.ecodes import EV_KEY, EV_ABS, ABS_MISC, EV_REL
|
||||
from keymapper.logger import logger
|
||||
from keymapper.key import Key
|
||||
from keymapper.state import custom_mapping
|
||||
from keymapper.getdevices import get_devices
|
||||
from keymapper.getdevices import get_devices, is_gamepad
|
||||
from keymapper import utils
|
||||
|
||||
CLOSE = 1
|
||||
@ -119,6 +119,11 @@ class _KeycodeReader:
|
||||
|
||||
If read is called without prior start_reading, no keycodes
|
||||
will be available.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
device_name : string
|
||||
As indexed in get_devices()
|
||||
"""
|
||||
if self._pipe is not None:
|
||||
self.stop_reading()
|
||||
@ -126,33 +131,39 @@ class _KeycodeReader:
|
||||
|
||||
self.virtual_devices = []
|
||||
|
||||
for name, group in get_devices().items():
|
||||
if device_name not in name:
|
||||
group = get_devices()[device_name]
|
||||
|
||||
# Watch over each one of the potentially multiple devices per hardware
|
||||
for path in group['paths']:
|
||||
try:
|
||||
device = evdev.InputDevice(path)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
# Watch over each one of the potentially multiple devices per
|
||||
# hardware
|
||||
for path in group['paths']:
|
||||
try:
|
||||
device = evdev.InputDevice(path)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
if evdev.ecodes.EV_KEY in device.capabilities():
|
||||
self.virtual_devices.append(device)
|
||||
|
||||
if evdev.ecodes.EV_KEY in device.capabilities():
|
||||
self.virtual_devices.append(device)
|
||||
|
||||
logger.debug(
|
||||
'Starting reading keycodes from "%s"',
|
||||
'", "'.join([device.name for device in self.virtual_devices])
|
||||
)
|
||||
logger.debug(
|
||||
'Starting reading keycodes from "%s"',
|
||||
'", "'.join([device.name for device in self.virtual_devices])
|
||||
)
|
||||
|
||||
pipe = multiprocessing.Pipe()
|
||||
self._pipe = pipe
|
||||
self._process = threading.Thread(target=self._read_worker)
|
||||
self._process.start()
|
||||
|
||||
def _pipe_event(self, event, device):
|
||||
"""Write the event into the pipe to the main process."""
|
||||
def _pipe_event(self, event, device, gamepad):
|
||||
"""Write the event into the pipe to the main process.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
event : evdev.InputEvent
|
||||
device : evdev.InputDevice
|
||||
gamepad : bool
|
||||
If true, ABS_X and ABS_Y might be mapped to buttons as well
|
||||
depending on the purpose configuration
|
||||
"""
|
||||
# value: 1 for down, 0 for up, 2 for hold.
|
||||
if self._pipe is None or self._pipe[1].closed:
|
||||
logger.debug('Pipe closed, reader stops.')
|
||||
@ -173,7 +184,7 @@ class _KeycodeReader:
|
||||
# which breaks the current workflow.
|
||||
return
|
||||
|
||||
if not utils.should_map_event_as_btn(event, custom_mapping):
|
||||
if not utils.should_map_event_as_btn(event, custom_mapping, gamepad):
|
||||
return
|
||||
|
||||
max_abs = utils.get_max_abs(device)
|
||||
@ -186,13 +197,18 @@ class _KeycodeReader:
|
||||
# using a thread that blocks instead of read_one made it easier
|
||||
# to debug via the logs, because the UI was not polling properly
|
||||
# at some point which caused logs for events not to be written.
|
||||
rlist = {device.fd: device for device in self.virtual_devices}
|
||||
rlist = {}
|
||||
gamepad = {}
|
||||
for device in self.virtual_devices:
|
||||
rlist[device.fd] = device
|
||||
gamepad[device.fd] = is_gamepad(device)
|
||||
|
||||
rlist[self._pipe[1]] = self._pipe[1]
|
||||
|
||||
while True:
|
||||
ready = select.select(rlist, [], [])[0]
|
||||
for fd in ready:
|
||||
readable = rlist[fd] # a device or a pipe
|
||||
readable = rlist[fd] # an InputDevice or a pipe
|
||||
if isinstance(readable, multiprocessing.connection.Connection):
|
||||
msg = readable.recv()
|
||||
if msg == CLOSE:
|
||||
@ -202,7 +218,11 @@ class _KeycodeReader:
|
||||
|
||||
try:
|
||||
for event in rlist[fd].read():
|
||||
self._pipe_event(event, readable)
|
||||
self._pipe_event(
|
||||
event,
|
||||
readable,
|
||||
gamepad.get(fd, False)
|
||||
)
|
||||
except OSError:
|
||||
logger.debug(
|
||||
'Device "%s" disappeared from the reader',
|
||||
|
@ -59,6 +59,9 @@ class Context:
|
||||
macros : dict
|
||||
mapping of ((type, code, value),) to _Macro objects.
|
||||
Combinations work similar as in key_to_code
|
||||
is_gamepad : bool
|
||||
if key-mapper considers this device to be a gamepad. If yes, ABS_X
|
||||
and ABS_Y events can be treated as buttons.
|
||||
"""
|
||||
def __init__(self, mapping):
|
||||
self.mapping = mapping
|
||||
@ -67,6 +70,7 @@ class Context:
|
||||
# might be a bit expensive
|
||||
self.key_to_code = self._map_keys_to_codes()
|
||||
self.macros = self._parse_macros()
|
||||
|
||||
self.left_purpose = None
|
||||
self.right_purpose = None
|
||||
self.update_purposes()
|
||||
|
@ -406,6 +406,8 @@ class Injector(multiprocessing.Process):
|
||||
source.path, source.fd
|
||||
)
|
||||
|
||||
gamepad = is_gamepad(source)
|
||||
|
||||
keycode_handler = KeycodeMapper(self.context, source, uinput)
|
||||
|
||||
async for event in source.async_read_loop():
|
||||
@ -415,7 +417,7 @@ class Injector(multiprocessing.Process):
|
||||
continue
|
||||
|
||||
# for mapped stuff
|
||||
if utils.should_map_event_as_btn(event, self.context.mapping):
|
||||
if utils.should_map_event_as_btn(event, self.context.mapping, gamepad):
|
||||
will_report_key_up = utils.will_report_key_up(event)
|
||||
|
||||
keycode_handler.handle_keycode(event)
|
||||
|
@ -54,6 +54,7 @@ def key_spam(self, key, msg, *args):
|
||||
anything that can be string formatted, but usually a tuple of
|
||||
(type, code, value) tuples
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
if not self.isEnabledFor(SPAM):
|
||||
return
|
||||
|
||||
|
@ -95,7 +95,7 @@ def will_report_key_up(event):
|
||||
return not is_wheel(event)
|
||||
|
||||
|
||||
def should_map_event_as_btn(event, mapping):
|
||||
def should_map_event_as_btn(event, mapping, gamepad):
|
||||
"""Does this event describe a button.
|
||||
|
||||
If it does, this function will make sure its value is one of [-1, 0, 1],
|
||||
@ -106,6 +106,13 @@ def should_map_event_as_btn(event, mapping):
|
||||
|
||||
Especially important for gamepad events, some of the buttons
|
||||
require special rules.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
event : evdev.InputEvent
|
||||
mapping : Mapping
|
||||
gamepad : bool
|
||||
If the device is treated as gamepad
|
||||
"""
|
||||
if (event.type, event.code) in STYLUS:
|
||||
return False
|
||||
@ -121,6 +128,9 @@ def should_map_event_as_btn(event, mapping):
|
||||
return False
|
||||
|
||||
if event.code in JOYSTICK:
|
||||
if not gamepad:
|
||||
return False
|
||||
|
||||
l_purpose = mapping.get('gamepad.joystick.left_purpose')
|
||||
r_purpose = mapping.get('gamepad.joystick.right_purpose')
|
||||
|
||||
@ -130,6 +140,8 @@ def should_map_event_as_btn(event, mapping):
|
||||
if event.code in [ABS_RX, ABS_RY] and r_purpose == BUTTONS:
|
||||
return True
|
||||
else:
|
||||
# for non-joystick buttons just always offer mapping them to
|
||||
# buttons
|
||||
return True
|
||||
|
||||
if is_wheel(event):
|
||||
|
@ -33,7 +33,7 @@ requests.
|
||||
- [x] map keys using a `modifier + modifier + ... + key` syntax
|
||||
- [ ] injecting keys that aren't available in the systems keyboard layout
|
||||
- [ ] injecting keys while abs capabilities are present. e.g. stylus buttons
|
||||
- [ ] ship with a list of all keys known to xkb and validate input in gui
|
||||
- [ ] ship with a list of all keys known to xkb and validate input in the gui
|
||||
|
||||
## Tests
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
<text x="22.0" y="14">pylint</text>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
||||
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.81</text>
|
||||
<text x="62.0" y="14">9.81</text>
|
||||
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.84</text>
|
||||
<text x="62.0" y="14">9.84</text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -56,57 +56,76 @@ class TestDevUtils(unittest.TestCase):
|
||||
mapping = Mapping()
|
||||
|
||||
# the function name is so horribly long
|
||||
def do(event):
|
||||
return utils.should_map_event_as_btn(event, mapping)
|
||||
def do(gamepad, event):
|
||||
return utils.should_map_event_as_btn(event, mapping, gamepad)
|
||||
|
||||
"""D-Pad"""
|
||||
|
||||
self.assertTrue(do(new_event(EV_ABS, ABS_HAT0X, 1)))
|
||||
self.assertTrue(do(new_event(EV_ABS, ABS_HAT0X, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_ABS, ABS_HAT0X, 1)))
|
||||
self.assertTrue(do(0, new_event(EV_ABS, ABS_HAT0X, -1)))
|
||||
|
||||
"""Mouse movements"""
|
||||
|
||||
self.assertTrue(do(new_event(EV_REL, REL_WHEEL, 1)))
|
||||
self.assertTrue(do(new_event(EV_REL, REL_WHEEL, -1)))
|
||||
self.assertTrue(do(new_event(EV_REL, REL_HWHEEL, 1)))
|
||||
self.assertTrue(do(new_event(EV_REL, REL_HWHEEL, -1)))
|
||||
self.assertFalse(do(new_event(EV_REL, REL_X, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_REL, REL_WHEEL, 1)))
|
||||
self.assertTrue(do(0, new_event(EV_REL, REL_WHEEL, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_REL, REL_HWHEEL, 1)))
|
||||
self.assertTrue(do(0, new_event(EV_REL, REL_HWHEEL, -1)))
|
||||
self.assertFalse(do(1, new_event(EV_REL, REL_X, -1)))
|
||||
|
||||
"""regular keys and buttons"""
|
||||
|
||||
self.assertTrue(do(new_event(EV_KEY, KEY_A, 1)))
|
||||
self.assertTrue(do(new_event(EV_ABS, ABS_HAT0X, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_KEY, KEY_A, 1)))
|
||||
self.assertTrue(do(0, new_event(EV_KEY, KEY_A, 1)))
|
||||
self.assertTrue(do(1, new_event(EV_ABS, ABS_HAT0X, -1)))
|
||||
self.assertTrue(do(0, new_event(EV_ABS, ABS_HAT0X, -1)))
|
||||
|
||||
"""mousepad events"""
|
||||
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_MT_SLOT, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_MT_POSITION_X, 1)))
|
||||
self.assertFalse(do(new_event(EV_KEY, ecodes.BTN_TOUCH, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_SLOT, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_SLOT, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_TOOL_Y, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MT_POSITION_X, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MT_POSITION_X, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_KEY, ecodes.BTN_TOUCH, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_KEY, ecodes.BTN_TOUCH, 1)))
|
||||
|
||||
"""stylus movements"""
|
||||
|
||||
self.assertFalse(do(new_event(EV_KEY, ecodes.BTN_DIGI, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_TILT_X, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_TILT_Y, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_DISTANCE, 1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_PRESSURE, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_KEY, ecodes.BTN_DIGI, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_KEY, ecodes.BTN_DIGI, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_TILT_X, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_TILT_X, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_TILT_Y, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_TILT_Y, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_DISTANCE, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_DISTANCE, 1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_PRESSURE, 1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_PRESSURE, 1)))
|
||||
|
||||
"""joysticks"""
|
||||
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_RX, 1234)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
|
||||
"""weird events"""
|
||||
|
||||
self.assertFalse(do(new_event(EV_ABS, ecodes.ABS_MISC, -1)))
|
||||
# without a purpose of BUTTONS it won't map any button, even for
|
||||
# gamepads
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RX, 1234)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_RX, 1234)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
|
||||
mapping.set('gamepad.joystick.right_purpose', BUTTONS)
|
||||
config.set('gamepad.joystick.left_purpose', BUTTONS)
|
||||
# but only for gamepads
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
self.assertTrue(do(1, new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
|
||||
self.assertTrue(do(new_event(EV_ABS, ecodes.ABS_Y, -1)))
|
||||
self.assertTrue(do(new_event(EV_ABS, ecodes.ABS_RY, -1)))
|
||||
"""weird events"""
|
||||
|
||||
self.assertFalse(do(0, new_event(EV_ABS, ecodes.ABS_MISC, -1)))
|
||||
self.assertFalse(do(1, new_event(EV_ABS, ecodes.ABS_MISC, -1)))
|
||||
|
||||
def test_normalize_value(self):
|
||||
def do(event):
|
||||
|
Loading…
Reference in New Issue
Block a user