diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py
index 5a90a092..17f4f59a 100755
--- a/keymapper/gtk/window.py
+++ b/keymapper/gtk/window.py
@@ -289,11 +289,13 @@ class Window:
def can_modify_mapping(self, *args):
"""Show a message if changing the mapping is not possible."""
- if self.dbus.is_injecting(self.selected_device):
- # because the device is in grab mode by the daemon and
- # therefore the original keycode inaccessible
- logger.info('Cannot change keycodes while injecting')
- self.show_status(CTX_ERROR, 'Use "Restore Defaults" before editing')
+ if not self.dbus.is_injecting(self.selected_device):
+ return
+
+ # because the device is in grab mode by the daemon and
+ # therefore the original keycode inaccessible
+ logger.info('Cannot change keycodes while injecting')
+ self.show_status(CTX_ERROR, 'Use "Restore Defaults" before editing')
def get_focused_row(self):
"""Get the Row and its child that is currently in focus."""
@@ -389,8 +391,9 @@ class Window:
self.check_macro_syntax()
except PermissionError as error:
- self.show_status(CTX_ERROR, 'Error: Permission denied!')
- logger.error(str(error))
+ error = str(error)
+ self.show_status(CTX_ERROR, 'Error: Permission denied!', error)
+ logger.error(error)
def on_delete_preset_clicked(self, _):
"""Delete a preset from the file system."""
@@ -413,9 +416,9 @@ class Window:
else:
self.show_status(CTX_APPLY, f'Applied preset "{preset}"')
- path = get_preset_path(self.selected_device, preset)
+ path = get_preset_path(device, preset)
xmodmap = get_config_path(XMODMAP_FILENAME)
- success = self.dbus.start_injecting(self.selected_device, path, xmodmap)
+ success = self.dbus.start_injecting(device, path, xmodmap)
if not success:
self.show_status(CTX_ERROR, 'Error: Could not grab devices!')
@@ -479,8 +482,9 @@ class Window:
self.get('preset_selection').append(new_preset, new_preset)
self.get('preset_selection').set_active_id(new_preset)
except PermissionError as error:
- self.show_status(CTX_ERROR, 'Error: Permission denied!')
- logger.error(str(error))
+ error = str(error)
+ self.show_status(CTX_ERROR, 'Error: Permission denied!', error)
+ logger.error(error)
def on_select_preset(self, dropdown):
"""Show the mappings of the preset."""
diff --git a/readme/coverage.svg b/readme/coverage.svg
index 03eae130..c6421901 100644
--- a/readme/coverage.svg
+++ b/readme/coverage.svg
@@ -17,7 +17,7 @@
coverage
- 90%
- 90%
+ 91%
+ 91%
\ No newline at end of file
diff --git a/tests/testcases/test_daemon.py b/tests/testcases/test_daemon.py
index f8fe8470..19f8942f 100644
--- a/tests/testcases/test_daemon.py
+++ b/tests/testcases/test_daemon.py
@@ -23,14 +23,16 @@ import os
import multiprocessing
import unittest
import time
+import subprocess
import evdev
from evdev.ecodes import EV_KEY, EV_ABS
from gi.repository import Gtk
+from pydbus import SystemBus
from keymapper.state import custom_mapping, system_mapping
from keymapper.config import config
-from keymapper.getdevices import get_devices, refresh_devices
+from keymapper.getdevices import get_devices
from keymapper.paths import get_preset_path
from keymapper.daemon import Daemon, get_dbus_interface, BUS_NAME
@@ -72,6 +74,10 @@ class TestDBusDaemon(unittest.TestCase):
self.assertEqual(self.interface.hello('foo'), 'foo')
+check_output = subprocess.check_output
+dbus_get = type(SystemBus()).get
+
+
class TestDaemon(unittest.TestCase):
new_fixture = '/dev/input/event9876'
@@ -86,8 +92,31 @@ class TestDaemon(unittest.TestCase):
self.daemon = None
evdev.InputDevice.grab = self.grab
+ subprocess.check_output = check_output
+ type(SystemBus()).get = dbus_get
+
cleanup()
+ def test_get_dbus_interface(self):
+ # no daemon runs, should return an instance of the object instead
+ self.assertFalse(is_service_running())
+ self.assertIsInstance(get_dbus_interface(), Daemon)
+ self.assertIsNone(get_dbus_interface(False))
+
+ subprocess.check_output = lambda *args: None
+ self.assertTrue(is_service_running())
+ # now it actually tries to use the dbus, but it fails
+ # because none exists, so it returns an instance again
+ self.assertIsInstance(get_dbus_interface(), Daemon)
+ self.assertIsNone(get_dbus_interface(False))
+
+ class FakeConnection:
+ pass
+
+ type(SystemBus()).get = lambda *args: FakeConnection()
+ self.assertIsInstance(get_dbus_interface(), FakeConnection)
+ self.assertIsInstance(get_dbus_interface(False), FakeConnection)
+
def test_daemon(self):
ev_1 = (EV_KEY, 9)
ev_2 = (EV_ABS, 12)
diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py
index f673bb63..3263a25b 100644
--- a/tests/testcases/test_integration.py
+++ b/tests/testcases/test_integration.py
@@ -25,7 +25,7 @@ import grp
import os
import unittest
import evdev
-from evdev.events import EV_KEY, EV_ABS
+from evdev.ecodes import EV_KEY, EV_ABS, BTN_LEFT, BTN_TOOL_DOUBLETAP
import json
from unittest.mock import patch
from importlib.util import spec_from_loader, module_from_spec
@@ -317,9 +317,17 @@ class TestIntegration(unittest.TestCase):
if key:
# modifies the keycode in the row not by writing into the input,
# but by sending an event
+
+ # click events are ignored because it would render the mouse
+ # useless. It can still be changed in the config files.
+ keycode_reader._pipe[1].send(InputEvent(EV_KEY, BTN_LEFT, 1))
+ time.sleep(0.1)
+ gtk_iteration()
+
keycode_reader._pipe[1].send(InputEvent(*key))
time.sleep(0.1)
gtk_iteration()
+
if expect_success:
self.assertEqual(row.get_keycode(), key)
css_classes = row.get_style_context().list_classes()
diff --git a/tests/testcases/test_mapping.py b/tests/testcases/test_mapping.py
index 794cff05..552d85d1 100644
--- a/tests/testcases/test_mapping.py
+++ b/tests/testcases/test_mapping.py
@@ -20,13 +20,12 @@
import os
-import shutil
-import json
import unittest
-from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X
+import json
+from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X, KEY_A
from keymapper.mapping import Mapping
-from keymapper.state import SystemMapping
+from keymapper.state import SystemMapping, XMODMAP_FILENAME
from keymapper.config import config
from keymapper.paths import get_preset_path
@@ -50,6 +49,20 @@ class TestSystemMapping(unittest.TestCase):
self.assertEqual(system_mapping.get('foo1'), 101)
self.assertEqual(system_mapping.get('bar2'), 202)
+ def test_xmodmap_file(self):
+ system_mapping = SystemMapping()
+ path = os.path.join(tmp, XMODMAP_FILENAME)
+ os.remove(path)
+
+ system_mapping.populate()
+ self.assertTrue(os.path.exists(path))
+ with open(path, 'r') as file:
+ content = json.load(file)
+ self.assertEqual(content['a'], KEY_A)
+ # only xmodmap stuff should be present
+ self.assertNotIn('key_a', content)
+ self.assertNotIn('KEY_A', content)
+
def test_system_mapping(self):
system_mapping = SystemMapping()
self.assertGreater(len(system_mapping._mapping), 100)
diff --git a/tests/testcases/test_reader.py b/tests/testcases/test_reader.py
index 1cc48cb4..cde91e31 100644
--- a/tests/testcases/test_reader.py
+++ b/tests/testcases/test_reader.py
@@ -20,10 +20,11 @@
import unittest
-
-from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X, KEY_COMMA
import time
+from evdev.ecodes import EV_KEY, EV_ABS, ABS_HAT0X, KEY_COMMA, BTN_LEFT, \
+ BTN_TOOL_DOUBLETAP
+
from keymapper.dev.reader import keycode_reader
from tests.test import InputEvent, pending_events, EVENT_READ_TIMEOUT