mirror of
https://github.com/sezanzeb/input-remapper
synced 2024-11-13 19:10:50 +00:00
more work for event type to mapping key
This commit is contained in:
parent
4b5c9e3143
commit
861ae868b2
@ -113,7 +113,7 @@ class _KeycodeReader:
|
||||
'got code:%s value:%s',
|
||||
event.code + KEYCODE_OFFSET, event.value
|
||||
)
|
||||
self._pipe[1].send(event.code + KEYCODE_OFFSET)
|
||||
self._pipe[1].send((event.type, event.code + KEYCODE_OFFSET))
|
||||
|
||||
def _read_worker(self):
|
||||
"""Process that reads keycodes and buffers them into a pipe."""
|
||||
@ -145,16 +145,16 @@ class _KeycodeReader:
|
||||
del rlist[fd]
|
||||
|
||||
def read(self):
|
||||
"""Get the newest keycode or None if none was pressed."""
|
||||
"""Get the newest tuple of event type, keycode or None."""
|
||||
if self._pipe is None:
|
||||
logger.debug('No pipe available to read from')
|
||||
return None
|
||||
|
||||
newest_keycode = None
|
||||
newest_event = (None, None)
|
||||
while self._pipe[0].poll():
|
||||
newest_keycode = self._pipe[0].recv()
|
||||
newest_event = self._pipe[0].recv()
|
||||
|
||||
return newest_keycode
|
||||
return newest_event
|
||||
|
||||
|
||||
keycode_reader = _KeycodeReader()
|
||||
|
@ -40,7 +40,10 @@ class Row(Gtk.ListBoxRow):
|
||||
"""A single, configurable key mapping."""
|
||||
__gtype_name__ = 'ListBoxRow'
|
||||
|
||||
def __init__(self, delete_callback, window, keycode=None, character=None):
|
||||
def __init__(
|
||||
self, delete_callback, window, ev_type=None, keycode=None,
|
||||
character=None
|
||||
):
|
||||
"""Construct a row widget."""
|
||||
super().__init__()
|
||||
self.device = window.selected_device
|
||||
@ -50,19 +53,26 @@ class Row(Gtk.ListBoxRow):
|
||||
self.character_input = None
|
||||
self.keycode_input = None
|
||||
|
||||
self.put_together(keycode, character)
|
||||
self.put_together(ev_type, keycode, character)
|
||||
|
||||
def get_keycode(self):
|
||||
"""Get the integer keycode from the left column."""
|
||||
"""Get a tuple of event_type and keycode from the left column.
|
||||
|
||||
Or None if no codes are mapped on this row.
|
||||
"""
|
||||
keycode = self.keycode_input.get_label()
|
||||
return int(keycode) if keycode else None
|
||||
if not keycode:
|
||||
return None
|
||||
|
||||
ev_type, keycode = keycode.split(',')
|
||||
return int(ev_type), int(keycode)
|
||||
|
||||
def get_character(self):
|
||||
"""Get the assigned character from the middle column."""
|
||||
character = self.character_input.get_text()
|
||||
return character if character else None
|
||||
|
||||
def set_new_keycode(self, new_keycode):
|
||||
def set_new_keycode(self, ev_type, new_keycode):
|
||||
"""Check if a keycode has been pressed and if so, display it."""
|
||||
# the newest_keycode is populated since the ui regularly polls it
|
||||
# in order to display it in the status bar.
|
||||
@ -78,7 +88,7 @@ class Row(Gtk.ListBoxRow):
|
||||
return
|
||||
|
||||
# keycode is already set by some other row
|
||||
if custom_mapping.get_character(EV_KEY, new_keycode) is not None:
|
||||
if custom_mapping.get_character(ev_type, new_keycode) is not None:
|
||||
msg = f'Keycode {new_keycode} is already mapped'
|
||||
logger.info(msg)
|
||||
self.window.get('status_bar').push(CTX_KEYCODE, msg)
|
||||
@ -86,7 +96,7 @@ class Row(Gtk.ListBoxRow):
|
||||
|
||||
# it's legal to display the keycode
|
||||
self.window.get('status_bar').remove_all(CTX_KEYCODE)
|
||||
self.keycode_input.set_label(str(new_keycode))
|
||||
self.keycode_input.set_label(f'{ev_type},{new_keycode}')
|
||||
# switch to the character, don't require mouse input because
|
||||
# that would overwrite the key with the mouse-button key if
|
||||
# the current device is a mouse. idle_add this so that the
|
||||
@ -100,7 +110,12 @@ class Row(Gtk.ListBoxRow):
|
||||
return
|
||||
|
||||
# else, the keycode has changed, the character is set, all good
|
||||
custom_mapping.change(EV_KEY, new_keycode, character, previous_keycode)
|
||||
custom_mapping.change(
|
||||
ev_type=ev_type,
|
||||
new_keycode=new_keycode,
|
||||
character=character,
|
||||
previous_keycode=previous_keycode
|
||||
)
|
||||
|
||||
def highlight(self):
|
||||
"""Mark this row as changed."""
|
||||
@ -112,19 +127,21 @@ class Row(Gtk.ListBoxRow):
|
||||
|
||||
def on_character_input_change(self, _):
|
||||
"""When the output character for that keycode is typed in."""
|
||||
keycode = self.get_keycode()
|
||||
key = self.get_keycode()
|
||||
character = self.get_character()
|
||||
|
||||
self.highlight()
|
||||
|
||||
if keycode is not None:
|
||||
custom_mapping.change(EV_KEY,
|
||||
previous_keycode=None,
|
||||
if key is not None:
|
||||
ev_type, keycode = key
|
||||
custom_mapping.change(
|
||||
ev_type=ev_type,
|
||||
new_keycode=keycode,
|
||||
character=character
|
||||
character=character,
|
||||
previous_keycode=None
|
||||
)
|
||||
|
||||
def put_together(self, keycode, character):
|
||||
def put_together(self, ev_type, keycode, character):
|
||||
"""Create all child GTK widgets and connect their signals."""
|
||||
delete_button = Gtk.EventBox()
|
||||
delete_button.add(Gtk.Image.new_from_icon_name(
|
||||
@ -141,7 +158,7 @@ class Row(Gtk.ListBoxRow):
|
||||
keycode_input.set_size_request(50, -1)
|
||||
|
||||
if keycode is not None:
|
||||
keycode_input.set_label(str(keycode))
|
||||
keycode_input.set_label(f'{ev_type},{keycode})')
|
||||
|
||||
# make the togglebutton go back to its normal state when doing
|
||||
# something else in the UI
|
||||
@ -178,9 +195,10 @@ class Row(Gtk.ListBoxRow):
|
||||
|
||||
def on_delete_button_clicked(self, *args):
|
||||
"""Destroy the row and remove it from the config."""
|
||||
keycode = self.get_keycode()
|
||||
if keycode is not None:
|
||||
custom_mapping.clear(EV_KEY, keycode)
|
||||
key = self.get_keycode()
|
||||
if key is not None:
|
||||
ev_type, keycode = key
|
||||
custom_mapping.clear(ev_type, keycode)
|
||||
self.character_input.set_text('')
|
||||
self.keycode_input.set_label('')
|
||||
self.delete_callback(self)
|
||||
|
@ -22,6 +22,8 @@
|
||||
"""User Interface."""
|
||||
|
||||
|
||||
from evdev.ecodes import EV_KEY
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GLib', '2.0')
|
||||
@ -213,24 +215,24 @@ class Window:
|
||||
"""To capture events from keyboard, mice and gamepads."""
|
||||
# the "event" event of Gtk.Window wouldn't trigger on gamepad
|
||||
# events, so it became a GLib timeout
|
||||
keycode = keycode_reader.read()
|
||||
ev_type, keycode = keycode_reader.read()
|
||||
|
||||
if keycode is None:
|
||||
if keycode is None or ev_type is None:
|
||||
return True
|
||||
|
||||
if keycode in [280, 333]:
|
||||
if ev_type == EV_KEY and keycode in [280, 333]:
|
||||
# disable mapping the left mouse button because it would break
|
||||
# the mouse. Also it is emitted right when focusing the row
|
||||
# which breaks the current workflow.
|
||||
return True
|
||||
|
||||
self.get('keycode').set_text(str(keycode))
|
||||
self.get('keycode').set_text(f'{ev_type},{keycode}')
|
||||
|
||||
# inform the currently selected row about the new keycode
|
||||
focused = self.window.get_focus()
|
||||
row = focused.get_parent().get_parent()
|
||||
if isinstance(focused, Gtk.ToggleButton) and isinstance(row, Row):
|
||||
row.set_new_keycode(keycode)
|
||||
row.set_new_keycode(ev_type, keycode)
|
||||
|
||||
return True
|
||||
|
||||
@ -374,10 +376,11 @@ class Window:
|
||||
custom_mapping.load(self.selected_device, self.selected_preset)
|
||||
|
||||
key_list = self.get('key_list')
|
||||
for (_, keycode), output in custom_mapping:
|
||||
for (ev_type, keycode), output in custom_mapping:
|
||||
single_key_mapping = Row(
|
||||
window=self,
|
||||
delete_callback=self.on_row_removed,
|
||||
ev_type=ev_type,
|
||||
keycode=keycode,
|
||||
character=output
|
||||
)
|
||||
|
@ -59,7 +59,8 @@ class Mapping:
|
||||
Parameters
|
||||
----------
|
||||
ev_type : int
|
||||
one of evdev.events. The original event
|
||||
one of evdev.events, taken from the original source event.
|
||||
Everything will be mapped to EV_KEY.
|
||||
new_keycode : int
|
||||
The source keycode, what the mouse would report without any
|
||||
modification. xkb keycode.
|
||||
|
@ -182,15 +182,15 @@ class TestIntegration(unittest.TestCase):
|
||||
|
||||
row = rows[0]
|
||||
|
||||
row.set_new_keycode(None)
|
||||
row.set_new_keycode(None, None)
|
||||
self.assertIsNone(row.get_keycode())
|
||||
self.assertEqual(len(custom_mapping), 0)
|
||||
row.set_new_keycode(30)
|
||||
row.set_new_keycode(EV_KEY, 30)
|
||||
self.assertEqual(len(custom_mapping), 0)
|
||||
self.assertEqual(row.get_keycode(), 30)
|
||||
row.set_new_keycode(30)
|
||||
self.assertEqual(row.get_keycode(), (EV_KEY, 30))
|
||||
row.set_new_keycode(EV_KEY, 30)
|
||||
self.assertEqual(len(custom_mapping), 0)
|
||||
self.assertEqual(row.get_keycode(), 30)
|
||||
self.assertEqual(row.get_keycode(), (EV_KEY, 30))
|
||||
|
||||
time.sleep(0.1)
|
||||
gtk_iteration()
|
||||
@ -205,10 +205,11 @@ class TestIntegration(unittest.TestCase):
|
||||
|
||||
self.assertEqual(custom_mapping.get_character(EV_KEY, 30), 'Shift_L')
|
||||
self.assertEqual(row.get_character(), 'Shift_L')
|
||||
self.assertEqual(row.get_keycode(), 30)
|
||||
self.assertEqual(row.get_keycode(), (EV_KEY, 30))
|
||||
|
||||
def change_empty_row(self, code, char, code_first=True, success=True):
|
||||
"""Modify the one empty row that always exists."""
|
||||
# this is not a test, it's a utility function for other tests.
|
||||
# wait for the window to create a new empty row if needed
|
||||
time.sleep(0.1)
|
||||
gtk_iteration()
|
||||
@ -231,11 +232,11 @@ class TestIntegration(unittest.TestCase):
|
||||
if code:
|
||||
# modifies the keycode in the row not by writing into the input,
|
||||
# but by sending an event
|
||||
keycode_reader._pipe[1].send(code)
|
||||
keycode_reader._pipe[1].send((EV_KEY, code))
|
||||
time.sleep(0.1)
|
||||
gtk_iteration()
|
||||
if success:
|
||||
self.assertEqual(row.get_keycode(), code)
|
||||
self.assertEqual(row.get_keycode(), (EV_KEY, code))
|
||||
self.assertIn(
|
||||
'changed',
|
||||
row.get_style_context().list_classes()
|
||||
@ -322,8 +323,12 @@ class TestIntegration(unittest.TestCase):
|
||||
def remove(row, code, char, num_rows_after):
|
||||
if code is not None and char is not None:
|
||||
self.assertEqual(custom_mapping.get_character(EV_KEY, code), char)
|
||||
|
||||
self.assertEqual(row.get_character(), char)
|
||||
self.assertEqual(row.get_keycode(), code)
|
||||
if code is None:
|
||||
self.assertIsNone(row.get_keycode())
|
||||
else:
|
||||
self.assertEqual(row.get_keycode(), (EV_KEY, code))
|
||||
row.on_delete_button_clicked()
|
||||
time.sleep(0.2)
|
||||
gtk_iteration()
|
||||
|
@ -22,6 +22,7 @@
|
||||
import unittest
|
||||
|
||||
import evdev
|
||||
from evdev.events import EV_KEY
|
||||
import time
|
||||
|
||||
from keymapper.dev.reader import keycode_reader
|
||||
@ -47,28 +48,28 @@ class TestReader(unittest.TestCase):
|
||||
|
||||
def test_reading(self):
|
||||
pending_events['device 1'] = [
|
||||
Event(evdev.events.EV_KEY, CODE_1, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_2, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_3, 1)
|
||||
Event(EV_KEY, CODE_1, 1),
|
||||
Event(EV_KEY, CODE_2, 1),
|
||||
Event(EV_KEY, CODE_3, 1)
|
||||
]
|
||||
keycode_reader.start_reading('device 1')
|
||||
|
||||
# sending anything arbitrary does not stop the pipe
|
||||
keycode_reader._pipe[0].send(1234)
|
||||
keycode_reader._pipe[0].send((EV_KEY, 1234))
|
||||
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
self.assertEqual(keycode_reader.read(), CODE_3 + 8)
|
||||
self.assertIsNone(keycode_reader.read())
|
||||
self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3 + 8))
|
||||
self.assertEqual(keycode_reader.read(), (None, None))
|
||||
|
||||
def test_wrong_device(self):
|
||||
pending_events['device 1'] = [
|
||||
Event(evdev.events.EV_KEY, CODE_1, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_2, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_3, 1)
|
||||
Event(EV_KEY, CODE_1, 1),
|
||||
Event(EV_KEY, CODE_2, 1),
|
||||
Event(EV_KEY, CODE_3, 1)
|
||||
]
|
||||
keycode_reader.start_reading('device 2')
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
self.assertIsNone(keycode_reader.read())
|
||||
self.assertEqual(keycode_reader.read(), (None, None))
|
||||
|
||||
def test_keymapper_devices(self):
|
||||
# Don't read from keymapper devices, their keycodes are not
|
||||
@ -76,28 +77,28 @@ class TestReader(unittest.TestCase):
|
||||
# intentionally programmed it won't even do that. But it was at some
|
||||
# point.
|
||||
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)
|
||||
Event(EV_KEY, CODE_1, 1),
|
||||
Event(EV_KEY, CODE_2, 1),
|
||||
Event(EV_KEY, CODE_3, 1)
|
||||
]
|
||||
keycode_reader.start_reading('device 2')
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
self.assertIsNone(keycode_reader.read())
|
||||
self.assertEqual(keycode_reader.read(), (None, None))
|
||||
|
||||
def test_clear(self):
|
||||
pending_events['device 1'] = [
|
||||
Event(evdev.events.EV_KEY, CODE_1, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_2, 1),
|
||||
Event(evdev.events.EV_KEY, CODE_3, 1)
|
||||
Event(EV_KEY, CODE_1, 1),
|
||||
Event(EV_KEY, CODE_2, 1),
|
||||
Event(EV_KEY, CODE_3, 1)
|
||||
]
|
||||
keycode_reader.start_reading('device 1')
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
keycode_reader.clear()
|
||||
self.assertIsNone(keycode_reader.read())
|
||||
self.assertEqual(keycode_reader.read(), (None, None))
|
||||
|
||||
def test_switch_device(self):
|
||||
pending_events['device 2'] = [Event(evdev.events.EV_KEY, CODE_1, 1)]
|
||||
pending_events['device 1'] = [Event(evdev.events.EV_KEY, CODE_3, 1)]
|
||||
pending_events['device 2'] = [Event(EV_KEY, CODE_1, 1)]
|
||||
pending_events['device 1'] = [Event(EV_KEY, CODE_3, 1)]
|
||||
|
||||
keycode_reader.start_reading('device 2')
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
@ -105,8 +106,8 @@ class TestReader(unittest.TestCase):
|
||||
keycode_reader.start_reading('device 1')
|
||||
time.sleep(EVENT_READ_TIMEOUT * 5)
|
||||
|
||||
self.assertEqual(keycode_reader.read(), CODE_3 + 8)
|
||||
self.assertIsNone(keycode_reader.read())
|
||||
self.assertEqual(keycode_reader.read(), (EV_KEY, CODE_3 + 8))
|
||||
self.assertEqual(keycode_reader.read(), (None, None))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user