some fixes for gamepads

This commit is contained in:
sezanzeb 2020-12-31 23:16:46 +01:00
parent 56b6573d42
commit 1d3a58553d
7 changed files with 60 additions and 25 deletions

View File

@ -98,15 +98,14 @@ def ensure_numlock(func):
def is_in_capabilities(key, capabilities): def is_in_capabilities(key, capabilities):
"""Are this key or all of its sub keys in the capabilities?""" """Are this key or one of its sub keys in the capabilities?
if isinstance(key[0], tuple):
# it's a key combination Parameters
----------
key : Key
"""
for sub_key in key: for sub_key in key:
if is_in_capabilities(sub_key, capabilities): if sub_key[1] in capabilities.get(sub_key[0], []):
return True
else:
ev_type, code, _ = key
if code in capabilities.get(ev_type, []):
return True return True
return False return False
@ -285,7 +284,7 @@ class KeycodeInjector:
if ecodes.BTN_MOUSE not in capabilities[EV_KEY]: if ecodes.BTN_MOUSE not in capabilities[EV_KEY]:
# to be able to move the cursor, this key capability is # to be able to move the cursor, this key capability is
# needed # needed
capabilities[EV_KEY] = [ecodes.BTN_MOUSE] capabilities[EV_KEY].append(ecodes.BTN_MOUSE)
# just like what python-evdev does in from_device # just like what python-evdev does in from_device
if ecodes.EV_SYN in capabilities: if ecodes.EV_SYN in capabilities:

View File

@ -223,7 +223,10 @@ class _KeycodeReader:
if event.value == 0: if event.value == 0:
if without_value in self._unreleased: if without_value in self._unreleased:
del self._unreleased[without_value] del self._unreleased[without_value]
continue
if without_value in self._unreleased:
# no duplicate down events (gamepad triggers)
continue continue
self._unreleased[without_value] = ( self._unreleased[without_value] = (

View File

@ -253,10 +253,11 @@ class Row(Gtk.ListBoxRow):
self.keycode_input.set_label(label) self.keycode_input.set_label(label)
# make the child label widget break lines, important for # make the child label widget break lines, important for
# long combinations # long combinations
self.keycode_input.get_child().set_line_wrap(True) label = self.keycode_input.get_child()
self.keycode_input.get_child().set_line_wrap_mode(2) label.set_line_wrap(True)
self.keycode_input.get_child().set_max_width_chars(15) label.set_line_wrap_mode(2)
self.keycode_input.get_child().set_justify(Gtk.Justification.CENTER) label.set_max_width_chars(13)
label.set_justify(Gtk.Justification.CENTER)
def put_together(self, character): def put_together(self, character):
"""Create all child GTK widgets and connect their signals.""" """Create all child GTK widgets and connect their signals."""

View File

@ -348,7 +348,12 @@ class Window:
# the "event" event of Gtk.Window wouldn't trigger on gamepad # the "event" event of Gtk.Window wouldn't trigger on gamepad
# events, so it became a GLib timeout to periodically check kernel # events, so it became a GLib timeout to periodically check kernel
# events. # events.
# letting go of one of the keys of a combination won't just make
# it return the leftover key, it will continue to return None because
# they have already been read.
key = keycode_reader.read() key = keycode_reader.read()
key and print(key)
if isinstance(focused, Gtk.ToggleButton): if isinstance(focused, Gtk.ToggleButton):
if not keycode_reader.are_keys_pressed(): if not keycode_reader.are_keys_pressed():

View File

@ -98,6 +98,7 @@ class Key:
return hash(self.keys) return hash(self.keys)
def __eq__(self, other): def __eq__(self, other):
print(self, 'eq', other)
if isinstance(other, tuple): if isinstance(other, tuple):
if isinstance(other[0], tuple): if isinstance(other[0], tuple):
# a combination ((1, 5, 1), (1, 3, 1)) # a combination ((1, 5, 1), (1, 3, 1))

View File

@ -95,23 +95,34 @@ class TestInjector(unittest.TestCase):
self.injector = KeycodeInjector('foo', mapping) self.injector = KeycodeInjector('foo', mapping)
fake_device = FakeDevice() fake_device = FakeDevice()
capabilities = self.injector._modify_capabilities( capabilities_1 = self.injector._modify_capabilities(
{60: macro}, {60: macro},
fake_device, fake_device,
abs_to_rel=False abs_to_rel=False
) )
self.assertIn(EV_KEY, capabilities) self.assertIn(EV_KEY, capabilities_1)
keys = capabilities[EV_KEY] keys = capabilities_1[EV_KEY]
self.assertIn(a, keys) self.assertIn(a, keys)
self.assertIn(one, keys) self.assertIn(one, keys)
self.assertIn(two, keys) self.assertIn(two, keys)
self.assertIn(shift_l, keys) self.assertIn(shift_l, keys)
self.assertNotIn(evdev.ecodes.EV_SYN, capabilities) self.assertNotIn(evdev.ecodes.EV_SYN, capabilities_1)
self.assertNotIn(evdev.ecodes.EV_FF, capabilities) self.assertNotIn(evdev.ecodes.EV_FF, capabilities_1)
self.assertNotIn(evdev.ecodes.EV_REL, capabilities) self.assertNotIn(evdev.ecodes.EV_REL, capabilities_1)
self.assertNotIn(evdev.ecodes.EV_ABS, capabilities) self.assertNotIn(evdev.ecodes.EV_ABS, capabilities_1)
capabilities_2 = self.injector._modify_capabilities(
{60: macro},
fake_device,
abs_to_rel=True
)
keys = capabilities_2[EV_KEY]
self.assertIn(a, keys)
self.assertIn(one, keys)
self.assertIn(two, keys)
self.assertIn(shift_l, keys)
def test_grab(self): def test_grab(self):
# path is from the fixtures # path is from the fixtures
@ -485,13 +496,13 @@ class TestInjector(unittest.TestCase):
self.assertEqual(len(injector._key_to_code), 3) self.assertEqual(len(injector._key_to_code), 3)
def test_is_in_capabilities(self): def test_is_in_capabilities(self):
key = (1, 2, 1) key = Key(1, 2, 1)
capabilities = { capabilities = {
1: [9, 2, 5] 1: [9, 2, 5]
} }
self.assertTrue(is_in_capabilities(key, capabilities)) self.assertTrue(is_in_capabilities(key, capabilities))
key = ((1, 2, 1), (1, 3, 1)) key = Key((1, 2, 1), (1, 3, 1))
capabilities = { capabilities = {
1: [9, 2, 5] 1: [9, 2, 5]
} }
@ -500,7 +511,7 @@ class TestInjector(unittest.TestCase):
# that make up one hardware device # that make up one hardware device
self.assertTrue(is_in_capabilities(key, capabilities)) self.assertTrue(is_in_capabilities(key, capabilities))
key = ((1, 2, 1), (1, 5, 1)) key = Key((1, 2, 1), (1, 5, 1))
capabilities = { capabilities = {
1: [9, 2, 5] 1: [9, 2, 5]
} }

View File

@ -21,9 +21,10 @@
import unittest import unittest
import time import time
import multiprocessing
from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X, ABS_HAT0Y, KEY_COMMA, \ from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X, ABS_HAT0Y, KEY_COMMA, \
BTN_LEFT, BTN_TOOL_DOUBLETAP BTN_LEFT, BTN_TOOL_DOUBLETAP, ABS_Z
from keymapper.dev.reader import keycode_reader from keymapper.dev.reader import keycode_reader
@ -127,6 +128,20 @@ class TestReader(unittest.TestCase):
self.assertEqual(keycode_reader.read(), None) self.assertEqual(keycode_reader.read(), None)
self.assertEqual(len(keycode_reader._unreleased), 1) self.assertEqual(len(keycode_reader._unreleased), 1)
def test_reading_ignore_duplicate_down(self):
pipe = multiprocessing.Pipe()
pipe[1].send(InputEvent(EV_ABS, ABS_Z, 1, 10))
keycode_reader._pipe = pipe
self.assertEqual(keycode_reader.read(), (EV_ABS, ABS_Z, 1))
self.assertEqual(keycode_reader.read(), None)
pipe[1].send(InputEvent(EV_ABS, ABS_Z, 1, 10))
# still none
self.assertEqual(keycode_reader.read(), None)
self.assertEqual(len(keycode_reader._unreleased), 1)
def test_wrong_device(self): def test_wrong_device(self):
pending_events['device 1'] = [ pending_events['device 1'] = [
InputEvent(EV_KEY, CODE_1, 1), InputEvent(EV_KEY, CODE_1, 1),