From ed248c62b9a5ffbb153be37874ae46d9f3ee19e2 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 18 Feb 2024 13:48:52 -0500 Subject: [PATCH] device: add callback to call when changing a setting --- lib/logitech_receiver/device.py | 9 +++++--- lib/logitech_receiver/diversion.py | 7 +++--- lib/logitech_receiver/notifications.py | 16 ++++++++------ lib/logitech_receiver/receiver.py | 9 ++++---- lib/logitech_receiver/settings.py | 8 +++---- lib/logitech_receiver/settings_templates.py | 24 ++++++++++----------- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/lib/logitech_receiver/device.py b/lib/logitech_receiver/device.py index 378363c1..1834caf4 100644 --- a/lib/logitech_receiver/device.py +++ b/lib/logitech_receiver/device.py @@ -63,7 +63,8 @@ class Device: short=None, long=None, product_id=None, - bus_id=None + bus_id=None, + setting_callback=None ): assert receiver or handle Device.instances.append(self) @@ -76,6 +77,7 @@ class Device: self.hidpp_short = short self.hidpp_long = long self.bluetooth = bus_id == 0x0005 # Bluetooth connections need long messages + self.setting_callback = setting_callback if receiver: assert number > 0 and number <= 15 # some receivers have devices past their max # of devices @@ -526,7 +528,7 @@ class Device: pass @classmethod - def open(self, device_info): + def open(self, device_info, setting_callback=None): """Opens a Logitech Device found attached to the machine, by Linux device path. :returns: An open file handle for the found receiver, or ``None``. """ @@ -541,7 +543,8 @@ class Device: short=device_info.hidpp_short, long=device_info.hidpp_long, product_id=device_info.product_id, - bus_id=device_info.bus_id + bus_id=device_info.bus_id, + setting_callback=setting_callback ) except OSError as e: logger.exception('open %s', device_info) diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index 3da2510f..4958b40a 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -1285,9 +1285,6 @@ class Set(Action): return 'Set: ' + ' '.join([str(a) for a in self.args]) def evaluate(self, feature, notification, device, status, last_result): - # importing here to avoid circular imports - from solaar.ui.config_panel import change_setting as _change_setting - if len(self.args) < 3: return None if logger.isEnabledFor(logging.INFO): @@ -1304,7 +1301,9 @@ class Set(Action): if args is None: logger.warning('Set Action: invalid args %s for setting %s of %s', self.args[2:], self.args[1], self.args[0]) return None - _change_setting(dev, setting, args) + setting.write(*args) + if device.setting_callback: + device.setting_callback(device, type(setting), args) return None def data(self): diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index babd0058..40315520 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -22,8 +22,6 @@ import threading as _threading from struct import unpack as _unpack -from solaar.ui.config_panel import record_setting - from . import diversion as _diversion from . import hidpp10 as _hidpp10 from . import hidpp20 as _hidpp20 @@ -413,7 +411,8 @@ def _process_feature_notification(device, status, n, feature): elif feature == _F.BACKLIGHT2: if (n.address == 0x00): level = _unpack('!B', n.data[1:2])[0] - record_setting(device, _st.Backlight2Level, [level]) + if device.setting_callback: + device.setting_callback(device, _st.Backlight2Level, [level]) elif feature == _F.REPROG_CONTROLS_V4: if n.address == 0x00: @@ -442,7 +441,8 @@ def _process_feature_notification(device, status, n, feature): if logger.isEnabledFor(logging.INFO): logger.info('%s: WHEEL: ratchet: %d', device, ratchet) if ratchet < 2: # don't process messages with unusual ratchet values - record_setting(device, _st.ScrollRatchet, [2 if ratchet else 1]) + if device.setting_callback: + device.setting_callback(device, _st.ScrollRatchet, [2 if ratchet else 1]) else: if logger.isEnabledFor(logging.INFO): logger.info('%s: unknown WHEEL %s', device, n) @@ -459,9 +459,11 @@ def _process_feature_notification(device, status, n, feature): elif (n.address == 0x10): resolution_index = _unpack('!B', n.data[:1])[0] profile_sector = _unpack('!H', device.feature_request(_F.ONBOARD_PROFILES, 0x40)[:2])[0] - for profile in device.profiles.profiles.values() if device.profiles else []: - if profile.sector == profile_sector: - record_setting(device, _st.AdjustableDpi, [profile.resolutions[resolution_index]]) + if device.setting_callback: + for profile in device.profiles.profiles.values() if device.profiles else []: + if profile.sector == profile_sector: + device.setting_callback(device, _st.AdjustableDpi, [profile.resolutions[resolution_index]]) + break _diversion.process_notification(device, status, n, feature) return True diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index fb2d469e..7b593880 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -47,12 +47,13 @@ class Receiver: number = 0xFF kind = None - def __init__(self, handle, path, product_id): + def __init__(self, handle, path, product_id, setting_callback=None): assert handle self.isDevice = False # some devices act as receiver so we need a property to distinguish them self.handle = handle self.path = path self.product_id = product_id + self.setting_callback = setting_callback product_info = _product_information(self.product_id) if not product_info: logger.warning('Unknown receiver type: %s', self.product_id) @@ -237,7 +238,7 @@ class Receiver: assert notification is None or notification.sub_id == 0x41 try: - dev = Device(self, number, notification) + dev = Device(self, number, notification, setting_callback=self.setting_callback) if logger.isEnabledFor(logging.INFO): logger.info('%s: found new device %d (%s)', self, number, dev.wpid) self._devices[number] = dev @@ -382,7 +383,7 @@ class Receiver: __bool__ = __nonzero__ = lambda self: self.handle is not None @classmethod - def open(self, device_info): + def open(self, device_info, setting_callback=None): """Opens a Logitech Receiver found attached to the machine, by Linux device path. :returns: An open file handle for the found receiver, or ``None``. @@ -390,7 +391,7 @@ class Receiver: try: handle = _base.open_path(device_info.path) if handle: - return Receiver(handle, device_info.path, device_info.product_id) + return Receiver(handle, device_info.path, device_info.product_id, setting_callback) except OSError as e: logger.exception('open %s', device_info) if e.errno == _errno.EACCES: diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py index 23f17d73..7ef1f98b 100644 --- a/lib/logitech_receiver/settings.py +++ b/lib/logitech_receiver/settings.py @@ -1447,10 +1447,10 @@ class ActionSettingRW: self.active = True if divertSetting: divertSetting.write_key_value(int(self.key.key), 1) + if self.device.setting_callback: + self.device.setting_callback(device, type(divertSetting), [self.key.key, 1]) device.add_notification_handler(self.name, handler) - from solaar.ui import status_changed as _status_changed self.activate_action() - _status_changed(device, refresh=True) # update main window else: logger.error('cannot enable %s on %s for key %s', self.name, device, key) else: # Disable @@ -1458,8 +1458,8 @@ class ActionSettingRW: self.active = False if divertSetting: divertSetting.write_key_value(int(self.key.key), 0) - from solaar.ui import status_changed as _status_changed - _status_changed(device, refresh=True) # update main window + if self.device.setting_callback: + self.device.setting_callback(device, type(divertSetting), [self.key.key, 0]) try: device.remove_notification_handler(self.name) except Exception: diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 314ef37b..33582a64 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -479,14 +479,14 @@ class ThumbInvert(_Setting): # change UI to show result of onboard profile change def profile_change(device, profile_sector): - from solaar.ui.config_panel import record_setting # prevent circular import - record_setting(device, OnboardProfiles, [profile_sector]) - for profile in device.profiles.profiles.values() if device.profiles else []: - if profile.sector == profile_sector: - resolution_index = profile.resolution_default_index - record_setting(device, AdjustableDpi, [profile.resolutions[resolution_index]]) - record_setting(device, ReportRate, [profile.report_rate]) - break + if device.setting_callback: + device.setting_callback(device, OnboardProfiles, [profile_sector]) + for profile in device.profiles.profiles.values() if device.profiles else []: + if profile.sector == profile_sector: + resolution_index = profile.resolution_default_index + device.setting_callback(device, AdjustableDpi, [profile.resolutions[resolution_index]]) + device.setting_callback(device, ReportRate, [profile.report_rate]) + break class OnboardProfiles(_Setting): @@ -772,8 +772,8 @@ class DpiSlidingXY(_RawXYProcessing): def setNewDpi(self, newDpiIdx): newDpi = self.dpiChoices[newDpiIdx] self.dpiSetting.write(newDpi) - from solaar.ui import status_changed as _status_changed - _status_changed(self.device, refresh=True) # update main window + if self.device.setting_callback: + self.device.setting_callback(self.device, type(self.dpiSetting), [newDpi]) def displayNewDpi(self, newDpiIdx): from solaar.ui import notify as _notify # import here to avoid circular import when running `solaar show`, @@ -1024,10 +1024,10 @@ class SpeedChange(_Setting): if newSpeed is not None: if speed_setting: speed_setting.write(newSpeed) + if self.device.setting_callback: + self.device.setting_callback(self.device, type(speed_setting), [newSpeed]) else: logger.error('cannot save sensitivity setting on %s', self.device) - from solaar.ui import status_changed as _status_changed - _status_changed(self.device, refresh=True) # update main window if self.device.persister: self.device.persister['_speed-change'] = currentSpeed