From db8741563b1603062fd6064de7ec8ad2ce6e7a0d Mon Sep 17 00:00:00 2001
From: Matthias Hagmann <16444067+MattHag@users.noreply.github.com>
Date: Wed, 14 Feb 2024 20:41:49 +0100
Subject: [PATCH] logitech_receiver: Move hidpp20 constants into new module
Related #1097
---
lib/logitech_receiver/hidpp10.py | 2 +-
lib/logitech_receiver/hidpp20.py | 247 +------------------
lib/logitech_receiver/hidpp20_constants.py | 248 ++++++++++++++++++++
lib/logitech_receiver/notifications.py | 7 +-
lib/logitech_receiver/settings.py | 10 +-
lib/logitech_receiver/settings_templates.py | 19 +-
lib/logitech_receiver/status.py | 4 +-
lib/solaar/cli/show.py | 39 +--
8 files changed, 296 insertions(+), 280 deletions(-)
create mode 100644 lib/logitech_receiver/hidpp20_constants.py
diff --git a/lib/logitech_receiver/hidpp10.py b/lib/logitech_receiver/hidpp10.py
index 2a6873d1e3..55484e8e78 100644
--- a/lib/logitech_receiver/hidpp10.py
+++ b/lib/logitech_receiver/hidpp10.py
@@ -23,8 +23,8 @@
from .common import bytes2int as _bytes2int
from .common import int2bytes as _int2bytes
from .common import strhex as _strhex
-from .hidpp20 import BATTERY_STATUS, FIRMWARE_KIND
from .hidpp10_constants import REGISTERS
+from .hidpp20_constants import BATTERY_STATUS, FIRMWARE_KIND
logger = logging.getLogger(__name__)
diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py
index 0d4b0c37a8..bd12ef18a5 100644
--- a/lib/logitech_receiver/hidpp20.py
+++ b/lib/logitech_receiver/hidpp20.py
@@ -36,6 +36,8 @@
from .common import bytes2int as _bytes2int
from .common import crc16 as _crc16
from .common import int2bytes as _int2bytes
+from .hidpp20_constants import (BATTERY_STATUS, CHARGE_LEVEL, CHARGE_STATUS, CHARGE_TYPE, DEVICE_KIND, ERROR, FEATURE,
+ FIRMWARE_KIND, GESTURE)
from .i18n import _
logger = logging.getLogger(__name__)
@@ -51,180 +53,6 @@ def hexint_presenter(dumper, data):
#
#
-# /,/p}' | sort -t= -k2
-# additional features names taken from https://github.com/cvuchener/hidpp and
-# https://github.com/Logitech/cpg-docs/tree/master/hidpp20
-"""Possible features available on a Logitech device.
-
-A particular device might not support all these features, and may support other
-unknown features as well.
-"""
-FEATURE = _NamedInts(
- ROOT=0x0000,
- FEATURE_SET=0x0001,
- FEATURE_INFO=0x0002,
- # Common
- DEVICE_FW_VERSION=0x0003,
- DEVICE_UNIT_ID=0x0004,
- DEVICE_NAME=0x0005,
- DEVICE_GROUPS=0x0006,
- DEVICE_FRIENDLY_NAME=0x0007,
- KEEP_ALIVE=0x0008,
- CONFIG_CHANGE=0x0020,
- CRYPTO_ID=0x0021,
- TARGET_SOFTWARE=0x0030,
- WIRELESS_SIGNAL_STRENGTH=0x0080,
- DFUCONTROL_LEGACY=0x00C0,
- DFUCONTROL_UNSIGNED=0x00C1,
- DFUCONTROL_SIGNED=0x00C2,
- DFUCONTROL=0x00C3,
- DFU=0x00D0,
- BATTERY_STATUS=0x1000,
- BATTERY_VOLTAGE=0x1001,
- UNIFIED_BATTERY=0x1004,
- CHARGING_CONTROL=0x1010,
- LED_CONTROL=0x1300,
- FORCE_PAIRING=0x1500,
- GENERIC_TEST=0x1800,
- DEVICE_RESET=0x1802,
- OOBSTATE=0x1805,
- CONFIG_DEVICE_PROPS=0x1806,
- CHANGE_HOST=0x1814,
- HOSTS_INFO=0x1815,
- BACKLIGHT=0x1981,
- BACKLIGHT2=0x1982,
- BACKLIGHT3=0x1983,
- ILLUMINATION=0x1990,
- PRESENTER_CONTROL=0x1A00,
- SENSOR_3D=0x1A01,
- REPROG_CONTROLS=0x1B00,
- REPROG_CONTROLS_V2=0x1B01,
- REPROG_CONTROLS_V2_2=0x1B02, # LogiOptions 2.10.73 features.xml
- REPROG_CONTROLS_V3=0x1B03,
- REPROG_CONTROLS_V4=0x1B04,
- REPORT_HID_USAGE=0x1BC0,
- PERSISTENT_REMAPPABLE_ACTION=0x1C00,
- WIRELESS_DEVICE_STATUS=0x1D4B,
- REMAINING_PAIRING=0x1DF0,
- FIRMWARE_PROPERTIES=0x1F1F,
- ADC_MEASUREMENT=0x1F20,
- # Mouse
- LEFT_RIGHT_SWAP=0x2001,
- SWAP_BUTTON_CANCEL=0x2005,
- POINTER_AXIS_ORIENTATION=0x2006,
- VERTICAL_SCROLLING=0x2100,
- SMART_SHIFT=0x2110,
- SMART_SHIFT_ENHANCED=0x2111,
- HI_RES_SCROLLING=0x2120,
- HIRES_WHEEL=0x2121,
- LOWRES_WHEEL=0x2130,
- THUMB_WHEEL=0x2150,
- MOUSE_POINTER=0x2200,
- ADJUSTABLE_DPI=0x2201,
- EXTENDED_ADJUSTABLE_DPI=0x2202,
- POINTER_SPEED=0x2205,
- ANGLE_SNAPPING=0x2230,
- SURFACE_TUNING=0x2240,
- XY_STATS=0x2250,
- WHEEL_STATS=0x2251,
- HYBRID_TRACKING=0x2400,
- # Keyboard
- FN_INVERSION=0x40A0,
- NEW_FN_INVERSION=0x40A2,
- K375S_FN_INVERSION=0x40A3,
- ENCRYPTION=0x4100,
- LOCK_KEY_STATE=0x4220,
- SOLAR_DASHBOARD=0x4301,
- KEYBOARD_LAYOUT=0x4520,
- KEYBOARD_DISABLE_KEYS=0x4521,
- KEYBOARD_DISABLE_BY_USAGE=0x4522,
- DUALPLATFORM=0x4530,
- MULTIPLATFORM=0x4531,
- KEYBOARD_LAYOUT_2=0x4540,
- CROWN=0x4600,
- # Touchpad
- TOUCHPAD_FW_ITEMS=0x6010,
- TOUCHPAD_SW_ITEMS=0x6011,
- TOUCHPAD_WIN8_FW_ITEMS=0x6012,
- TAP_ENABLE=0x6020,
- TAP_ENABLE_EXTENDED=0x6021,
- CURSOR_BALLISTIC=0x6030,
- TOUCHPAD_RESOLUTION=0x6040,
- TOUCHPAD_RAW_XY=0x6100,
- TOUCHMOUSE_RAW_POINTS=0x6110,
- TOUCHMOUSE_6120=0x6120,
- GESTURE=0x6500,
- GESTURE_2=0x6501,
- # Gaming Devices
- GKEY=0x8010,
- MKEYS=0x8020,
- MR=0x8030,
- BRIGHTNESS_CONTROL=0x8040,
- REPORT_RATE=0x8060,
- EXTENDED_ADJUSTABLE_REPORT_RATE=0x8061,
- COLOR_LED_EFFECTS=0x8070,
- RGB_EFFECTS=0x8071,
- PER_KEY_LIGHTING=0x8080,
- PER_KEY_LIGHTING_V2=0x8081,
- MODE_STATUS=0x8090,
- ONBOARD_PROFILES=0x8100,
- MOUSE_BUTTON_SPY=0x8110,
- LATENCY_MONITORING=0x8111,
- GAMING_ATTACHMENTS=0x8120,
- FORCE_FEEDBACK=0x8123,
- # Headsets
- SIDETONE=0x8300,
- EQUALIZER=0x8310,
- HEADSET_OUT=0x8320,
- # Fake features for Solaar internal use
- MOUSE_GESTURE=0xFE00,
-)
-FEATURE._fallback = lambda x: 'unknown:%04X' % x
-
-FEATURE_FLAG = _NamedInts(internal=0x20, hidden=0x40, obsolete=0x80)
-
-DEVICE_KIND = _NamedInts(
- keyboard=0x00, remote_control=0x01, numpad=0x02, mouse=0x03, touchpad=0x04, trackball=0x05, presenter=0x06, receiver=0x07
-)
-
-FIRMWARE_KIND = _NamedInts(Firmware=0x00, Bootloader=0x01, Hardware=0x02, Other=0x03)
-
-BATTERY_OK = lambda status: status not in (BATTERY_STATUS.invalid_battery, BATTERY_STATUS.thermal_error)
-
-BATTERY_STATUS = _NamedInts(
- discharging=0x00,
- recharging=0x01,
- almost_full=0x02,
- full=0x03,
- slow_recharge=0x04,
- invalid_battery=0x05,
- thermal_error=0x06
-)
-
-ONBOARD_MODES = _NamedInts(MODE_NO_CHANGE=0x00, MODE_ONBOARD=0x01, MODE_HOST=0x02)
-
-CHARGE_STATUS = _NamedInts(charging=0x00, full=0x01, not_charging=0x02, error=0x07)
-
-CHARGE_LEVEL = _NamedInts(average=50, full=90, critical=5)
-
-CHARGE_TYPE = _NamedInts(standard=0x00, fast=0x01, slow=0x02)
-
-ERROR = _NamedInts(
- unknown=0x01,
- invalid_argument=0x02,
- out_of_range=0x03,
- hardware_error=0x04,
- logitech_internal=0x05,
- invalid_feature_index=0x06,
- invalid_function=0x07,
- busy=0x08,
- unsupported=0x09
-)
-
-#
-#
-#
-
class FeaturesArray(dict):
@@ -745,71 +573,6 @@ def _query_key(self, index: int):
logger.warn(f"Key with index {index} was expected to exist but device doesn't report it.")
-# Gesture Ids for feature GESTURE_2
-GESTURE = _NamedInts(
- Tap1Finger=1, # task Left_Click
- Tap2Finger=2, # task Right_Click
- Tap3Finger=3,
- Click1Finger=4, # task Left_Click
- Click2Finger=5, # task Right_Click
- Click3Finger=6,
- DoubleTap1Finger=10,
- DoubleTap2Finger=11,
- DoubleTap3Finger=12,
- Track1Finger=20, # action MovePointer
- TrackingAcceleration=21,
- TapDrag1Finger=30, # action Drag
- TapDrag2Finger=31, # action SecondaryDrag
- Drag3Finger=32,
- TapGestures=33, # group all tap gestures under a single UI setting
- FnClickGestureSuppression=34, # suppresses Tap and Edge gestures, toggled by Fn+Click
- Scroll1Finger=40, # action ScrollOrPageXY / ScrollHorizontal
- Scroll2Finger=41, # action ScrollOrPageXY / ScrollHorizontal
- Scroll2FingerHoriz=42, # action ScrollHorizontal
- Scroll2FingerVert=43, # action WheelScrolling
- Scroll2FingerStateless=44,
- NaturalScrolling=45, # affects native HID wheel reporting by gestures, not when diverted
- Thumbwheel=46, # action WheelScrolling
- VScrollInertia=48,
- VScrollBallistics=49,
- Swipe2FingerHoriz=50, # action PageScreen
- Swipe3FingerHoriz=51, # action PageScreen
- Swipe4FingerHoriz=52, # action PageScreen
- Swipe3FingerVert=53,
- Swipe4FingerVert=54,
- LeftEdgeSwipe1Finger=60,
- RightEdgeSwipe1Finger=61,
- BottomEdgeSwipe1Finger=62,
- TopEdgeSwipe1Finger=63,
- LeftEdgeSwipe1Finger2=64, # task HorzScrollNoRepeatSet
- RightEdgeSwipe1Finger2=65, # task 122 ??
- BottomEdgeSwipe1Finger2=66, #
- TopEdgeSwipe1Finger2=67, # task 121 ??
- LeftEdgeSwipe2Finger=70,
- RightEdgeSwipe2Finger=71,
- BottomEdgeSwipe2Finger=72,
- TopEdgeSwipe2Finger=73,
- Zoom2Finger=80, # action Zoom
- Zoom2FingerPinch=81, # ZoomBtnInSet
- Zoom2FingerSpread=82, # ZoomBtnOutSet
- Zoom3Finger=83,
- Zoom2FingerStateless=84, # action Zoom
- TwoFingersPresent=85,
- Rotate2Finger=87,
- Finger1=90,
- Finger2=91,
- Finger3=92,
- Finger4=93,
- Finger5=94,
- Finger6=95,
- Finger7=96,
- Finger8=97,
- Finger9=98,
- Finger10=99,
- DeviceSpecificRawData=100,
-)
-GESTURE._fallback = lambda x: 'unknown:%04X' % x
-
# Param Ids for feature GESTURE_2
PARAM = _NamedInts(
ExtraCapabilities=1, # not suitable for use
@@ -911,9 +674,11 @@ def _offset_mask(self, index): # offset and mask
else:
return (None, None)
- enable_offset_mask = lambda gesture: gesture._offset_mask(gesture.index)
+ def enable_offset_mask(gesture):
+ return gesture._offset_mask(gesture.index)
- diversion_offset_mask = lambda gesture: gesture._offset_mask(gesture.diversion_index)
+ def diversion_offset_mask(gesture):
+ return gesture._offset_mask(gesture.diversion_index)
def enabled(self): # is the gesture enabled?
if self._enabled is None and self.index is not None:
diff --git a/lib/logitech_receiver/hidpp20_constants.py b/lib/logitech_receiver/hidpp20_constants.py
new file mode 100644
index 0000000000..72cc5e272b
--- /dev/null
+++ b/lib/logitech_receiver/hidpp20_constants.py
@@ -0,0 +1,248 @@
+from .common import NamedInts
+
+# /,/p}' | sort -t= -k2
+# additional features names taken from https://github.com/cvuchener/hidpp and
+# https://github.com/Logitech/cpg-docs/tree/master/hidpp20
+"""Possible features available on a Logitech device.
+
+A particular device might not support all these features, and may support other
+unknown features as well.
+"""
+FEATURE = NamedInts(
+ ROOT=0x0000,
+ FEATURE_SET=0x0001,
+ FEATURE_INFO=0x0002,
+ # Common
+ DEVICE_FW_VERSION=0x0003,
+ DEVICE_UNIT_ID=0x0004,
+ DEVICE_NAME=0x0005,
+ DEVICE_GROUPS=0x0006,
+ DEVICE_FRIENDLY_NAME=0x0007,
+ KEEP_ALIVE=0x0008,
+ CONFIG_CHANGE=0x0020,
+ CRYPTO_ID=0x0021,
+ TARGET_SOFTWARE=0x0030,
+ WIRELESS_SIGNAL_STRENGTH=0x0080,
+ DFUCONTROL_LEGACY=0x00C0,
+ DFUCONTROL_UNSIGNED=0x00C1,
+ DFUCONTROL_SIGNED=0x00C2,
+ DFUCONTROL=0x00C3,
+ DFU=0x00D0,
+ BATTERY_STATUS=0x1000,
+ BATTERY_VOLTAGE=0x1001,
+ UNIFIED_BATTERY=0x1004,
+ CHARGING_CONTROL=0x1010,
+ LED_CONTROL=0x1300,
+ FORCE_PAIRING=0x1500,
+ GENERIC_TEST=0x1800,
+ DEVICE_RESET=0x1802,
+ OOBSTATE=0x1805,
+ CONFIG_DEVICE_PROPS=0x1806,
+ CHANGE_HOST=0x1814,
+ HOSTS_INFO=0x1815,
+ BACKLIGHT=0x1981,
+ BACKLIGHT2=0x1982,
+ BACKLIGHT3=0x1983,
+ ILLUMINATION=0x1990,
+ PRESENTER_CONTROL=0x1A00,
+ SENSOR_3D=0x1A01,
+ REPROG_CONTROLS=0x1B00,
+ REPROG_CONTROLS_V2=0x1B01,
+ REPROG_CONTROLS_V2_2=0x1B02, # LogiOptions 2.10.73 features.xml
+ REPROG_CONTROLS_V3=0x1B03,
+ REPROG_CONTROLS_V4=0x1B04,
+ REPORT_HID_USAGE=0x1BC0,
+ PERSISTENT_REMAPPABLE_ACTION=0x1C00,
+ WIRELESS_DEVICE_STATUS=0x1D4B,
+ REMAINING_PAIRING=0x1DF0,
+ FIRMWARE_PROPERTIES=0x1F1F,
+ ADC_MEASUREMENT=0x1F20,
+ # Mouse
+ LEFT_RIGHT_SWAP=0x2001,
+ SWAP_BUTTON_CANCEL=0x2005,
+ POINTER_AXIS_ORIENTATION=0x2006,
+ VERTICAL_SCROLLING=0x2100,
+ SMART_SHIFT=0x2110,
+ SMART_SHIFT_ENHANCED=0x2111,
+ HI_RES_SCROLLING=0x2120,
+ HIRES_WHEEL=0x2121,
+ LOWRES_WHEEL=0x2130,
+ THUMB_WHEEL=0x2150,
+ MOUSE_POINTER=0x2200,
+ ADJUSTABLE_DPI=0x2201,
+ EXTENDED_ADJUSTABLE_DPI=0x2202,
+ POINTER_SPEED=0x2205,
+ ANGLE_SNAPPING=0x2230,
+ SURFACE_TUNING=0x2240,
+ XY_STATS=0x2250,
+ WHEEL_STATS=0x2251,
+ HYBRID_TRACKING=0x2400,
+ # Keyboard
+ FN_INVERSION=0x40A0,
+ NEW_FN_INVERSION=0x40A2,
+ K375S_FN_INVERSION=0x40A3,
+ ENCRYPTION=0x4100,
+ LOCK_KEY_STATE=0x4220,
+ SOLAR_DASHBOARD=0x4301,
+ KEYBOARD_LAYOUT=0x4520,
+ KEYBOARD_DISABLE_KEYS=0x4521,
+ KEYBOARD_DISABLE_BY_USAGE=0x4522,
+ DUALPLATFORM=0x4530,
+ MULTIPLATFORM=0x4531,
+ KEYBOARD_LAYOUT_2=0x4540,
+ CROWN=0x4600,
+ # Touchpad
+ TOUCHPAD_FW_ITEMS=0x6010,
+ TOUCHPAD_SW_ITEMS=0x6011,
+ TOUCHPAD_WIN8_FW_ITEMS=0x6012,
+ TAP_ENABLE=0x6020,
+ TAP_ENABLE_EXTENDED=0x6021,
+ CURSOR_BALLISTIC=0x6030,
+ TOUCHPAD_RESOLUTION=0x6040,
+ TOUCHPAD_RAW_XY=0x6100,
+ TOUCHMOUSE_RAW_POINTS=0x6110,
+ TOUCHMOUSE_6120=0x6120,
+ GESTURE=0x6500,
+ GESTURE_2=0x6501,
+ # Gaming Devices
+ GKEY=0x8010,
+ MKEYS=0x8020,
+ MR=0x8030,
+ BRIGHTNESS_CONTROL=0x8040,
+ REPORT_RATE=0x8060,
+ EXTENDED_ADJUSTABLE_REPORT_RATE=0x8061,
+ COLOR_LED_EFFECTS=0x8070,
+ RGB_EFFECTS=0x8071,
+ PER_KEY_LIGHTING=0x8080,
+ PER_KEY_LIGHTING_V2=0x8081,
+ MODE_STATUS=0x8090,
+ ONBOARD_PROFILES=0x8100,
+ MOUSE_BUTTON_SPY=0x8110,
+ LATENCY_MONITORING=0x8111,
+ GAMING_ATTACHMENTS=0x8120,
+ FORCE_FEEDBACK=0x8123,
+ # Headsets
+ SIDETONE=0x8300,
+ EQUALIZER=0x8310,
+ HEADSET_OUT=0x8320,
+ # Fake features for Solaar internal use
+ MOUSE_GESTURE=0xFE00,
+)
+FEATURE._fallback = lambda x: 'unknown:%04X' % x
+
+FEATURE_FLAG = NamedInts(internal=0x20, hidden=0x40, obsolete=0x80)
+
+DEVICE_KIND = NamedInts(
+ keyboard=0x00, remote_control=0x01, numpad=0x02, mouse=0x03, touchpad=0x04, trackball=0x05, presenter=0x06, receiver=0x07
+)
+
+FIRMWARE_KIND = NamedInts(Firmware=0x00, Bootloader=0x01, Hardware=0x02, Other=0x03)
+
+
+def BATTERY_OK(status):
+ return status not in (BATTERY_STATUS.invalid_battery, BATTERY_STATUS.thermal_error)
+
+
+BATTERY_STATUS = NamedInts(
+ discharging=0x00,
+ recharging=0x01,
+ almost_full=0x02,
+ full=0x03,
+ slow_recharge=0x04,
+ invalid_battery=0x05,
+ thermal_error=0x06
+)
+
+ONBOARD_MODES = NamedInts(MODE_NO_CHANGE=0x00, MODE_ONBOARD=0x01, MODE_HOST=0x02)
+
+CHARGE_STATUS = NamedInts(charging=0x00, full=0x01, not_charging=0x02, error=0x07)
+
+CHARGE_LEVEL = NamedInts(average=50, full=90, critical=5)
+
+CHARGE_TYPE = NamedInts(standard=0x00, fast=0x01, slow=0x02)
+
+ERROR = NamedInts(
+ unknown=0x01,
+ invalid_argument=0x02,
+ out_of_range=0x03,
+ hardware_error=0x04,
+ logitech_internal=0x05,
+ invalid_feature_index=0x06,
+ invalid_function=0x07,
+ busy=0x08,
+ unsupported=0x09
+)
+
+# Gesture Ids for feature GESTURE_2
+GESTURE = NamedInts(
+ Tap1Finger=1, # task Left_Click
+ Tap2Finger=2, # task Right_Click
+ Tap3Finger=3,
+ Click1Finger=4, # task Left_Click
+ Click2Finger=5, # task Right_Click
+ Click3Finger=6,
+ DoubleTap1Finger=10,
+ DoubleTap2Finger=11,
+ DoubleTap3Finger=12,
+ Track1Finger=20, # action MovePointer
+ TrackingAcceleration=21,
+ TapDrag1Finger=30, # action Drag
+ TapDrag2Finger=31, # action SecondaryDrag
+ Drag3Finger=32,
+ TapGestures=33, # group all tap gestures under a single UI setting
+ FnClickGestureSuppression=34, # suppresses Tap and Edge gestures, toggled by Fn+Click
+ Scroll1Finger=40, # action ScrollOrPageXY / ScrollHorizontal
+ Scroll2Finger=41, # action ScrollOrPageXY / ScrollHorizontal
+ Scroll2FingerHoriz=42, # action ScrollHorizontal
+ Scroll2FingerVert=43, # action WheelScrolling
+ Scroll2FingerStateless=44,
+ NaturalScrolling=45, # affects native HID wheel reporting by gestures, not when diverted
+ Thumbwheel=46, # action WheelScrolling
+ VScrollInertia=48,
+ VScrollBallistics=49,
+ Swipe2FingerHoriz=50, # action PageScreen
+ Swipe3FingerHoriz=51, # action PageScreen
+ Swipe4FingerHoriz=52, # action PageScreen
+ Swipe3FingerVert=53,
+ Swipe4FingerVert=54,
+ LeftEdgeSwipe1Finger=60,
+ RightEdgeSwipe1Finger=61,
+ BottomEdgeSwipe1Finger=62,
+ TopEdgeSwipe1Finger=63,
+ LeftEdgeSwipe1Finger2=64, # task HorzScrollNoRepeatSet
+ RightEdgeSwipe1Finger2=65, # task 122 ??
+ BottomEdgeSwipe1Finger2=66, #
+ TopEdgeSwipe1Finger2=67, # task 121 ??
+ LeftEdgeSwipe2Finger=70,
+ RightEdgeSwipe2Finger=71,
+ BottomEdgeSwipe2Finger=72,
+ TopEdgeSwipe2Finger=73,
+ Zoom2Finger=80, # action Zoom
+ Zoom2FingerPinch=81, # ZoomBtnInSet
+ Zoom2FingerSpread=82, # ZoomBtnOutSet
+ Zoom3Finger=83,
+ Zoom2FingerStateless=84, # action Zoom
+ TwoFingersPresent=85,
+ Rotate2Finger=87,
+ Finger1=90,
+ Finger2=91,
+ Finger3=92,
+ Finger4=93,
+ Finger5=94,
+ Finger6=95,
+ Finger7=96,
+ Finger8=97,
+ Finger9=98,
+ Finger10=99,
+ DeviceSpecificRawData=100,
+)
+GESTURE._fallback = lambda x: 'unknown:%04X' % x
+
+# Param Ids for feature GESTURE_2
+PARAM = NamedInts(
+ ExtraCapabilities=1, # not suitable for use
+ PixelZone=2, # 4 2-byte integers, left, bottom, width, height; pixels
+ RatioZone=3, # 4 bytes, left, bottom, width, height; unit 1/240 pad size
+ ScaleFactor=4, # 2-byte integer, with 256 as normal scale
+)
+PARAM._fallback = lambda x: 'unknown:%04X' % x
diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py
index 4dbb3db375..da94bb11d6 100644
--- a/lib/logitech_receiver/notifications.py
+++ b/lib/logitech_receiver/notifications.py
@@ -25,6 +25,7 @@
from . import diversion as _diversion
from . import hidpp10 as _hidpp10
from . import hidpp20 as _hidpp20
+from . import hidpp20_constants as _hidpp20_constants
from . import settings_templates as _st
from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID
from .common import strhex as _strhex
@@ -35,7 +36,7 @@
logger = logging.getLogger(__name__)
_R = _hidpp10.REGISTERS
-_F = _hidpp20.FEATURE
+_F = _hidpp20_constants.FEATURE
#
#
@@ -351,14 +352,14 @@ def _process_feature_notification(device, status, n, feature):
charge, lux, adc = _unpack('!BHH', n.data[:5])
# guesstimate the battery voltage, emphasis on 'guess'
# status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672)
- status_text = _hidpp20.BATTERY_STATUS.discharging
+ status_text = _hidpp20_constants.BATTERY_STATUS.discharging
if n.address == 0x00:
status[_K.LIGHT_LEVEL] = None
status.set_battery_info(charge, None, status_text, None)
elif n.address == 0x10:
status[_K.LIGHT_LEVEL] = lux
if lux > 200:
- status_text = _hidpp20.BATTERY_STATUS.recharging
+ status_text = _hidpp20_constants.BATTERY_STATUS.recharging
status.set_battery_info(charge, None, status_text, None)
elif n.address == 0x20:
if logger.isEnabledFor(logging.DEBUG):
diff --git a/lib/logitech_receiver/settings.py b/lib/logitech_receiver/settings.py
index 5bc136b389..23f17d7334 100644
--- a/lib/logitech_receiver/settings.py
+++ b/lib/logitech_receiver/settings.py
@@ -22,7 +22,7 @@
from struct import unpack as _unpack
from time import sleep as _sleep
-from . import hidpp20 as _hidpp20
+from . import hidpp20_constants as _hidpp20_constants
from .common import NamedInt as _NamedInt
from .common import NamedInts as _NamedInts
from .common import bytes2int as _bytes2int
@@ -1416,7 +1416,7 @@ def read(self, device): # need to return bytes, as if read from device
def write(self, device, data_bytes):
def handler(device, n): # Called on notification events from the device
- if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == _hidpp20.FEATURE.REPROG_CONTROLS_V4:
+ if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V4:
if n.address == 0x00:
cids = _unpack('!HHHH', n.data[:8])
if not self.pressed and int(self.key.key) in cids: # trigger key pressed
@@ -1478,11 +1478,11 @@ def __init__(self, device, name=''):
self.keys = [] # the keys that can initiate processing
self.initiating_key = None # the key that did initiate processing
self.active = False
- self.feature_offset = device.features[_hidpp20.FEATURE.REPROG_CONTROLS_V4]
+ self.feature_offset = device.features[_hidpp20_constants.FEATURE.REPROG_CONTROLS_V4]
assert self.feature_offset is not False
def handler(self, device, n): # Called on notification events from the device
- if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == _hidpp20.FEATURE.REPROG_CONTROLS_V4:
+ if n.sub_id < 0x40 and device.features.get_feature(n.sub_id) == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V4:
if n.address == 0x00:
cids = _unpack('!HHHH', n.data[:8])
## generalize to list of keys
@@ -1550,7 +1550,7 @@ def key_action(self, key): # acction to take when some other diverted key is pr
def apply_all_settings(device):
- if device.features and _hidpp20.FEATURE.HIRES_WHEEL in device.features:
+ if device.features and _hidpp20_constants.FEATURE.HIRES_WHEEL in device.features:
_sleep(0.2) # delay to try to get out of race condition with Linux HID++ driver
persister = getattr(device, 'persister', None)
sensitives = persister.get('_sensitive', {}) if persister else {}
diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py
index 2dd600604b..e6144fe191 100644
--- a/lib/logitech_receiver/settings_templates.py
+++ b/lib/logitech_receiver/settings_templates.py
@@ -25,6 +25,7 @@
from . import hidpp10_constants as _hidpp10_constants
from . import hidpp20 as _hidpp20
+from . import hidpp20_constants as _hidpp20_constants
from . import special_keys as _special_keys
from .common import NamedInt as _NamedInt
from .common import NamedInts as _NamedInts
@@ -55,9 +56,9 @@
_DK = _hidpp10_constants.DEVICE_KIND
_R = _hidpp10_constants.REGISTERS
-_F = _hidpp20.FEATURE
+_F = _hidpp20_constants.FEATURE
-_GG = _hidpp20.GESTURE
+_GG = _hidpp20_constants.GESTURE
_GP = _hidpp20.PARAM
# Setting classes are used to control the settings that the Solaar GUI shows and manipulates.
@@ -510,8 +511,8 @@ class _rw_class(_FeatureRW): # no longer needed - set Onboard Profiles to disab
def write(self, device, data_bytes):
# Host mode is required for report rate to be adjustable
- if _hidpp20.get_onboard_mode(device) != _hidpp20.ONBOARD_MODES.MODE_HOST:
- _hidpp20.set_onboard_mode(device, _hidpp20.ONBOARD_MODES.MODE_HOST)
+ if _hidpp20.get_onboard_mode(device) != _hidpp20_constants.ONBOARD_MODES.MODE_HOST:
+ _hidpp20.set_onboard_mode(device, _hidpp20_constants.ONBOARD_MODES.MODE_HOST)
return super().write(device, data_bytes)
class validator_class(_ChoicesV):
@@ -556,8 +557,8 @@ def read(self, device, data_bytes=b''):
def write(self, device, data_bytes):
# Host mode is required for report rate to be adjustable
- if _hidpp20.get_onboard_mode(device) != _hidpp20.ONBOARD_MODES.MODE_HOST:
- _hidpp20.set_onboard_mode(device, _hidpp20.ONBOARD_MODES.MODE_HOST)
+ if _hidpp20.get_onboard_mode(device) != _hidpp20_constants.ONBOARD_MODES.MODE_HOST:
+ _hidpp20.set_onboard_mode(device, _hidpp20_constants.ONBOARD_MODES.MODE_HOST)
return super().write(device, data_bytes)
class validator_class(_ChoicesV):
@@ -805,7 +806,7 @@ def release_action(self):
logger.info('mouse gesture notification %s', self.data)
payload = _pack('!' + (len(self.data) * 'h'), *self.data)
notification = _HIDPP_Notification(0, 0, 0, 0, payload)
- _process_notification(self.device, self.device.status, notification, _hidpp20.FEATURE.MOUSE_GESTURE)
+ _process_notification(self.device, self.device.status, notification, _F.MOUSE_GESTURE)
self.fsmState = 'idle'
def move_action(self, dx, dy):
@@ -1195,7 +1196,7 @@ class Gesture2Gestures(_BitFieldOMSetting):
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
+ choices_universe = _hidpp20_constants.GESTURE
_labels = _GESTURE2_GESTURES_LABELS
class validator_class(_BitFieldOMV):
@@ -1213,7 +1214,7 @@ class Gesture2Divert(_BitFieldOMSetting):
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
+ choices_universe = _hidpp20_constants.GESTURE
_labels = _GESTURE2_GESTURES_LABELS
class validator_class(_BitFieldOMV):
diff --git a/lib/logitech_receiver/status.py b/lib/logitech_receiver/status.py
index 324a5a09d0..03e4d2ff4e 100644
--- a/lib/logitech_receiver/status.py
+++ b/lib/logitech_receiver/status.py
@@ -218,8 +218,8 @@ def set_battery_info(self, level, nextLevel, status, voltage, timestamp=None):
old_voltage, self[KEYS.BATTERY_VOLTAGE] = self.get(KEYS.BATTERY_VOLTAGE), voltage
charging = status in (
- _hidpp20_constants.BATTERY_STATUS.recharging, _hidpp20_constants.BATTERY_STATUS.almost_full, _hidpp20_constants.BATTERY_STATUS.full,
- _hidpp20_constants.BATTERY_STATUS.slow_recharge
+ _hidpp20_constants.BATTERY_STATUS.recharging, _hidpp20_constants.BATTERY_STATUS.almost_full,
+ _hidpp20_constants.BATTERY_STATUS.full, _hidpp20_constants.BATTERY_STATUS.slow_recharge
)
old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py
index fad81e9fc6..5500e1e783 100644
--- a/lib/solaar/cli/show.py
+++ b/lib/solaar/cli/show.py
@@ -20,13 +20,14 @@
from logitech_receiver import hidpp10 as _hidpp10
from logitech_receiver import hidpp10_constants as _hidpp10_constants
from logitech_receiver import hidpp20 as _hidpp20
+from logitech_receiver import hidpp20_constants as _hidpp20_constants
from logitech_receiver import receiver as _receiver
from logitech_receiver import settings_templates as _settings_templates
from logitech_receiver.common import NamedInt as _NamedInt
from logitech_receiver.common import strhex as _strhex
from solaar import NAME, __version__
-_F = _hidpp20.FEATURE
+_F = _hidpp20_constants.FEATURE
def _print_receiver(receiver):
@@ -142,11 +143,11 @@ def _print_device(dev, num=None):
for feature, index in dev.features.enumerate():
flags = dev.request(0x0000, feature.bytes(2))
flags = 0 if flags is None else ord(flags[1:2])
- flags = _hidpp20.FEATURE_FLAG.flag_names(flags)
+ flags = _hidpp20_constants.FEATURE_FLAG.flag_names(flags)
version = dev.features.get_feature_version(int(feature))
version = version if version else 0
print(' %2d: %-22s {%04X} V%s %s ' % (index, feature, feature, version, ', '.join(flags)))
- if feature == _hidpp20.FEATURE.HIRES_WHEEL:
+ if feature == _hidpp20_constants.FEATURE.HIRES_WHEEL:
wheel = _hidpp20.get_hires_wheel(dev)
if wheel:
multi, has_invert, has_switch, inv, res, target, ratchet = wheel
@@ -163,7 +164,7 @@ def _print_device(dev, num=None):
print(' HID++ notification')
else:
print(' HID notification')
- elif feature == _hidpp20.FEATURE.MOUSE_POINTER:
+ elif feature == _hidpp20_constants.FEATURE.MOUSE_POINTER:
mouse_pointer = _hidpp20.get_mouse_pointer_info(dev)
if mouse_pointer:
print(' DPI: %s' % mouse_pointer['dpi'])
@@ -176,13 +177,13 @@ def _print_device(dev, num=None):
print(' Provide vertical tuning, trackball')
else:
print(' No vertical tuning, standard mice')
- elif feature == _hidpp20.FEATURE.VERTICAL_SCROLLING:
+ elif feature == _hidpp20_constants.FEATURE.VERTICAL_SCROLLING:
vertical_scrolling_info = _hidpp20.get_vertical_scrolling_info(dev)
if vertical_scrolling_info:
print(' Roller type: %s' % vertical_scrolling_info['roller'])
print(' Ratchet per turn: %s' % vertical_scrolling_info['ratchet'])
print(' Scroll lines: %s' % vertical_scrolling_info['lines'])
- elif feature == _hidpp20.FEATURE.HI_RES_SCROLLING:
+ elif feature == _hidpp20_constants.FEATURE.HI_RES_SCROLLING:
scrolling_mode, scrolling_resolution = _hidpp20.get_hi_res_scrolling_info(dev)
if scrolling_mode:
print(' Hi-res scrolling enabled')
@@ -190,30 +191,30 @@ def _print_device(dev, num=None):
print(' Hi-res scrolling disabled')
if scrolling_resolution:
print(' Hi-res scrolling multiplier: %s' % scrolling_resolution)
- elif feature == _hidpp20.FEATURE.POINTER_SPEED:
+ elif feature == _hidpp20_constants.FEATURE.POINTER_SPEED:
pointer_speed = _hidpp20.get_pointer_speed_info(dev)
if pointer_speed:
print(' Pointer Speed: %s' % pointer_speed)
- elif feature == _hidpp20.FEATURE.LOWRES_WHEEL:
+ elif feature == _hidpp20_constants.FEATURE.LOWRES_WHEEL:
wheel_status = _hidpp20.get_lowres_wheel_status(dev)
if wheel_status:
print(' Wheel Reports: %s' % wheel_status)
- elif feature == _hidpp20.FEATURE.NEW_FN_INVERSION:
+ elif feature == _hidpp20_constants.FEATURE.NEW_FN_INVERSION:
inversion = _hidpp20.get_new_fn_inversion(dev)
if inversion:
inverted, default_inverted = inversion
print(' Fn-swap:', 'enabled' if inverted else 'disabled')
print(' Fn-swap default:', 'enabled' if default_inverted else 'disabled')
- elif feature == _hidpp20.FEATURE.HOSTS_INFO:
+ elif feature == _hidpp20_constants.FEATURE.HOSTS_INFO:
host_names = _hidpp20.get_host_names(dev)
for host, (paired, name) in host_names.items():
print(' Host %s (%s): %s' % (host, 'paired' if paired else 'unpaired', name))
- elif feature == _hidpp20.FEATURE.DEVICE_NAME:
+ elif feature == _hidpp20_constants.FEATURE.DEVICE_NAME:
print(' Name: %s' % _hidpp20.get_name(dev))
print(' Kind: %s' % _hidpp20.get_kind(dev))
- elif feature == _hidpp20.FEATURE.DEVICE_FRIENDLY_NAME:
+ elif feature == _hidpp20_constants.FEATURE.DEVICE_FRIENDLY_NAME:
print(' Friendly Name: %s' % _hidpp20.get_friendly_name(dev))
- elif feature == _hidpp20.FEATURE.DEVICE_FW_VERSION:
+ elif feature == _hidpp20_constants.FEATURE.DEVICE_FW_VERSION:
for fw in _hidpp20.get_firmware(dev):
extras = _strhex(fw.extras) if fw.extras else ''
print(' Firmware: %s %s %s %s' % (fw.kind, fw.name, fw.version, extras))
@@ -221,12 +222,12 @@ def _print_device(dev, num=None):
if ids:
unitId, modelId, tid_map = ids
print(' Unit ID: %s Model ID: %s Transport IDs: %s' % (unitId, modelId, tid_map))
- elif feature == _hidpp20.FEATURE.REPORT_RATE or feature == _hidpp20.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE:
+ elif feature == _hidpp20_constants.FEATURE.REPORT_RATE or feature == _hidpp20_constants.FEATURE.EXTENDED_ADJUSTABLE_REPORT_RATE:
print(' Report Rate: %s' % _hidpp20.get_polling_rate(dev))
- elif feature == _hidpp20.FEATURE.REMAINING_PAIRING:
+ elif feature == _hidpp20_constants.FEATURE.REMAINING_PAIRING:
print(' Remaining Pairings: %d' % _hidpp20.get_remaining_pairing(dev))
- elif feature == _hidpp20.FEATURE.ONBOARD_PROFILES:
- if _hidpp20.get_onboard_mode(dev) == _hidpp20.ONBOARD_MODES.MODE_HOST:
+ elif feature == _hidpp20_constants.FEATURE.ONBOARD_PROFILES:
+ if _hidpp20.get_onboard_mode(dev) == _hidpp20_constants.ONBOARD_MODES.MODE_HOST:
mode = 'Host'
else:
mode = 'On-Board'
@@ -252,9 +253,9 @@ def _print_device(dev, num=None):
print(' Has %d reprogrammable keys:' % len(dev.keys))
for k in dev.keys:
# TODO: add here additional variants for other REPROG_CONTROLS
- if dev.keys.keyversion == _hidpp20.FEATURE.REPROG_CONTROLS_V2:
+ if dev.keys.keyversion == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V2:
print(' %2d: %-26s => %-27s %s' % (k.index, k.key, k.default_task, ', '.join(k.flags)))
- if dev.keys.keyversion == _hidpp20.FEATURE.REPROG_CONTROLS_V4:
+ if dev.keys.keyversion == _hidpp20_constants.FEATURE.REPROG_CONTROLS_V4:
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'