device: better support for extended ajustable dpi
This commit is contained in:
parent
ab94f1be07
commit
8de3a1d2e2
|
@ -1377,6 +1377,79 @@ _yaml.add_representer(OnboardProfiles, OnboardProfiles.to_yaml)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class ExtendedDpi:
|
||||||
|
"""Information about the DPI possibilities from EXTENDED_ADJUSTABLE_DPI feature"""
|
||||||
|
|
||||||
|
def __init__(self, device):
|
||||||
|
self._device = device
|
||||||
|
self.has_y = False
|
||||||
|
reply = device.feature_request(FEATURE.EXTENDED_ADJUSTABLE_DPI.feature, 0x10, 0x00)
|
||||||
|
self.levels = reply[1]
|
||||||
|
self.has_y = bool(reply[2] & 0x01)
|
||||||
|
self.has_lod = bool(reply[2] & 0x02)
|
||||||
|
self.has_profile = bool(reply[2] & 0x08)
|
||||||
|
dpilist_x = self.produce_dpi_list(FEATURE.EXTENDED_ADJUSTABLE_DPI, 0x20, device, 0)
|
||||||
|
dpilist_y = self.produce_dpi_list(FEATURE.EXTENDED_ADJUSTABLE_DPI, 0x20, device, 1) if self.has_y else []
|
||||||
|
print("DPY LIST X", dpilist_x)
|
||||||
|
print("DPY LIST Y", dpilist_y)
|
||||||
|
self.read()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def produce_dpi_list(feature, function, device, direction):
|
||||||
|
reply = device.feature_request(feature, function, 0x00, direction, 0x00)
|
||||||
|
assert reply, "Oops, DPI list cannot be retrieved!"
|
||||||
|
dpi_bytes = reply[3:]
|
||||||
|
i = 1
|
||||||
|
while dpi_bytes[-2:] != b"\x00\x00":
|
||||||
|
reply = device.feature_request(feature, function, 0x00, direction, i)
|
||||||
|
assert reply, "Oops, DPI list cannot be retrieved!"
|
||||||
|
dpi_bytes += reply[3:]
|
||||||
|
i += 1
|
||||||
|
dpi_list = []
|
||||||
|
i = 0
|
||||||
|
while i < len(dpi_bytes):
|
||||||
|
val = _bytes2int(dpi_bytes[i : i + 2])
|
||||||
|
if val == 0:
|
||||||
|
break
|
||||||
|
if val >> 13 == 0b111:
|
||||||
|
step = val & 0x1FFF
|
||||||
|
last = _bytes2int(dpi_bytes[i + 2 : i + 4])
|
||||||
|
assert len(dpi_list) > 0 and last > dpi_list[-1], f"Invalid DPI list item: {val!r}"
|
||||||
|
dpi_list += range(dpi_list[-1] + step, last + 1, step)
|
||||||
|
i += 4
|
||||||
|
else:
|
||||||
|
dpi_list.append(val)
|
||||||
|
i += 2
|
||||||
|
return dpi_list
|
||||||
|
|
||||||
|
def read_list(self, reply, size):
|
||||||
|
list = []
|
||||||
|
for i in range(0, self.levels * size + 1, size):
|
||||||
|
if reply[i : i + 1] != b"\x00\x00":
|
||||||
|
list.append(_bytes2int(reply[i : i + size]))
|
||||||
|
return list
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
reply = self._device.feature_request(self.feature, 0x50, 0x00)
|
||||||
|
self.x = _bytes2int(reply[1:3])
|
||||||
|
self.y = _bytes2int(reply[5:7])
|
||||||
|
self.lod = reply[9]
|
||||||
|
self.default_x = _bytes2int(reply[3:5])
|
||||||
|
self.default_y = _bytes2int(reply[7:9])
|
||||||
|
self.x_list = self.read_list(self._device.feature_request(self.feature, 0x30, 0x00, 0)[2:], 2)
|
||||||
|
self.y_list = self.read_list(self._device.feature_request(self.feature, 0x30, 0x00, 1)[2:], 2)
|
||||||
|
self.lod_list = self.read_list(self._device.feature_request(self.feature, 0x40, 0x00)[1:], 1)
|
||||||
|
|
||||||
|
def set(self, x, y, lod):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.lod = lod
|
||||||
|
|
||||||
|
def write_current(self):
|
||||||
|
data_bytes = _int2bytes(self.x, 2) + _int2bytes(self.y, 2) + _int2bytes(self.lod, 1)
|
||||||
|
return self._device.feature_request(self.feature, 0x60, 0x00, data_bytes)
|
||||||
|
|
||||||
|
|
||||||
def feature_request(device, feature, function=0x00, *params, no_reply=False):
|
def feature_request(device, feature, function=0x00, *params, no_reply=False):
|
||||||
if device.online and device.features:
|
if device.online and device.features:
|
||||||
if feature in device.features:
|
if feature in device.features:
|
||||||
|
|
|
@ -932,71 +932,48 @@ class DivertKeys(_Settings):
|
||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
|
||||||
class AdjustableDpi(_Setting):
|
def produce_dpi_list(feature, function, ignore, device, direction):
|
||||||
"""Pointer Speed feature"""
|
dpi_bytes = b""
|
||||||
|
for i in range(0, 0x100): # there will be only a very few iterations performed
|
||||||
|
reply = device.feature_request(feature, function, 0x00, direction, i)
|
||||||
|
assert reply, "Oops, DPI list cannot be retrieved!"
|
||||||
|
dpi_bytes += reply[ignore:]
|
||||||
|
if dpi_bytes[-2:] == b"\x00\x00":
|
||||||
|
break
|
||||||
|
dpi_list = []
|
||||||
|
i = 0
|
||||||
|
while i < len(dpi_bytes):
|
||||||
|
val = _bytes2int(dpi_bytes[i : i + 2])
|
||||||
|
if val == 0:
|
||||||
|
break
|
||||||
|
if val >> 13 == 0b111:
|
||||||
|
step = val & 0x1FFF
|
||||||
|
last = _bytes2int(dpi_bytes[i + 2 : i + 4])
|
||||||
|
assert len(dpi_list) > 0 and last > dpi_list[-1], f"Invalid DPI list item: {val!r}"
|
||||||
|
dpi_list += range(dpi_list[-1] + step, last + 1, step)
|
||||||
|
i += 4
|
||||||
|
else:
|
||||||
|
dpi_list.append(val)
|
||||||
|
i += 2
|
||||||
|
return dpi_list
|
||||||
|
|
||||||
# Assume sensorIdx 0 (there is only one sensor)
|
|
||||||
|
class AdjustableDpi(_Setting):
|
||||||
name = "dpi"
|
name = "dpi"
|
||||||
label = _("Sensitivity (DPI)")
|
label = _("Sensitivity (DPI)")
|
||||||
description = _("Mouse movement sensitivity")
|
description = _("Mouse movement sensitivity")
|
||||||
feature = _F.ADJUSTABLE_DPI
|
feature = _F.ADJUSTABLE_DPI
|
||||||
rw_options = {"read_fnid": 0x20, "write_fnid": 0x30}
|
rw_options = {"read_fnid": 0x20, "write_fnid": 0x30}
|
||||||
choices_universe = _NamedInts.range(100, 4000, str, 50)
|
choices_universe = _NamedInts.range(100, 4000, str, 50)
|
||||||
sensor_list_function = 0x10
|
|
||||||
sensor_list_bytes_ignore = 1
|
|
||||||
|
|
||||||
class validator_class(_ChoicesV):
|
class validator_class(_ChoicesV):
|
||||||
@staticmethod
|
|
||||||
def produce_dpi_list(setting_class, device, direction):
|
|
||||||
reply = device.feature_request(setting_class.feature, setting_class.sensor_list_function, 0x00, direction, 0x00)
|
|
||||||
assert reply, "Oops, DPI list cannot be retrieved!"
|
|
||||||
dpi_bytes = reply[setting_class.sensor_list_bytes_ignore :]
|
|
||||||
i = 1
|
|
||||||
while dpi_bytes[-2:] != b"\x00\x00":
|
|
||||||
reply = device.feature_request(setting_class.feature, setting_class.sensor_list_function, 0x00, direction, i)
|
|
||||||
assert reply, "Oops, DPI list cannot be retrieved!"
|
|
||||||
dpi_bytes += reply[setting_class.sensor_list_bytes_ignore :]
|
|
||||||
i += 1
|
|
||||||
dpi_list = []
|
|
||||||
i = 0
|
|
||||||
while i < len(dpi_bytes):
|
|
||||||
val = _bytes2int(dpi_bytes[i : i + 2])
|
|
||||||
if val == 0:
|
|
||||||
break
|
|
||||||
if val >> 13 == 0b111:
|
|
||||||
step = val & 0x1FFF
|
|
||||||
last = _bytes2int(dpi_bytes[i + 2 : i + 4])
|
|
||||||
assert len(dpi_list) > 0 and last > dpi_list[-1], f"Invalid DPI list item: {val!r}"
|
|
||||||
dpi_list += range(dpi_list[-1] + step, last + 1, step)
|
|
||||||
i += 4
|
|
||||||
else:
|
|
||||||
dpi_list.append(val)
|
|
||||||
i += 2
|
|
||||||
return dpi_list
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, setting_class, device):
|
def build(cls, setting_class, device):
|
||||||
y = False
|
dpilist = produce_dpi_list(setting_class.feature, 0x10, 1, device, 0)
|
||||||
if setting_class.feature == _F.EXTENDED_ADJUSTABLE_DPI:
|
setting = cls(choices=_NamedInts.list(dpilist), byte_count=2, write_prefix_bytes=b"\x00") if dpilist else None
|
||||||
reply = device.feature_request(setting_class.feature, 0x10, 0x00)
|
setting.dpilist = dpilist
|
||||||
y = reply[2] & 0x01
|
|
||||||
reply = device.feature_request(setting_class.feature, setting_class.sensor_list_function, 0x00, 0x00, 0x00)
|
|
||||||
assert reply, "Oops, DPI list cannot be retrieved!"
|
|
||||||
dpilist_x = cls.produce_dpi_list(setting_class, device, 0)
|
|
||||||
dpilist_y = cls.produce_dpi_list(setting_class, device, 1) if y else []
|
|
||||||
print("DPY LIST X", dpilist_x)
|
|
||||||
print("DPY LIST Y", dpilist_y)
|
|
||||||
setting = cls(choices=_NamedInts.list(dpilist_x), byte_count=2, write_prefix_bytes=b"\x00") if dpilist_x else None
|
|
||||||
setting.y = y
|
|
||||||
return setting
|
return setting
|
||||||
|
|
||||||
def prepare_write(self, new_value, current_value=None):
|
|
||||||
data_bytes = super().prepare_write(new_value, current_value)
|
|
||||||
if self.y:
|
|
||||||
bytes = data_bytes[len(self._write_prefix_bytes) :]
|
|
||||||
data_bytes = self._write_prefix_bytes + bytes + bytes
|
|
||||||
return data_bytes
|
|
||||||
|
|
||||||
def validate_read(self, reply_bytes): # special validator to use default DPI if needed
|
def validate_read(self, reply_bytes): # special validator to use default DPI if needed
|
||||||
reply_value = _bytes2int(reply_bytes[1:3])
|
reply_value = _bytes2int(reply_bytes[1:3])
|
||||||
if reply_value == 0: # use default value instead
|
if reply_value == 0: # use default value instead
|
||||||
|
@ -1006,14 +983,80 @@ class AdjustableDpi(_Setting):
|
||||||
return valid_value
|
return valid_value
|
||||||
|
|
||||||
|
|
||||||
class ExtendedAdjustableDpi(AdjustableDpi):
|
class ExtendedAdjustableDpi(_Setting):
|
||||||
# the extended version allows for two dimensions, longer dpi descriptions
|
# the extended version allows for two dimensions, longer dpi descriptions, but still assume only one sensor
|
||||||
# still assume only one sensor (and X only?)
|
|
||||||
name = "dpi_extended"
|
name = "dpi_extended"
|
||||||
|
label = _("Sensitivity (DPI)")
|
||||||
|
description = _("Mouse movement sensitivity") + "\n" + _("May need Onboard Profiles set to Disable to be effective.")
|
||||||
feature = _F.EXTENDED_ADJUSTABLE_DPI
|
feature = _F.EXTENDED_ADJUSTABLE_DPI
|
||||||
rw_options = {"read_fnid": 0x50, "write_fnid": 0x60}
|
rw_options = {"read_fnid": 0x50, "write_fnid": 0x60}
|
||||||
sensor_list_function = 0x20
|
keys_universe = _NamedInts(X=0, Y=1, LOD=2)
|
||||||
sensor_list_bytes_ignore = 3
|
choices_universe = _NamedInts.range(100, 4000, str, 50)
|
||||||
|
choices_universe[0] = "LOW"
|
||||||
|
choices_universe[1] = "MEDIUM"
|
||||||
|
choices_universe[2] = "HIGH"
|
||||||
|
keys = _NamedInts(X=0, Y=1, LOD=2)
|
||||||
|
|
||||||
|
def write_key_value(self, key, value, save=True):
|
||||||
|
if isinstance(self._value, dict):
|
||||||
|
self._value[key] = value
|
||||||
|
else:
|
||||||
|
self._value = {key: value}
|
||||||
|
result = self.write(self._value, save)
|
||||||
|
return result[key] if isinstance(result, dict) else result
|
||||||
|
|
||||||
|
class validator_class(_ChoicesMapV):
|
||||||
|
@classmethod
|
||||||
|
def build(cls, setting_class, device):
|
||||||
|
reply = device.feature_request(setting_class.feature, 0x10, 0x00)
|
||||||
|
y = bool(reply[2] & 0x01)
|
||||||
|
lod = bool(reply[2] & 0x02)
|
||||||
|
choices_map = {}
|
||||||
|
dpilist_x = produce_dpi_list(setting_class.feature, 0x20, 3, device, 0)
|
||||||
|
choices_map[setting_class.keys["X"]] = _NamedInts.list(dpilist_x)
|
||||||
|
if y:
|
||||||
|
dpilist_y = produce_dpi_list(setting_class.feature, 0x20, 3, device, 1)
|
||||||
|
choices_map[setting_class.keys["Y"]] = _NamedInts.list(dpilist_y)
|
||||||
|
if lod:
|
||||||
|
choices_map[setting_class.keys["LOD"]] = _NamedInts(LOW=0, MEDIUM=1, HIGH=2)
|
||||||
|
validator = cls(choices_map=choices_map, byte_count=2, write_prefix_bytes=b"\x00")
|
||||||
|
validator.y = y
|
||||||
|
validator.lod = lod
|
||||||
|
validator.keys = setting_class.keys
|
||||||
|
return validator
|
||||||
|
|
||||||
|
def validate_read(self, reply_bytes): # special validator to read entire setting
|
||||||
|
dpi_x = _bytes2int(reply_bytes[3:5]) if reply_bytes[1:3] == 0 else _bytes2int(reply_bytes[1:3])
|
||||||
|
assert dpi_x in self.choices[0], f"{self.__class__.__name__}: failed to validate dpi_x value {dpi_x:04X}"
|
||||||
|
value = {self.keys["X"]: dpi_x}
|
||||||
|
if self.y:
|
||||||
|
dpi_y = _bytes2int(reply_bytes[7:9]) if reply_bytes[5:7] == 0 else _bytes2int(reply_bytes[5:7])
|
||||||
|
assert dpi_y in self.choices[1], f"{self.__class__.__name__}: failed to validate dpi_y value {dpi_y:04X}"
|
||||||
|
value[self.keys["Y"]] = dpi_y
|
||||||
|
if self.lod:
|
||||||
|
lod = reply_bytes[9]
|
||||||
|
assert lod in self.choices[2], f"{self.__class__.__name__}: failed to validate lod value {lod:02X}"
|
||||||
|
value[self.keys["LOD"]] = lod
|
||||||
|
return value
|
||||||
|
|
||||||
|
def prepare_write(self, new_value, current_value=None): # special preparer to write entire setting
|
||||||
|
data_bytes = self._write_prefix_bytes
|
||||||
|
if new_value[self.keys["X"]] not in self.choices[self.keys["X"]]:
|
||||||
|
raise ValueError(f"invalid value {new_value!r}")
|
||||||
|
data_bytes += _int2bytes(new_value[0], 2)
|
||||||
|
if self.y:
|
||||||
|
if new_value[self.keys["Y"]] not in self.choices[self.keys["Y"]]:
|
||||||
|
raise ValueError(f"invalid value {new_value!r}")
|
||||||
|
data_bytes += _int2bytes(new_value[self.keys["Y"]], 2)
|
||||||
|
else:
|
||||||
|
data_bytes += b"\x00\x00"
|
||||||
|
if self.lod:
|
||||||
|
if new_value[self.keys["LOD"]] not in self.choices[self.keys["LOD"]]:
|
||||||
|
raise ValueError(f"invalid value {new_value!r}")
|
||||||
|
data_bytes += _int2bytes(new_value[self.keys["LOD"]], 1)
|
||||||
|
else:
|
||||||
|
data_bytes += b"\x00"
|
||||||
|
return data_bytes
|
||||||
|
|
||||||
|
|
||||||
class SpeedChange(_Setting):
|
class SpeedChange(_Setting):
|
||||||
|
|
|
@ -381,18 +381,20 @@ simple_tests = [
|
||||||
hidpp.Response("05", 0x0C30, "05"),
|
hidpp.Response("05", 0x0C30, "05"),
|
||||||
),
|
),
|
||||||
Setup(
|
Setup(
|
||||||
FeatureTest(settings_templates.AdjustableDpi, 800, 400, version=0x03),
|
FeatureTest(settings_templates.AdjustableDpi, 800, 400, 0x30, "000190"),
|
||||||
common.NamedInts.list([400, 800, 1600]),
|
common.NamedInts.list([400, 800, 1600]),
|
||||||
hidpp.Response("000190032006400000000000000000", 0x0410),
|
hidpp.Response("040003", 0x0000, "2201"), # ADJUSTABLE_DPI
|
||||||
|
hidpp.Response("000190032006400000", 0x0410, "000000"),
|
||||||
hidpp.Response("000320", 0x0420),
|
hidpp.Response("000320", 0x0420),
|
||||||
hidpp.Response("000190", 0x0430, "000190"),
|
hidpp.Response("000190", 0x0430, "000190"),
|
||||||
),
|
),
|
||||||
Setup(
|
Setup(
|
||||||
FeatureTest(settings_templates.AdjustableDpi, 1600, 400, version=0x03),
|
FeatureTest(settings_templates.AdjustableDpi, 256, 512, 0x30, "000200"),
|
||||||
common.NamedInts.list([400, 800, 1600]),
|
common.NamedInts.list([256, 512]),
|
||||||
hidpp.Response("000190032006400000000000000000", 0x0410),
|
hidpp.Response("040003", 0x0000, "2201"), # ADJUSTABLE_DPI
|
||||||
hidpp.Response("0000000640", 0x0420),
|
hidpp.Response("000100e10002000000", 0x0410, "000000"),
|
||||||
hidpp.Response("000190", 0x0430, "000190"),
|
hidpp.Response("000100", 0x0420),
|
||||||
|
hidpp.Response("000200", 0x0430, "000200"),
|
||||||
),
|
),
|
||||||
Setup(
|
Setup(
|
||||||
FeatureTest(settings_templates.AdjustableDpi, 400, 800, version=0x03),
|
FeatureTest(settings_templates.AdjustableDpi, 400, 800, version=0x03),
|
||||||
|
@ -401,26 +403,31 @@ simple_tests = [
|
||||||
hidpp.Response("000190", 0x0420),
|
hidpp.Response("000190", 0x0420),
|
||||||
hidpp.Response("000320", 0x0430, "000320"),
|
hidpp.Response("000320", 0x0430, "000320"),
|
||||||
),
|
),
|
||||||
# Setup(
|
|
||||||
# FeatureTest(settings_templates.ExtendedAdjustableDpi, 256, 512, version=0x09),
|
|
||||||
# common.NamedInts.list([256, 512]),
|
|
||||||
# hidpp.Response("000000", 0x0910, "00"), # no y direction
|
|
||||||
# hidpp.Response("0000000100e10002000000", 0x0920, "000000"),
|
|
||||||
# hidpp.Response("000100", 0x0950),
|
|
||||||
# hidpp.Response("000200", 0x0960, "000200"),
|
|
||||||
# ),
|
|
||||||
# Setup(
|
|
||||||
# FeatureTest(settings_templates.ExtendedAdjustableDpi, 0x64, 0x164, version=0x09),
|
|
||||||
# common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164, 0x01C4]),
|
|
||||||
# hidpp.Response("000001", 0x0910, "00"), # supports y direction
|
|
||||||
# hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000000"),
|
|
||||||
# hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000001"),
|
|
||||||
# hidpp.Response("00000000000000000000000000000000", 0x0920, "000002"),
|
|
||||||
# hidpp.Response("000064", 0x0950),
|
|
||||||
# hidpp.Response("0001640164", 0x0960, "0001640164"),
|
|
||||||
# ),
|
|
||||||
Setup(
|
Setup(
|
||||||
FeatureTest(settings_templates.Multiplatform, 0, 1),
|
FeatureTest(settings_templates.ExtendedAdjustableDpi, 256, 512, 0x60, "000200"),
|
||||||
|
common.NamedInts.list([256, 512]),
|
||||||
|
hidpp.Response("090000", 0x0000, "2202"), # EXTENDED_ADJUSTABLE_DPI
|
||||||
|
hidpp.Response("000000", 0x0910, "00"), # no y direction
|
||||||
|
hidpp.Response("0000000100e10002000000", 0x0920, "000000"),
|
||||||
|
hidpp.Response("000100", 0x0950),
|
||||||
|
hidpp.Response("000200", 0x0960, "000200"),
|
||||||
|
),
|
||||||
|
Setup(
|
||||||
|
FeatureTest(settings_templates.ExtendedAdjustableDpi, 0x64, 0x164, 0x60, "0001640164"),
|
||||||
|
common.NamedInts.list([0x064, 0x074, 0x084, 0x0A4, 0x0C4, 0x0E4, 0x0124, 0x0164, 0x01C4]),
|
||||||
|
hidpp.Response("090000", 0x0000, "2202"), # EXTENDED_ADJUSTABLE_DPI
|
||||||
|
hidpp.Response("000001", 0x0910, "00"), # supports y direction
|
||||||
|
hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000000"),
|
||||||
|
hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000001"),
|
||||||
|
hidpp.Response("00000000000000000000000000000000", 0x0920, "000002"),
|
||||||
|
hidpp.Response("0000000064E0100084E02000C4E02000", 0x0920, "000100"),
|
||||||
|
hidpp.Response("000001E4E0400124E0400164E06001C4", 0x0920, "000101"),
|
||||||
|
hidpp.Response("00000000000000000000000000000000", 0x0920, "000102"),
|
||||||
|
hidpp.Response("000064", 0x0950),
|
||||||
|
hidpp.Response("0001640164", 0x0960, "0001640164"),
|
||||||
|
),
|
||||||
|
Setup(
|
||||||
|
FeatureTest(settings_templates.Multiplatform, 0, 1, 0x30, "FF01"),
|
||||||
common.NamedInts(**{"MacOS 0.1-0.5": 0, "iOS 0.1-0.7": 1, "Linux 0.2-0.9": 2, "Windows 0.3-0.9": 3}),
|
common.NamedInts(**{"MacOS 0.1-0.5": 0, "iOS 0.1-0.7": 1, "Linux 0.2-0.9": 2, "Windows 0.3-0.9": 3}),
|
||||||
hidpp.Response("020004000001", 0x0400),
|
hidpp.Response("020004000001", 0x0400),
|
||||||
hidpp.Response("00FF200000010005", 0x0410, "00"),
|
hidpp.Response("00FF200000010005", 0x0410, "00"),
|
||||||
|
|
Loading…
Reference in New Issue