diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index 6d5c868c..f5450129 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -260,8 +260,8 @@ class ReprogrammableKey: return NamedInt(self._cid, task) @property - def flags(self) -> List[str]: - return list(common.flag_names(KeyFlag, self._flags)) + def flags(self) -> KeyFlag: + return KeyFlag(self._flags) class ReprogrammableKeyV4(ReprogrammableKey): @@ -321,10 +321,10 @@ class ReprogrammableKeyV4(ReprogrammableKey): return ret @property - def mapping_flags(self) -> List[str]: + def mapping_flags(self) -> MappingFlag: if self._mapping_flags is None: self._getCidReporting() - return list(common.flag_names(MappingFlag, self._mapping_flags)) + return MappingFlag(self._mapping_flags) def set_diverted(self, value: bool): """If set, the control is diverted temporarily and reports presses as HID++ events.""" @@ -407,7 +407,7 @@ class ReprogrammableKeyV4(ReprogrammableKey): bfield = 0 for f, v in flags.items(): - key_flag = FLAG_TO_CAPABILITY[f].name.lower() + key_flag = FLAG_TO_CAPABILITY[f] if v and key_flag not in self.flags: raise exceptions.FeatureNotSupported( msg=f'Tried to set mapping flag "{f}" on control "{self.key}" ' diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index c67c9473..0800b435 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -38,6 +38,8 @@ from . import settings from . import settings_validator from . import special_keys from .hidpp10_constants import Registers +from .hidpp20 import KeyFlag +from .hidpp20 import MappingFlag from .hidpp20_constants import GestureId from .hidpp20_constants import ParamId @@ -898,7 +900,7 @@ class DivertKeys(settings.Settings): def read(self, device, key): key_index = device.keys.index(key) key_struct = device.keys[key_index] - return b"\x00\x00\x01" if "diverted" in key_struct.mapping_flags else b"\x00\x00\x00" + return b"\x00\x00\x01" if MappingFlag.DIVERTED in key_struct.mapping_flags else b"\x00\x00\x00" def write(self, device, key, data_bytes): key_index = device.keys.index(key) @@ -926,20 +928,20 @@ class DivertKeys(settings.Settings): sliding = gestures = None choices = {} if device.keys: - for k in device.keys: - if "divertable" in k.flags and "virtual" not in k.flags: - if "raw XY" in k.flags: - choices[k.key] = setting_class.choices_gesture + for key in device.keys: + if KeyFlag.DIVERTABLE in key.flags and KeyFlag.VIRTUAL not in key.flags: + if KeyFlag.RAW_XY in key.flags: + choices[key.key] = setting_class.choices_gesture if gestures is None: gestures = MouseGesturesXY(device, name="MouseGestures") if _F.ADJUSTABLE_DPI in device.features: - choices[k.key] = setting_class.choices_universe + choices[key.key] = setting_class.choices_universe if sliding is None: sliding = DpiSlidingXY( device, name="DpiSliding", show_notification=desktop_notifications.show ) else: - choices[k.key] = setting_class.choices_divert + choices[key.key] = setting_class.choices_divert if not choices: return None validator = cls(choices, key_byte_count=2, byte_count=1, mask=0x01) @@ -1111,7 +1113,7 @@ class SpeedChange(settings.Setting): def build(cls, setting_class, device): key_index = device.keys.index(special_keys.CONTROL.DPI_Change) key = device.keys[key_index] if key_index is not None else None - if key is not None and "divertable" in key.flags: + if key is not None and KeyFlag.DIVERTABLE in key.flags: keys = [setting_class.choices_extra, key.key] return cls(choices=common.NamedInts.list(keys), byte_count=2) diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index 99a91413..e26a617f 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -277,7 +277,6 @@ def _print_device(dev, num=None): print(" %2d: %-26s, default: %-27s => %-26s" % (k.index, k.key, k.default_task, k.mapped_to)) gmask_fmt = ",".join(k.group_mask) gmask_fmt = gmask_fmt if gmask_fmt else "empty" - print(f" {', '.join(k.flags)}, pos:{int(k.pos)}, group:{int(k.group):1}, group mask:{gmask_fmt}") flag_names = list(common.flag_names(hidpp20.KeyFlag, k.flags.value)) print( f" {', '.join(flag_names)}, pos:{int(k.pos)}, group:{int(k.group):1}, group mask:{gmask_fmt}" diff --git a/tests/logitech_receiver/test_hidpp20_complex.py b/tests/logitech_receiver/test_hidpp20_complex.py index f8e9e340..c0f70467 100644 --- a/tests/logitech_receiver/test_hidpp20_complex.py +++ b/tests/logitech_receiver/test_hidpp20_complex.py @@ -22,6 +22,7 @@ from logitech_receiver import exceptions from logitech_receiver import hidpp20 from logitech_receiver import hidpp20_constants from logitech_receiver import special_keys +from logitech_receiver.hidpp20 import KeyFlag from logitech_receiver.hidpp20_constants import GestureId from . import fake_hidpp @@ -154,13 +155,13 @@ def test_FeaturesArray_getitem(device, expected0, expected1, expected2, expected @pytest.mark.parametrize( - "device, index, cid, task_id, flags, default_task, flag_names", + "device, index, cid, task_id, flags, default_task, expected_flags", [ - (device_standard, 2, 1, 1, 0x30, "Volume Up", ["reprogrammable", "divertable"]), - (device_standard, 1, 2, 2, 0x20, "Volume Down", ["divertable"]), + (device_standard, 2, 1, 1, 0x30, "Volume Up", KeyFlag.REPROGRAMMABLE | KeyFlag.DIVERTABLE), + (device_standard, 1, 2, 2, 0x20, "Volume Down", KeyFlag.DIVERTABLE), ], ) -def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task, flag_names): +def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task, expected_flags): key = hidpp20.ReprogrammableKey(device, index, cid, task_id, flags) assert key._device == device @@ -170,14 +171,38 @@ def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task assert key._flags == flags assert key.key == special_keys.CONTROL[cid] assert key.default_task == common.NamedInt(cid, default_task) - assert sorted(list(key.flags)) == sorted(flag_names) + assert key.flags == expected_flags @pytest.mark.parametrize( - "device, index, cid, task_id, flags, pos, group, gmask, default_task, flag_names, group_names", + "device, index, cid, task_id, flags, pos, group, gmask, default_task, expected_flags, group_names", [ - (device_standard, 1, 0x51, 0x39, 0x60, 0, 1, 1, "Right Click", ["divertable", "persistently divertable"], ["g1"]), - (device_standard, 2, 0x52, 0x3A, 0x11, 1, 2, 3, "Mouse Middle Button", ["mse", "reprogrammable"], ["g1", "g2"]), + ( + device_standard, + 1, + 0x51, + 0x39, + 0x60, + 0, + 1, + 1, + "Right Click", + KeyFlag.DIVERTABLE | KeyFlag.PERSISTENTLY_DIVERTABLE, + ["g1"], + ), + ( + device_standard, + 2, + 0x52, + 0x3A, + 0x11, + 1, + 2, + 3, + "Mouse Middle Button", + KeyFlag.MSE | KeyFlag.REPROGRAMMABLE, + ["g1", "g2"], + ), ( device_standard, 3, @@ -188,13 +213,13 @@ def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task 2, 7, "Mouse Back Button", - ["reprogrammable", "raw_xy"], + KeyFlag.REPROGRAMMABLE | KeyFlag.RAW_XY, ["g1", "g2", "g3"], ), ], ) def test_reprogrammable_key_v4_key( - device, index, cid, task_id, flags, pos, group, gmask, default_task, flag_names, group_names + device, index, cid, task_id, flags, pos, group, gmask, default_task, expected_flags, group_names ): key = hidpp20.ReprogrammableKeyV4(device, index, cid, task_id, flags, pos, group, gmask) @@ -208,21 +233,21 @@ def test_reprogrammable_key_v4_key( assert key._gmask == gmask assert key.key == special_keys.CONTROL[cid] assert key.default_task == common.NamedInt(cid, default_task) - assert sorted(list(key.flags)) == sorted(flag_names) + assert key.flags == expected_flags assert list(key.group_mask) == group_names @pytest.mark.parametrize( - "responses, index, mapped_to, remappable_to, mapping_flags", + "responses, index, mapped_to, remappable_to, expected_mapping_flags", [ - (fake_hidpp.responses_key, 1, "Right Click", common.UnsortedNamedInts(Right_Click=81, Left_Click=80), []), - (fake_hidpp.responses_key, 2, "Left Click", None, ["diverted"]), - (fake_hidpp.responses_key, 3, "Mouse Back Button", None, ["diverted", "persistently diverted"]), - (fake_hidpp.responses_key, 4, "Mouse Forward Button", None, ["diverted", "raw XY diverted"]), + (fake_hidpp.responses_key, 1, "Right Click", common.UnsortedNamedInts(Right_Click=81, Left_Click=80), MappingFlag(0)), + (fake_hidpp.responses_key, 2, "Left Click", None, MappingFlag.DIVERTED), + (fake_hidpp.responses_key, 3, "Mouse Back Button", None, MappingFlag.DIVERTED | MappingFlag.PERSISTENTLY_DIVERTED), + (fake_hidpp.responses_key, 4, "Mouse Forward Button", None, MappingFlag.DIVERTED | MappingFlag.RAW_XY_DIVERTED), ], ) # these fields need access all the key data, so start by setting up a device and its key data -def test_reprogrammable_key_v4_query(responses, index, mapped_to, remappable_to, mapping_flags): +def test_reprogrammable_key_v4_query(responses, index, mapped_to, remappable_to, expected_mapping_flags): device = fake_hidpp.Device( "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 ) @@ -232,7 +257,7 @@ def test_reprogrammable_key_v4_query(responses, index, mapped_to, remappable_to, assert key.mapped_to == mapped_to assert (key.remappable_to == remappable_to) or remappable_to is None - assert list(key.mapping_flags) == mapping_flags + assert key.mapping_flags == expected_mapping_flags @pytest.mark.parametrize( @@ -244,7 +269,7 @@ def test_reprogrammable_key_v4_query(responses, index, mapped_to, remappable_to, (fake_hidpp.responses_key, 4, False, False, False, 0x50, ["0056020000", "0056080000", "0056200000", "0056000050"]), ], ) -def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_diverted, rawXY_reporting, remap, sets, mocker): +def test_reprogrammable_key_v4_set(responses, index, diverted, persistently_diverted, rawXY_reporting, remap, sets, mocker): responses += [fake_hidpp.Response(r, 0x530, r) for r in sets] device = fake_hidpp.Device( "KEY", responses=responses, feature=hidpp20_constants.SupportedFeature.REPROG_CONTROLS_V4, offset=5 @@ -254,21 +279,21 @@ def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_divert spy_request = mocker.spy(device, "request") key = device.keys[index] - _mapping_flags = list(key.mapping_flags) + _mapping_flags = key.mapping_flags if hidpp20.KeyFlag.DIVERTABLE in key.flags or not diverted: key.set_diverted(diverted) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_diverted(diverted) - assert ("diverted" in list(key.mapping_flags)) == (diverted and hidpp20.KeyFlag.DIVERTABLE in key.flags) + assert (MappingFlag.DIVERTED in key.mapping_flags) == (diverted and hidpp20.KeyFlag.DIVERTABLE in key.flags) if hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.flags or not persistently_diverted: key.set_persistently_diverted(persistently_diverted) else: with pytest.raises(exceptions.FeatureNotSupported): key.set_persistently_diverted(persistently_diverted) - assert (hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.mapping_flags) == ( + assert (hidpp20.MappingFlag.PERSISTENTLY_DIVERTED in key.mapping_flags) == ( persistently_diverted and hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.flags ) @@ -277,7 +302,7 @@ def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_divert else: with pytest.raises(exceptions.FeatureNotSupported): key.set_rawXY_reporting(rawXY_reporting) - assert ("raw XY diverted" in list(key.mapping_flags)) == (rawXY_reporting and hidpp20.KeyFlag.RAW_XY in key.flags) + assert (MappingFlag.RAW_XY_DIVERTED in key.mapping_flags) == (rawXY_reporting and hidpp20.KeyFlag.RAW_XY in key.flags) if remap in key.remappable_to or remap == 0: key.remap(remap)