got rid of the confusing xkb keycode offset

This commit is contained in:
sezanzeb 2020-12-02 21:36:54 +01:00
parent d355806c5a
commit 8ac2092e74
11 changed files with 62 additions and 77 deletions

View File

@ -33,7 +33,7 @@ from evdev.ecodes import EV_KEY, EV_ABS
from keymapper.logger import logger from keymapper.logger import logger
from keymapper.getdevices import get_devices from keymapper.getdevices import get_devices
from keymapper.state import system_mapping, KEYCODE_OFFSET from keymapper.state import system_mapping
from keymapper.dev.keycode_mapper import handle_keycode, \ from keymapper.dev.keycode_mapper import handle_keycode, \
should_map_event_as_btn should_map_event_as_btn
from keymapper.dev.ev_abs_mapper import ev_abs_mapper, JOYSTICK from keymapper.dev.ev_abs_mapper import ev_abs_mapper, JOYSTICK
@ -144,7 +144,7 @@ class KeycodeInjector:
needed = False needed = False
for (ev_type, keycode), _ in self.mapping: for (ev_type, keycode), _ in self.mapping:
# TODO test ev_type # TODO test ev_type
if keycode - KEYCODE_OFFSET in capabilities.get(ev_type, []): if keycode in capabilities.get(ev_type, []):
needed = True needed = True
break break
@ -208,7 +208,7 @@ class KeycodeInjector:
keycode = system_mapping.get(character) keycode = system_mapping.get(character)
if keycode is not None: if keycode is not None:
capabilities[EV_KEY].append(keycode - KEYCODE_OFFSET) capabilities[EV_KEY].append(keycode)
if abs_to_rel: if abs_to_rel:
del capabilities[ecodes.EV_ABS] del capabilities[ecodes.EV_ABS]
@ -315,7 +315,7 @@ class KeycodeInjector:
'macro writes code:%s value:%d char:%s', 'macro writes code:%s value:%d char:%s',
keycode, value, character keycode, value, character
) )
uinput.write(EV_KEY, keycode - KEYCODE_OFFSET, value) uinput.write(EV_KEY, keycode, value)
uinput.syn() uinput.syn()
async def _keycode_loop(self, device, uinput, abs_to_rel): async def _keycode_loop(self, device, uinput, abs_to_rel):
@ -340,8 +340,6 @@ class KeycodeInjector:
logger.debug('Parsing macros') logger.debug('Parsing macros')
macros = {} macros = {}
for (ev_type, keycode), output in self.mapping: for (ev_type, keycode), output in self.mapping:
keycode -= KEYCODE_OFFSET
if '(' in output and ')' in output and len(output) >= 4: if '(' in output and ')' in output and len(output) >= 4:
# probably a macro # probably a macro
macros[keycode] = parse( macros[keycode] = parse(
@ -355,7 +353,7 @@ class KeycodeInjector:
logger.error('Don\'t know what %s is', output) logger.error('Don\'t know what %s is', output)
continue continue
code_code_mapping[keycode] = target_keycode - KEYCODE_OFFSET code_code_mapping[keycode] = target_keycode
logger.debug( logger.debug(
'Started injecting into %s, fd %s', 'Started injecting into %s, fd %s',

View File

@ -27,7 +27,6 @@ import asyncio
import evdev import evdev
from keymapper.logger import logger from keymapper.logger import logger
from keymapper.state import KEYCODE_OFFSET
from keymapper.dev.ev_abs_mapper import JOYSTICK from keymapper.dev.ev_abs_mapper import JOYSTICK
@ -60,8 +59,7 @@ def handle_keycode(code_code_mapping, macros, event, uinput):
Parameters Parameters
---------- ----------
code_code_mapping : dict code_code_mapping : dict
mapping of linux-keycode to linux-keycode. No need to substract mapping of linux-keycode to linux-keycode.
anything before writing to the device.
macros : dict macros : dict
mapping of linux-keycode to _Macro objects mapping of linux-keycode to _Macro objects
""" """
@ -72,10 +70,6 @@ def handle_keycode(code_code_mapping, macros, event, uinput):
input_keycode = event.code input_keycode = event.code
input_type = event.type input_type = event.type
# for logging purposes. It should log the same keycode as xev and gtk,
# which is also displayed in the UI.
xkb_keycode = input_keycode + KEYCODE_OFFSET
if input_keycode in macros: if input_keycode in macros:
if event.value != 1: if event.value != 1:
# only key-down events trigger macros # only key-down events trigger macros
@ -84,7 +78,7 @@ def handle_keycode(code_code_mapping, macros, event, uinput):
macro = macros[input_keycode] macro = macros[input_keycode]
logger.spam( logger.spam(
'got code:%s value:%s, maps to macro %s', 'got code:%s value:%s, maps to macro %s',
xkb_keycode, input_keycode,
event.value, event.value,
macro.code macro.code
) )
@ -96,15 +90,15 @@ def handle_keycode(code_code_mapping, macros, event, uinput):
target_type = evdev.events.EV_KEY target_type = evdev.events.EV_KEY
logger.spam( logger.spam(
'got code:%s value:%s event:%s, maps to EV_KEY:%s', 'got code:%s value:%s event:%s, maps to EV_KEY:%s',
xkb_keycode, input_keycode,
event.value, event.value,
evdev.ecodes.EV[event.type], evdev.ecodes.EV[event.type],
target_keycode + KEYCODE_OFFSET target_keycode
) )
else: else:
logger.spam( logger.spam(
'got unmapped code:%s value:%s', 'got unmapped code:%s value:%s',
xkb_keycode, input_keycode,
event.value, event.value,
) )
target_keycode = input_keycode target_keycode = input_keycode

View File

@ -30,7 +30,6 @@ import evdev
from keymapper.logger import logger from keymapper.logger import logger
from keymapper.getdevices import get_devices, refresh_devices from keymapper.getdevices import get_devices, refresh_devices
from keymapper.state import KEYCODE_OFFSET
from keymapper.dev.keycode_mapper import should_map_event_as_btn from keymapper.dev.keycode_mapper import should_map_event_as_btn
@ -114,11 +113,11 @@ class _KeycodeReader:
if should_map_event_as_btn(event.type, event.code): if should_map_event_as_btn(event.type, event.code):
logger.spam( logger.spam(
'got code:%s value:%s type:%s', 'got code:%s value:%s type:%s',
event.code + KEYCODE_OFFSET, event.code,
event.value, event.value,
evdev.ecodes.EV[event.type] evdev.ecodes.EV[event.type]
) )
self._pipe[1].send((event.type, event.code + KEYCODE_OFFSET)) self._pipe[1].send((event.type, event.code))
def _read_worker(self): def _read_worker(self):
"""Process that reads keycodes and buffers them into a pipe.""" """Process that reads keycodes and buffers them into a pipe."""

View File

@ -63,15 +63,14 @@ class Mapping:
Everything will be mapped to EV_KEY. Everything will be mapped to EV_KEY.
new_keycode : int new_keycode : int
The source keycode, what the mouse would report without any The source keycode, what the mouse would report without any
modification. xkb keycode. modification.
character : string or string[] character : string or string[]
A single character known to xkb, Examples: KP_1, Shift_L, a, B. A single character known to xkb or linux.
Can also be an array, which is used for reading the xkbmap output Examples: KP_1, Shift_L, a, B, BTN_LEFT.
completely.
previous_keycode : int or None previous_keycode : int or None
If None, will not remove any previous mapping. If you recently If None, will not remove any previous mapping. If you recently
used 10 for new_keycode and want to overwrite that with 11, used 10 for new_keycode and want to overwrite that with 11,
provide 5 here. xkb keycode. provide 5 here.
""" """
try: try:
new_keycode = int(new_keycode) new_keycode = int(new_keycode)
@ -98,7 +97,6 @@ class Mapping:
Parameters Parameters
---------- ----------
keycode : int keycode : int
the xkb keycode
ev_type : int ev_type : int
one of evdev.events one of evdev.events
""" """

View File

@ -30,8 +30,8 @@ import evdev
from keymapper.mapping import Mapping from keymapper.mapping import Mapping
# offset between xkb and linux keycodes. linux keycodes are lower # xkb uses keycodes that are 8 higher than those from evdev
KEYCODE_OFFSET = 8 XKB_KEYCODE_OFFSET = 8
def populate_system_mapping(): def populate_system_mapping():
@ -42,10 +42,10 @@ def populate_system_mapping():
mappings = re.findall(r'(\d+) = (.+)\n', xmodmap) mappings = re.findall(r'(\d+) = (.+)\n', xmodmap)
for keycode, names in mappings: for keycode, names in mappings:
for name in names.split(): for name in names.split():
mapping[name] = int(keycode) mapping[name] = int(keycode) - XKB_KEYCODE_OFFSET
for name, ecode in evdev.ecodes.ecodes.items(): for name, ecode in evdev.ecodes.ecodes.items():
mapping[name] = ecode + KEYCODE_OFFSET mapping[name] = ecode
return mapping return mapping

View File

@ -149,8 +149,7 @@ class Event:
type : int type : int
one of evdev.ecodes.EV_* one of evdev.ecodes.EV_*
code : int code : int
keyboard event code as known to linux. E.g. 2 for the '1' button, keyboard event code as known to linux. E.g. 2 for the '1' button
which would be 10 in xkb
value : int value : int
1 for down, 0 for up, 2 for hold 1 for down, 0 for up, 2 for hold
""" """

View File

@ -31,7 +31,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
from keymapper.state import custom_mapping, system_mapping, \ from keymapper.state import custom_mapping, system_mapping, \
clear_system_mapping, KEYCODE_OFFSET clear_system_mapping
from keymapper.config import config from keymapper.config import config
from keymapper.daemon import Daemon, get_dbus_interface, BUS_NAME from keymapper.daemon import Daemon, get_dbus_interface, BUS_NAME
@ -97,7 +97,7 @@ class TestDaemon(unittest.TestCase):
config.set_autoload_preset('device 2', preset) config.set_autoload_preset('device 2', preset)
pending_events['device 2'] = [ pending_events['device 2'] = [
Event(evdev.events.EV_KEY, keycode_from_1 - KEYCODE_OFFSET, 0), Event(evdev.events.EV_KEY, keycode_from_1, 0),
] ]
self.daemon = Daemon() self.daemon = Daemon()
@ -108,15 +108,15 @@ class TestDaemon(unittest.TestCase):
event = uinput_write_history_pipe[0].recv() event = uinput_write_history_pipe[0].recv()
self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.type, evdev.events.EV_KEY)
self.assertEqual(event.code, keycode_to_1 - KEYCODE_OFFSET) self.assertEqual(event.code, keycode_to_1)
self.assertEqual(event.value, 0) self.assertEqual(event.value, 0)
self.daemon.stop_injecting('device 2') self.daemon.stop_injecting('device 2')
self.assertFalse(self.daemon.is_injecting('device 2')) self.assertFalse(self.daemon.is_injecting('device 2'))
pending_events['device 2'] = [ pending_events['device 2'] = [
Event(evdev.events.EV_KEY, keycode_from_2 - KEYCODE_OFFSET, 1), Event(evdev.events.EV_KEY, keycode_from_2, 1),
Event(evdev.events.EV_KEY, keycode_from_2 - KEYCODE_OFFSET, 0), Event(evdev.events.EV_KEY, keycode_from_2, 0),
] ]
time.sleep(0.2) time.sleep(0.2)
@ -126,12 +126,12 @@ class TestDaemon(unittest.TestCase):
event = uinput_write_history_pipe[0].recv() event = uinput_write_history_pipe[0].recv()
self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.type, evdev.events.EV_KEY)
self.assertEqual(event.code, keycode_to_2 - KEYCODE_OFFSET) self.assertEqual(event.code, keycode_to_2)
self.assertEqual(event.value, 1) self.assertEqual(event.value, 1)
event = uinput_write_history_pipe[0].recv() event = uinput_write_history_pipe[0].recv()
self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.type, evdev.events.EV_KEY)
self.assertEqual(event.code, keycode_to_2 - KEYCODE_OFFSET) self.assertEqual(event.code, keycode_to_2)
self.assertEqual(event.value, 0) self.assertEqual(event.value, 0)

View File

@ -30,12 +30,12 @@ from keymapper.dev.injector import is_numlock_on, toggle_numlock,\
ensure_numlock, KeycodeInjector ensure_numlock, KeycodeInjector
from keymapper.dev.keycode_mapper import handle_keycode from keymapper.dev.keycode_mapper import handle_keycode
from keymapper.state import custom_mapping, system_mapping, \ from keymapper.state import custom_mapping, system_mapping, \
clear_system_mapping, KEYCODE_OFFSET clear_system_mapping
from keymapper.mapping import Mapping from keymapper.mapping import Mapping
from keymapper.config import config from keymapper.config import config
from keymapper.dev.macros import parse from keymapper.dev.macros import parse
from tests.test import uinput_write_history, Event, pending_events, fixtures, \ from tests.test import Event, pending_events, fixtures, \
clear_write_history, EVENT_READ_TIMEOUT, uinput_write_history_pipe, \ clear_write_history, EVENT_READ_TIMEOUT, uinput_write_history_pipe, \
MAX_ABS MAX_ABS
@ -81,7 +81,7 @@ class TestInjector(unittest.TestCase):
character='a' character='a'
) )
maps_to = system_mapping['a'] - KEYCODE_OFFSET maps_to = system_mapping['a']
self.injector = KeycodeInjector('foo', mapping) self.injector = KeycodeInjector('foo', mapping)
fake_device = FakeDevice() fake_device = FakeDevice()
@ -302,14 +302,14 @@ class TestInjector(unittest.TestCase):
# keycode used in X and in the mappings # keycode used in X and in the mappings
pending_events['device 2'] = [ pending_events['device 2'] = [
# should execute a macro # should execute a macro
Event(EV_KEY, 0, 1), Event(EV_KEY, 8, 1),
Event(EV_KEY, 0, 0), Event(EV_KEY, 8, 0),
# normal keystroke # normal keystroke
Event(EV_KEY, 1, 1), Event(EV_KEY, 9, 1),
Event(EV_KEY, 1, 0), Event(EV_KEY, 9, 0),
# just pass those over without modifying # just pass those over without modifying
Event(EV_KEY, 2, 1), Event(EV_KEY, 10, 1),
Event(EV_KEY, 2, 0), Event(EV_KEY, 10, 0),
Event(3124, 3564, 6542), Event(3124, 3564, 6542),
] ]
@ -334,30 +334,30 @@ class TestInjector(unittest.TestCase):
# keystrokes are all over the place. # keystrokes are all over the place.
# just check if they are there and if so, remove them from the list. # just check if they are there and if so, remove them from the list.
ev_key = EV_KEY ev_key = EV_KEY
self.assertIn((ev_key, code_q - KEYCODE_OFFSET, 1), history) self.assertIn((ev_key, code_q, 1), history)
self.assertIn((ev_key, code_q - KEYCODE_OFFSET, 0), history) self.assertIn((ev_key, code_q, 0), history)
self.assertIn((ev_key, code_w - KEYCODE_OFFSET, 1), history) self.assertIn((ev_key, code_w, 1), history)
self.assertIn((ev_key, code_w - KEYCODE_OFFSET, 0), history) self.assertIn((ev_key, code_w, 0), history)
index_q_1 = history.index((ev_key, code_q - KEYCODE_OFFSET, 1)) index_q_1 = history.index((ev_key, code_q, 1))
index_q_0 = history.index((ev_key, code_q - KEYCODE_OFFSET, 0)) index_q_0 = history.index((ev_key, code_q, 0))
index_w_1 = history.index((ev_key, code_w - KEYCODE_OFFSET, 1)) index_w_1 = history.index((ev_key, code_w, 1))
index_w_0 = history.index((ev_key, code_w - KEYCODE_OFFSET, 0)) index_w_0 = history.index((ev_key, code_w, 0))
self.assertGreater(index_q_0, index_q_1) self.assertGreater(index_q_0, index_q_1)
self.assertGreater(index_w_1, index_q_0) self.assertGreater(index_w_1, index_q_0)
self.assertGreater(index_w_0, index_w_1) self.assertGreater(index_w_0, index_w_1)
del history[index_q_1] del history[index_q_1]
index_q_0 = history.index((ev_key, code_q - KEYCODE_OFFSET, 0)) index_q_0 = history.index((ev_key, code_q, 0))
del history[index_q_0] del history[index_q_0]
index_w_1 = history.index((ev_key, code_w - KEYCODE_OFFSET, 1)) index_w_1 = history.index((ev_key, code_w, 1))
del history[index_w_1] del history[index_w_1]
index_w_0 = history.index((ev_key, code_w - KEYCODE_OFFSET, 0)) index_w_0 = history.index((ev_key, code_w, 0))
del history[index_w_0] del history[index_w_0]
# the rest should be in order. # the rest should be in order.
self.assertEqual(history[0], (ev_key, code_a - KEYCODE_OFFSET, 1)) self.assertEqual(history[0], (ev_key, code_a, 1))
self.assertEqual(history[1], (ev_key, code_a - KEYCODE_OFFSET, 0)) self.assertEqual(history[1], (ev_key, code_a, 0))
self.assertEqual(history[2], (ev_key, input_b - KEYCODE_OFFSET, 1)) self.assertEqual(history[2], (ev_key, input_b, 1))
self.assertEqual(history[3], (ev_key, input_b - KEYCODE_OFFSET, 0)) self.assertEqual(history[3], (ev_key, input_b, 0))
self.assertEqual(history[4], (3124, 3564, 6542)) self.assertEqual(history[4], (3124, 3564, 6542))

