742 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			742 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
| # -*- python-mode -*-
 | |
| 
 | |
| ## Copyright (C) 2012-2013  Daniel Pavel
 | |
| ##
 | |
| ## This program is free software; you can redistribute it and/or modify
 | |
| ## it under the terms of the GNU General Public License as published by
 | |
| ## the Free Software Foundation; either version 2 of the License, or
 | |
| ## (at your option) any later version.
 | |
| ##
 | |
| ## This program is distributed in the hope that it will be useful,
 | |
| ## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| ## GNU General Public License for more details.
 | |
| ##
 | |
| ## You should have received a copy of the GNU General Public License along
 | |
| ## with this program; if not, write to the Free Software Foundation, Inc.,
 | |
| ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | |
| 
 | |
| from collections import namedtuple
 | |
| 
 | |
| from .common import NamedInts as _NamedInts
 | |
| from .hidpp10 import DEVICE_KIND as _DK
 | |
| from .hidpp10 import REGISTERS as _R
 | |
| from .settings_templates import FeatureSettings as _FS
 | |
| from .settings_templates import RegisterSettings as _RS
 | |
| 
 | |
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| _DeviceDescriptor = namedtuple(
 | |
|     '_DeviceDescriptor',
 | |
|     ('name', 'kind', 'wpid', 'codename', 'protocol', 'registers', 'settings', 'persister', 'usbid', 'interface', 'btid')
 | |
| )
 | |
| del namedtuple
 | |
| 
 | |
| DEVICES_WPID = {}
 | |
| DEVICES = {}
 | |
| 
 | |
| 
 | |
| def _D(
 | |
|     name,
 | |
|     codename=None,
 | |
|     kind=None,
 | |
|     wpid=None,
 | |
|     protocol=None,
 | |
|     registers=None,
 | |
|     settings=None,
 | |
|     persister=None,
 | |
|     usbid=None,
 | |
|     interface=None,
 | |
|     btid=None,
 | |
| ):
 | |
|     assert name
 | |
| 
 | |
|     if kind is None:
 | |
|         kind = (
 | |
|             _DK.mouse if 'Mouse' in name else _DK.keyboard if 'Keyboard' in name else _DK.numpad
 | |
|             if 'Number Pad' in name else _DK.touchpad if 'Touchpad' in name else _DK.trackball if 'Trackball' in name else None
 | |
|         )
 | |
|     assert kind is not None, 'descriptor for %s does not have kind set' % name
 | |
| 
 | |
|     # heuristic: the codename is the last word in the device name
 | |
|     if codename is None and ' ' in name:
 | |
|         codename = name.split(' ')[-1]
 | |
|     assert codename is not None, 'descriptor for %s does not have codename set' % name
 | |
| 
 | |
|     if protocol is not None:
 | |
|         # ? 2.0 devices should not have any registers
 | |
|         _kind = lambda s: s._rw.kind if hasattr(s, '_rw') else s._rw_kind
 | |
|         if protocol < 2.0:
 | |
|             assert settings is None or all(_kind(s) == 1 for s in settings)
 | |
|         else:
 | |
|             assert registers is None
 | |
|             assert settings is None or all(_kind(s) == 2 for s in settings)
 | |
| 
 | |
|         if wpid:
 | |
|             for w in wpid if isinstance(wpid, tuple) else (wpid, ):
 | |
|                 if protocol > 1.0:
 | |
|                     assert w[0:1] == '4', '%s has protocol %0.1f, wpid %s' % (name, protocol, w)
 | |
|                 else:
 | |
|                     if w[0:1] == '1':
 | |
|                         assert kind == _DK.mouse, '%s has protocol %0.1f, wpid %s' % (name, protocol, w)
 | |
|                     elif w[0:1] == '2':
 | |
|                         assert kind in (_DK.keyboard, _DK.numpad), '%s has protocol %0.1f, wpid %s' % (name, protocol, w)
 | |
| 
 | |
|     device_descriptor = _DeviceDescriptor(
 | |
|         name=name,
 | |
|         kind=kind,
 | |
|         wpid=wpid,
 | |
|         codename=codename,
 | |
|         protocol=protocol,
 | |
|         registers=registers,
 | |
|         settings=settings,
 | |
|         persister=persister,
 | |
|         usbid=usbid,
 | |
|         interface=interface,
 | |
|         btid=btid
 | |
|     )
 | |
