diff --git a/bin/key-mapper-control b/bin/key-mapper-control
index 4a1c1656..5f98918e 100755
--- a/bin/key-mapper-control
+++ b/bin/key-mapper-control
@@ -133,10 +133,12 @@ def communicate(options, daemon):
# if device was specified, autoload for that one. if None autoload
# for all devices.
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:
group = require_group()
- daemon.autoload_single(group.key)
+ daemon.autoload_single(group.key, timeout=2)
if options.command == START:
group = require_group()
diff --git a/data/99-key-mapper.rules b/data/99-key-mapper.rules
new file mode 100644
index 00000000..2f432a28
--- /dev/null
+++ b/data/99-key-mapper.rules
@@ -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}"
diff --git a/data/key-mapper.rules b/data/key-mapper.rules
deleted file mode 100644
index dd0b9aae..00000000
--- a/data/key-mapper.rules
+++ /dev/null
@@ -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}"
diff --git a/keymapper/daemon.py b/keymapper/daemon.py
index 73a024ff..da79c39c 100644
--- a/keymapper/daemon.py
+++ b/keymapper/daemon.py
@@ -98,6 +98,19 @@ class AutoloadHistory:
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:
"""Starts injecting keycodes based on the configuration.
@@ -150,7 +163,7 @@ class Daemon:
self.config_dir = None
- if USER != 'root':
+ if USER != "root":
self.set_config_dir(get_config_path())
self.autoload_history = AutoloadHistory()
@@ -326,6 +339,7 @@ class Daemon:
self.start_injecting(group.key, preset)
self.autoload_history.remember(group.key, preset)
+ @remove_timeout
def autoload_single(self, group_key):
"""Inject the configured autoload preset for the device.
@@ -353,6 +367,7 @@ class Daemon:
self._autoload(group_key)
+ @remove_timeout
def autoload(self):
"""Load all autoloaded presets for the current config_dir.
diff --git a/keymapper/injection/injector.py b/keymapper/injection/injector.py
index 28f3ae2e..81ff15ab 100644
--- a/keymapper/injection/injector.py
+++ b/keymapper/injection/injector.py
@@ -409,15 +409,19 @@ class Injector(multiprocessing.Process):
# stopped event loop most likely
pass
except OSError as error:
- logger.error(str(error))
+ logger.error("Failed to run injector coroutines: %s", str(error))
if len(coroutines) > 0:
# expected when stop_injecting is called,
# during normal operation as well as tests this point is not
# reached otherwise.
- logger.debug("asyncio coroutines ended")
+ logger.debug("Injector coroutines ended")
for source in sources:
# ungrab at the end to make the next injection process not fail
# its grabs
- source.ungrab()
+ try:
+ source.ungrab()
+ except OSError as error:
+ # it might have disappeared
+ logger.debug("OSError for ungrab on %s: %s", source.path, str(error))
diff --git a/readme/pylint.svg b/readme/pylint.svg
index 2733c52e..280836f4 100644
--- a/readme/pylint.svg
+++ b/readme/pylint.svg
@@ -17,7 +17,7 @@
pylint
- 9.65
- 9.65
+ 9.66
+ 9.66
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 0f2adccd..c780353e 100644
--- a/setup.py
+++ b/setup.py
@@ -113,7 +113,7 @@ setup(
('/usr/lib/systemd/system', ['data/key-mapper.service']),
('/etc/dbus-1/system.d/', ['data/keymapper.Control.conf']),
('/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-service']),
('/usr/bin/', ['bin/key-mapper-control']),
diff --git a/tests/testcases/test_daemon.py b/tests/testcases/test_daemon.py
index 82a300c2..7967d24f 100644
--- a/tests/testcases/test_daemon.py
+++ b/tests/testcases/test_daemon.py
@@ -222,15 +222,15 @@ class TestDaemon(unittest.TestCase):
self.assertEqual(event.value, 1)
def test_config_dir(self):
- config.set('foo', 'bar')
- self.assertEqual(config.get('foo'), 'bar')
+ config.set("foo", "bar")
+ self.assertEqual(config.get("foo"), "bar")
# 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
# it knows where to look for configuration files.
self.daemon = Daemon()
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):
if os.path.exists(get_config_path("xmodmap.json")):