View File

@ -36,7 +36,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
from keymapper.state import custom_mapping, system_mapping, \ from keymapper.state import custom_mapping, system_mapping, \
clear_system_mapping, KEYCODE_OFFSET clear_system_mapping
from keymapper.paths import CONFIG, get_config_path from keymapper.paths import CONFIG, get_config_path
from keymapper.config import config from keymapper.config import config
from keymapper.dev.reader import keycode_reader from keymapper.dev.reader import keycode_reader
@ -408,8 +408,8 @@ class TestIntegration(unittest.TestCase):
system_mapping['a'] = keycode_to system_mapping['a'] = keycode_to
pending_events['device 2'] = [ pending_events['device 2'] = [
Event(evdev.events.EV_KEY, keycode_from - KEYCODE_OFFSET, 1), Event(evdev.events.EV_KEY, keycode_from, 1),
Event(evdev.events.EV_KEY, keycode_from - KEYCODE_OFFSET, 0) Event(evdev.events.EV_KEY, keycode_from, 0)
] ]
custom_mapping.save('device 2', 'foo preset') custom_mapping.save('device 2', 'foo preset')
@ -428,12 +428,12 @@ class TestIntegration(unittest.TestCase):
event = uinput_write_history_pipe[0].recv() event = uinput_write_history_pipe[0].recv()
self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.type, evdev.events.EV_KEY)
self.assertEqual(event.code, keycode_to - KEYCODE_OFFSET) self.assertEqual(event.code, keycode_to)
self.assertEqual(event.value, 1) self.assertEqual(event.value, 1)
event = uinput_write_history_pipe[0].recv() event = uinput_write_history_pipe[0].recv()
self.assertEqual(event.type, evdev.events.EV_KEY) self.assertEqual(event.type, evdev.events.EV_KEY)
self.assertEqual(event.code, keycode_to - KEYCODE_OFFSET) self.assertEqual(event.code, keycode_to)
self.assertEqual(event.value, 0) self.assertEqual(event.value, 0)
def test_stop_injecting(self): def test_stop_injecting(self):
@ -446,7 +446,7 @@ class TestIntegration(unittest.TestCase):
# not all of those events should be processed, since that takes some # not all of those events should be processed, since that takes some
# time due to time.sleep in the fakes and the injection is stopped. # time due to time.sleep in the fakes and the injection is stopped.
pending_events['device 2'] = [Event(1, keycode_from - KEYCODE_OFFSET, 1)] * 100 pending_events['device 2'] = [Event(1, keycode_from, 1)] * 100
custom_mapping.save('device 2', 'foo preset') custom_mapping.save('device 2', 'foo preset')

