settings: add scroll ratchet force setting
This commit is contained in:
parent
b96d0bbe0b
commit
cff0110f81
Binary file not shown.
|
|
@ -99,8 +99,11 @@ class State(enum.Enum):
|
||||||
# mask is used to keep only some bits from a sequence of bits, this can be an integer or a byte string,
|
# mask is used to keep only some bits from a sequence of bits, this can be an integer or a byte string,
|
||||||
# read_skip_byte_count is the number of bytes to ignore at the beginning of the read value (default 0),
|
# read_skip_byte_count is the number of bytes to ignore at the beginning of the read value (default 0),
|
||||||
# write_prefix_bytes is a byte string to write before the value (default empty).
|
# write_prefix_bytes is a byte string to write before the value (default empty).
|
||||||
|
|
||||||
# RangeValidator is for an integer in a range. It takes
|
# RangeValidator is for an integer in a range. It takes
|
||||||
# byte_count is number of bytes that the value is stored in (defaults to size of max_value).
|
# byte_count is number of bytes that the value is stored in (defaults to size of max_value).
|
||||||
|
# read_skip_byte_count is as for BooleanV
|
||||||
|
# write_prefix_bytes is as for BooleanV
|
||||||
# RangeValidator uses min_value and max_value from the setting class as minimum and maximum.
|
# RangeValidator uses min_value and max_value from the setting class as minimum and maximum.
|
||||||
|
|
||||||
# ChoicesValidator is for symbolic choices. It takes one positional and three keyword arguments:
|
# ChoicesValidator is for symbolic choices. It takes one positional and three keyword arguments:
|
||||||
|
|
@ -698,6 +701,38 @@ class ScrollRatchetEnhanced(ScrollRatchet):
|
||||||
rw_options = {"read_fnid": 0x10, "write_fnid": 0x20}
|
rw_options = {"read_fnid": 0x10, "write_fnid": 0x20}
|
||||||
|
|
||||||
|
|
||||||
|
class ScrollRatchetTorque(settings.Setting):
|
||||||
|
name = "scroll-ratchet-torque"
|
||||||
|
label = _("Scroll Wheel Ratchet Torque")
|
||||||
|
description = _("Change the torque needed to overcome the ratchet.")
|
||||||
|
feature = _F.SMART_SHIFT_ENHANCED
|
||||||
|
min_value = 1
|
||||||
|
max_value = 100
|
||||||
|
rw_options = {"read_fnid": 0x10, "write_fnid": 0x20}
|
||||||
|
|
||||||
|
class rw_class(settings.FeatureRW):
|
||||||
|
def write(self, device, data_bytes):
|
||||||
|
ratchetSetting = next(filter(lambda s: s.name == "scroll-ratchet", device.settings), None)
|
||||||
|
if ratchetSetting: # for MX Master 4, the ratchet setting needs to be written for changes to take effect
|
||||||
|
ratchet_value = ratchetSetting.read(True)
|
||||||
|
data_bytes = ratchet_value.to_bytes(1, "big") + data_bytes[1:]
|
||||||
|
result = super().write(device, data_bytes)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class validator_class(settings_validator.RangeValidator):
|
||||||
|
@classmethod
|
||||||
|
def build(cls, setting_class, device):
|
||||||
|
reply = device.feature_request(_F.SMART_SHIFT_ENHANCED, 0x00)
|
||||||
|
if reply[0] & 0x01: # device supports tunable torque
|
||||||
|
return cls(
|
||||||
|
min_value=setting_class.min_value,
|
||||||
|
max_value=setting_class.max_value,
|
||||||
|
byte_count=1,
|
||||||
|
write_prefix_bytes=b"\x00\x00", # don't change mode or disengage, but see above
|
||||||
|
read_skip_byte_count=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# the keys for the choice map are Logitech controls (from special_keys)
|
# the keys for the choice map are Logitech controls (from special_keys)
|
||||||
# each choice value is a NamedInt with the string from a task (to be shown to the user)
|
# each choice value is a NamedInt with the string from a task (to be shown to the user)
|
||||||
# and the integer being the control number for that task (to be written to the device)
|
# and the integer being the control number for that task (to be written to the device)
|
||||||
|
|
@ -1756,6 +1791,7 @@ SETTINGS: list[settings.Setting] = [
|
||||||
HiresSmoothResolution, # working
|
HiresSmoothResolution, # working
|
||||||
HiresMode, # simple
|
HiresMode, # simple
|
||||||
ScrollRatchet, # simple
|
ScrollRatchet, # simple
|
||||||
|
ScrollRatchetTorque,
|
||||||
SmartShift, # working
|
SmartShift, # working
|
||||||
ScrollRatchetEnhanced,
|
ScrollRatchetEnhanced,
|
||||||
SmartShiftEnhanced, # simple
|
SmartShiftEnhanced, # simple
|
||||||
|
|
|
||||||
|
|
@ -531,12 +531,13 @@ class RangeValidator(Validator):
|
||||||
kwargs["max_value"] = setting_class.max_value
|
kwargs["max_value"] = setting_class.max_value
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
def __init__(self, min_value=0, max_value=255, byte_count=1):
|
def __init__(self, min_value=0, max_value=255, byte_count=1, read_skip_byte_count=0, write_prefix_bytes=b""):
|
||||||
assert max_value > min_value
|
assert max_value > min_value
|
||||||
self.min_value = min_value
|
self.min_value = min_value
|
||||||
self.max_value = max_value
|
self.max_value = max_value
|
||||||
|
self.read_skip_byte_count = read_skip_byte_count
|
||||||
|
self.write_prefix_bytes = write_prefix_bytes
|
||||||
self.needs_current_value = True # read and check before write (needed for ADC power and probably a good idea anyway)
|
self.needs_current_value = True # read and check before write (needed for ADC power and probably a good idea anyway)
|
||||||
|
|
||||||
self._byte_count = math.ceil(math.log(max_value + 1, 256))
|
self._byte_count = math.ceil(math.log(max_value + 1, 256))
|
||||||
if byte_count:
|
if byte_count:
|
||||||
assert self._byte_count <= byte_count
|
assert self._byte_count <= byte_count
|
||||||
|
|
@ -544,7 +545,7 @@ class RangeValidator(Validator):
|
||||||
assert self._byte_count < 8
|
assert self._byte_count < 8
|
||||||
|
|
||||||
def validate_read(self, reply_bytes):
|
def validate_read(self, reply_bytes):
|
||||||
reply_value = common.bytes2int(reply_bytes[: self._byte_count])
|
reply_value = common.bytes2int(reply_bytes[self.read_skip_byte_count : self.read_skip_byte_count + self._byte_count])
|
||||||
assert reply_value >= self.min_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}"
|
assert reply_value >= self.min_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}"
|
||||||
assert reply_value <= self.max_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}"
|
assert reply_value <= self.max_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}"
|
||||||
return reply_value
|
return reply_value
|
||||||
|
|
@ -553,7 +554,7 @@ class RangeValidator(Validator):
|
||||||
if new_value < self.min_value or new_value > self.max_value:
|
if new_value < self.min_value or new_value > self.max_value:
|
||||||
raise ValueError(f"invalid choice {new_value!r}")
|
raise ValueError(f"invalid choice {new_value!r}")
|
||||||
current_value = self.validate_read(current_value) if current_value is not None else None
|
current_value = self.validate_read(current_value) if current_value is not None else None
|
||||||
to_write = common.int2bytes(new_value, self._byte_count)
|
to_write = self.write_prefix_bytes + common.int2bytes(new_value, self._byte_count)
|
||||||
# current value is known and same as value to be written return None to signal not to write it
|
# current value is known and same as value to be written return None to signal not to write it
|
||||||
return None if current_value is not None and current_value == new_value else to_write
|
return None if current_value is not None and current_value == new_value else to_write
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue