From 437cb533aa5748dc2bd473a32e060582ad22f286 Mon Sep 17 00:00:00 2001 From: sezanzeb Date: Fri, 20 Nov 2020 21:38:19 +0100 Subject: [PATCH] config for autoloading presets --- keymapper/config.py | 108 ++++++++++++++++++++------------------ keymapper/getdevices.py | 20 ++++--- tests/testcases/config.py | 67 +++++++++++++++++++++++ 3 files changed, 134 insertions(+), 61 deletions(-) create mode 100644 tests/testcases/config.py diff --git a/keymapper/config.py b/keymapper/config.py index f4ca1f05..f388663e 100644 --- a/keymapper/config.py +++ b/keymapper/config.py @@ -24,6 +24,8 @@ import os import json +import shutil +import copy from keymapper.paths import CONFIG from keymapper.logger import logger @@ -33,57 +35,63 @@ CONFIG_PATH = os.path.join(CONFIG, 'config') # an empty config with basic expected substructures INITIAL_CONFIG = { - 'autoload': [], + 'autoload': {}, 'map_EV_REL_devices': True } -_config = INITIAL_CONFIG.copy() - -def set_autoload_preset(device, preset): - """Set a preset to be automatically applied on start.""" - _config['autoload'].append({ - 'device': device, - 'preset': preset - }) - - -def iterate_autoload_presets(): - """Yield tuples of (device, preset).""" - for entry in _config['autoload']: - yield entry['device'], entry['preset'] - - -def set_modify_movement_devices(active): - """Set if devices that control movements should also be mapped. - - This causes many movements event to be passed through python code, - and if this ever seems to affect the responsiveness of mouse movements, - it can be disabled. This may make mapping some keys of the device - impossible. - """ - global _config - _config['map_EV_REL_devices'] = active - - -def load_config(): - """Load the config from the file system.""" - global _config - - if not os.path.exists(CONFIG_PATH): - # has not yet been saved - logger.debug('Config file not found') - _config = INITIAL_CONFIG.copy() - return - - with open(CONFIG_PATH, 'r') as f: - _config = INITIAL_CONFIG.copy() - _config.update(json.load(f)) - logger.info('Loaded config from %s', CONFIG_PATH) - - -def save_config(): - """Save the config to the file system.""" - with open(CONFIG_PATH, 'w') as f: - json.dump(_config, f) - logger.info('Saved config to %s', CONFIG_PATH) +class _Config: + def __init__(self): + self._config = {} + self.load_config() + + def set_autoload_preset(self, device, preset): + """Set a preset to be automatically applied on start.""" + self._config['autoload'][device] = preset + + def iterate_autoload_presets(self): + """get tuples of (device, preset).""" + return self._config['autoload'].items() + + def set_modify_movement_devices(self, active): + """Set if devices that control movements should also be mapped. + + This causes many movements event to be passed through python code, + and if this ever seems to affect the responsiveness of mouse movements, + it can be disabled. This is just an optional precaution. Disabling this + may make mapping some keys of the device impossible. + """ + self._config['map_EV_REL_devices'] = active + + def may_modify_movement_devices(self): + """Get if devices that control movements may be modified as well.""" + return self._config['map_EV_REL_devices'] + + def load_config(self): + """Load the config from the file system.""" + if not os.path.exists(CONFIG_PATH): + # has not yet been saved + logger.info('Creating initial config') + self._config = copy.deepcopy(INITIAL_CONFIG) + self.save_config() + return + + with open(CONFIG_PATH, 'r') as f: + self._config = copy.deepcopy(INITIAL_CONFIG) + self._config.update(json.load(f)) + logger.info('Loaded config from %s', CONFIG_PATH) + + def clear_config(self): + """Needed for tests.""" + self._config = copy.deepcopy(INITIAL_CONFIG) + + def save_config(self): + """Save the config to the file system.""" + with open(CONFIG_PATH, 'w') as f: + json.dump(self._config, f, indent=4) + logger.info('Saved config to %s', CONFIG_PATH) + shutil.chown(CONFIG_PATH, os.getlogin()) + f.write('\n') + + +config = _Config() diff --git a/keymapper/getdevices.py b/keymapper/getdevices.py index feba7bf2..9240f9ef 100644 --- a/keymapper/getdevices.py +++ b/keymapper/getdevices.py @@ -27,6 +27,7 @@ import multiprocessing import evdev from keymapper.logger import logger +from keymapper.config import config _devices = None @@ -71,17 +72,14 @@ class _GetDevicesProcess(multiprocessing.Process): if evdev.ecodes.EV_KEY not in capabilities: continue - if evdev.ecodes.EV_REL in capabilities: - # skip devices that control movement, because I would like - # to not affect the mouse movement performance - # TODO check for config, is set enable mapping buttons - # of that device as well - # TODO speaking of config, add checkbox to automatically load - # a preset. Store a mapping of device: preset somewhere to - # automatically load them. Autoloading presets should be a - # different executable, and controlling stuff in the gui - # should send a dbus message to the different bin to stop - # doing stuff + if ( + not config.may_modify_movement_devices() + and evdev.ecodes.EV_REL in capabilities + ): + # skip devices that control movement to avoid affecting + # their performance due to the amount of their events. + # TODO add checkbox to automatically load + # a preset on login logger.debug( 'Skipping %s to avoid impairing mouse movement', device.path diff --git a/tests/testcases/config.py b/tests/testcases/config.py new file mode 100644 index 00000000..ac1ab5e8 --- /dev/null +++ b/tests/testcases/config.py @@ -0,0 +1,67 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# key-mapper - GUI for device specific keyboard mappings +# Copyright (C) 2020 sezanzeb +# +# This file is part of key-mapper. +# +# key-mapper is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# key-mapper is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with key-mapper. If not, see . + + +import unittest + +from keymapper.config import config + + +class TestConfig(unittest.TestCase): + def tearDown(self): + config.clear_config() + self.assertEqual(len(config.iterate_autoload_presets()), 0) + config.save_config() + + def test_autoload(self): + config.set_autoload_preset('d1', 'a') + self.assertEqual(len(config.iterate_autoload_presets()), 1) + + config.set_autoload_preset('d2', 'b') + self.assertEqual(len(config.iterate_autoload_presets()), 2) + + config.set_autoload_preset('d2', 'c') + self.assertEqual(len(config.iterate_autoload_presets()), 2) + + self.assertListEqual( + list(config.iterate_autoload_presets()), + [('d1', 'a'), ('d2', 'c')] + ) + + def test_save_load(self): + config.set_autoload_preset('d1', 'a') + config.set_autoload_preset('d2', 'b') + config.set_modify_movement_devices(True) + config.save_config() + + # ignored after load + config.set_modify_movement_devices(False) + config.set_autoload_preset('d3', 'c') + + config.load_config() + self.assertListEqual( + list(config.iterate_autoload_presets()), + [('d1', 'a'), ('d2', 'b')] + ) + self.assertTrue(config.may_modify_movement_devices()) + + +if __name__ == "__main__": + unittest.main()