parent
0bf7a78553
commit
f54eeb7998
|
@ -21,6 +21,7 @@ import socket
|
|||
import struct
|
||||
import threading
|
||||
|
||||
from enum import Flag
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
|
@ -42,7 +43,6 @@ from .common import BatteryLevelApproximation
|
|||
from .common import BatteryStatus
|
||||
from .common import FirmwareKind
|
||||
from .common import NamedInt
|
||||
from .common import NamedInts
|
||||
from .hidpp20_constants import CHARGE_STATUS
|
||||
from .hidpp20_constants import DEVICE_KIND
|
||||
from .hidpp20_constants import ChargeLevel
|
||||
|
@ -80,22 +80,27 @@ class Device(Protocol):
|
|||
...
|
||||
|
||||
|
||||
# Capabilities and desired software handling for a control
|
||||
# Ref: https://drive.google.com/file/d/10imcbmoxTJ1N510poGdsviEhoFfB_Ua4/view
|
||||
# We treat bytes 4 and 8 of `getCidInfo` as a single bitfield
|
||||
KEY_FLAG = NamedInts(
|
||||
analytics_key_events=0x400,
|
||||
force_raw_XY=0x200,
|
||||
raw_XY=0x100,
|
||||
virtual=0x80,
|
||||
persistently_divertable=0x40,
|
||||
divertable=0x20,
|
||||
reprogrammable=0x10,
|
||||
FN_sensitive=0x08,
|
||||
nonstandard=0x04,
|
||||
is_FN=0x02,
|
||||
mse=0x01,
|
||||
)
|
||||
class KeyFlag(Flag):
|
||||
"""Capabilities and desired software handling for a control.
|
||||
|
||||
Ref: https://drive.google.com/file/d/10imcbmoxTJ1N510poGdsviEhoFfB_Ua4/view
|
||||
We treat bytes 4 and 8 of `getCidInfo` as a single bitfield
|
||||
"""
|
||||
|
||||
ANALYTICS_KEY_EVENTS = 0x400
|
||||
FORCE_RAW_XY = 0x200
|
||||
RAW_XY = 0x100
|
||||
VIRTUAL = 0x80
|
||||
PERSISTENTLY_DIVERTABLE = 0x40
|
||||
DIVERTABLE = 0x20
|
||||
REPROGRAMMABLE = 0x10
|
||||
FN_SENSITIVE = 0x08
|
||||
NONSTANDARD = 0x04
|
||||
IS_FN = 0x02
|
||||
MSE = 0x01
|
||||
|
||||
def __str__(self):
|
||||
return self.name.replace("_", " ")
|
||||
|
||||
|
||||
class FeaturesArray(dict):
|
||||
|
@ -211,7 +216,7 @@ class ReprogrammableKey:
|
|||
- flags {List[str]} -- capabilities and desired software handling of the control
|
||||
"""
|
||||
|
||||
def __init__(self, device: Device, index, cid, task_id, flags):
|
||||
def __init__(self, device: Device, index: int, cid: int, task_id: int, flags):
|
||||
self._device = device
|
||||
self.index = index
|
||||
self._cid = cid
|
||||
|
@ -236,7 +241,7 @@ class ReprogrammableKey:
|
|||
|
||||
@property
|
||||
def flags(self) -> List[str]:
|
||||
return KEY_FLAG.flag_names(self._flags)
|
||||
return list(common.flag_names(KeyFlag, self._flags))
|
||||
|
||||
|
||||
class ReprogrammableKeyV4(ReprogrammableKey):
|
||||
|
@ -373,19 +378,20 @@ class ReprogrammableKeyV4(ReprogrammableKey):
|
|||
|
||||
# The capability required to set a given reporting flag.
|
||||
FLAG_TO_CAPABILITY = {
|
||||
special_keys.MAPPING_FLAG.diverted: KEY_FLAG.divertable,
|
||||
special_keys.MAPPING_FLAG.persistently_diverted: KEY_FLAG.persistently_divertable,
|
||||
special_keys.MAPPING_FLAG.analytics_key_events_reporting: KEY_FLAG.analytics_key_events,
|
||||
special_keys.MAPPING_FLAG.force_raw_XY_diverted: KEY_FLAG.force_raw_XY,
|
||||
special_keys.MAPPING_FLAG.raw_XY_diverted: KEY_FLAG.raw_XY,
|
||||
special_keys.MAPPING_FLAG.diverted: KeyFlag.DIVERTABLE,
|
||||
special_keys.MAPPING_FLAG.persistently_diverted: KeyFlag.PERSISTENTLY_DIVERTABLE,
|
||||
special_keys.MAPPING_FLAG.analytics_key_events_reporting: KeyFlag.ANALYTICS_KEY_EVENTS,
|
||||
special_keys.MAPPING_FLAG.force_raw_XY_diverted: KeyFlag.FORCE_RAW_XY,
|
||||
special_keys.MAPPING_FLAG.raw_XY_diverted: KeyFlag.RAW_XY,
|
||||
}
|
||||
|
||||
bfield = 0
|
||||
for f, v in flags.items():
|
||||
if v and FLAG_TO_CAPABILITY[f] not in self.flags:
|
||||
key_flag = FLAG_TO_CAPABILITY[f].name.lower()
|
||||
if v and key_flag not in self.flags:
|
||||
raise exceptions.FeatureNotSupported(
|
||||
msg=f'Tried to set mapping flag "{f}" on control "{self.key}" '
|
||||
+ f'which does not support "{FLAG_TO_CAPABILITY[f]}" on device {self._device}.'
|
||||
+ f'which does not support "{key_flag}" on device {self._device}.'
|
||||
)
|
||||
bfield |= int(f) if v else 0
|
||||
bfield |= int(f) << 1 # The 'Xvalid' bit
|
||||
|
|
|
@ -170,7 +170,7 @@ 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 list(key.flags) == flag_names
|
||||
assert sorted(list(key.flags)) == sorted(flag_names)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -188,7 +188,7 @@ def test_reprogrammable_key_key(device, index, cid, task_id, flags, default_task
|
|||
2,
|
||||
7,
|
||||
"Mouse Back Button",
|
||||
["reprogrammable", "raw XY"],
|
||||
["reprogrammable", "raw_xy"],
|
||||
["g1", "g2", "g3"],
|
||||
),
|
||||
],
|
||||
|
@ -208,7 +208,7 @@ 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 list(key.flags) == flag_names
|
||||
assert sorted(list(key.flags)) == sorted(flag_names)
|
||||
assert list(key.group_mask) == group_names
|
||||
|
||||
|
||||
|
@ -256,26 +256,28 @@ def test_ReprogrammableKeyV4_set(responses, index, diverted, persistently_divert
|
|||
key = device.keys[index]
|
||||
_mapping_flags = list(key.mapping_flags)
|
||||
|
||||
if "divertable" in key.flags or not diverted:
|
||||
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 "divertable" in key.flags)
|
||||
assert ("diverted" in list(key.mapping_flags)) == (diverted and hidpp20.KeyFlag.DIVERTABLE in key.flags)
|
||||
|
||||
if "persistently divertable" in key.flags or not persistently_diverted:
|
||||
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 ("persistently diverted" in key.mapping_flags) == (persistently_diverted and "persistently divertable" in key.flags)
|
||||
assert (hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.mapping_flags) == (
|
||||
persistently_diverted and hidpp20.KeyFlag.PERSISTENTLY_DIVERTABLE in key.flags
|
||||
)
|
||||
|
||||
if "raw XY" in key.flags or not rawXY_reporting:
|
||||
if hidpp20.KeyFlag.RAW_XY in key.flags or not rawXY_reporting:
|
||||
key.set_rawXY_reporting(rawXY_reporting)
|
||||
else:
|
||||
with pytest.raises(exceptions.FeatureNotSupported):
|
||||
key.set_rawXY_reporting(rawXY_reporting)
|
||||
assert ("raw XY diverted" in list(key.mapping_flags)) == (rawXY_reporting and "raw XY" in key.flags)
|
||||
assert ("raw XY diverted" in list(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)
|
||||
|
|
Loading…
Reference in New Issue