diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index 15b65c5d62..78a6be21bb 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -713,7 +713,7 @@ def build(cls, setting_class, device): class DpiSlidingXY(_RawXYProcessing): def activate_action(self): - self.dpiSetting = next(filter(lambda s: s.name == 'dpi', self.device.settings), None) + self.dpiSetting = next(filter(lambda s: s.name == 'dpi' or s.name == 'dpi_extended', self.device.settings), None) self.dpiChoices = list(self.dpiSetting.choices) self.otherDpiIdx = self.device.persister.get('_dpi-sliding', -1) if self.device.persister else -1 if not isinstance(self.otherDpiIdx, int) or self.otherDpiIdx < 0 or self.otherDpiIdx >= len(self.dpiChoices): @@ -778,7 +778,7 @@ def move_action(self, dx, dy): class MouseGesturesXY(_RawXYProcessing): def activate_action(self): - self.dpiSetting = next(filter(lambda s: s.name == 'dpi', self.device.settings), None) + self.dpiSetting = next(filter(lambda s: s.name == 'dpi' or s.name == 'dpi_extended', self.device.settings), None) self.fsmState = 'idle' self.initialize_data() @@ -924,28 +924,36 @@ class AdjustableDpi(_Setting): feature = _F.ADJUSTABLE_DPI rw_options = {'read_fnid': 0x20, 'write_fnid': 0x30} choices_universe = _NamedInts.range(200, 4000, str, 50) + sensor_list_bytes_ignore = 1 class validator_class(_ChoicesV): @classmethod def build(cls, setting_class, device): - # [1] getSensorDpiList(sensorIdx) - reply = device.feature_request(_F.ADJUSTABLE_DPI, 0x10) + # [1] getSensorDpiList(sensorIdx) - works for both features + reply = device.feature_request(_F.ADJUSTABLE_DPI, 0x10, 0x00, 0x00) assert reply, 'Oops, DPI list cannot be retrieved!' + dpi_bytes = reply[setting_class.sensor_list_bytes_ignore:] + i = 1 + while _bytes2int(dpi_bytes[-2:]) != 0: + reply = device.feature_request(_F.ADJUSTABLE_DPI, 0x10, 0x00, i) + assert reply, 'Oops, DPI list cannot be retrieved!' + dpi_bytes += reply[setting_class.sensor_list_bytes_ignore:] dpi_list = [] - step = None - for val in _unpack('!7H', reply[1:1 + 14]): + i = 0 + while i < len(dpi_bytes): + val = _bytes2int(dpi_bytes[i:i + 2]) if val == 0: break if val >> 13 == 0b111: - assert step is None and len(dpi_list) == 1, \ - 'Invalid DPI list item: %r' % val step = val & 0x1fff + last = _bytes2int(dpi_bytes[i + 2:i + 4]) + assert len(dpi_list) > 0 and last > dpi_list[-1], 'Invalid DPI list item: %r' % val + dpi_list += range(dpi_list[-1] + step, last + 1, step) + i += 4 else: dpi_list.append(val) - if step: - assert len(dpi_list) == 2, 'Invalid DPI list range: %r' % dpi_list - dpi_list = range(dpi_list[0], dpi_list[1] + 1, step) + i += 2 return cls(choices=_NamedInts.list(dpi_list), byte_count=3) if dpi_list else None def validate_read(self, reply_bytes): # special validator to use default DPI if needed @@ -957,6 +965,17 @@ def validate_read(self, reply_bytes): # special validator to use default DPI if return valid_value +class ExtendedAdjustableDpi(AdjustableDpi): + # the extended version allows for two dimensions, longer dpi descriptions + # still assume only one sensor (and X only?) + # [5] getSensorDpiParameters(sensorIdx) → sensorIdx, dpiX, defaultDpiX, dpiY, defaultDpiY, lod + # [6] setSensorDpiParameters(sensorIdx, dpiX, dpiY, lod) → sensorIdx, dpiX, dpiY, lod + name = 'dpi_extended' + feature = _F.EXTENDED_ADJUSTABLE_DPI + rw_options = {'read_fnid': 0x50, 'write_fnid': 0x60} + sensor_list_bytes_ignore = 3 + + class SpeedChange(_Setting): """Implements the ability to switch Sensitivity by clicking on the DPI_Change button.""" name = 'speed-change' @@ -1484,6 +1503,7 @@ def build(cls, device): ExtendedReportRate, PointerSpeed, # simple AdjustableDpi, # working + ExtendedAdjustableDpi, SpeedChange, # Backlight, # not working - disabled temporarily Backlight2, # working