Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a callback to signal changes to settings #2312

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions lib/logitech_receiver/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def __init__(
short=None,
long=None,
product_id=None,
bus_id=None
bus_id=None,
setting_callback=None
):
assert receiver or handle
Device.instances.append(self)
Expand All @@ -76,6 +77,7 @@ def __init__(
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
Expand Down Expand Up @@ -526,7 +528,7 @@ def notify_devices(self): # no need to notify, as there are none
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``.
"""
Expand All @@ -541,7 +543,8 @@ def open(self, device_info):
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)
Expand Down
7 changes: 3 additions & 4 deletions lib/logitech_receiver/diversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,9 +1285,6 @@ def __str__(self):
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):
Expand All @@ -1304,7 +1301,9 @@ def evaluate(self, feature, notification, device, status, last_result):
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):
Expand Down
16 changes: 9 additions & 7 deletions lib/logitech_receiver/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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
9 changes: 5 additions & 4 deletions lib/logitech_receiver/receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -237,7 +238,7 @@ def register_new_device(self, number, notification=None):
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
Expand Down Expand Up @@ -382,15 +383,15 @@ def __str__(self):
__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``.
"""
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:
Expand Down
8 changes: 4 additions & 4 deletions lib/logitech_receiver/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1447,19 +1447,19 @@ def handler(device, n): # Called on notification events from the device
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
if self.active:
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:
Expand Down
24 changes: 12 additions & 12 deletions lib/logitech_receiver/settings_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -772,8 +772,8 @@ def activate_action(self):
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`,
Expand Down Expand Up @@ -1024,10 +1024,10 @@ def press_action(self): # switch sensitivity
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

Expand Down
5 changes: 4 additions & 1 deletion lib/logitech_receiver/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(self, receiver, changed_callback):

self[KEYS.ERROR] = None

def __str__(self):
def to_string(self):
count = len(self._receiver)
return (
_('No paired devices.')
Expand All @@ -103,6 +103,9 @@ def __str__(self):
}
)

def __str__(self):
self.to_string()

def changed(self, alert=ALERT.NOTIFICATION, reason=None):
self._changed_callback(self._receiver, alert=alert, reason=reason)

Expand Down
2 changes: 1 addition & 1 deletion lib/solaar/gtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def main():
logger.warning('Solaar udev file not found in expected location')
logger.warning('See https://pwr-solaar.github.io/Solaar/installation for more information')
try:
_listener.setup_scanner(_ui.status_changed, _common.error_dialog)
_listener.setup_scanner(_ui.status_changed, _ui.setting_changed, _common.error_dialog)

if args.restart_on_wake_up:
_upower.watch(_listener.start_all, _listener.stop_all)
Expand Down
12 changes: 7 additions & 5 deletions lib/solaar/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,12 @@ def __str__(self):


def _start(device_info):
assert _status_callback
assert _status_callback and _setting_callback
isDevice = device_info.isDevice
if not isDevice:
receiver = _receiver.Receiver.open(device_info)
receiver = _receiver.Receiver.open(device_info, _setting_callback)
else:
receiver = _device.Device.open(device_info)
receiver = _device.Device.open(device_info, _setting_callback)
configuration.attach_to(receiver)

if receiver:
Expand Down Expand Up @@ -374,14 +374,16 @@ def ping_all(resuming=False):


_status_callback = None
_setting_callback = None
_error_callback = None


def setup_scanner(status_changed_callback, error_callback):
global _status_callback, _error_callback
def setup_scanner(status_changed_callback, setting_changed_callback, error_callback):
global _status_callback, _error_callback, _setting_callback
assert _status_callback is None, 'scanner was already set-up'

_status_callback = status_changed_callback
_setting_callback = setting_changed_callback
_error_callback = error_callback

_base.notify_on_receivers_glib(_process_receiver_event)
Expand Down
6 changes: 5 additions & 1 deletion lib/solaar/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from logitech_receiver.status import ALERT
from solaar.i18n import _
from solaar.ui.config_panel import change_setting
from solaar.ui.config_panel import change_setting, record_setting
from solaar.ui.window import find_device

from . import common, diversion_rules, notify, tray, window
Expand Down Expand Up @@ -131,3 +131,7 @@ def _status_changed(device, alert, reason, refresh=False):

def status_changed(device, alert=ALERT.NONE, reason=None, refresh=False):
GLib.idle_add(_status_changed, device, alert, reason, refresh)


def setting_changed(device, setting_class, vals):
record_setting(device, setting_class, vals)
Loading