| 
 | |
|     if usbid:
 | |
|         found = get_usbid(usbid)
 | |
|         assert found is None, 'duplicate usbid in device descriptors: %s' % (found, )
 | |
|     if btid:
 | |
|         found = get_btid(btid)
 | |
|         assert found is None, 'duplicate btid in device descriptors: %s' % (found, )
 | |
| 
 | |
|     assert codename not in DEVICES, 'duplicate codename in device descriptors: %s' % (DEVICES[codename], )
 | |
|     DEVICES[codename] = device_descriptor
 | |
| 
 | |
|     if wpid:
 | |
|         for w in wpid if isinstance(wpid, tuple) else (wpid, ):
 | |
|             assert w not in DEVICES_WPID, 'duplicate wpid in device descriptors: %s' % (DEVICES_WPID[w], )
 | |
|             DEVICES_WPID[w] = device_descriptor
 | |
| 
 | |
| 
 | |
| def get_wpid(wpid):
 | |
|     return DEVICES_WPID.get(wpid)
 | |
| 
 | |
| 
 | |
| def get_codename(codename):
 | |
|     return DEVICES.get(codename)
 | |
| 
 | |
| 
 | |
| def get_usbid(usbid):
 | |
|     if isinstance(usbid, str):
 | |
|         usbid = int(usbid, 16)
 | |
|     found = next((x for x in DEVICES.values() if x.usbid == usbid), None)
 | |
|     return found
 | |
| 
 | |
| 
 | |
| def get_btid(btid):
 | |
|     if isinstance(btid, str):
 | |
|         btid = int(btid, 16)
 | |
|     found = next((x for x in DEVICES.values() if x.btid == btid), None)
 | |
|     return found
 | |
| 
 | |
| 
 | |
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| _PERFORMANCE_MX_DPIS = _NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100))
 | |
| 
 | |
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| # Some HID++1.0 registers and HID++2.0 features can be discovered at run-time,
 | |
| # so they are not specified here.
 | |
| #
 | |
| # For known registers, however, please do specify them here -- avoids
 | |
| # unnecessary communication with the device and makes it easier to make certain
 | |
| # decisions when querying the device's state.
 | |
| #
 | |
| # Specify a negative value to blacklist a certain register for a device.
 | |
| #
 | |
| # Usually, state registers (battery, leds, some features, etc) are only used by
 | |
| # HID++ 1.0 devices, while HID++ 2.0 devices use features for the same
 | |
| # functionalities. This is a rule that's been discovered by trial-and-error,
 | |
| # so it may change in the future.
 | |
| 
 | |
| # Well-known registers (in hex):
 | |
| #  * 00 - notification flags (all devices)
 | |
| #    01 - mice: smooth scrolling
 | |
| #    07 - battery status
 | |
| #    09 - keyboards: FN swap (if it has the FN key)
 | |
| #    0D - battery charge
 | |
| #       a device may have either the 07 or 0D register available;
 | |
| #       no known device uses both
 | |
| #    51 - leds
 | |
| #    63 - mice: DPI
 | |
| #  * F1 - firmware info
 | |
| # Some registers appear to be universally supported, no matter the HID++ version
 | |
| # (marked with *). The rest may or may not be supported, and their values may or
 | |
| # may not mean the same thing across different devices.
 | |
| 
 | |
| # The 'codename' and 'kind' fields are usually guessed from the device name,
 | |
| # but in some cases (like the Logitech Cube) that heuristic fails and they have
 | |
| # to be specified.
 | |
| #
 | |
| # The 'protocol' and 'wpid' fields are optional (they can be discovered at
 | |
| # runtime), but specifying them here speeds up device discovery and reduces the
 | |
| # USB traffic Solaar has to do to fully identify peripherals.
 | |
| # Same goes for HID++ 2.0 feature settings (like _feature_fn_swap).
 | |
| #
 | |
| # The 'registers' field indicates read-only registers, specifying a state. These
 | |
| # are valid (AFAIK) only to HID++ 1.0 devices.
 | |
| # The 'settings' field indicates a read/write register; based on them Solaar
 | |
| # generates, at runtime, the settings controls in the device panel. HID++ 1.0
 | |
| # devices may only have register-based settings; HID++ 2.0 devices may only have
 | |
| # feature-based settings.
 | |
| 
 | |
| # Keyboards
 | |
| 
 | |
| _D('Wireless Keyboard K230', protocol=2.0, wpid='400D')
 | |
| _D('Wireless Keyboard K270(unifying)', protocol=2.0, wpid='4003')
 | |
| _D(
 | |
|     'Wireless Keyboard MK270',
 | |
|     protocol=2.0,
 | |
|     wpid='4023',
 | |
|     settings=[_FS.fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard K270',
 | |
|     protocol=1.0,
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard MK300',
 | |
|     protocol=1.0,
 | |
|     wpid='0068',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| 
 | |
| _D(
 | |
|     'Wireless Keyboard MK320',
 | |
|     protocol=1.0,
 | |
|     wpid='200F',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D('Wireless Keyboard MK330')
 | |
| _D(
 | |
|     'Wireless Compact Keyboard K340',
 | |
|     protocol=1.0,
 | |
|     wpid='2007',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Wave Keyboard K350',
 | |
|     protocol=1.0,
 | |
|     wpid='200A',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard K360',
 | |
|     protocol=2.0,
 | |
|     wpid='4004',
 | |
|     settings=[_FS.fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard K375s',
 | |
|     protocol=2.0,
 | |
|     wpid='4061',
 | |
|     settings=[_FS.k375s_fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Touch Keyboard K400',
 | |
|     protocol=2.0,
 | |
|     wpid=('400E', '4024'),
 | |
|     settings=[_FS.fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Touch Keyboard K400 Plus',
 | |
|     codename='K400 Plus',
 | |
|     protocol=2.0,
 | |
|     wpid='404D',
 | |
|     settings=[
 | |
|         _FS.new_fn_swap(),
 | |
|         _FS.reprogrammable_keys(),
 | |
|         _FS.disable_keyboard_keys(),
 | |
|         _FS.gesture2_gestures(),
 | |
|         _FS.gesture2_params(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard K520',
 | |
|     protocol=1.0,
 | |
|     wpid='2011',
 | |
|     registers=(_R.battery_status, ),
 | |
|     settings=[
 | |
|         _RS.fn_swap(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Number Pad N545',
 | |
|     protocol=1.0,
 | |
|     wpid='2006',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D('Wireless Keyboard MK550')
 | |
| _D(
 | |
|     'Wireless Keyboard MK700',
 | |
|     protocol=1.0,
 | |
|     wpid='2008',
 | |
|     registers=(_R.battery_status, ),
 | |
|     settings=[
 | |
|         _RS.fn_swap(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Solar Keyboard K750',
 | |
|     protocol=2.0,
 | |
|     wpid='4002',
 | |
|     settings=[_FS.fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Multi-Device Keyboard K780',
 | |
|     protocol=4.5,
 | |
|     wpid='405B',
 | |
|     settings=[_FS.new_fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Illuminated Keyboard K800',
 | |
|     protocol=1.0,
 | |
|     wpid='2010',
 | |
|     registers=(
 | |
|         _R.battery_status,
 | |
|         _R.three_leds,
 | |
|     ),
 | |
|     settings=[
 | |
|         _RS.fn_swap(),
 | |
|         _RS.hand_detection(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Illuminated Keyboard K800 new',
 | |
|     codename='K800 new',
 | |
|     protocol=4.5,
 | |
|     wpid='406E',
 | |
|     settings=[_FS.fn_swap()],
 | |
| )
 | |
| _D(
 | |
|     'Illuminated Living-Room Keyboard K830',
 | |
|     protocol=2.0,
 | |
|     wpid='4032',
 | |
|     settings=[_FS.new_fn_swap()],
 | |
| )
 | |
| _D('Craft Advanced Keyboard', codename='Craft', protocol=4.5, wpid='4066', btid=0xB350)
 | |
| _D('MX Keys Keyboard', codename='MX Keys', protocol=4.5, wpid='408A', btid=0xB35B)
 | |
| _D(
 | |
|     'Wireless Keyboard S510',
 | |
|     codename='S510',
 | |
|     protocol=1.0,
 | |
|     wpid='0056',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Keyboard EX100',
 | |
|     codename='EX100',
 | |
|     protocol=1.0,
 | |
|     wpid='0065',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'G915 TKL LIGHTSPEED Wireless RGB Mechanical Gaming Keyboard',
 | |
|     codename='G915 TKL',
 | |
|     protocol=4.2,
 | |
|     wpid='408E',
 | |
|     usbid=0xC343,
 | |
| )
 | |
| 
 | |
| # Mice
 | |
| 
 | |
| _D('Wireless Mouse M150', protocol=2.0, wpid='4022')
 | |
| _D('Wireless Mouse M175', protocol=2.0, wpid='4008')
 | |
| _D(
 | |
|     'Wireless Mouse M185 new',
 | |
|     codename='M185n',
 | |
|     protocol=4.5,
 | |
|     wpid='4054',
 | |
|     settings=[
 | |
|         _FS.lowres_smooth_scroll(),
 | |
|         _FS.pointer_speed(),
 | |
|     ]
 | |
| )
 | |
| # Apparently Logitech uses wpid 4055 for three different mice
 | |
| # That's not so strange, as M185 is used on both Unifying-ready and non-Unifying-ready mice
 | |
| _D(
 | |
|     'Wireless Mouse M185/M235/M310',
 | |
|     codename='M185/M235/M310',
 | |
|     protocol=4.5,
 | |
|     wpid='4055',
 | |
|     settings=[
 | |
|         _FS.lowres_smooth_scroll(),
 | |
|         _FS.pointer_speed(),
 | |
|     ]
 | |
| )
 | |
| _D('Wireless Mouse M185', protocol=2.0, wpid='4038')
 | |
| _D('Wireless Mouse M187', protocol=2.0, wpid='4019')
 | |
| _D('Wireless Mouse M215', protocol=1.0, wpid='1020')
 | |
| _D(
 | |
|     'Wireless Mouse M305',
 | |
|     protocol=1.0,
 | |
|     wpid='101F',
 | |
|     registers=(_R.battery_status, ),
 | |
|     settings=[
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Mouse M310',
 | |
|     protocol=1.0,
 | |
|     wpid='1024',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D('Wireless Mouse M315')
 | |
| _D('Wireless Mouse M317')
 | |
| _D('Wireless Mouse M325', protocol=2.0, wpid='400A', settings=[
 | |
|     _FS.hi_res_scroll(),
 | |
| ])
 | |
| _D('Wireless Mouse M345', protocol=2.0, wpid='4017')
 | |
| _D(
 | |
|     'Wireless Mouse M350',
 | |
|     protocol=1.0,
 | |
|     wpid='101C',
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D('Wireless Mouse Pebble M350', codename='Pebble', protocol=2.0, wpid='4080')
 | |
| _D(
 | |
|     'Wireless Mouse M505',
 | |
|     codename='M505/B605',
 | |
|     protocol=1.0,
 | |
|     wpid='101D',
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Wireless Mouse M510',
 | |
|     protocol=1.0,
 | |
|     wpid='1025',
 | |
|     registers=(_R.battery_status, ),
 | |
|     settings=[
 | |
|         # _RS.smooth_scroll(),	# writing the bit to the register doesn't cause an error, but the bit doesn't turn on
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D('Wireless Mouse M510', codename='M510v2', protocol=2.0, wpid='4051', settings=[
 | |
|     _FS.lowres_smooth_scroll(),
 | |
| ])
 | |
| _D('Couch Mouse M515', protocol=2.0, wpid='4007')
 | |
| _D('Wireless Mouse M525', protocol=2.0, wpid='4013')
 | |
| _D(
 | |
|     'Multi Device Silent Mouse M585/M590',
 | |
|     codename='M585/M590',
 | |
|     protocol=4.5,
 | |
|     wpid='406B',
 | |
|     settings=[
 | |
|         _FS.lowres_smooth_scroll(),
 | |
|         _FS.pointer_speed(),
 | |
|     ],
 | |
| )
 | |
| _D('Touch Mouse M600', protocol=2.0, wpid='401A')
 | |
| _D(
 | |
|     'Marathon Mouse M705 (M-R0009)',
 | |
|     codename='M705 (M-R0009)',
 | |
|     protocol=1.0,
 | |
|     wpid='101B',
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Marathon Mouse M705 (M-R0073)',
 | |
|     codename='M705 (M-R0073)',
 | |
|     protocol=4.5,
 | |
|     wpid='406D',
 | |
|     settings=[
 | |
|         _FS.hires_smooth_invert(),
 | |
|         #        _FS.hires_smooth_resolution(),
 | |
|         _FS.pointer_speed(),
 | |
|     ]
 | |
| )
 | |
| _D('Zone Touch Mouse T400')
 | |
| _D('Touch Mouse T620', protocol=2.0)
 | |
| _D('Logitech Cube', kind=_DK.mouse, protocol=2.0)
 | |
| _D(
 | |
|     'Anywhere Mouse MX',
 | |
|     codename='Anywhere MX',
 | |
|     protocol=1.0,
 | |
|     wpid='1017',
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Anywhere Mouse MX 2',
 | |
|     codename='Anywhere MX 2',
 | |
|     protocol=4.5,
 | |
|     wpid='404A',
 | |
|     settings=[
 | |
|         _FS.hires_smooth_invert(),
 | |
|         #        _FS.hires_smooth_resolution(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'Performance Mouse MX',
 | |
|     codename='Performance MX',
 | |
|     protocol=1.0,
 | |
|     wpid='101A',
 | |
|     registers=(
 | |
|         _R.battery_status,
 | |
|         _R.three_leds,
 | |
|     ),
 | |
|     settings=[
 | |
|         _RS.dpi(choices=_PERFORMANCE_MX_DPIS),
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| _D(
 | |
|     'Wireless Mouse MX Master',
 | |
|     codename='MX Master',
 | |
|     protocol=4.5,
 | |
|     wpid='4041',
 | |
|     btid=0xb012,
 | |
|     settings=[
 | |
|         _FS.hires_smooth_invert(),
 | |
|         #        _FS.hires_smooth_resolution(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| _D(
 | |
|     'Wireless Mouse MX Master 2S',
 | |
|     codename='MX Master 2S',
 | |
|     protocol=4.5,
 | |
|     wpid='4069',
 | |
|     btid=0xb019,
 | |
|     settings=[
 | |
|         _FS.hires_smooth_invert(),
 | |
|         #        _FS.hires_smooth_resolution(),
 | |
|         _FS.gesture2_gestures(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| _D('MX Master 3 Wireless Mouse', codename='MX Master 3', protocol=4.5, wpid='4082', btid=0xb023)
 | |
| 
 | |
| _D('MX Vertical Wireless Mouse', codename='MX Vertical', protocol=4.5, wpid='407B', btid=0xb020, usbid=0xc08a)
 | |
| 
 | |
| _D(
 | |
|     'G7 Cordless Laser Mouse',
 | |
|     codename='G7',
 | |
|     protocol=1.0,
 | |
|     wpid='1002',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'G700 Gaming Mouse',
 | |
|     codename='G700',
 | |
|     protocol=1.0,
 | |
|     wpid='1023',
 | |
|     usbid=0xc06b,
 | |
|     interface=1,
 | |
|     registers=(
 | |
|         _R.battery_status,
 | |
|         _R.three_leds,
 | |
|     ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'G700s Gaming Mouse',
 | |
|     codename='G700s',
 | |
|     protocol=1.0,
 | |
|     wpid='102A',
 | |
|     usbid=0xc07c,
 | |
|     interface=1,
 | |
|     registers=(
 | |
|         _R.battery_status,
 | |
|         _R.three_leds,
 | |
|     ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| _D('G102 Lightsync Mouse', codename='G102', usbid=0xc092, interface=1)
 | |
| _D('G403 Gaming Mouse', codename='G403', usbid=0xc082)
 | |
| _D('G502 Hero Gaming Mouse', codename='G502 Hero', usbid=0xc08d)
 | |
| _D('G703 Lightspeed Gaming Mouse', codename='G703', usbid=0xc087)
 | |
| _D('G703 Hero Gaming Mouse', codename='G703 Hero', usbid=0xc090)
 | |
| _D('G900 Chaos Spectrum Gaming Mouse', codename='G900', usbid=0xc081)
 | |
| _D('G903 Lightspeed Gaming Mouse', codename='G903', usbid=0xc086)
 | |
| _D('G903 Hero Gaming Mouse', codename='G903 Hero', usbid=0xc091)
 | |
| _D('GPro Gaming Mouse', codename='GPro', usbid=0xc088)
 | |
| _D(
 | |
|     'PRO X Wireless',
 | |
|     kind='mouse',
 | |
|     codename='PRO X',
 | |
|     protocol=4.2,
 | |
|     wpid='4093',
 | |
|     usbid=0xc094,
 | |
| )
 | |
| 
 | |
| _D('M500S Mouse', codename='M500S', usbid=0xc093, interface=1)
 | |
| 
 | |
| _D(
 | |
|     'LX5 Cordless Mouse',
 | |
|     codename='LX5',
 | |
|     protocol=1.0,
 | |
|     wpid='0036',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Mouse M30',
 | |
|     codename='M30',
 | |
|     protocol=1.0,
 | |
|     wpid='0085',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'Wireless Mouse EX100',
 | |
|     codename='EX100m',
 | |
|     protocol=1.0,
 | |
|     wpid='003F',
 | |
|     registers=(_R.battery_status, ),
 | |
|     # settings=[ _RS.smooth_scroll(), ], # command accepted, but no change in whell action
 | |
| )
 | |
| 
 | |
| # Trackballs
 | |
| 
 | |
| _D('Wireless Trackball M570')
 | |
| 
 | |
| # Touchpads
 | |
| 
 | |
| _D('Wireless Rechargeable Touchpad T650', protocol=2.0, wpid='4101')
 | |
| _D('Wireless Touchpad', codename='Wireless Touch', protocol=2.0, wpid='4011')
 | |
| 
 | |
| #
 | |
| # Classic Nano peripherals (that don't support the Unifying protocol).
 | |
| # A wpid is necessary to properly identify them.
 | |
| #
 | |
| 
 | |
| _D(
 | |
|     'VX Nano Cordless Laser Mouse',
 | |
|     codename='VX Nano',
 | |
|     protocol=1.0,
 | |
|     wpid=('100B', '100F'),
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| _D(
 | |
|     'V450 Nano Cordless Laser Mouse',
 | |
|     codename='V450 Nano',
 | |
|     protocol=1.0,
 | |
|     wpid='1011',
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D(
 | |
|     'V550 Nano Cordless Laser Mouse',
 | |
|     codename='V550 Nano',
 | |
|     protocol=1.0,
 | |
|     wpid='1013',
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| # Mini receiver mice
 | |
| 
 | |
| _D(
 | |
|     'MX610 Laser Cordless Mouse',
 | |
|     codename='MX610',
 | |
|     protocol=1.0,
 | |
|     wpid='1001',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'MX620 Laser Cordless Mouse',
 | |
|     codename='MX620',
 | |
|     protocol=1.0,
 | |
|     wpid=('100A', '1016'),
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D(
 | |
|     'MX610 Left-Handled Mouse',
 | |
|     codename='MX610L',
 | |
|     protocol=1.0,
 | |
|     wpid='1004',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'V400 Laser Cordless Mouse',
 | |
|     codename='V400',
 | |
|     protocol=1.0,
 | |
|     wpid='1003',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'V450 Laser Cordless Mouse',
 | |
|     codename='V450',
 | |
|     protocol=1.0,
 | |
|     wpid='1005',
 | |
|     registers=(_R.battery_status, ),
 | |
| )
 | |
| _D(
 | |
|     'VX Revolution',
 | |
|     codename='VX Revolution',
 | |
|     kind=_DK.mouse,
 | |
|     protocol=1.0,
 | |
|     wpid=('1006', '100D', '0612'),  # WPID 0612 from Issue #921
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D(
 | |
|     'MX Air',
 | |
|     codename='MX Air',
 | |
|     protocol=1.0,
 | |
|     kind=_DK.mouse,
 | |
|     wpid=('1007', '100E'),
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D(
 | |
|     'MX Revolution',
 | |
|     codename='MX Revolution',
 | |
|     protocol=1.0,
 | |
|     kind=_DK.mouse,
 | |
|     wpid=('1008', '100C'),
 | |
|     registers=(_R.battery_charge, ),
 | |
| )
 | |
| _D(
 | |
|     'MX 1100 Cordless Laser Mouse',
 | |
|     codename='MX 1100',
 | |
|     protocol=1.0,
 | |
|     kind=_DK.mouse,
 | |
|     wpid='1014',
 | |
|     registers=(_R.battery_charge, ),
 | |
|     settings=[
 | |
|         _RS.smooth_scroll(),
 | |
|         _RS.side_scroll(),
 | |
|     ],
 | |
| )
 | |
| 
 | |
| # Some exotics...
 | |
| 
 | |
| _D('Fujitsu Sonic Mouse', codename='Sonic', protocol=1.0, wpid='1029')
 |