View File

@ -35,10 +35,8 @@ class TestMapping(unittest.TestCase):
# not actually a mapping object, just a dict # not actually a mapping object, just a dict
mapping = populate_system_mapping() mapping = populate_system_mapping()
self.assertGreater(len(mapping), 100) self.assertGreater(len(mapping), 100)
# xkb keycode 10 is typically mapped to '1' self.assertEqual(mapping['1'], 2)
self.assertEqual(mapping['1'], 10) self.assertEqual(mapping['KEY_1'], 2)
# linux keycodes are properly increased to the xkb keycodes
self.assertEqual(mapping['KEY_1'], 10)
self.assertEqual(mapping['KEY_LEFTSHIFT'], mapping['Shift_L']) self.assertEqual(mapping['KEY_LEFTSHIFT'], mapping['Shift_L'])
def test_clone(self): def test_clone(self):

View File

@ -25,7 +25,6 @@ from evdev.events import EV_KEY
import time import time
from keymapper.dev.reader import keycode_reader from keymapper.dev.reader import keycode_reader
from keymapper.state import KEYCODE_OFFSET
from tests.test import Event, pending_events, EVENT_READ_TIMEOUT from tests.test import Event, pending_events, EVENT_READ_TIMEOUT
@ -58,7 +57,7 @@ class TestReader(unittest.TestCase):
keycode_reader._pipe[0].send((EV_KEY, 1234)) keycode_reader._pipe[0].send((EV_KEY, 1234))
time.sleep(EVENT_READ_TIMEOUT * 5) time.sleep(EVENT_READ_TIMEOUT * 5)
self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3 + KEYCODE_OFFSET)) self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3))
self.assertEqual(keycode_reader.read(), (None, None)) self.assertEqual(keycode_reader.read(), (None, None))
def test_wrong_device(self): def test_wrong_device(self):
@ -106,7 +105,7 @@ class TestReader(unittest.TestCase):
keycode_reader.start_reading('device 1') keycode_reader.start_reading('device 1')
time.sleep(EVENT_READ_TIMEOUT * 5) time.sleep(EVENT_READ_TIMEOUT * 5)
self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3 + KEYCODE_OFFSET)) self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3))
self.assertEqual(keycode_reader.read(), (None, None)) self.assertEqual(keycode_reader.read(), (None, None))