From f9618dd0ab5d6e2beff356b258b456654d939a0b Mon Sep 17 00:00:00 2001 From: Alex Cherkayev Date: Sun, 12 Feb 2017 20:55:50 +0200 Subject: [PATCH] Add M185 keys reporting using REPROG CONTROLS V4 in cli Add smooth scrolling using LOWRES_WHEEL Add mouse speed selection using POINTER_SPEED --- lib/logitech_receiver/common.py | 10 ++++++++ lib/logitech_receiver/hidpp20.py | 26 ++++++++++++++++++--- lib/logitech_receiver/settings_templates.py | 23 ++++++++++++++++++ lib/solaar/cli/show.py | 8 +++++-- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/lib/logitech_receiver/common.py b/lib/logitech_receiver/common.py index 8e3124ec..b6cfb67d 100644 --- a/lib/logitech_receiver/common.py +++ b/lib/logitech_receiver/common.py @@ -279,4 +279,14 @@ ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', [ 'task', 'flags']) +ReprogrammableKeyInfoV4 = namedtuple('ReprogrammableKeyInfoV4', [ + 'index', + 'key', + 'task', + 'flags', + 'pos', + 'group', + 'group_mask', + 'remapped']) + del namedtuple diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index 4b8d9712..7faff999 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -28,6 +28,7 @@ del getLogger from .common import (FirmwareInfo as _FirmwareInfo, ReprogrammableKeyInfo as _ReprogrammableKeyInfo, + ReprogrammableKeyInfoV4 as _ReprogrammableKeyInfoV4, KwException as _KwException, NamedInts as _NamedInts, pack as _pack, @@ -300,11 +301,12 @@ class FeaturesArray(object): class KeysArray(object): """A sequence of key mappings supported by a HID++ 2.0 device.""" - __slots__ = ('device', 'keys') + __slots__ = ('device', 'keys', 'keyversion') def __init__(self, device, count): assert device is not None self.device = device + self.keyversion = 0 self.keys = [None] * count def __getitem__(self, index): @@ -312,13 +314,28 @@ class KeysArray(object): if index < 0 or index >= len(self.keys): raise IndexError(index) + # TODO: add here additional variants for other REPROG_CONTROLS if self.keys[index] is None: keydata = feature_request(self.device, FEATURE.REPROG_CONTROLS, 0x10, index) + self.keyversion=1 + if keydata is None: + keydata = feature_request(self.device, FEATURE.REPROG_CONTROLS_V4, 0x10, index) + self.keyversion=4 if keydata: - key, key_task, flags = _unpack('!HHB', keydata[:5]) + key, key_task, flags, pos, group, gmask = _unpack('!HHBBBB', keydata[:8]) ctrl_id_text = special_keys.CONTROL[key] ctrl_task_text = special_keys.TASK[key_task] - self.keys[index] = _ReprogrammableKeyInfo(index, ctrl_id_text, ctrl_task_text, flags) + if self.keyversion == 1: + self.keys[index] = _ReprogrammableKeyInfo(index, ctrl_id_text, ctrl_task_text, flags) + if self.keyversion == 4: + mapped_data = feature_request(self.device, FEATURE.REPROG_CONTROLS_V4, 0x20, key&0xff00, key&0xff) + if mapped_data: + remap_key, remap_flag, remapped = _unpack('!HBH', mapped_data[:5]) + # if key not mapped map it to itself for display + if remapped == 0: + remapped = key + remapped_text = special_keys.CONTROL[remapped] + self.keys[index] = _ReprogrammableKeyInfoV4(index, ctrl_id_text, ctrl_task_text, flags, pos, group, gmask, remapped_text) return self.keys[index] @@ -439,7 +456,10 @@ def get_battery(device): def get_keys(device): + # TODO: add here additional variants for other REPROG_CONTROLS count = feature_request(device, FEATURE.REPROG_CONTROLS) + if count is None: + count = feature_request(device, FEATURE.REPROG_CONTROLS_V4) if count: return KeysArray(device, ord(count[:1])) diff --git a/lib/logitech_receiver/settings_templates.py b/lib/logitech_receiver/settings_templates.py index f7fbbd21..6604a147 100644 --- a/lib/logitech_receiver/settings_templates.py +++ b/lib/logitech_receiver/settings_templates.py @@ -122,6 +122,7 @@ _SIDE_SCROLL = ('side-scroll', _("Side Scrolling"), _("When disabled, pushing the wheel sideways sends custom button events\n" "instead of the standard side-scrolling events.")) _DPI = ('dpi', _("Sensitivity (DPI)"), None) +_POINTER_SPEED = ('pointer_speed', _("Sensitivity (Pointer Speed)"), None) _FN_SWAP = ('fn-swap', _("Swap Fx function"), _("When set, the F1..F12 keys will activate their special function,\n" "and you must hold the FN key to activate their standard function.") @@ -179,6 +180,11 @@ def _feature_smooth_scroll(): label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2], device_kind=_DK.mouse) +def _feature_lowres_smooth_scroll(): + return feature_toggle(_SMOOTH_SCROLL[0], _F.LOWRES_WHEEL, + label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2], + device_kind=_DK.mouse) + def _feature_smart_shift(): _MIN_SMART_SHIFT_VALUE = 0 _MAX_SMART_SHIFT_VALUE = 50 @@ -250,6 +256,15 @@ def _feature_adjustable_dpi(): label=_DPI[1], description=_DPI[2], device_kind=_DK.mouse) +def _feature_pointer_speed(): + """Pointer Speed feature""" + # min and max values taken from usb traces of Win software + return feature_range(_POINTER_SPEED[0], _F.POINTER_SPEED, 0x002e, 0x01ff, + read_function_id=0x0, + write_function_id=0x10, + bytes_count=2, + label=_POINTER_SPEED[1], description=_POINTER_SPEED[2], + device_kind=_DK.mouse) # # # @@ -259,8 +274,10 @@ _SETTINGS_LIST = namedtuple('_SETTINGS_LIST', [ 'fn_swap', 'new_fn_swap', 'smooth_scroll', + 'lowres_smooth_scroll', 'side_scroll', 'dpi', + 'pointer_speed', 'hand_detection', 'typing_illumination', 'smart_shift', @@ -271,8 +288,10 @@ RegisterSettings = _SETTINGS_LIST( fn_swap=_register_fn_swap, new_fn_swap=None, smooth_scroll=_register_smooth_scroll, + lowres_smooth_scroll=None, side_scroll=_register_side_scroll, dpi=_register_dpi, + pointer_speed=None, hand_detection=_register_hand_detection, typing_illumination=None, smart_shift=None, @@ -281,8 +300,10 @@ FeatureSettings = _SETTINGS_LIST( fn_swap=_feature_fn_swap, new_fn_swap=_feature_new_fn_swap, smooth_scroll=_feature_smooth_scroll, + lowres_smooth_scroll=_feature_lowres_smooth_scroll, side_scroll=None, dpi=_feature_adjustable_dpi, + pointer_speed=_feature_pointer_speed, hand_detection=None, typing_illumination=None, smart_shift=_feature_smart_shift, @@ -320,7 +341,9 @@ def check_feature_settings(device, already_known): already_known.append(feature(device)) check_feature(_SMOOTH_SCROLL[0], _F.HI_RES_SCROLLING) + check_feature(_SMOOTH_SCROLL[0], _F.LOWRES_WHEEL) check_feature(_FN_SWAP[0], _F.FN_INVERSION) check_feature(_FN_SWAP[0], _F.NEW_FN_INVERSION, 'new_fn_swap') check_feature(_DPI[0], _F.ADJUSTABLE_DPI) + check_feature(_POINTER_SPEED[0], _F.POINTER_SPEED) check_feature(_SMART_SHIFT[0], _F.SMART_SHIFT) diff --git a/lib/solaar/cli/show.py b/lib/solaar/cli/show.py index 29f7ca62..e69c2a01 100644 --- a/lib/solaar/cli/show.py +++ b/lib/solaar/cli/show.py @@ -98,8 +98,12 @@ def _print_device(dev): print (' Has %d reprogrammable keys:' % len(dev.keys)) for k in dev.keys: flags = _special_keys.KEY_FLAG.flag_names(k.flags) - print (' %2d: %-26s => %-27s %s' % (k.index, k.key, k.task, ', '.join(flags))) - + # TODO: add here additional variants for other REPROG_CONTROLS + if dev.keys.keyversion == 1: + print (' %2d: %-26s => %-27s %s' % (k.index, k.key, k.task, ', '.join(flags))) + if dev.keys.keyversion == 4: + print (' %2d: %-26s, default: %-27s => %-26s' % (k.index, k.key, k.task, k.remapped)) + print (' %s, pos:%d, group:%1d, gmask:%d' % ( ', '.join(flags), k.pos, k.group, k.group_mask)) if dev.online: battery = _hidpp20.get_battery(dev) if battery is None: