diff --git a/keymapper/X.py b/keymapper/X.py index 78d63e07..82d5db74 100644 --- a/keymapper/X.py +++ b/keymapper/X.py @@ -381,14 +381,15 @@ def create_default_symbols(): ensure_symlink() + contents = generate_symbols(DEFAULT_SYMBOLS_NAME, None, defaults) + if not os.path.exists(DEFAULT_SYMBOLS): logger.info('Creating %s', DEFAULT_SYMBOLS) os.mknod(DEFAULT_SYMBOLS) - # TODO test that it is included in the config files - # TODO write test about it being created only if the path doesnt exist with open(DEFAULT_SYMBOLS, 'w') as f: - contents = generate_symbols(DEFAULT_SYMBOLS_NAME, None, defaults) if contents is not None: logger.info('Updating default mappings') f.write(contents) + else: + logger.error('Failed to write default mappings') diff --git a/keymapper/gtk/row.py b/keymapper/gtk/row.py index 5aa981fb..c1c2d93d 100644 --- a/keymapper/gtk/row.py +++ b/keymapper/gtk/row.py @@ -25,11 +25,10 @@ import gi gi.require_version('Gtk', '3.0') gi.require_version('GLib', '2.0') -from gi.repository import Gtk, GLib +from gi.repository import Gtk from keymapper.mapping import custom_mapping from keymapper.logger import logger -from keymapper.linux import keycode_reader CTX_KEYCODE = 2 diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py index 68fc8fb8..8d0bbd55 100755 --- a/keymapper/gtk/window.py +++ b/keymapper/gtk/window.py @@ -33,7 +33,7 @@ from keymapper.X import create_setxkbmap_config, apply_preset, \ from keymapper.presets import get_presets, find_newest_preset, \ delete_preset, rename_preset from keymapper.logger import logger -from keymapper.linux import get_devices, keycode_reader +from keymapper.linux import get_devices from keymapper.gtk.row import Row from keymapper.gtk.unsaved import unsaved_changes_dialog, GO_BACK @@ -245,9 +245,6 @@ class Window: self.selected_preset = None self.populate_presets() - GLib.idle_add( - lambda: keycode_reader.start_reading(self.selected_device) - ) def on_create_preset_clicked(self, button): """Create a new preset and select it.""" diff --git a/keymapper/linux.py b/keymapper/linux.py index c88c40ec..da999fde 100644 --- a/keymapper/linux.py +++ b/keymapper/linux.py @@ -47,14 +47,8 @@ def can_grab(path): class KeycodeReader: """Keeps reading keycodes in the background for the UI to use. - This was written before I figured out there is get_keycode in GLib. - - A new arriving keycode indicates that a button was pressed, so the - UI can keep checking for a new keycode on this object and act like the - keycode went right to the input box. - - GTK inputs cannot listen on keys that don't write a character, so - they have to repeatedly ask for new data (in this solution). + When a button was pressed, the newest keycode can be obtained from this + object. This was written before I figured out there is get_keycode in Gdk. """ def __init__(self): self.virtual_devices = [] @@ -102,7 +96,8 @@ class KeycodeReader: return newest_keycode -keycode_reader = KeycodeReader() +# not used anymore since the overlooked get_keycode function is now being used +# keycode_reader = KeycodeReader() def get_devices(): diff --git a/keymapper/paths.py b/keymapper/paths.py index 8eff11b6..de290082 100644 --- a/keymapper/paths.py +++ b/keymapper/paths.py @@ -38,33 +38,44 @@ USERS_SYMBOLS = os.path.join( os.getlogin().replace(' ', '_') ) -# those are the same for every preset and user +# those are the same for every preset and user, they are needed to make the +# presets work. KEYCODES_PATH = '/usr/share/X11/xkb/keycodes/key-mapper' -def get_home_path(device, preset=None): +def get_home_path(device=None, preset=None): """Get the path to the config file in /usr.""" + if device is None: + return HOME_PATH + device = device.strip() + if preset is not None: preset = preset.strip() return os.path.join(HOME_PATH, device, preset).replace(' ', '_') - else: + + if device is not None: return os.path.join(HOME_PATH, device.replace(' ', '_')) DEFAULT_SYMBOLS = get_home_path('default') -def get_usr_path(device, preset=None): +def get_usr_path(device=None, preset=None): """Get the path to the config file in /usr. This folder is a symlink and the files are in ~/.config/key-mapper If preset is omitted, returns the folder for the device. """ + if device is None: + return USERS_SYMBOLS + device = device.strip() + if preset is not None: preset = preset.strip() return os.path.join(USERS_SYMBOLS, device, preset).replace(' ', '_') - else: + + if device is not None: return os.path.join(USERS_SYMBOLS, device.replace(' ', '_')) diff --git a/tests/test.py b/tests/test.py index 881a5668..830c82f4 100644 --- a/tests/test.py +++ b/tests/test.py @@ -28,9 +28,9 @@ import unittest # quickly fake some stuff before any other file gets a chance to import # the original version from keymapper import paths -paths.X11_SYMBOLS = '/tmp/key-mapper-test/X11/symbols/key-mapper/user' +paths.X11_SYMBOLS = '/tmp/key-mapper-test/X11/symbols' paths.USERS_SYMBOLS = '/tmp/key-mapper-test/X11/symbols/key-mapper/user' -paths.DEFAULT_SYMBOLS = '/tmp/key-mapper-test/X11/symbols/key-mapper/user/default' +paths.DEFAULT_SYMBOLS = '/tmp/key-mapper-test/user/.config/default' paths.HOME_PATH = '/tmp/key-mapper-test/user/.config' paths.KEYCODES_PATH = '/tmp/key-mapper-test/X11/keycodes/key-mapper' diff --git a/tests/testcases/config.py b/tests/testcases/config.py index 1fb18ad5..2fb006bb 100644 --- a/tests/testcases/config.py +++ b/tests/testcases/config.py @@ -20,12 +20,15 @@ import os +import time import unittest import shutil from keymapper.X import custom_mapping, generate_symbols, \ - create_identity_mapping, create_setxkbmap_config, get_home_path -from keymapper.paths import KEYCODES_PATH, USERS_SYMBOLS, HOME_PATH + create_identity_mapping, create_setxkbmap_config, \ + get_preset_name, create_default_symbols +from keymapper.paths import get_home_path, get_usr_path, KEYCODES_PATH, \ + HOME_PATH, USERS_SYMBOLS from test import tmp @@ -62,6 +65,35 @@ class TestConfig(unittest.TestCase): self.assertIn('key <11> { [ KP_1 ] };', content) self.assertIn('key <12> { [ 3 ] };', content) + self.assertIn('include "key-mapper/user/default"', content) + self.assertIn(get_preset_name('device_a', 'preset_b'), content) + + def test_default_symbols(self): + # keycodes are missing + self.assertRaises( + FileNotFoundError, + create_default_symbols + ) + create_identity_mapping() + create_default_symbols() + + self.assertTrue(os.path.exists(get_home_path('default'))) + self.assertTrue(os.path.exists(get_usr_path('default'))) + self.assertTrue(os.path.islink(get_usr_path())) + + with open(get_home_path('default'), 'r') as f: + content = f.read() + self.assertNotIn('include', content) + # this is pretty much the same on every keyboard + self.assertIn('key <10> { [ 1', content) + self.assertIn('key <11> { [ 2', content) + self.assertIn('key <12> { [ 3', content) + self.assertIn('key <65> { [ space ] };', content) + + def test_get_preset_name(self): + self.assertEqual(get_preset_name('a', 'b'), 'key-mapper/user/a/b') + self.assertEqual(get_preset_name('a'), 'key-mapper/user/a') + def test_generate_content(self): self.assertRaises( FileNotFoundError, @@ -72,17 +104,31 @@ class TestConfig(unittest.TestCase): # create the identity mapping, because it is required for # generate_symbols create_identity_mapping() - self.assertTrue(os.path.exists(KEYCODES_PATH)) - with open(KEYCODES_PATH, 'r') as f: - keycodes = f.read() - self.assertIn('<8> = 8;', keycodes) - self.assertIn('<255> = 255;', keycodes) content = generate_symbols('device/preset') self.assertIn('key <10> { [ a ] };', content) self.assertIn('key <11> { [ KP_1 ] };', content) self.assertIn('key <12> { [ 3 ] };', content) + def test_identity_mapping(self): + create_identity_mapping() + self.assertTrue(os.path.exists(KEYCODES_PATH)) + with open(KEYCODES_PATH, 'r') as f: + content = f.read() + self.assertIn('minimum = 8;', content) + # whatever the maximum is, might change if mouse buttons + # can be supported at some point as well (no idea how) + self.assertIn('maximum =', content) + self.assertIn('<8> = 8;', content) + self.assertIn('<255> = 255;', content) + # this is stuff that should only be found in symbol files + self.assertNotIn('include', content) + self.assertNotIn('name', content) + # to make sure they are used together with format, which + # changes {{ to {. + self.assertNotIn('{{', content) + self.assertNotIn('}}', content) + if __name__ == "__main__": unittest.main() diff --git a/tests/testcases/integration.py b/tests/testcases/integration.py index 56e51556..75e9281f 100644 --- a/tests/testcases/integration.py +++ b/tests/testcases/integration.py @@ -34,7 +34,6 @@ from gi.repository import Gtk from keymapper.mapping import custom_mapping from keymapper.paths import USERS_SYMBOLS, HOME_PATH, KEYCODES_PATH -from keymapper.linux import keycode_reader from test import tmp @@ -114,7 +113,7 @@ class Integration(unittest.TestCase): self.keycode = keycode def get_keycode(self): - return [False, self.keycode] + return [True, self.keycode] def change_empty_row(keycode, character): """Modify the one empty row that always exists.""" diff --git a/tests/testcases/test.py b/tests/testcases/test.py index a74e6676..b3601bea 100644 --- a/tests/testcases/test.py +++ b/tests/testcases/test.py @@ -22,7 +22,8 @@ import unittest from keymapper.linux import get_devices -from keymapper.paths import USERS_SYMBOLS, X11_SYMBOLS, DEFAULT_SYMBOLS +from keymapper.paths import USERS_SYMBOLS, X11_SYMBOLS, HOME_PATH, \ + DEFAULT_SYMBOLS class TestTest(unittest.TestCase): @@ -30,8 +31,11 @@ class TestTest(unittest.TestCase): self.assertIn('device 1', get_devices()) def test_paths(self): + # stuff in /usr self.assertTrue(USERS_SYMBOLS.startswith(X11_SYMBOLS)) - self.assertTrue(DEFAULT_SYMBOLS.startswith(X11_SYMBOLS)) + + # stuff in /home + self.assertTrue(DEFAULT_SYMBOLS.startswith(HOME_PATH)) if __name__ == "__main__":