receiver: add setting for MULTIPLATFORM and DUALPLATFORM feature

This commit is contained in:
Peter F. Patel-Schneider 2020-07-09 06:48:03 -04:00
parent 1dc59fd374
commit c9c472e391
4 changed files with 180 additions and 162 deletions

View File

@ -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: |

View File

@ -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

View File

@ -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])

View File

@ -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: