Skip to content

Commit

Permalink
ui: better startup behavior for LED effect settings
Browse files Browse the repository at this point in the history
  • Loading branch information
pfps committed Feb 8, 2024
1 parent fdc4068 commit 3ea7ce3
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 22 deletions.
2 changes: 2 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ Some mice store one or more profiles, which control aspects of the behavior of t

Profiles can control the rate at which the mouse reports movement, the resolution of the the movement reports, what the mouse buttons do, and its LED effects. Solaar can dump the entire set of profiles into a YAML file can load an entire set of profiles from a file. Users can edit the file to effect changes to the profiles. Solaar has a setting that switches between profiles or disables all profiles. When switching between profiles or using a button to change resolution Solaar keeps track of the changes in the settings for these features.

When profiles are active changes cannot be made to the Report Rate setting. Changes can be made to the Sensitivity setting and to LED settings. To keep the profile values make these setting ignored.

A profile file has some bookkeeping information, including profile version and the name of the device, and a sequence of profiles.

Each profile has the following fields:
Expand Down
4 changes: 2 additions & 2 deletions lib/logitech_receiver/hidpp20.py
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,8 @@ class LEDParam:
LEDParam.form: 1
}
LEDEffects = {
0x0: [_NamedInt(0x0, _('Disable')), {}],
0x1: [_NamedInt(0x1, _('Fixed')), {
0x0: [_NamedInt(0x0, _('Disabled')), {}],
0x1: [_NamedInt(0x1, _('Static')), {
LEDParam.color: 0,
LEDParam.ramp: 3
}],
Expand Down
10 changes: 6 additions & 4 deletions lib/logitech_receiver/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def read(self, cached=True):
reply = self._rw.read(self._device)
if reply:
self._value = self._validator.validate_read(reply)
if self._device.persister and self.name not in self._device.persister:
if self._value is not None and self._device.persister and self.name not in self._device.persister:
# Don't update the persister if it already has a value,
# otherwise the first read might overwrite the value we wanted.
self._device.persister[self.name] = self._value if self.persist else None
Expand Down Expand Up @@ -1216,15 +1216,17 @@ class HeteroValidator(Validator):
def build(cls, setting_class, device, **kwargs):
return cls(**kwargs)

def __init__(self, data_class=None, options=None):
def __init__(self, data_class=None, options=None, readable=True):
assert data_class is not None and options is not None
self.data_class = data_class
self.options = options
self.readable = readable
self.needs_current_value = False

def validate_read(self, reply_bytes):
reply_value = self.data_class.from_bytes(reply_bytes, options=self.options)
return reply_value
if self.readable:
reply_value = self.data_class.from_bytes(reply_bytes, options=self.options)
return reply_value

def prepare_write(self, new_value, current_value=None):
new_value.options = self.options
Expand Down
8 changes: 4 additions & 4 deletions lib/logitech_receiver/settings_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,20 +1450,20 @@ class LEDZoneSetting(_Setting):
feature = _F.COLOR_LED_EFFECTS
color_field = {'name': _LEDP.color, 'kind': _KIND.choice, 'label': None, 'choices': colors}
speed_field = {'name': _LEDP.speed, 'kind': _KIND.range, 'label': _('Speed'), 'min': 0, 'max': 255}
period_field = {'name': _LEDP.period, 'kind': _KIND.range, 'label': _('Period'), 'min': 0, 'max': 5000}
period_field = {'name': _LEDP.period, 'kind': _KIND.range, 'label': _('Period'), 'min': 100, 'max': 5000}
intensity_field = {'name': _LEDP.intensity, 'kind': _KIND.range, 'label': _('Intensity'), 'min': 0, 'max': 100}
ramp_field = {'name': _LEDP.ramp, 'kind': _KIND.choice, 'label': _('Ramp'), 'choices': _hidpp20.LEDRampChoices}
# form_field = { 'name': _LEDP.form, 'kind': _KIND.choice, 'label': _('Form'), 'choices': _hidpp20.LEDFormChoices }
possible_fields = [color_field, speed_field, period_field, intensity_field, ramp_field]

@classmethod
def build(cls, device):
zone_infos = _hidpp20.LEDEffectsInfo(device).zones
infos = _hidpp20.LEDEffectsInfo(device)
settings = []
for zone in zone_infos:
for zone in infos.zones:
prefix = zone.index.to_bytes(1)
rw = _FeatureRW(_F.COLOR_LED_EFFECTS, read_fnid=0xE0, write_fnid=0x30, prefix=prefix)
validator = _HeteroV(data_class=_hidpp20.LEDEffectIndexed, options=zone.effects)
validator = _HeteroV(data_class=_hidpp20.LEDEffectIndexed, options=zone.effects, readable=infos.readable)
setting = cls(device, rw, validator)
setting.name = cls.name + str(int(zone.location))
setting.label = _('LEDs') + ' ' + str(_hidpp20.LEDZoneLocations[zone.location])
Expand Down
38 changes: 26 additions & 12 deletions lib/solaar/ui/config_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def _read_async(setting, force_read, sbox, device_is_online, sensitive):

def _do_read(s, force, sb, online, sensitive):
v = s.read(not force)
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, priority=99)
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, True, priority=99)

_ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)

