setting: add setting to divert gestures
This commit is contained in:
parent
681a06d8d7
commit
d115ade2ea
|
@ -932,7 +932,7 @@ ACTION_ID._fallback = lambda x: 'unknown:%04X' % x
|
|||
|
||||
|
||||
class Gesture:
|
||||
def __init__(self, device, low, high, next_index):
|
||||
def __init__(self, device, low, high, next_index, next_diversion_index):
|
||||
self._device = device
|
||||
self.id = low
|
||||
self.gesture = GESTURE[low]
|
||||
|
@ -942,31 +942,51 @@ class Gesture:
|
|||
self.desired_software_default = high & 0x08
|
||||
self.persistent = high & 0x10
|
||||
self.default_enabled = high & 0x20
|
||||
self.index = None
|
||||
if self.can_be_enabled or self.default_enabled:
|
||||
self.index = next_index
|
||||
self.offset, self.mask = self._offset_mask()
|
||||
self.index = next_index if self.can_be_enabled or self.default_enabled else None
|
||||
self.diversion_index = next_diversion_index if self.can_be_diverted else None
|
||||
self._enabled = None
|
||||
self._diverted = None
|
||||
|
||||
def _offset_mask(self): # offset and mask
|
||||
if self.index is not None:
|
||||
offset = self.index >> 3 # 8 gestures per byte
|
||||
mask = 0x1 << (self.index % 8)
|
||||
def _offset_mask(self, index): # offset and mask
|
||||
if index is not None:
|
||||
offset = index >> 3 # 8 gestures per byte
|
||||
mask = 0x1 << (index % 8)
|
||||
return (offset, mask)
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
enable_offset_mask = lambda gesture: gesture._offset_mask(gesture.index)
|
||||
|
||||
diversion_offset_mask = lambda gesture: gesture._offset_mask(gesture.diversion_index)
|
||||
|
||||
def enabled(self): # is the gesture enabled?
|
||||
if self.offset is not None:
|
||||
result = feature_request(self._device, FEATURE.GESTURE_2, 0x10, self.offset, 0x01, self.mask)
|
||||
return bool(result[0] & self.mask) if result else None
|
||||
if self._enabled is None and self.index is not None:
|
||||
offset, mask = self.enable_offset_mask()
|
||||
result = feature_request(self._device, FEATURE.GESTURE_2, 0x10, offset, 0x01, mask)
|
||||
self._enabled = bool(result[0] & mask) if result else None
|
||||
return self._enabled
|
||||
|
||||
def set(self, enable): # enable or disable the gesture
|
||||
if not self.can_be_enabled:
|
||||
return None
|
||||
if self.offset is not None:
|
||||
reply = feature_request(
|
||||
self._device, FEATURE.GESTURE_2, 0x20, self.offset, 0x01, self.mask, self.mask if enable else 0x00
|
||||
)
|
||||
if self.index is not None:
|
||||
offset, mask = self.enable_offset_mask()
|
||||
reply = feature_request(self._device, FEATURE.GESTURE_2, 0x20, offset, 0x01, mask, mask if enable else 0x00)
|
||||
return reply
|
||||
|
||||
def diverted(self): # is the gesture diverted?
|
||||
if self._diverted is None and self.diversion_index is not None:
|
||||
offset, mask = self.diversion_offset_mask()
|
||||
result = feature_request(self._device, FEATURE.GESTURE_2, 0x30, offset, 0x01, mask)
|
||||
self._diverted = bool(result[0] & mask) if result else None
|
||||
return self._diverted
|
||||
|
||||
def divert(self, diverted): # divert or undivert the gesture
|
||||
if not self.can_be_diverted:
|
||||
return None
|
||||
if self.diversion_index is not None:
|
||||
offset, mask = self.diversion_offset_mask()
|
||||
reply = feature_request(self._device, FEATURE.GESTURE_2, 0x40, offset, 0x01, mask, mask if diverted else 0x00)
|
||||
return reply
|
||||
|
||||
def as_int(self):
|
||||
|
@ -976,7 +996,7 @@ class Gesture:
|
|||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Gesture {self.gesture} offset={self.offset} mask={self.mask}>'
|
||||
return f'<Gesture {self.gesture} index={self.index} diversion_index={self.diversion_index}>'
|
||||
|
||||
# allow a gesture to be used as a settings reader/writer to enable and disable the gesture
|
||||
read = enabled
|
||||
|
@ -1072,7 +1092,7 @@ class Gestures:
|
|||
self.params = {}
|
||||
self.specs = {}
|
||||
index = 0
|
||||
next_gesture_index = 0
|
||||
next_gesture_index = next_divsn_index = 0
|
||||
field_high = 0x00
|
||||
while field_high != 0x01: # end of fields
|
||||
# retrieve the next eight fields
|
||||
|
@ -1085,8 +1105,9 @@ class Gestures:
|
|||
if field_high == 0x1: # end of fields
|
||||
break
|
||||
elif field_high & 0x80:
|
||||
gesture = Gesture(device, field_low, field_high, next_gesture_index)
|
||||
gesture = Gesture(device, field_low, field_high, next_gesture_index, next_divsn_index)
|
||||
next_gesture_index = next_gesture_index if gesture.index is None else next_gesture_index + 1
|
||||
next_divsn_index = next_divsn_index if gesture.diversion_index is None else next_divsn_index + 1
|
||||
self.gestures[gesture.gesture] = gesture
|
||||
elif field_high & 0xF0 == 0x30 or field_high & 0xF0 == 0x20:
|
||||
param = Param(device, field_low, field_high)
|
||||
|
@ -1100,7 +1121,6 @@ class Gestures:
|
|||
else:
|
||||
_log.warn(f'Unimplemented GESTURE_2 field {field_low} {field_high} found.')
|
||||
index += 1
|
||||
device._gestures = self
|
||||
|
||||
def gesture(self, gesture):
|
||||
return self.gestures.get(gesture, None)
|
||||
|
|
|
@ -811,28 +811,32 @@ class BitFieldWithOffsetAndMaskValidator(Validator):
|
|||
kind = KIND.multiple_toggle
|
||||
sep = 0x01
|
||||
|
||||
def __init__(self, options, byte_count=None):
|
||||
def __init__(self, options, om_method=None, byte_count=None):
|
||||
assert (isinstance(options, list))
|
||||
# each element of options must have .offset and .mask,
|
||||
# and its int representation must be its id (not its index)
|
||||
# each element of options is an instance of a class
|
||||
# that has an id (which is used as an index in other dictionaries)
|
||||
# and where om_method is a method that returns a byte offset and byte mask
|
||||
# that says how to access and modify the bit toogle for the option
|
||||
self.options = options
|
||||
self.om_method = om_method
|
||||
# to retrieve the options efficiently:
|
||||
self._option_from_key = {}
|
||||
self._mask_from_offset = {}
|
||||
self._option_from_offset_mask = {}
|
||||
for opt in options:
|
||||
offset, mask = om_method(opt)
|
||||
self._option_from_key[int(opt)] = opt
|
||||
try:
|
||||
self._mask_from_offset[opt.offset] |= opt.mask
|
||||
self._mask_from_offset[offset] |= mask
|
||||
except KeyError:
|
||||
self._mask_from_offset[opt.offset] = opt.mask
|
||||
self._mask_from_offset[offset] = mask
|
||||
try:
|
||||
mask_to_opt = self._option_from_offset_mask[opt.offset]
|
||||
mask_to_opt = self._option_from_offset_mask[offset]
|
||||
except KeyError:
|
||||
mask_to_opt = {}
|
||||
self._option_from_offset_mask[opt.offset] = mask_to_opt
|
||||
mask_to_opt[opt.mask] = opt
|
||||
self.byte_count = (max(x.mask.bit_length() for x in options) + 7) // 8
|
||||
self._option_from_offset_mask[offset] = mask_to_opt
|
||||
mask_to_opt[mask] = opt
|
||||
self.byte_count = (max(om_method(x)[1].bit_length() for x in options) + 7) // 8 # is this correct??
|
||||
if byte_count:
|
||||
assert (isinstance(byte_count, int) and byte_count >= self.byte_count)
|
||||
self.byte_count = byte_count
|
||||
|
@ -849,8 +853,9 @@ class BitFieldWithOffsetAndMaskValidator(Validator):
|
|||
option = self._option_from_key.get(key, None)
|
||||
if option is None:
|
||||
return None
|
||||
b = option.offset << (8 * (self.byte_count + 1))
|
||||
b |= (self.sep << (8 * self.byte_count)) | option.mask
|
||||
offset, mask = option.om_method(option)
|
||||
b = offset << (8 * (self.byte_count + 1))
|
||||
b |= (self.sep << (8 * self.byte_count)) | mask
|
||||
return _int2bytes(b, self.byte_count + 2)
|
||||
|
||||
def validate_read(self, reply_bytes_dict):
|
||||
|
@ -872,10 +877,11 @@ class BitFieldWithOffsetAndMaskValidator(Validator):
|
|||
w = {}
|
||||
for k, v in new_value.items():
|
||||
option = self._option_from_key[int(k)]
|
||||
if option.offset not in w:
|
||||
w[option.offset] = 0
|
||||
offset, mask = self.om_method(option)
|
||||
if offset not in w:
|
||||
w[offset] = 0
|
||||
if v:
|
||||
w[option.offset] |= option.mask
|
||||
w[offset] |= mask
|
||||
return [
|
||||
_int2bytes((offset << (8 * (2 * self.byte_count + 1)))
|
||||
| (self.sep << (16 * self.byte_count))
|
||||
|
|
|
@ -459,6 +459,7 @@ class ReprogrammableKeys(_Settings):
|
|||
@classmethod
|
||||
def build(cls, setting_class, device):
|
||||
choices = {}
|
||||
if device.keys:
|
||||
for k in device.keys:
|
||||
tgts = k.remappable_to
|
||||
if len(tgts) > 1:
|
||||
|
@ -494,6 +495,7 @@ class DivertKeys(_Settings):
|
|||
@classmethod
|
||||
def build(cls, setting_class, device):
|
||||
choices = {}
|
||||
if device.keys:
|
||||
for k in device.keys:
|
||||
if 'divertable' in k.flags and 'virtual' not in k.flags:
|
||||
choices[k.key] = setting_class.choices_universe
|
||||
|
@ -982,14 +984,32 @@ class Gesture2Gestures(_BitFieldOMSetting):
|
|||
description = _('Tweak the mouse/touchpad behaviour.')
|
||||
feature = _F.GESTURE_2
|
||||
rw_options = {'read_fnid': 0x10, 'write_fnid': 0x20}
|
||||
validator_options = {'om_method': _hidpp20.Gesture.enable_offset_mask}
|
||||
choices_universe = _hidpp20.GESTURE
|
||||
_labels = _GESTURE2_GESTURES_LABELS
|
||||
|
||||
class validator_class(_BitFieldOMV):
|
||||
@classmethod
|
||||
def build(cls, setting_class, device):
|
||||
options = [g for g in _hidpp20.get_gestures(device).gestures.values() if g.can_be_enabled or g.default_enabled]
|
||||
return cls(options) if options else None
|
||||
def build(cls, setting_class, device, om_method=None):
|
||||
options = [g for g in device.gestures.gestures.values() if g.can_be_enabled or g.default_enabled]
|
||||
return cls(options, om_method=om_method) if options else None
|
||||
|
||||
|
||||
class Gesture2Divert(_BitFieldOMSetting):
|
||||
name = 'gesture2-divert'
|
||||
label = _('Gestures Diversion')
|
||||
description = _('Divert mouse/touchpad gestures.')
|
||||
feature = _F.GESTURE_2
|
||||
rw_options = {'read_fnid': 0x30, 'write_fnid': 0x40}
|
||||
validator_options = {'om_method': _hidpp20.Gesture.diversion_offset_mask}
|
||||
choices_universe = _hidpp20.GESTURE
|
||||
_labels = _GESTURE2_GESTURES_LABELS
|
||||
|
||||
class validator_class(_BitFieldOMV):
|
||||
@classmethod
|
||||
def build(cls, setting_class, device, om_method=None):
|
||||
options = [g for g in device.gestures.gestures.values() if g.can_be_diverted]
|
||||
return cls(options, om_method=om_method) if options else None
|
||||
|
||||
|
||||
class Gesture2Params(_LongSettings):
|
||||
|
@ -1147,6 +1167,7 @@ SETTINGS = [
|
|||
DualPlatform, # simple
|
||||
ChangeHost, # working
|
||||
Gesture2Gestures, # working
|
||||
Gesture2Divert,
|
||||
Gesture2Params, # working
|
||||
]
|
||||
|
||||
|
|
|
@ -262,7 +262,10 @@ def _print_device(dev, num=None):
|
|||
(len(dev.gestures.gestures), len(dev.gestures.params), len(dev.gestures.specs))
|
||||
)
|
||||
for k in dev.gestures.gestures.values():
|
||||
print(' %-26s Enabled (%4s): %s' % (k.gesture, k.index, k.enabled()))
|
||||
print(
|
||||
' %-26s Enabled(%4s): %-5s Diverted:(%4s) %s' %
|
||||
(k.gesture, k.index, k.enabled(), k.diversion_index, k.diverted())
|
||||
)
|
||||
for k in dev.gestures.params.values():
|
||||
print(' %-26s Value (%4s): %s [Default: %s]' % (k.param, k.index, k.value, k.default_value))
|
||||
for k in dev.gestures.specs.values():
|
||||
|
|
Loading…
Reference in New Issue