receiver: add setting for MULTIPLATFORM and DUALPLATFORM feature
This commit is contained in:
parent
1dc59fd374
commit
c9c472e391
|
@ -75,8 +75,8 @@ Feature | ID | Status | Notes
|
||||||
`KEYBOARD_LAYOUT` | `0x4520` | :x: | read only
|
`KEYBOARD_LAYOUT` | `0x4520` | :x: | read only
|
||||||
`KEYBOARD_DISABLE_KEYS` | `0x4521` | :heavy_check_mark: | `_feature_disable_keyboard_keys`
|
`KEYBOARD_DISABLE_KEYS` | `0x4521` | :heavy_check_mark: | `_feature_disable_keyboard_keys`
|
||||||
`KEYBOARD_DISABLE_BY_USAGE` | `0x4522` | :x: |
|
`KEYBOARD_DISABLE_BY_USAGE` | `0x4522` | :x: |
|
||||||
`DUALPLATFORM` | `0x4530` | :x: | :wrench:
|
`DUALPLATFORM` | `0x4530` | :heavy_check_mark: | `_feature_dualplatform`, untested
|
||||||
`MULTIPLATFORM` | `0x4531` | :x: | :wrench:
|
`MULTIPLATFORM` | `0x4531` | :heavy_check_mark: | `_feature_multiplatform`
|
||||||
`KEYBOARD_LAYOUT_2` | `0x4540` | :x: | read only
|
`KEYBOARD_LAYOUT_2` | `0x4540` | :x: | read only
|
||||||
`CROWN` | `0x4600` | :x: |
|
`CROWN` | `0x4600` | :x: |
|
||||||
`TOUCHPAD_FW_ITEMS` | `0x6010` | :x: |
|
`TOUCHPAD_FW_ITEMS` | `0x6010` | :x: |
|
||||||
|
|
|
@ -45,7 +45,7 @@ class Setting(object):
|
||||||
Needs to be instantiated for each specific device."""
|
Needs to be instantiated for each specific device."""
|
||||||
__slots__ = ('name', 'label', 'description', 'kind', 'device_kind', 'feature', '_rw', '_validator', '_device', '_value')
|
__slots__ = ('name', 'label', 'description', 'kind', 'device_kind', 'feature', '_rw', '_validator', '_device', '_value')
|
||||||
|
|
||||||
def __init__(self, name, rw, validator, kind=None, label=None, description=None, device_kind=None, feature=None):
|
def __init__(self, name, rw, validator, kind=None, label=None, description=None, device_kind=None, feature=None, **kwargs):
|
||||||
assert name
|
assert name
|
||||||
self.name = name
|
self.name = name
|
||||||
self.label = label or name
|
self.label = label or name
|
||||||
|
@ -434,7 +434,7 @@ class FeatureRW(object):
|
||||||
default_read_fnid = 0x00
|
default_read_fnid = 0x00
|
||||||
default_write_fnid = 0x10
|
default_write_fnid = 0x10
|
||||||
|
|
||||||
def __init__(self, feature, read_fnid=default_read_fnid, write_fnid=default_write_fnid):
|
def __init__(self, feature, read_fnid=default_read_fnid, write_fnid=default_write_fnid, **kwargs):
|
||||||
assert isinstance(feature, _NamedInt)
|
assert isinstance(feature, _NamedInt)
|
||||||
self.feature = feature
|
self.feature = feature
|
||||||
self.read_fnid = read_fnid
|
self.read_fnid = read_fnid
|
||||||
|
@ -453,23 +453,30 @@ class FeatureRWMap(FeatureRW):
|
||||||
kind = _NamedInt(0x02, 'feature')
|
kind = _NamedInt(0x02, 'feature')
|
||||||
default_read_fnid = 0x00
|
default_read_fnid = 0x00
|
||||||
default_write_fnid = 0x10
|
default_write_fnid = 0x10
|
||||||
default_key_bytes = 1
|
default_key_bytes_count = 1
|
||||||
|
|
||||||
def __init__(self, feature, read_fnid=default_read_fnid, write_fnid=default_write_fnid, key_bytes=default_key_bytes):
|
def __init__(
|
||||||
|
self,
|
||||||
|
feature,
|
||||||
|
read_fnid=default_read_fnid,
|
||||||
|
write_fnid=default_write_fnid,
|
||||||
|
key_bytes_count=default_key_bytes_count,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
assert isinstance(feature, _NamedInt)
|
assert isinstance(feature, _NamedInt)
|
||||||
self.feature = feature
|
self.feature = feature
|
||||||
self.read_fnid = read_fnid
|
self.read_fnid = read_fnid
|
||||||
self.write_fnid = write_fnid
|
self.write_fnid = write_fnid
|
||||||
self.key_bytes = key_bytes
|
self.key_bytes_count = key_bytes_count
|
||||||
|
|
||||||
def read(self, device, key):
|
def read(self, device, key):
|
||||||
assert self.feature is not None
|
assert self.feature is not None
|
||||||
key_bytes = _int2bytes(key, self.key_bytes)
|
key_bytes = _int2bytes(key, self.key_bytes_count)
|
||||||
return device.feature_request(self.feature, self.read_fnid, key_bytes)
|
return device.feature_request(self.feature, self.read_fnid, key_bytes)
|
||||||
|
|
||||||
def write(self, device, key, data_bytes):
|
def write(self, device, key, data_bytes):
|
||||||
assert self.feature is not None
|
assert self.feature is not None
|
||||||
key_bytes = _int2bytes(key, self.key_bytes)
|
key_bytes = _int2bytes(key, self.key_bytes_count)
|
||||||
return device.feature_request(self.feature, self.write_fnid, key_bytes, data_bytes)
|
return device.feature_request(self.feature, self.write_fnid, key_bytes, data_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
@ -488,7 +495,7 @@ class BooleanValidator(object):
|
||||||
# mask specifies all the affected bits in the value
|
# mask specifies all the affected bits in the value
|
||||||
default_mask = 0xFF
|
default_mask = 0xFF
|
||||||
|
|
||||||
def __init__(self, true_value=default_true, false_value=default_false, mask=default_mask):
|
def __init__(self, true_value=default_true, false_value=default_false, mask=default_mask, **kwargs):
|
||||||
if isinstance(true_value, int):
|
if isinstance(true_value, int):
|
||||||
assert isinstance(false_value, int)
|
assert isinstance(false_value, int)
|
||||||
if mask is None:
|
if mask is None:
|
||||||
|
@ -593,7 +600,7 @@ class BitFieldValidator(object):
|
||||||
|
|
||||||
kind = KIND.multiple_toggle
|
kind = KIND.multiple_toggle
|
||||||
|
|
||||||
def __init__(self, options, byte_count=None):
|
def __init__(self, options, byte_count=None, **kwargs):
|
||||||
assert (isinstance(options, list))
|
assert (isinstance(options, list))
|
||||||
self.options = options
|
self.options = options
|
||||||
self.byte_count = (max(x.bit_length() for x in options) + 7) // 8
|
self.byte_count = (max(x.bit_length() for x in options) + 7) // 8
|
||||||
|
@ -621,14 +628,12 @@ class BitFieldValidator(object):
|
||||||
|
|
||||||
|
|
||||||
class ChoicesValidator(object):
|
class ChoicesValidator(object):
|
||||||
__slots__ = ('choices', 'flag', '_bytes_count', 'needs_current_value')
|
|
||||||
|
|
||||||
kind = KIND.choice
|
kind = KIND.choice
|
||||||
"""Translates between NamedInts and a byte sequence.
|
"""Translates between NamedInts and a byte sequence.
|
||||||
:param choices: a list of NamedInts
|
:param choices: a list of NamedInts
|
||||||
:param bytes_count: the size of the derived byte sequence. If None, it
|
:param bytes_count: the size of the derived byte sequence. If None, it
|
||||||
will be calculated from the choices."""
|
will be calculated from the choices."""
|
||||||
def __init__(self, choices, bytes_count=None):
|
def __init__(self, choices, bytes_count=None, read_skip_bytes_count=None, write_prefix_bytes=b'', **_ignore):
|
||||||
assert choices is not None
|
assert choices is not None
|
||||||
assert isinstance(choices, _NamedInts)
|
assert isinstance(choices, _NamedInts)
|
||||||
assert len(choices) > 2
|
assert len(choices) > 2
|
||||||
|
@ -641,9 +646,13 @@ class ChoicesValidator(object):
|
||||||
assert self._bytes_count <= bytes_count
|
assert self._bytes_count <= bytes_count
|
||||||
self._bytes_count = bytes_count
|
self._bytes_count = bytes_count
|
||||||
assert self._bytes_count < 8
|
assert self._bytes_count < 8
|
||||||
|
self._read_skip_bytes_count = read_skip_bytes_count if read_skip_bytes_count else 0
|
||||||
|
self._write_prefix_bytes = write_prefix_bytes if write_prefix_bytes else b''
|
||||||
|
assert self._bytes_count + self._read_skip_bytes_count <= 14
|
||||||
|
assert self._bytes_count + len(self._write_prefix_bytes) <= 14
|
||||||
|
|
||||||
def validate_read(self, reply_bytes):
|
def validate_read(self, reply_bytes):
|
||||||
reply_value = _bytes2int(reply_bytes[:self._bytes_count])
|
reply_value = _bytes2int(reply_bytes[self._read_skip_bytes_count:self._read_skip_bytes_count + self._bytes_count])
|
||||||
valid_value = self.choices[reply_value]
|
valid_value = self.choices[reply_value]
|
||||||
assert valid_value is not None, '%s: failed to validate read value %02X' % (self.__class__.__name__, reply_value)
|
assert valid_value is not None, '%s: failed to validate read value %02X' % (self.__class__.__name__, reply_value)
|
||||||
return valid_value
|
return valid_value
|
||||||
|
@ -664,13 +673,22 @@ class ChoicesValidator(object):
|
||||||
if choice is None:
|
if choice is None:
|
||||||
raise ValueError('invalid choice %r' % new_value)
|
raise ValueError('invalid choice %r' % new_value)
|
||||||
assert isinstance(choice, _NamedInt)
|
assert isinstance(choice, _NamedInt)
|
||||||
return choice.bytes(self._bytes_count)
|
return self._write_prefix_bytes + choice.bytes(self._bytes_count)
|
||||||
|
|
||||||
|
|
||||||
class ChoicesMapValidator(ChoicesValidator):
|
class ChoicesMapValidator(ChoicesValidator):
|
||||||
kind = KIND.map_choice
|
kind = KIND.map_choice
|
||||||
|
|
||||||
def __init__(self, choices_map, key_bytes_count=None, skip_bytes_count=None, value_bytes_count=None, extra_default=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
choices_map,
|
||||||
|
key_bytes_count=None,
|
||||||
|
bytes_count=None,
|
||||||
|
read_skip_bytes_count=0,
|
||||||
|
write_prefix_bytes=b'',
|
||||||
|
extra_default=None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
assert choices_map is not None
|
assert choices_map is not None
|
||||||
assert isinstance(choices_map, dict)
|
assert isinstance(choices_map, dict)
|
||||||
max_key_bits = 0
|
max_key_bits = 0
|
||||||
|
@ -682,24 +700,25 @@ class ChoicesMapValidator(ChoicesValidator):
|
||||||
for key_value in choices:
|
for key_value in choices:
|
||||||
assert isinstance(key_value, _NamedInt)
|
assert isinstance(key_value, _NamedInt)
|
||||||
max_value_bits = max(max_value_bits, key_value.bit_length())
|
max_value_bits = max(max_value_bits, key_value.bit_length())
|
||||||
self.choices = choices_map
|
|
||||||
self.needs_current_value = False
|
|
||||||
self.extra_default = extra_default
|
|
||||||
|
|
||||||
self._key_bytes_count = (max_key_bits + 7) // 8
|
self._key_bytes_count = (max_key_bits + 7) // 8
|
||||||
if key_bytes_count:
|
if key_bytes_count:
|
||||||
assert self._key_bytes_count <= key_bytes_count
|
assert self._key_bytes_count <= key_bytes_count
|
||||||
self._key_bytes_count = key_bytes_count
|
self._key_bytes_count = key_bytes_count
|
||||||
self._value_bytes_count = (max_value_bits + 7) // 8
|
self._bytes_count = (max_value_bits + 7) // 8
|
||||||
if value_bytes_count:
|
if bytes_count:
|
||||||
assert self._value_bytes_count <= value_bytes_count
|
assert self._bytes_count <= bytes_count
|
||||||
self._value_bytes_count = value_bytes_count
|
self._bytes_count = bytes_count
|
||||||
self._skip_bytes_count = skip_bytes_count if skip_bytes_count is not None else 0
|
self.choices = choices_map
|
||||||
self._bytes_count = self._key_bytes_count + self._skip_bytes_count + self._value_bytes_count
|
self.needs_current_value = False
|
||||||
|
self.extra_default = extra_default
|
||||||
|
self._read_skip_bytes_count = read_skip_bytes_count if read_skip_bytes_count else 0
|
||||||
|
self._write_prefix_bytes = write_prefix_bytes if write_prefix_bytes else b''
|
||||||
|
assert self._bytes_count + self._read_skip_bytes_count + self._key_bytes_count <= 14
|
||||||
|
assert self._bytes_count + len(self._write_prefix_bytes) + self._key_bytes_count <= 14
|
||||||
|
|
||||||
def validate_read(self, reply_bytes, key):
|
def validate_read(self, reply_bytes, key):
|
||||||
start = self._key_bytes_count + self._skip_bytes_count
|
start = self._key_bytes_count + self._read_skip_bytes_count
|
||||||
end = start + self._value_bytes_count
|
end = start + self._bytes_count
|
||||||
reply_value = _bytes2int(reply_bytes[start:end])
|
reply_value = _bytes2int(reply_bytes[start:end])
|
||||||
# reprogrammable keys starts out as 0, which is not a choice, so don't use assert here
|
# reprogrammable keys starts out as 0, which is not a choice, so don't use assert here
|
||||||
if self.extra_default is not None and self.extra_default == reply_value:
|
if self.extra_default is not None and self.extra_default == reply_value:
|
||||||
|
@ -712,7 +731,7 @@ class ChoicesMapValidator(ChoicesValidator):
|
||||||
choices = self.choices[key]
|
choices = self.choices[key]
|
||||||
if new_value not in choices and new_value != self.extra_default:
|
if new_value not in choices and new_value != self.extra_default:
|
||||||
raise ValueError('invalid choice %r' % new_value)
|
raise ValueError('invalid choice %r' % new_value)
|
||||||
return _int2bytes(new_value, self._skip_bytes_count + self._value_bytes_count)
|
return self._write_prefix_bytes + new_value.to_bytes(self._bytes_count, 'big')
|
||||||
|
|
||||||
|
|
||||||
class RangeValidator(object):
|
class RangeValidator(object):
|
||||||
|
@ -724,7 +743,7 @@ class RangeValidator(object):
|
||||||
:param max_value: maximum accepted value (inclusive)
|
:param max_value: maximum accepted value (inclusive)
|
||||||
:param bytes_count: the size of the derived byte sequence. If None, it
|
:param bytes_count: the size of the derived byte sequence. If None, it
|
||||||
will be calculated from the range."""
|
will be calculated from the range."""
|
||||||
def __init__(self, min_value, max_value, bytes_count=None):
|
def __init__(self, min_value, max_value, bytes_count=None, **kwargs):
|
||||||
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
|
||||||
|
|
|
@ -82,8 +82,8 @@ def register_choices(name, register, choices, kind=_KIND.choice, label=None, des
|
||||||
def feature_toggle(
|
def feature_toggle(
|
||||||
name,
|
name,
|
||||||
feature,
|
feature,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
read_fnid=_FeatureRW.default_read_fnid,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
write_fnid=_FeatureRW.default_write_fnid,
|
||||||
true_value=_BooleanV.default_true,
|
true_value=_BooleanV.default_true,
|
||||||
false_value=_BooleanV.default_false,
|
false_value=_BooleanV.default_false,
|
||||||
mask=_BooleanV.default_mask,
|
mask=_BooleanV.default_mask,
|
||||||
|
@ -92,7 +92,7 @@ def feature_toggle(
|
||||||
device_kind=None
|
device_kind=None
|
||||||
):
|
):
|
||||||
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_fnid=read_fnid, write_fnid=write_fnid)
|
||||||
return _Setting(name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind)
|
return _Setting(name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind)
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,15 +100,15 @@ def feature_bitfield_toggle(
|
||||||
name,
|
name,
|
||||||
feature,
|
feature,
|
||||||
options,
|
options,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
read_fnid=_FeatureRW.default_read_fnid,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
write_fnid=_FeatureRW.default_write_fnid,
|
||||||
label=None,
|
label=None,
|
||||||
description=None,
|
description=None,
|
||||||
device_kind=None
|
device_kind=None
|
||||||
):
|
):
|
||||||
assert options
|
assert options
|
||||||
validator = _BitFieldV(options)
|
validator = _BitFieldV(options)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_fnid=read_fnid, write_fnid=write_fnid)
|
||||||
return _BitFieldSetting(
|
return _BitFieldSetting(
|
||||||
name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind
|
name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind
|
||||||
)
|
)
|
||||||
|
@ -118,8 +118,8 @@ def feature_bitfield_toggle_dynamic(
|
||||||
name,
|
name,
|
||||||
feature,
|
feature,
|
||||||
options_callback,
|
options_callback,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
read_fnid=_FeatureRW.default_read_fnid,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
write_fnid=_FeatureRW.default_write_fnid,
|
||||||
label=None,
|
label=None,
|
||||||
description=None,
|
description=None,
|
||||||
device_kind=None
|
device_kind=None
|
||||||
|
@ -130,8 +130,8 @@ def feature_bitfield_toggle_dynamic(
|
||||||
name,
|
name,
|
||||||
feature,
|
feature,
|
||||||
options,
|
options,
|
||||||
read_function_id=read_function_id,
|
read_fnid=read_fnid,
|
||||||
write_function_id=write_function_id,
|
write_fnid=write_fnid,
|
||||||
label=label,
|
label=label,
|
||||||
description=description,
|
description=description,
|
||||||
device_kind=device_kind
|
device_kind=device_kind
|
||||||
|
@ -142,51 +142,21 @@ def feature_bitfield_toggle_dynamic(
|
||||||
return instantiate
|
return instantiate
|
||||||
|
|
||||||
|
|
||||||
def feature_choices(
|
def feature_choices(name, feature, choices, **kwargs):
|
||||||
name,
|
|
||||||
feature,
|
|
||||||
choices,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
bytes_count=None,
|
|
||||||
label=None,
|
|
||||||
description=None,
|
|
||||||
device_kind=None
|
|
||||||
):
|
|
||||||
assert choices
|
assert choices
|
||||||
validator = _ChoicesV(choices, bytes_count=bytes_count)
|
validator = _ChoicesV(choices, **kwargs)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, **kwargs)
|
||||||
return _Setting(
|
return _Setting(name, rw, validator, kind=_KIND.choice, **kwargs)
|
||||||
name, rw, validator, feature=feature, kind=_KIND.choice, label=label, description=description, device_kind=device_kind
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def feature_choices_dynamic(
|
def feature_choices_dynamic(name, feature, choices_callback, **kwargs):
|
||||||
name,
|
|
||||||
feature,
|
|
||||||
choices_callback,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
bytes_count=None,
|
|
||||||
label=None,
|
|
||||||
description=None,
|
|
||||||
device_kind=None
|
|
||||||
):
|
|
||||||
# Proxy that obtains choices dynamically from a device
|
# Proxy that obtains choices dynamically from a device
|
||||||
def instantiate(device):
|
def instantiate(device):
|
||||||
# Obtain choices for this feature
|
# Obtain choices for this feature
|
||||||
choices = choices_callback(device)
|
choices = choices_callback(device)
|
||||||
setting = feature_choices(
|
if not choices: # no choices, so don't create a setting
|
||||||
name,
|
return None
|
||||||
feature,
|
setting = feature_choices(name, feature, choices, **kwargs)
|
||||||
choices,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
bytes_count=bytes_count,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind
|
|
||||||
)
|
|
||||||
return setting(device)
|
return setting(device)
|
||||||
|
|
||||||
instantiate._rw_kind = _FeatureRW.kind
|
instantiate._rw_kind = _FeatureRW.kind
|
||||||
|
@ -195,75 +165,20 @@ def feature_choices_dynamic(
|
||||||
|
|
||||||
# maintain a mapping from keys (NamedInts) to one of a list of choices (NamedInts), default is first one
|
# maintain a mapping from keys (NamedInts) to one of a list of choices (NamedInts), default is first one
|
||||||
# the setting is stored as a JSON-compatible object mapping the key int (as a string) to the choice int
|
# the setting is stored as a JSON-compatible object mapping the key int (as a string) to the choice int
|
||||||
# extra_default is an extra value that comes from the device that also means the default
|
def feature_map_choices(name, feature, choicesmap, **kwargs):
|
||||||
def feature_map_choices(
|
|
||||||
name,
|
|
||||||
feature,
|
|
||||||
choicesmap,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
key_bytes_count=None,
|
|
||||||
skip_bytes_count=None,
|
|
||||||
value_bytes_count=None,
|
|
||||||
label=None,
|
|
||||||
description=None,
|
|
||||||
device_kind=None,
|
|
||||||
extra_default=None
|
|
||||||
):
|
|
||||||
assert choicesmap
|
assert choicesmap
|
||||||
validator = _ChoicesMapV(
|
validator = _ChoicesMapV(choicesmap, **kwargs)
|
||||||
choicesmap,
|
rw = _FeatureRWMap(feature, **kwargs)
|
||||||
key_bytes_count=key_bytes_count,
|
return _Settings(name, rw, validator, kind=_KIND.map_choice, **kwargs)
|
||||||
skip_bytes_count=skip_bytes_count,
|
|
||||||
value_bytes_count=value_bytes_count,
|
|
||||||
extra_default=extra_default
|
|
||||||
)
|
|
||||||
rw = _FeatureRWMap(feature, read_function_id, write_function_id, key_bytes=key_bytes_count)
|
|
||||||
return _Settings(
|
|
||||||
name,
|
|
||||||
rw,
|
|
||||||
validator,
|
|
||||||
feature=feature,
|
|
||||||
kind=_KIND.map_choice,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def feature_map_choices_dynamic(
|
def feature_map_choices_dynamic(name, feature, choices_callback, **kwargs):
|
||||||
name,
|
|
||||||
feature,
|
|
||||||
choices_callback,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
key_bytes_count=None,
|
|
||||||
skip_bytes_count=None,
|
|
||||||
value_bytes_count=None,
|
|
||||||
label=None,
|
|
||||||
description=None,
|
|
||||||
device_kind=None,
|
|
||||||
extra_default=None
|
|
||||||
):
|
|
||||||
# Proxy that obtains choices dynamically from a device
|
# Proxy that obtains choices dynamically from a device
|
||||||
def instantiate(device):
|
def instantiate(device):
|
||||||
choices = choices_callback(device)
|
choices = choices_callback(device)
|
||||||
if not choices: # no choices, so don't create a Setting
|
if not choices: # no choices, so don't create a Setting
|
||||||
return None
|
return None
|
||||||
setting = feature_map_choices(
|
setting = feature_map_choices(name, feature, choices, **kwargs)
|
||||||
name,
|
|
||||||
feature,
|
|
||||||
choices,
|
|
||||||
read_function_id,
|
|
||||||
write_function_id,
|
|
||||||
key_bytes_count=key_bytes_count,
|
|
||||||
skip_bytes_count=skip_bytes_count,
|
|
||||||
value_bytes_count=value_bytes_count,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind,
|
|
||||||
extra_default=extra_default
|
|
||||||
)
|
|
||||||
return setting(device)
|
return setting(device)
|
||||||
|
|
||||||
instantiate._rw_kind = _FeatureRWMap.kind
|
instantiate._rw_kind = _FeatureRWMap.kind
|
||||||
|
@ -275,8 +190,8 @@ def feature_range(
|
||||||
feature,
|
feature,
|
||||||
min_value,
|
min_value,
|
||||||
max_value,
|
max_value,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
read_fnid=_FeatureRW.default_read_fnid,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
write_fnid=_FeatureRW.default_write_fnid,
|
||||||
rw=None,
|
rw=None,
|
||||||
bytes_count=None,
|
bytes_count=None,
|
||||||
label=None,
|
label=None,
|
||||||
|
@ -285,7 +200,7 @@ def feature_range(
|
||||||
):
|
):
|
||||||
validator = _RangeV(min_value, max_value, bytes_count=bytes_count)
|
validator = _RangeV(min_value, max_value, bytes_count=bytes_count)
|
||||||
if rw is None:
|
if rw is None:
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_fnid=read_fnid, write_fnid=write_fnid)
|
||||||
return _Setting(
|
return _Setting(
|
||||||
name, rw, validator, feature=feature, kind=_KIND.range, label=label, description=description, device_kind=device_kind
|
name, rw, validator, feature=feature, kind=_KIND.range, label=label, description=description, device_kind=device_kind
|
||||||
)
|
)
|
||||||
|
@ -342,10 +257,22 @@ _REPROGRAMMABLE_KEYS = (
|
||||||
_('Changing important actions (such as for the left mouse button) can result in an unusable system.')
|
_('Changing important actions (such as for the left mouse button) can result in an unusable system.')
|
||||||
)
|
)
|
||||||
_DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific keyboard keys.'))
|
_DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific keyboard keys.'))
|
||||||
|
_PLATFORM = ('multiplatform', _('Set OS'), _('Change keys to match OS.'))
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
# Keyword arguments for setting template functions:
|
||||||
#
|
# label, description - label and tooltip to be shown in GUI
|
||||||
|
# device_kind - the kinds of devices that setting is suitable for (NOT CURRENTLY USED)
|
||||||
|
# read_fnid, write_fnid - default 0x00 and 0x10 function numbers (times 16) to read and write setting
|
||||||
|
# bytes_count - default 1 - number of bytes for the data (ignoring the key)
|
||||||
|
# only for boolean settings
|
||||||
|
# true_value, false_value, mask - integer or byte strings for boolean settings
|
||||||
|
# only for map choices
|
||||||
|
# key_bytes_count - default 1 - number of bytes in the key
|
||||||
|
# extra_default - extra value that cannot be set but means same as default value
|
||||||
|
# only for choices and map choices
|
||||||
|
# read_skip_bytes_count - default 0 - number of bytes to ignore before the data when reading
|
||||||
|
# write_prefix_bytes - default None - bytes to put before the data writing
|
||||||
|
|
||||||
|
|
||||||
def _register_hand_detection(
|
def _register_hand_detection(
|
||||||
|
@ -463,8 +390,8 @@ def _feature_hires_smooth_invert():
|
||||||
return feature_toggle(
|
return feature_toggle(
|
||||||
_HIRES_INV[0],
|
_HIRES_INV[0],
|
||||||
_F.HIRES_WHEEL,
|
_F.HIRES_WHEEL,
|
||||||
read_function_id=0x10,
|
read_fnid=0x10,
|
||||||
write_function_id=0x20,
|
write_fnid=0x20,
|
||||||
true_value=0x04,
|
true_value=0x04,
|
||||||
mask=0x04,
|
mask=0x04,
|
||||||
label=_HIRES_INV[1],
|
label=_HIRES_INV[1],
|
||||||
|
@ -477,8 +404,8 @@ def _feature_hires_smooth_resolution():
|
||||||
return feature_toggle(
|
return feature_toggle(
|
||||||
_HIRES_RES[0],
|
_HIRES_RES[0],
|
||||||
_F.HIRES_WHEEL,
|
_F.HIRES_WHEEL,
|
||||||
read_function_id=0x10,
|
read_fnid=0x10,
|
||||||
write_function_id=0x20,
|
write_fnid=0x20,
|
||||||
true_value=0x02,
|
true_value=0x02,
|
||||||
mask=0x02,
|
mask=0x02,
|
||||||
label=_HIRES_RES[1],
|
label=_HIRES_RES[1],
|
||||||
|
@ -562,8 +489,8 @@ def _feature_adjustable_dpi():
|
||||||
_DPI[0],
|
_DPI[0],
|
||||||
_F.ADJUSTABLE_DPI,
|
_F.ADJUSTABLE_DPI,
|
||||||
_feature_adjustable_dpi_choices,
|
_feature_adjustable_dpi_choices,
|
||||||
read_function_id=0x20,
|
read_fnid=0x20,
|
||||||
write_function_id=0x30,
|
write_fnid=0x30,
|
||||||
bytes_count=3,
|
bytes_count=3,
|
||||||
label=_DPI[1],
|
label=_DPI[1],
|
||||||
description=_DPI[2],
|
description=_DPI[2],
|
||||||
|
@ -579,8 +506,8 @@ def _feature_pointer_speed():
|
||||||
_F.POINTER_SPEED,
|
_F.POINTER_SPEED,
|
||||||
0x002e,
|
0x002e,
|
||||||
0x01ff,
|
0x01ff,
|
||||||
read_function_id=0x0,
|
read_fnid=0x0,
|
||||||
write_function_id=0x10,
|
write_fnid=0x10,
|
||||||
bytes_count=2,
|
bytes_count=2,
|
||||||
label=_POINTER_SPEED[1],
|
label=_POINTER_SPEED[1],
|
||||||
description=_POINTER_SPEED[2],
|
description=_POINTER_SPEED[2],
|
||||||
|
@ -624,15 +551,16 @@ def _feature_reprogrammable_keys():
|
||||||
_REPROGRAMMABLE_KEYS[0],
|
_REPROGRAMMABLE_KEYS[0],
|
||||||
_F.REPROG_CONTROLS_V4,
|
_F.REPROG_CONTROLS_V4,
|
||||||
_feature_reprogrammable_keys_choices,
|
_feature_reprogrammable_keys_choices,
|
||||||
read_function_id=0x20,
|
read_fnid=0x20,
|
||||||
write_function_id=0x30,
|
write_fnid=0x30,
|
||||||
key_bytes_count=2,
|
key_bytes_count=2,
|
||||||
skip_bytes_count=1,
|
bytes_count=2,
|
||||||
value_bytes_count=2,
|
read_skip_bytes_count=1,
|
||||||
|
write_prefix_bytes=b'\x00',
|
||||||
|
extra_default=0,
|
||||||
label=_REPROGRAMMABLE_KEYS[1],
|
label=_REPROGRAMMABLE_KEYS[1],
|
||||||
description=_REPROGRAMMABLE_KEYS[2],
|
description=_REPROGRAMMABLE_KEYS[2],
|
||||||
device_kind=(_DK.keyboard, ),
|
device_kind=(_DK.keyboard, ),
|
||||||
extra_default=0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -647,14 +575,83 @@ def _feature_disable_keyboard_keys():
|
||||||
_DISABLE_KEYS[0],
|
_DISABLE_KEYS[0],
|
||||||
_F.KEYBOARD_DISABLE_KEYS,
|
_F.KEYBOARD_DISABLE_KEYS,
|
||||||
_feature_disable_keyboard_keys_key_list,
|
_feature_disable_keyboard_keys_key_list,
|
||||||
read_function_id=0x10,
|
read_fnid=0x10,
|
||||||
write_function_id=0x20,
|
write_fnid=0x20,
|
||||||
label=_DISABLE_KEYS[1],
|
label=_DISABLE_KEYS[1],
|
||||||
description=_DISABLE_KEYS[2],
|
description=_DISABLE_KEYS[2],
|
||||||
device_kind=(_DK.keyboard, )
|
device_kind=(_DK.keyboard, )
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# muultiplatform OS bits
|
||||||
|
OSS = [('Linux', 0x0400), ('MacOS', 0x2000), ('Windows', 0x0100), ('iOS', 0x4000), ('Android', 0x1000), ('WebOS', 0x8000),
|
||||||
|
('Chrome', 0x0800), ('WinEmb', 0x0200), ('Tizen', 0x0001)]
|
||||||
|
|
||||||
|
|
||||||
|
def _feature_multiplatform_choices(device):
|
||||||
|
def _str_os_versions(low, high):
|
||||||
|
def _str_os_version(version):
|
||||||
|
if version == 0:
|
||||||
|
return ''
|
||||||
|
elif version & 0xFF:
|
||||||
|
return str(version >> 8) + '.' + str(version & 0xFF)
|
||||||
|
else:
|
||||||
|
return str(version >> 8)
|
||||||
|
|
||||||
|
return '' if low == 0 and high == 0 else ' ' + _str_os_version(low) + '-' + _str_os_version(high)
|
||||||
|
|
||||||
|
infos = device.feature_request(_F.MULTIPLATFORM)
|
||||||
|
assert infos, 'Oops, multiplatform count cannot be retrieved!'
|
||||||
|
flags, _ignore, num_descriptors = _unpack('!BBB', infos[:3])
|
||||||
|
if not (flags & 0x02): # can't set platform so don't create setting
|
||||||
|
return []
|
||||||
|
descriptors = []
|
||||||
|
for index in range(0, num_descriptors):
|
||||||
|
descriptor = device.feature_request(_F.MULTIPLATFORM, 0x10, index)
|
||||||
|
platform, _ignore, os_flags, low, high = _unpack('!BBHHH', descriptor[:8])
|
||||||
|
descriptors.append((platform, os_flags, low, high))
|
||||||
|
choices = _NamedInts()
|
||||||
|
for os_name, os_bit in OSS:
|
||||||
|
for platform, os_flags, low, high in descriptors:
|
||||||
|
os = os_name + _str_os_versions(low, high)
|
||||||
|
if os_bit & os_flags and platform not in choices and os not in choices:
|
||||||
|
choices[platform] = os
|
||||||
|
return choices
|
||||||
|
|
||||||
|
|
||||||
|
def _feature_multiplatform():
|
||||||
|
return feature_choices_dynamic(
|
||||||
|
_PLATFORM[0],
|
||||||
|
_F.MULTIPLATFORM,
|
||||||
|
_feature_multiplatform_choices,
|
||||||
|
read_fnid=0x00,
|
||||||
|
read_skip_bytes_count=6,
|
||||||
|
write_fnid=0x30,
|
||||||
|
write_prefix_bytes=b'\xff',
|
||||||
|
label=_PLATFORM[1],
|
||||||
|
description=_PLATFORM[2],
|
||||||
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PLATFORMS = _NamedInts()
|
||||||
|
PLATFORMS[0x00] = 'iOS, MacOS'
|
||||||
|
PLATFORMS[0x01] = 'Android, Windows'
|
||||||
|
|
||||||
|
|
||||||
|
def _feature_dualplatform():
|
||||||
|
return feature_choices(
|
||||||
|
_PLATFORM[0],
|
||||||
|
_F.DUALPLATFORM,
|
||||||
|
PLATFORMS,
|
||||||
|
read_fnid=0x10,
|
||||||
|
write_fnid=0x20,
|
||||||
|
label=_PLATFORM[1],
|
||||||
|
description=_PLATFORM[2],
|
||||||
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -681,6 +678,8 @@ _SETTINGS_TABLE = [
|
||||||
_S(_BACKLIGHT[0], _F.BACKLIGHT2, _feature_backlight2),
|
_S(_BACKLIGHT[0], _F.BACKLIGHT2, _feature_backlight2),
|
||||||
_S(_REPROGRAMMABLE_KEYS[0], _F.REPROG_CONTROLS_V4, _feature_reprogrammable_keys),
|
_S(_REPROGRAMMABLE_KEYS[0], _F.REPROG_CONTROLS_V4, _feature_reprogrammable_keys),
|
||||||
_S(_DISABLE_KEYS[0], _F.KEYBOARD_DISABLE_KEYS, _feature_disable_keyboard_keys),
|
_S(_DISABLE_KEYS[0], _F.KEYBOARD_DISABLE_KEYS, _feature_disable_keyboard_keys),
|
||||||
|
_S(_PLATFORM[0], _F.MULTIPLATFORM, _feature_multiplatform),
|
||||||
|
_S(_PLATFORM[0], _F.DUALPLATFORM, _feature_dualplatform, identifier='dualplatform')
|
||||||
]
|
]
|
||||||
|
|
||||||
_SETTINGS_LIST = namedtuple('_SETTINGS_LIST', [s[4] for s in _SETTINGS_TABLE])
|
_SETTINGS_LIST = namedtuple('_SETTINGS_LIST', [s[4] for s in _SETTINGS_TABLE])
|
||||||
|
|
|
@ -90,7 +90,7 @@ def _create_choice_control(setting):
|
||||||
c = Gtk.ComboBoxText()
|
c = Gtk.ComboBoxText()
|
||||||
# TODO i18n text entries
|
# TODO i18n text entries
|
||||||
for entry in setting.choices:
|
for entry in setting.choices:
|
||||||
c.append(str(entry), str(entry))
|
c.append(str(int(entry)), str(entry))
|
||||||
c.connect('changed', _combo_notify, setting)
|
c.connect('changed', _combo_notify, setting)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ def _create_map_choice_control(setting):
|
||||||
def _map_populate_value_box(valueBox, setting, key_choice):
|
def _map_populate_value_box(valueBox, setting, key_choice):
|
||||||
choices = None
|
choices = None
|
||||||
choices = setting.choices[key_choice]
|
choices = setting.choices[key_choice]
|
||||||
current = setting._value.get(str(key_choice)) # just in case the persisted value is missing some keys
|
current = setting._value.get(str(key_choice)) if setting._value else None
|
||||||
if choices:
|
if choices:
|
||||||
# TODO i18n text entries
|
# TODO i18n text entries
|
||||||
for choice in choices:
|
for choice in choices:
|
||||||
|
|
Loading…
Reference in New Issue