Expand Down Expand Up @@ -124,7 +124,8 @@ def __init__(self, sbox, delegate=None):
self.connect('notify::active', self.changed)

def set_value(self, value):
self.set_state(value)
if value is not None:
self.set_state(value)

def get_value(self):
return self.get_state()
Expand Down Expand Up @@ -179,7 +180,8 @@ def get_value(self):
return int(self.get_active_id()) if self.get_active_id() is not None else None

def set_value(self, value):
self.set_active_id(str(int(value)))
if value is not None:
self.set_active_id(str(int(value)))

def get_choice(self):
id = self.get_value()
Expand Down Expand Up @@ -217,7 +219,8 @@ def get_value(self):
return int(choice) if choice is not None else None

def set_value(self, value):
self.set_text(str(next((x for x in self.choices if x == value), None)))
if value is not None:
self.set_text(str(next((x for x in self.choices if x == value), None)))

def get_choice(self):
key = self.get_text()
Expand Down Expand Up @@ -264,6 +267,8 @@ def get_value(self):
return self.valueBox.get_value()

def set_value(self, value):
if value is None:
return
self.valueBox.set_sensitive(self.get_sensitive())
key = int(self.keyBox.get_active_id())
if value.get(key) is not None:
Expand Down Expand Up @@ -370,6 +375,8 @@ def toggle_notify(self, switch, active):
_write_async(self.sbox.setting, new_state, self.sbox, key=int(key))

def set_value(self, value):
if value is None:
return
active = 0
total = len(self._label_control_pairs)
to_join = []
Expand Down Expand Up @@ -452,6 +459,8 @@ def _write(self, control, item, sub_item):
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))

def set_value(self, value):
if value is None:
return
b = ''
n = 0
for ch in self._items:
Expand Down Expand Up @@ -512,6 +521,8 @@ def _write(self, control, item):
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))

def set_value(self, value):
if value is None:
return
b = ''
n = len(self._items)
for h in self._items:
Expand All @@ -527,7 +538,7 @@ def set_value(self, value):
self._button.set_tooltip_text(b)


# control an ID key that determines what else to show
# control with an ID key that determines what else to show
class HeteroKeyControl(Gtk.HBox, Control):

def __init__(self, sbox, delegate=None):
Expand All @@ -538,6 +549,7 @@ def __init__(self, sbox, delegate=None):
if item['label']:
item_lblbox = Gtk.Label(item['label'])
self.pack_start(item_lblbox, False, False, 0)
item_lblbox.set_visible(False)
else:
item_lblbox = None
if item['kind'] == _SETTING_KIND.choice:
Expand All @@ -555,6 +567,7 @@ def __init__(self, sbox, delegate=None):
item_box.set_increments(1, 5)
item_box.connect('value-changed', self.changed)
self.pack_start(item_box, True, True, 0)
item_box.set_visible(False)
self._items[str(item['name'])] = (item_lblbox, item_box)

def get_value(self):
Expand All @@ -566,11 +579,12 @@ def get_value(self):

def set_value(self, value):
self.set_sensitive(False)
for k, v in value.__dict__.items():
if k in self._items:
(lblbox, box) = self._items[k]
box.set_value(v)
self.setup_visibles(value.ID)
if value is not None:
for k, v in value.__dict__.items():
if k in self._items:
(lblbox, box) = self._items[k]
box.set_value(v)
self.setup_visibles(value.ID if value is not None else 0)

def setup_visibles(self, ID):
fields = self.sbox.setting.fields_map[ID][1] if ID in self.sbox.setting.fields_map else {}
Expand Down Expand Up @@ -694,10 +708,10 @@ def _create_sbox(s, device):
return sbox


def _update_setting_item(sbox, value, is_online=True, sensitive=True):
def _update_setting_item(sbox, value, is_online=True, sensitive=True, nullOK=False):
# sbox._spinner.set_visible(False) # don't repack item box
sbox._spinner.stop()
if value is None:
if value is None and not nullOK:
sbox._control.set_sensitive(False)
_change_icon(False, sbox._change_icon)
sbox._failed.set_visible(is_online)
Expand Down

0 comments on commit 3ea7ce3

Please sign in to comment.