diff --git a/data/key-mapper.glade b/data/key-mapper.glade index e78728c4..091062e6 100644 --- a/data/key-mapper.glade +++ b/data/key-mapper.glade @@ -541,7 +541,7 @@ True True - + False diff --git a/keymapper/config.py b/keymapper/config.py index ced14988..b47d436a 100644 --- a/keymapper/config.py +++ b/keymapper/config.py @@ -195,6 +195,10 @@ class GlobalConfig(ConfigBase): if preset is not None: self.set(f'autoload.{device}', preset) else: + logger.info( + 'Not loading injecting for "%s" automatically anmore', + device + ) self.remove(f'autoload.{device}') def iterate_autoload_presets(self): diff --git a/keymapper/dev/injector.py b/keymapper/dev/injector.py index 791d6e50..24424e54 100644 --- a/keymapper/dev/injector.py +++ b/keymapper/dev/injector.py @@ -46,20 +46,27 @@ CLOSE = 0 def is_numlock_on(): """Get the current state of the numlock.""" - xset_q = subprocess.check_output(['xset', 'q']).decode() - num_lock_status = re.search( - r'Num Lock:\s+(.+?)\s', - xset_q - ) + try: + xset_q = subprocess.check_output(['xset', 'q']).decode() + num_lock_status = re.search( + r'Num Lock:\s+(.+?)\s', + xset_q + ) - if num_lock_status is not None: - return num_lock_status[1] == 'on' + if num_lock_status is not None: + return num_lock_status[1] == 'on' - return False + return False + except subprocess.CalledProcessError: + # tty + return None def set_numlock(state): """Set the numlock to a given state of True or False.""" + if state is None: + return + value = { True: 'on', False: 'off' @@ -81,8 +88,11 @@ def ensure_numlock(func): # for some reason, grabbing a device can modify the num lock state. # remember it and apply back later numlock_before = is_numlock_on() + result = func(*args, **kwargs) + set_numlock(numlock_before) + return result return wrapped diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py index 5b722786..60c7cf05 100755 --- a/keymapper/gtk/window.py +++ b/keymapper/gtk/window.py @@ -70,6 +70,22 @@ def get_selected_row_bg(): return color.to_string() +class HandlerDisabled: + """Safely modify a widget without causing handlers to be called. + + Use in a with statement. + """ + def __init__(self, widget, handler): + self.widget = widget + self.handler = handler + + def __enter__(self): + self.widget.handler_block_by_func(self.handler) + + def __exit__(self, *_): + self.widget.handler_unblock_by_func(self.handler) + + class Window: """User Interface.""" def __init__(self): @@ -358,7 +374,7 @@ class Window: keycode_reader.start_reading(device) GLib.timeout_add(10, self.show_device_mapping_status) - def on_preset_autoload_switch_activate(self, _, active): + def on_autoload_switch(self, _, active): """Load the preset automatically next time the user logs in.""" device = self.selected_device preset = self.selected_preset @@ -442,10 +458,12 @@ class Window: key_list.insert(single_key_mapping, -1) autoload_switch = self.get('preset_autoload_switch') - autoload_switch.set_active(config.is_autoloaded( - self.selected_device, - self.selected_preset - )) + + with HandlerDisabled(autoload_switch, self.on_autoload_switch): + autoload_switch.set_active(config.is_autoloaded( + self.selected_device, + self.selected_preset + )) self.get('preset_name_input').set_text('') self.add_empty() diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py index 6bf6a014..770943ee 100644 --- a/tests/testcases/test_integration.py +++ b/tests/testcases/test_integration.py @@ -122,15 +122,20 @@ class TestIntegration(unittest.TestCase): self.assertFalse(self.window.show_device_mapping_status()) def test_autoload(self): - self.window.on_preset_autoload_switch_activate(None, False) + self.window.on_autoload_switch(None, False) self.assertFalse(config.is_autoloaded( self.window.selected_device, self.window.selected_preset )) - # select a preset for the first device self.window.on_select_device(FakeDropdown('device 1')) - self.window.on_preset_autoload_switch_activate(None, True) + gtk_iteration() + self.assertFalse(self.window.get('preset_autoload_switch').get_active()) + + # select a preset for the first device + self.window.get('preset_autoload_switch').set_active(True) + gtk_iteration() + self.assertTrue(self.window.get('preset_autoload_switch').get_active()) self.assertTrue(config.is_autoloaded('device 1', 'new preset')) self.assertFalse(config.is_autoloaded('device 2', 'new preset')) self.assertListEqual( @@ -138,9 +143,18 @@ class TestIntegration(unittest.TestCase): [('device 1', 'new preset')] ) + # switch the preset, the switch should be correct and the config + # not changed. + self.window.on_create_preset_clicked(None) + gtk_iteration() + self.assertEqual(self.window.selected_preset, 'new preset 2') + self.assertFalse(self.window.get('preset_autoload_switch').get_active()) + self.assertTrue(config.is_autoloaded('device 1', 'new preset')) + # select a preset for the second device self.window.on_select_device(FakeDropdown('device 2')) - self.window.on_preset_autoload_switch_activate(None, True) + self.window.get('preset_autoload_switch').set_active(True) + gtk_iteration() self.assertTrue(config.is_autoloaded('device 1', 'new preset')) self.assertTrue(config.is_autoloaded('device 2', 'new preset')) self.assertListEqual( @@ -149,7 +163,8 @@ class TestIntegration(unittest.TestCase): ) # disable autoloading for the second device - self.window.on_preset_autoload_switch_activate(None, False) + self.window.get('preset_autoload_switch').set_active(False) + gtk_iteration() self.assertTrue(config.is_autoloaded('device 1', 'new preset')) self.assertFalse(config.is_autoloaded('device 2', 'new preset')) self.assertListEqual(