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,
|
||||
# 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).
|
||||
|
||||
# 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).
|
||||
# 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.
|
||||
|
||||
# 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}
|
||||
|
||||
|
||||
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)
|
||||
# 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)
|
||||
|
|
@ -1756,6 +1791,7 @@ SETTINGS: list[settings.Setting] = [
|
|||
HiresSmoothResolution, # working
|
||||
HiresMode, # simple
|
||||
ScrollRatchet, # simple
|
||||
ScrollRatchetTorque,
|
||||
SmartShift, # working
|
||||
ScrollRatchetEnhanced,
|
||||
SmartShiftEnhanced, # simple
|
||||
|
|
|
|||
|
|
@ -531,12 +531,13 @@ class RangeValidator(Validator):
|
|||
kwargs["max_value"] = setting_class.max_value
|
||||
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
|
||||
self.min_value = min_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._byte_count = math.ceil(math.log(max_value + 1, 256))
|
||||
if byte_count:
|
||||
assert self._byte_count <= byte_count
|
||||
|
|
@ -544,7 +545,7 @@ class RangeValidator(Validator):
|
|||
assert self._byte_count < 8
|
||||
|
||||
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.max_value, f"{self.__class__.__name__}: failed to validate read value {reply_value:02X}"
|
||||
return reply_value
|
||||
|
|
@ -553,7 +554,7 @@ class RangeValidator(Validator):
|
|||
if new_value < self.min_value or new_value > self.max_value:
|
||||
raise ValueError(f"invalid choice {new_value!r}")
|
||||
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
|
||||
return None if current_value is not None and current_value == new_value else to_write
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue