#197 Using timeouts for calls to the daemon in key-mapper-control

This commit is contained in:
sezanzeb 2021-11-04 11:41:13 +01:00
parent f9e7ef1445
commit 1d79ad369b
8 changed files with 40 additions and 15 deletions

View File

@ -133,10 +133,12 @@ def communicate(options, daemon):
# if device was specified, autoload for that one. if None autoload # if device was specified, autoload for that one. if None autoload
# for all devices. # for all devices.
if options.device is None: if options.device is None:
daemon.autoload() # timeout is not documented, for more info see
# https://github.com/LEW21/pydbus/blob/master/pydbus/proxy_method.py
daemon.autoload(timeout=10)
else: else:
group = require_group() group = require_group()
daemon.autoload_single(group.key) daemon.autoload_single(group.key, timeout=2)
if options.command == START: if options.command == START:
group = require_group() group = require_group()

7
data/99-key-mapper.rules Normal file
View File

@ -0,0 +1,7 @@
# helpful commands:
# udevadm monitor --property
# udevadm info --query=all --name=/dev/input/event3
# to test changes:
# sudo udevadm control --reload-rules
# journalctl -f
ACTION=="add", SUBSYSTEM=="input", RUN+="/bin/key-mapper-control --command autoload --device $env{DEVNAME}"

View File

@ -1,3 +0,0 @@
# udevadm monitor --property
# udevadm info --query=all --name=/dev/input/event3
ACTION=="add", SUBSYSTEM=="input", RUN+="/bin/key-mapper-control --command autoload --device $env{DEVNAME}"

View File

@ -98,6 +98,19 @@ class AutoloadHistory:
return False return False
def remove_timeout(func):
"""Remove timeout to ensure the call works if the daemon is not a proxy."""
# the timeout kwarg is a feature of pydbus. This is needed to make tests work
# that create a Daemon by calling its constructor instead of using pydbus.
def wrapped(*args, **kwargs):
if "timeout" in kwargs:
del kwargs["timeout"]
return func(*args, **kwargs)
return wrapped
class Daemon: class Daemon:
"""Starts injecting keycodes based on the configuration. """Starts injecting keycodes based on the configuration.
@ -150,7 +163,7 @@ class Daemon:
self.config_dir = None self.config_dir = None
if USER != 'root': if USER != "root":
self.set_config_dir(get_config_path()) self.set_config_dir(get_config_path())
self.autoload_history = AutoloadHistory() self.autoload_history = AutoloadHistory()
@ -326,6 +339,7 @@ class Daemon:
self.start_injecting(group.key, preset) self.start_injecting(group.key, preset)
self.autoload_history.remember(group.key, preset) self.autoload_history.remember(group.key, preset)
@remove_timeout
def autoload_single(self, group_key): def autoload_single(self, group_key):
"""Inject the configured autoload preset for the device. """Inject the configured autoload preset for the device.
@ -353,6 +367,7 @@ class Daemon:
self._autoload(group_key) self._autoload(group_key)
@remove_timeout
def autoload(self): def autoload(self):
"""Load all autoloaded presets for the current config_dir. """Load all autoloaded presets for the current config_dir.

View File

@ -409,15 +409,19 @@ class Injector(multiprocessing.Process):
# stopped event loop most likely # stopped event loop most likely
pass pass
except OSError as error: except OSError as error:
logger.error(str(error)) logger.error("Failed to run injector coroutines: %s", str(error))
if len(coroutines) > 0: if len(coroutines) > 0:
# expected when stop_injecting is called, # expected when stop_injecting is called,
# during normal operation as well as tests this point is not # during normal operation as well as tests this point is not
# reached otherwise. # reached otherwise.
logger.debug("asyncio coroutines ended") logger.debug("Injector coroutines ended")
for source in sources: for source in sources:
# ungrab at the end to make the next injection process not fail # ungrab at the end to make the next injection process not fail
# its grabs # its grabs
try:
source.ungrab() source.ungrab()
except OSError as error:
# it might have disappeared
logger.debug("OSError for ungrab on %s: %s", source.path, str(error))

View File

@ -17,7 +17,7 @@
<text x="22.0" y="14">pylint</text> <text x="22.0" y="14">pylint</text>
</g> </g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.65</text> <text x="63.0" y="15" fill="#010101" fill-opacity=".3">9.66</text>
<text x="62.0" y="14">9.65</text> <text x="62.0" y="14">9.66</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -113,7 +113,7 @@ setup(
('/usr/lib/systemd/system', ['data/key-mapper.service']), ('/usr/lib/systemd/system', ['data/key-mapper.service']),
('/etc/dbus-1/system.d/', ['data/keymapper.Control.conf']), ('/etc/dbus-1/system.d/', ['data/keymapper.Control.conf']),
('/etc/xdg/autostart/', ['data/key-mapper-autoload.desktop']), ('/etc/xdg/autostart/', ['data/key-mapper-autoload.desktop']),
('/usr/lib/udev/rules.d', ['data/key-mapper.rules']), ('/usr/lib/udev/rules.d', ['data/99-key-mapper.rules']),
('/usr/bin/', ['bin/key-mapper-gtk']), ('/usr/bin/', ['bin/key-mapper-gtk']),
('/usr/bin/', ['bin/key-mapper-service']), ('/usr/bin/', ['bin/key-mapper-service']),
('/usr/bin/', ['bin/key-mapper-control']), ('/usr/bin/', ['bin/key-mapper-control']),

View File

@ -222,15 +222,15 @@ class TestDaemon(unittest.TestCase):
self.assertEqual(event.value, 1) self.assertEqual(event.value, 1)
def test_config_dir(self): def test_config_dir(self):
config.set('foo', 'bar') config.set("foo", "bar")
self.assertEqual(config.get('foo'), 'bar') self.assertEqual(config.get("foo"), "bar")
# freshly loads the config and therefore removes the previosly added key. # freshly loads the config and therefore removes the previosly added key.
# This is important so that if the service is started via sudo or pkexec # This is important so that if the service is started via sudo or pkexec
# it knows where to look for configuration files. # it knows where to look for configuration files.
self.daemon = Daemon() self.daemon = Daemon()
self.assertEqual(self.daemon.config_dir, get_config_path()) self.assertEqual(self.daemon.config_dir, get_config_path())
self.assertIsNone(config.get('foo')) self.assertIsNone(config.get("foo"))
def test_refresh_on_start(self): def test_refresh_on_start(self):
if os.path.exists(get_config_path("xmodmap.json")): if os.path.exists(get_config_path("xmodmap.json")):