diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index 284c702b..2b05ef58 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -412,6 +412,43 @@ class Report(Condition): return {'Report': self.report} +# Setting(device, setting, [key], value...) +class Setting(Condition): + def __init__(self, args): + if not (isinstance(args, list) and len(args) > 2): + _log.warn('rule Setting argument not list with minimum length 3: %s', args) + self.args = [] + else: + self.args = args + + def __str__(self): + return 'Setting: ' + ' '.join([str(a) for a in self.args]) + + def evaluate(self, report, notification, device, status, last_result): + import solaar.ui.window as _window + if len(self.args) < 3: + return None + dev = _window.find_device(self.args[0]) if self.args[0] is not None else device + if dev is None: + _log.warn('Setting condition: device %s is not known', self.args[0]) + return False + setting = next((s for s in dev.settings if s.name == self.args[1]), None) + if setting is None: + _log.warn('Setting condition: setting %s is not the name of a setting for %s', self.args[1], dev.name) + return None + # should the value argument be checked to be sure it is acceptable?? needs to be careful about boolean toggle + # TODO add compare methods for more validators + try: + result = setting.compare(self.args[2:], setting.read()) + except Exception as e: + _log.warn('Setting condition: error when checking setting %s: %s', self.args, e) + result = False + return result + + def data(self): + return {'Setting': self.args[:]} + + MODIFIERS = { 'Shift': int(Gdk.ModifierType.SHIFT_MASK), 'Control': int(Gdk.ModifierType.CONTROL_MASK), @@ -635,7 +672,7 @@ class KeyPress(Action): def evaluate(self, feature, notification, device, status, last_result): current = gkeymap.get_modifier_state() if _log.isEnabledFor(_INFO): - _log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.keys]) + _log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.key_symbols]) self.keyDown(self.key_symbols, current) self.keyUp(reversed(self.key_symbols), current) if x11: @@ -819,6 +856,7 @@ COMPONENTS = { 'MouseProcess': MouseProcess, 'Feature': Feature, 'Report': Report, + 'Setting': Setting, 'Modifiers': Modifiers, 'Key': Key, 'Test': Test, diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py index 415ea91c..4d31e24a 100644 --- a/lib/logitech_receiver/settings.py +++ b/lib/logitech_receiver/settings.py @@ -67,6 +67,11 @@ class Validator: def to_string(cls, value): return (str(value)) + def compare(self, args, current): + if len(args) != 1: + return False + return args[0] == current + class BooleanValidator(Validator): __slots__ = ('true_value', 'false_value', 'read_skip_byte_count', 'write_prefix_bytes', 'mask', 'needs_current_value') @@ -316,6 +321,9 @@ class Setting: def acceptable(self, args, current): return self._validator.acceptable(args, current) if self._validator else None + def compare(self, args, current): + return self._validator.compare(args, current) if self._validator else None + def apply(self): assert hasattr(self, '_value') assert hasattr(self, '_device') @@ -789,6 +797,14 @@ class BitFieldValidator(Validator): val = bool_or_toggle(current[str(int(key))], args[1]) return None if val is None else [str(int(key)), val] + def compare(self, args, current): + if len(args) != 2: + return False + key = next((key for key in self.options if key == args[0]), None) + if key is None: + return False + return args[1] == current[str(int(key))] + class BitFieldWithOffsetAndMaskValidator(Validator): __slots__ = ('byte_count', 'options', '_option_from_key', '_mask_from_offset', '_option_from_offset_mask') @@ -878,7 +894,15 @@ class BitFieldWithOffsetAndMaskValidator(Validator): if key is None: return None val = bool_or_toggle(current[str(int(key))], args[1]) - return None if val is None else [str(key), val] + return None if val is None else [str(int(key)), val] + + def compare(self, args, current): + if len(args) != 2: + return False + key = next((option.id for option in self.options if option.as_int() == args[0]), None) + if key is None: + return False + return args[1] == current[str(int(key))] class ChoicesValidator(Validator): @@ -1026,6 +1050,14 @@ class ChoicesMapValidator(ChoicesValidator): choice = next((item for item in choices if item == args[1]), None) return [str(int(key)), int(choice)] if choice is not None else None + def compare(self, args, current): + if len(args) != 2: + return False + key = next((key for key in self.choices if key == int(args[0])), None) + if key is None: + return False + return args[1] == current[str(int(key))] + class RangeValidator(Validator): kind = KIND.range @@ -1071,6 +1103,14 @@ class RangeValidator(Validator): # None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args) return None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args + def compare(self, args, current): + if len(args) == 1: + return args[0] == current + elif len(args) == 2: + return args[0] <= current and current <= args[1] + else: + return False + class MultipleRangeValidator(Validator): @@ -1157,6 +1197,10 @@ class MultipleRangeValidator(Validator): return None return [str(int(item)), {**args[1]}] + def commpare(self, args, current): + _log.warn('compare not implemented for multiple range settings') + return False + class ActionSettingRW: """Special RW class for settings that turn on and off special processing when a key or button is depressed"""