properly reads keycode device for devices that are being injected into

xkb
sezanzeb 4 years ago committed by sezanzeb
parent 6f0e8d97fb
commit 3bce3f50f6

@ -35,8 +35,7 @@ CONFIG_PATH = os.path.join(CONFIG, 'config')
# an empty config with basic expected substructures
INITIAL_CONFIG = {
'autoload': {},
'map_EV_REL_devices': True
'autoload': {}
}
@ -53,25 +52,6 @@ class _Config:
"""get tuples of (device, preset)."""
return self._config['autoload'].items()
def set_modify_movement_devices(self, active):
"""Set if devices that control movements should also be mapped.
This causes many movements event to be passed through python code,
and if this ever seems to affect the responsiveness of mouse movements,
it can be disabled. This is just an optional precaution. Disabling this
may make mapping some keys of the device impossible.
"""
self._config['map_EV_REL_devices'] = active
def may_modify_movement_devices(self):
"""Get if devices that control movements may be modified as well.
Since movement events happen quite often and fast, I'd like to
add the option to disabling mapping those if it affects their
performance. TODO figure out which devices to inject to instead?
"""
return self._config['map_EV_REL_devices']
def load_config(self):
"""Load the config from the file system."""
if not os.path.exists(CONFIG_PATH):

@ -70,18 +70,6 @@ class _GetDevicesProcess(multiprocessing.Process):
if evdev.ecodes.EV_KEY not in capabilities:
continue
if (
not config.may_modify_movement_devices()
and evdev.ecodes.EV_REL in capabilities
):
# TODO add checkbox to automatically load
# a preset on login
logger.debug(
'Skipping %s to avoid impairing mouse movement',
device.path
)
continue
usb = device.phys.split('/')[0]
if grouped.get(usb) is None:
grouped[usb] = []
@ -128,6 +116,10 @@ def get_devices(include_keymapper=False):
paths is a list of files in /dev/input that belong to the devices.
They are grouped by usb port.
Since this needs to do some stuff with /dev and spawn processes the
result is cached. Use refresh_devices if you need up to date
devices.
"""
global _devices
if _devices is None:
@ -144,8 +136,7 @@ def get_devices(include_keymapper=False):
# filter the result
result = {}
for device in _devices.keys():
if include_keymapper and device.startswith('key-mapper'):
result[device] = _devices[device]
if not include_keymapper and device.startswith('key-mapper'):
continue
result[device] = _devices[device]

@ -50,9 +50,6 @@ CTX_APPLY = 1
CTX_ERROR = 3
# TODO test on wayland
def get_selected_row_bg():
"""Get the background color that a row is going to have when selected."""
# ListBoxRows can be selected, but either they are always selectable
@ -226,6 +223,9 @@ class Window:
CTX_APPLY,
f'Applied the system default'
)
# restart reading because after injecting the device landscape
# changes a bit
keycode_reader.start_reading(self.selected_device)
def on_save_preset_clicked(self, button):
"""Save changes to a preset to the file system."""
@ -281,6 +281,10 @@ class Window:
f'Could not grab device "{self.selected_device}"'
)
# restart reading because after injecting the device landscape
# changes a bit
keycode_reader.start_reading(self.selected_device)
def on_select_device(self, dropdown):
"""List all presets, create one if none exist yet."""
if dropdown.get_active_id() == self.selected_device:

@ -264,9 +264,6 @@ class KeycodeInjector:
if len(self.processes) == 0:
raise OSError('Could not grab any device')
# key-mapper devices were added, freshly scan /dev/input
refresh_devices()
@ensure_numlock
def stop_injecting(self):
"""Stop injecting keycodes."""
@ -278,5 +275,3 @@ class KeycodeInjector:
if process.is_alive():
process.terminate()
self.processes[i] = None
refresh_devices()

@ -103,7 +103,6 @@ class Mapping:
def load(self, device, preset):
"""Load a dumped JSON from home to overwrite the mappings."""
# TODO test
path = get_config_path(device, preset)
logger.info('Loading preset from %s', path)
@ -125,7 +124,6 @@ class Mapping:
def save(self, device, preset):
"""Dump as JSON into home."""
# TODO test
path = get_config_path(device, preset)
logger.info('Saving preset to %s', path)

@ -25,7 +25,7 @@
import evdev
from keymapper.logger import logger
from keymapper.getdevices import get_devices
from keymapper.getdevices import get_devices, refresh_devices
class _KeycodeReader:
@ -50,25 +50,23 @@ class _KeycodeReader:
If read is called without prior start_reading, no keycodes
will be available.
Parameters
----------
device : string
The name of the device.
"""
groups = get_devices(include_keymapper=True)
for name, group in groups.items():
# make sure this sees up to date devices, including those created
# by key-mapper
refresh_devices()
self.virtual_devices = []
for name, group in get_devices(include_keymapper=True).items():
# also find stuff like "key-mapper {device}"
if device not in name:
return
paths = group['paths']
continue
# Watch over each one of the potentially multiple devices per
# hardware
self.virtual_devices = [
self.virtual_devices += [
evdev.InputDevice(path)
for path in paths
for path in group['paths']
]
logger.debug(

@ -48,7 +48,7 @@ fixtures = {
'name': 'device 1 foo'
},
'/dev/input/event10': {
'capabilities': {evdev.ecodes.EV_KEY: []},
'capabilities': {evdev.ecodes.EV_KEY: list(evdev.ecodes.keys.keys())},
'phys': 'usb-0000:03:00.0-1/input3',
'name': 'device 1'
},
@ -65,7 +65,7 @@ fixtures = {
# device 2
'/dev/input/event20': {
'capabilities': {evdev.ecodes.EV_KEY: []},
'capabilities': {evdev.ecodes.EV_KEY: list(evdev.ecodes.keys.keys())},
'phys': 'usb-0000:03:00.0-2/input1',
'name': 'device 2'
},
@ -77,10 +77,10 @@ fixtures = {
'name': 'device 3'
},
# key-mapper devices are also ignored, another instance of key-mapper
# started injecting apparently.
# key-mapper devices are not displayed in the ui, some instance
# of key-mapper started injecting apparently.
'/dev/input/event40': {
'capabilities': {evdev.ecodes.EV_KEY: []},
'capabilities': {evdev.ecodes.EV_KEY: list(evdev.ecodes.keys.keys())},
'phys': 'key-mapper/input1',
'name': 'key-mapper device 2'
},
@ -170,6 +170,7 @@ def patch_evdev():
class UInput:
def __init__(self, *args, **kwargs):
self.fd = 0
self.device = InputDevice('/dev/input/event40')
pass
def write(self, type, code, value):

@ -48,11 +48,9 @@ class TestConfig(unittest.TestCase):
def test_save_load(self):
config.set_autoload_preset('d1', 'a')
config.set_autoload_preset('d2', 'b')
config.set_modify_movement_devices(True)
config.save_config()
# ignored after load
config.set_modify_movement_devices(False)
config.set_autoload_preset('d3', 'c')
config.load_config()
@ -60,7 +58,6 @@ class TestConfig(unittest.TestCase):
list(config.iterate_autoload_presets()),
[('d1', 'a'), ('d2', 'b')]
)
self.assertTrue(config.may_modify_movement_devices())
if __name__ == "__main__":

@ -21,8 +21,7 @@
import unittest
from keymapper.getdevices import _GetDevicesProcess
from keymapper.config import config
from keymapper.getdevices import _GetDevicesProcess, get_devices
class FakePipe:
@ -54,14 +53,16 @@ class TestGetDevices(unittest.TestCase):
'device 2': {
'paths': ['/dev/input/event20'],
'devices': ['device 2']
}
},
'key-mapper device 2': {
'paths': ['/dev/input/event40'],
'devices': ['key-mapper device 2']
},
})
self.assertDictEqual(pipe.devices, get_devices(include_keymapper=True))
def test_map_movement_devices(self):
pipe = FakePipe()
config.set_modify_movement_devices(False)
_GetDevicesProcess(pipe).run()
self.assertDictEqual(pipe.devices, {
def test_get_devices_2(self):
self.assertDictEqual(get_devices(), {
'device 1': {
'paths': [
'/dev/input/event11',
@ -75,8 +76,8 @@ class TestGetDevices(unittest.TestCase):
]
},
'device 2': {
'paths': ['/dev/input/event20'],
'devices': ['device 2']
'paths': ['/dev/input/event20'],
'devices': ['device 2']
}
})

@ -60,6 +60,18 @@ class TestReader(unittest.TestCase):
]
self.assertIsNone(keycode_reader.read())
def test_keymapper_devices(self):
# key-mapper creates devices in /dev, which are also used for
# reading since the original device is in grab-mode
keycode_reader.start_reading('device 2')
pending_events['key-mapper device 2'] = [
Event(evdev.events.EV_KEY, CODE_1, 1),
Event(evdev.events.EV_KEY, CODE_2, 1),
Event(evdev.events.EV_KEY, CODE_3, 1)
]
self.assertEqual(keycode_reader.read(), CODE_3 + 8)
self.assertIsNone(keycode_reader.read())
def test_clear(self):
keycode_reader.start_reading('device 1')
pending_events['device 1'] = [

Loading…
Cancel
Save