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")):