mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-04 12:00:16 +00:00
properly reads keycode device for devices that are being injected into
This commit is contained in:
parent
2afde0039f
commit
06a10c64b4
@ -35,8 +35,7 @@ CONFIG_PATH = os.path.join(CONFIG, 'config')
|
|||||||
|
|
||||||
# an empty config with basic expected substructures
|
# an empty config with basic expected substructures
|
||||||
INITIAL_CONFIG = {
|
INITIAL_CONFIG = {
|
||||||
'autoload': {},
|
'autoload': {}
|
||||||
'map_EV_REL_devices': True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -53,25 +52,6 @@ class _Config:
|
|||||||
"""get tuples of (device, preset)."""
|
"""get tuples of (device, preset)."""
|
||||||
return self._config['autoload'].items()
|
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):
|
def load_config(self):
|
||||||
"""Load the config from the file system."""
|
"""Load the config from the file system."""
|
||||||
if not os.path.exists(CONFIG_PATH):
|
if not os.path.exists(CONFIG_PATH):
|
||||||
|
@ -64,24 +64,16 @@ class _GetDevicesProcess(multiprocessing.Process):
|
|||||||
# "Logitech USB Keyboard" and "Logitech USB Keyboard Consumer Control"
|
# "Logitech USB Keyboard" and "Logitech USB Keyboard Consumer Control"
|
||||||
grouped = {}
|
grouped = {}
|
||||||
for device in devices:
|
for device in devices:
|
||||||
|
if device.phys.startswith('key-mapper'):
|
||||||
|
# injector device, not really periphery
|
||||||
|
continue
|
||||||
|
|
||||||
# only keyboard devices
|
# only keyboard devices
|
||||||
# https://www.kernel.org/doc/html/latest/input/event-codes.html
|
# https://www.kernel.org/doc/html/latest/input/event-codes.html
|
||||||
capabilities = device.capabilities().keys()
|
capabilities = device.capabilities().keys()
|
||||||
if evdev.ecodes.EV_KEY not in capabilities:
|
if evdev.ecodes.EV_KEY not in capabilities:
|
||||||
continue
|
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]
|
usb = device.phys.split('/')[0]
|
||||||
if grouped.get(usb) is None:
|
if grouped.get(usb) is None:
|
||||||
grouped[usb] = []
|
grouped[usb] = []
|
||||||
@ -117,7 +109,7 @@ def refresh_devices():
|
|||||||
return get_devices()
|
return get_devices()
|
||||||
|
|
||||||
|
|
||||||
def get_devices(include_keymapper=False):
|
def get_devices():
|
||||||
"""Group devices and get relevant infos per group.
|
"""Group devices and get relevant infos per group.
|
||||||
|
|
||||||
Returns a list containing mappings of
|
Returns a list containing mappings of
|
||||||
@ -128,6 +120,10 @@ def get_devices(include_keymapper=False):
|
|||||||
paths is a list of files in /dev/input that belong to the devices.
|
paths is a list of files in /dev/input that belong to the devices.
|
||||||
|
|
||||||
They are grouped by usb port.
|
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
|
global _devices
|
||||||
if _devices is None:
|
if _devices is None:
|
||||||
@ -141,13 +137,4 @@ def get_devices(include_keymapper=False):
|
|||||||
names = [f'"{name}"' for name in _devices]
|
names = [f'"{name}"' for name in _devices]
|
||||||
logger.info('Found %s', ', '.join(names))
|
logger.info('Found %s', ', '.join(names))
|
||||||
|
|
||||||
# filter the result
|
return _devices
|
||||||
result = {}
|
|
||||||
for device in _devices.keys():
|
|
||||||
if include_keymapper and device.startswith('key-mapper'):
|
|
||||||
result[device] = _devices[device]
|
|
||||||
continue
|
|
||||||
|
|
||||||
result[device] = _devices[device]
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
@ -226,6 +226,9 @@ class Window:
|
|||||||
CTX_APPLY,
|
CTX_APPLY,
|
||||||
f'Applied the system default'
|
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):
|
def on_save_preset_clicked(self, button):
|
||||||
"""Save changes to a preset to the file system."""
|
"""Save changes to a preset to the file system."""
|
||||||
@ -281,6 +284,10 @@ class Window:
|
|||||||
f'Could not grab device "{self.selected_device}"'
|
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):
|
def on_select_device(self, dropdown):
|
||||||
"""List all presets, create one if none exist yet."""
|
"""List all presets, create one if none exist yet."""
|
||||||
if dropdown.get_active_id() == self.selected_device:
|
if dropdown.get_active_id() == self.selected_device:
|
||||||
|
@ -264,9 +264,6 @@ class KeycodeInjector:
|
|||||||
if len(self.processes) == 0:
|
if len(self.processes) == 0:
|
||||||
raise OSError('Could not grab any device')
|
raise OSError('Could not grab any device')
|
||||||
|
|
||||||
# key-mapper devices were added, freshly scan /dev/input
|
|
||||||
refresh_devices()
|
|
||||||
|
|
||||||
@ensure_numlock
|
@ensure_numlock
|
||||||
def stop_injecting(self):
|
def stop_injecting(self):
|
||||||
"""Stop injecting keycodes."""
|
"""Stop injecting keycodes."""
|
||||||
@ -278,5 +275,3 @@ class KeycodeInjector:
|
|||||||
if process.is_alive():
|
if process.is_alive():
|
||||||
process.terminate()
|
process.terminate()
|
||||||
self.processes[i] = None
|
self.processes[i] = None
|
||||||
|
|
||||||
refresh_devices()
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
import evdev
|
import evdev
|
||||||
|
|
||||||
from keymapper.logger import logger
|
from keymapper.logger import logger
|
||||||
from keymapper.getdevices import get_devices
|
from keymapper.getdevices import get_devices, refresh_devices
|
||||||
|
|
||||||
|
|
||||||
class _KeycodeReader:
|
class _KeycodeReader:
|
||||||
@ -50,25 +50,22 @@ class _KeycodeReader:
|
|||||||
|
|
||||||
If read is called without prior start_reading, no keycodes
|
If read is called without prior start_reading, no keycodes
|
||||||
will be available.
|
will be available.
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
device : string
|
|
||||||
The name of the device.
|
|
||||||
"""
|
"""
|
||||||
groups = get_devices(include_keymapper=True)
|
# make sure this sees up to date devices
|
||||||
for name, group in groups.items():
|
refresh_devices()
|
||||||
|
|
||||||
|
self.virtual_devices = []
|
||||||
|
|
||||||
|
for name, group in get_devices().items():
|
||||||
# also find stuff like "key-mapper {device}"
|
# also find stuff like "key-mapper {device}"
|
||||||
if device not in name:
|
if device not in name:
|
||||||
return
|
continue
|
||||||
|
|
||||||
paths = group['paths']
|
|
||||||
|
|
||||||
# Watch over each one of the potentially multiple devices per
|
# Watch over each one of the potentially multiple devices per
|
||||||
# hardware
|
# hardware
|
||||||
self.virtual_devices = [
|
self.virtual_devices += [
|
||||||
evdev.InputDevice(path)
|
evdev.InputDevice(path)
|
||||||
for path in paths
|
for path in group['paths']
|
||||||
]
|
]
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -48,7 +48,7 @@ fixtures = {
|
|||||||
'name': 'device 1 foo'
|
'name': 'device 1 foo'
|
||||||
},
|
},
|
||||||
'/dev/input/event10': {
|
'/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',
|
'phys': 'usb-0000:03:00.0-1/input3',
|
||||||
'name': 'device 1'
|
'name': 'device 1'
|
||||||
},
|
},
|
||||||
@ -65,7 +65,7 @@ fixtures = {
|
|||||||
|
|
||||||
# device 2
|
# device 2
|
||||||
'/dev/input/event20': {
|
'/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',
|
'phys': 'usb-0000:03:00.0-2/input1',
|
||||||
'name': 'device 2'
|
'name': 'device 2'
|
||||||
},
|
},
|
||||||
@ -77,10 +77,10 @@ fixtures = {
|
|||||||
'name': 'device 3'
|
'name': 'device 3'
|
||||||
},
|
},
|
||||||
|
|
||||||
# key-mapper devices are also ignored, another instance of key-mapper
|
# key-mapper devices are also ignored, some instance of key-mapper
|
||||||
# started injecting apparently.
|
# started injecting apparently.
|
||||||
'/dev/input/event40': {
|
'/dev/input/event40': {
|
||||||
'capabilities': {evdev.ecodes.EV_KEY: []},
|
'capabilities': {evdev.ecodes.EV_KEY: list(evdev.ecodes.keys.keys())},
|
||||||
'phys': 'key-mapper/input1',
|
'phys': 'key-mapper/input1',
|
||||||
'name': 'key-mapper device 2'
|
'name': 'key-mapper device 2'
|
||||||
},
|
},
|
||||||
@ -170,6 +170,7 @@ def patch_evdev():
|
|||||||
class UInput:
|
class UInput:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.fd = 0
|
self.fd = 0
|
||||||
|
self.device = InputDevice('/dev/input/event40')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def write(self, type, code, value):
|
def write(self, type, code, value):
|
||||||
|
@ -48,11 +48,9 @@ class TestConfig(unittest.TestCase):
|
|||||||
def test_save_load(self):
|
def test_save_load(self):
|
||||||
config.set_autoload_preset('d1', 'a')
|
config.set_autoload_preset('d1', 'a')
|
||||||
config.set_autoload_preset('d2', 'b')
|
config.set_autoload_preset('d2', 'b')
|
||||||
config.set_modify_movement_devices(True)
|
|
||||||
config.save_config()
|
config.save_config()
|
||||||
|
|
||||||
# ignored after load
|
# ignored after load
|
||||||
config.set_modify_movement_devices(False)
|
|
||||||
config.set_autoload_preset('d3', 'c')
|
config.set_autoload_preset('d3', 'c')
|
||||||
|
|
||||||
config.load_config()
|
config.load_config()
|
||||||
@ -60,7 +58,6 @@ class TestConfig(unittest.TestCase):
|
|||||||
list(config.iterate_autoload_presets()),
|
list(config.iterate_autoload_presets()),
|
||||||
[('d1', 'a'), ('d2', 'b')]
|
[('d1', 'a'), ('d2', 'b')]
|
||||||
)
|
)
|
||||||
self.assertTrue(config.may_modify_movement_devices())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from keymapper.getdevices import _GetDevicesProcess
|
from keymapper.getdevices import _GetDevicesProcess
|
||||||
from keymapper.config import config
|
|
||||||
|
|
||||||
|
|
||||||
class FakePipe:
|
class FakePipe:
|
||||||
@ -57,29 +56,6 @@ class TestGetDevices(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
def test_map_movement_devices(self):
|
|
||||||
pipe = FakePipe()
|
|
||||||
config.set_modify_movement_devices(False)
|
|
||||||
_GetDevicesProcess(pipe).run()
|
|
||||||
self.assertDictEqual(pipe.devices, {
|
|
||||||
'device 1': {
|
|
||||||
'paths': [
|
|
||||||
'/dev/input/event11',
|
|
||||||
'/dev/input/event10',
|
|
||||||
'/dev/input/event13'
|
|
||||||
],
|
|
||||||
'devices': [
|
|
||||||
'device 1 foo',
|
|
||||||
'device 1',
|
|
||||||
'device 1'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'device 2': {
|
|
||||||
'paths': ['/dev/input/event20'],
|
|
||||||
'devices': ['device 2']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user