moved settings templates into separate .py
This commit is contained in:
parent
b1e9480f5a
commit
ceba698678
|
|
@ -4,58 +4,9 @@
|
|||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from .common import NamedInts as _NamedInts
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from . import settings as _settings
|
||||
|
||||
#
|
||||
# common strings for settings
|
||||
#
|
||||
|
||||
_SMOOTH_SCROLL = ('smooth-scroll', 'Smooth Scrolling',
|
||||
'High-sensitivity mode for vertical scroll with the wheel.')
|
||||
_DPI = ('dpi', 'Sensitivity (DPI)', 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.\n'
|
||||
'\n'
|
||||
'When unset, the F1..F12 keys will activate their standard function,\n'
|
||||
'and you must hold the FN key to activate their special function.'))
|
||||
|
||||
# this register is only applicable to HID++ 1.0 devices, it should not exist with HID++ 2.0 devices
|
||||
# using Features
|
||||
def _register_fn_swap(register=0x09, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
||||
return _settings.register_toggle(_FN_SWAP[0], register, true_value=true_value, mask=mask,
|
||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
||||
|
||||
def _register_smooth_scroll(register=0x01, true_value=0x40, mask=0x40):
|
||||
return _settings.register_toggle(_SMOOTH_SCROLL[0], register, true_value=true_value, mask=mask,
|
||||
label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
||||
|
||||
|
||||
_PERFORMANCE_MX_DPIS = _NamedInts.range(0x81, 0x8F, lambda x: str((x - 0x80) * 100))
|
||||
def _register_dpi(register=0x63, choices=None):
|
||||
return _settings.register_choices(_DPI[0], register, choices,
|
||||
label=_DPI[1], description=_DPI[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
||||
|
||||
|
||||
def _feature_fn_swap():
|
||||
return _settings.feature_toggle(_FN_SWAP[0], _hidpp20.FEATURE.FN_INVERSION,
|
||||
write_returns_value=True,
|
||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
||||
|
||||
|
||||
def check_features(device, already_known):
|
||||
"""Try to auto-detect device settings by the HID++ 2.0 features they have."""
|
||||
if device.protocol is not None and device.protocol < 2.0:
|
||||
return
|
||||
if not any(s.name == _FN_SWAP[0] for s in already_known) and _hidpp20.FEATURE.FN_INVERSION in device.features:
|
||||
already_known.append(_feature_fn_swap())
|
||||
from .common import NamedInts as _NamedInts
|
||||
from .settings_templates import Register as _R, Feature as _F
|
||||
|
||||
#
|
||||
#
|
||||
|
|
@ -86,7 +37,11 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
|
|||
|
||||
if protocol is not None:
|
||||
# ? 2.0 devices should not have any registers
|
||||
assert protocol < 2.0 or registers is None
|
||||
if protocol < 2.0:
|
||||
assert settings is None or all(s._rw.kind == 1 for s in settings)
|
||||
else:
|
||||
assert registers is None
|
||||
assert settings is None or all(s._rw.kind == 2 for s in settings)
|
||||
|
||||
DEVICES[codename] = _DeviceDescriptor(
|
||||
name=name,
|
||||
|
|
@ -105,6 +60,12 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
|
|||
#
|
||||
#
|
||||
|
||||
_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.
|
||||
#
|
||||
|
|
@ -143,9 +104,12 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
|
|||
# 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.
|
||||
# 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.
|
||||
# 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
|
||||
|
||||
|
|
@ -154,29 +118,29 @@ _D('Wireless Keyboard K270')
|
|||
_D('Wireless Keyboard K350')
|
||||
_D('Wireless Keyboard K360', protocol=2.0, wpid='4004',
|
||||
settings=[
|
||||
_feature_fn_swap()
|
||||
_F.fn_swap()
|
||||
],
|
||||
)
|
||||
_D('Wireless Touch Keyboard K400', protocol=2.0, wpid='4024',
|
||||
settings=[
|
||||
_feature_fn_swap()
|
||||
_F.fn_swap()
|
||||
],
|
||||
)
|
||||
_D('Wireless Keyboard MK700', protocol=1.0, wpid='2008',
|
||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07},
|
||||
settings=[
|
||||
_register_fn_swap(),
|
||||
_R.fn_swap(),
|
||||
],
|
||||
)
|
||||
_D('Wireless Solar Keyboard K750', protocol=2.0, wpid='4002',
|
||||
settings=[
|
||||
_feature_fn_swap()
|
||||
_F.fn_swap()
|
||||
],
|
||||
)
|
||||
_D('Wireless Illuminated Keyboard K800', protocol=1.0, wpid='2010',
|
||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07, '3leds': 0x51},
|
||||
settings=[
|
||||
_register_fn_swap(),
|
||||
_R.fn_swap(),
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -199,7 +163,7 @@ _D('Wireless Mouse M505')
|
|||
_D('Wireless Mouse M510', protocol=1.0, wpid='1025',
|
||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07},
|
||||
settings=[
|
||||
_register_smooth_scroll(),
|
||||
_R.smooth_scroll(),
|
||||
],
|
||||
)
|
||||
_D('Couch Mouse M515', protocol=2.0)
|
||||
|
|
@ -208,7 +172,7 @@ _D('Touch Mouse M600')
|
|||
_D('Marathon Mouse M705', protocol=1.0, wpid='101B',
|
||||
registers={'battery_charge': 0x0D},
|
||||
settings=[
|
||||
_register_smooth_scroll(),
|
||||
_R.smooth_scroll(),
|
||||
],
|
||||
)
|
||||
_D('Zone Touch Mouse T400')
|
||||
|
|
@ -218,7 +182,7 @@ _D('Anywhere Mouse MX', codename='Anywhere MX')
|
|||
_D('Performance Mouse MX', codename='Performance MX', protocol=1.0, wpid='101A',
|
||||
registers={'battery_charge': -0x0D, 'battery_status': 0x07, '3leds': 0x51},
|
||||
settings=[
|
||||
_register_dpi(choices=_PERFORMANCE_MX_DPIS),
|
||||
_R.dpi(choices=_PERFORMANCE_MX_DPIS),
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -239,6 +203,6 @@ _D('Wireless Touchpad', codename='Wireless Touch', protocol=2.0, wpid='4011')
|
|||
_D('VX Nano Cordless Laser Mouse', codename='VX Nano', protocol=1.0, wpid='100F',
|
||||
registers={'battery_charge': 0x0D, 'battery_status': -0x07},
|
||||
settings=[
|
||||
_register_smooth_scroll(),
|
||||
_R.smooth_scroll(),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from .common import NamedInt as _NamedInt, NamedInts as _NamedInts
|
|||
|
||||
KIND = _NamedInts(toggle=0x1, choice=0x02, range=0x12)
|
||||
|
||||
class _Setting(object):
|
||||
class Setting(object):
|
||||
"""A setting descriptor.
|
||||
Needs to be instantiated for each specific device."""
|
||||
__slots__ = ['name', 'label', 'description', 'kind', 'persister', 'device_kind',
|
||||
|
|
@ -41,10 +41,10 @@ class _Setting(object):
|
|||
p = device.protocol
|
||||
if p == 1.0:
|
||||
# HID++ 1.0 devices do not support features
|
||||
assert self._rw.kind == _RegisterRW.kind
|
||||
assert self._rw.kind == RegisterRW.kind
|
||||
elif p >= 2.0:
|
||||
# HID++ 2.0 devices do not support registers
|
||||
assert self._rw.kind == _FeatureRW.kind
|
||||
assert self._rw.kind == FeatureRW.kind
|
||||
|
||||
o = _copy(self)
|
||||
o._value = None
|
||||
|
|
@ -53,14 +53,24 @@ class _Setting(object):
|
|||
|
||||
@property
|
||||
def choices(self):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
|
||||
return self._validator.choices if self._validator.kind & KIND.choice else None
|
||||
|
||||
def read(self, cached=True):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
|
||||
if self._value is None and self.persister:
|
||||
# We haven't read a value from the device yet,
|
||||
# maybe we have something in the configuration.
|
||||
self._value = self.persister.get(self.name)
|
||||
|
||||
if cached and self._value is not None:
|
||||
if self.persister and self.name not in self.persister:
|
||||
# If this is a new device (or a new setting for an old device),
|
||||
# make sure to save its current value for the next time.
|
||||
self.persister[self.name] = self._value
|
||||
return self._value
|
||||
|
||||
|
|
@ -69,10 +79,15 @@ class _Setting(object):
|
|||
if reply:
|
||||
self._value = self._validator.validate_read(reply)
|
||||
if self.persister and self.name not in self.persister:
|
||||
# Don't update the persister if it already has a value,
|
||||
# otherwise the first read might overwrite the value we wanted.
|
||||
self.persister[self.name] = self._value
|
||||
return self._value
|
||||
|
||||
def write(self, value):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
|
||||
if self._device:
|
||||
data_bytes = self._validator.prepare_write(value)
|
||||
reply = self._rw.write(self._device, data_bytes)
|
||||
|
|
@ -83,6 +98,9 @@ class _Setting(object):
|
|||
return self._value
|
||||
|
||||
def apply(self):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
|
||||
if self._value is not None:
|
||||
self.write(self._value)
|
||||
|
||||
|
|
@ -97,7 +115,7 @@ class _Setting(object):
|
|||
# read/write low-level operators
|
||||
#
|
||||
|
||||
class _RegisterRW(object):
|
||||
class RegisterRW(object):
|
||||
__slots__ = ['register']
|
||||
|
||||
kind = _NamedInt(0x01, 'register')
|
||||
|
|
@ -113,7 +131,7 @@ class _RegisterRW(object):
|
|||
return device.write_register(self.register, data_bytes)
|
||||
|
||||
|
||||
class _FeatureRW(object):
|
||||
class FeatureRW(object):
|
||||
__slots__ = ['feature', 'read_fnid', 'write_fnid']
|
||||
|
||||
kind = _NamedInt(0x02, 'feature')
|
||||
|
|
@ -139,7 +157,7 @@ class _FeatureRW(object):
|
|||
# handle the conversion from read bytes, to setting value, and back
|
||||
#
|
||||
|
||||
class _BooleanValidator(object):
|
||||
class BooleanValidator(object):
|
||||
__slots__ = ['true_value', 'false_value', 'mask', 'write_returns_value']
|
||||
|
||||
kind = KIND.toggle
|
||||
|
|
@ -181,7 +199,7 @@ class _BooleanValidator(object):
|
|||
return bool(value)
|
||||
|
||||
|
||||
class _ChoicesValidator(object):
|
||||
class ChoicesValidator(object):
|
||||
__slots__ = ['choices', 'write_returns_value']
|
||||
|
||||
kind = KIND.choice
|
||||
|
|
@ -218,33 +236,5 @@ class _ChoicesValidator(object):
|
|||
# be any reply_bytes to check
|
||||
return self.choices[value]
|
||||
|
||||
#
|
||||
# pre-defined basic setting descriptors
|
||||
#
|
||||
|
||||
def register_toggle(name, register,
|
||||
true_value=_BooleanValidator.default_true, false_value=_BooleanValidator.default_false,
|
||||
mask=_BooleanValidator.default_mask, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
rw = _RegisterRW(register)
|
||||
validator = _BooleanValidator(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
||||
|
||||
|
||||
def register_choices(name, register, choices,
|
||||
kind=KIND.choice, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
assert choices
|
||||
rw = _RegisterRW(register)
|
||||
validator = _ChoicesValidator(choices, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, kind=kind, label=label, description=description, device_kind=device_kind)
|
||||
|
||||
|
||||
def feature_toggle(name, feature,
|
||||
read_function_id=_FeatureRW.default_read_fnid, write_function_id=_FeatureRW.default_write_fnid,
|
||||
true_value=_BooleanValidator.default_true, false_value=_BooleanValidator.default_false,
|
||||
mask=_BooleanValidator.default_mask, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||
validator = _BooleanValidator(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
||||
__all__ = ('KIND', 'Setting', 'RegisterRW', 'FeatureRW', 'BooleanValidator', 'ChoicesValidator')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
from . import hidpp10 as _hidpp10
|
||||
from . import hidpp20 as _hidpp20
|
||||
from .settings import (
|
||||
KIND as _KIND,
|
||||
Setting as _Setting,
|
||||
RegisterRW as _RegisterRW,
|
||||
FeatureRW as _FeatureRW,
|
||||
BooleanValidator as _BooleanV,
|
||||
ChoicesValidator as _ChoicesV,
|
||||
)
|
||||
|
||||
#
|
||||
# pre-defined basic setting descriptors
|
||||
#
|
||||
|
||||
def register_toggle(name, register,
|
||||
true_value=_BooleanV.default_true, false_value=_BooleanV.default_false,
|
||||
mask=_BooleanV.default_mask, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
rw = _RegisterRW(register)
|
||||
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
||||
|
||||
|
||||
def register_choices(name, register, choices,
|
||||
kind=_KIND.choice, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
assert choices
|
||||
rw = _RegisterRW(register)
|
||||
validator = _ChoicesV(choices, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, kind=kind, label=label, description=description, device_kind=device_kind)
|
||||
|
||||
|
||||
def feature_toggle(name, feature,
|
||||
read_function_id=_FeatureRW.default_read_fnid, write_function_id=_FeatureRW.default_write_fnid,
|
||||
true_value=_BooleanV.default_true, false_value=_BooleanV.default_false,
|
||||
mask=_BooleanV.default_mask, write_returns_value=False,
|
||||
label=None, description=None, device_kind=None):
|
||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask, write_returns_value=write_returns_value)
|
||||
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
||||
|
||||
#
|
||||
# common strings for settings
|
||||
#
|
||||
|
||||
_SMOOTH_SCROLL = ('smooth-scroll', 'Smooth Scrolling',
|
||||
'High-sensitivity mode for vertical scroll with the wheel.')
|
||||
_DPI = ('dpi', 'Sensitivity (DPI)', 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.\n'
|
||||
'\n'
|
||||
'When unset, the F1..F12 keys will activate their standard function,\n'
|
||||
'and you must hold the FN key to activate their special function.'))
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def _register_fn_swap(register=0x09, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
||||
return register_toggle(_FN_SWAP[0], register, true_value=true_value, mask=mask,
|
||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
||||
|
||||
def _register_smooth_scroll(register=0x01, true_value=0x40, mask=0x40):
|
||||
return register_toggle(_SMOOTH_SCROLL[0], register, true_value=true_value, mask=mask,
|
||||
label=_SMOOTH_SCROLL[1], description=_SMOOTH_SCROLL[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
||||
|
||||
def _register_dpi(register=0x63, choices=None):
|
||||
return register_choices(_DPI[0], register, choices,
|
||||
label=_DPI[1], description=_DPI[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.mouse)
|
||||
|
||||
|
||||
def _feature_fn_swap():
|
||||
return feature_toggle(_FN_SWAP[0], _hidpp20.FEATURE.FN_INVERSION,
|
||||
write_returns_value=True,
|
||||
label=_FN_SWAP[1], description=_FN_SWAP[2],
|
||||
device_kind=_hidpp10.DEVICE_KIND.keyboard)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from collections import namedtuple
|
||||
_SETTINGS_LIST = namedtuple('_SETTINGS_LIST', [
|
||||
'fn_swap',
|
||||
'smooth_scroll',
|
||||
'dpi',
|
||||
'hand_detection',
|
||||
'typing_illumination',
|
||||
])
|
||||
del namedtuple
|
||||
|
||||
Register = _SETTINGS_LIST(
|
||||
fn_swap=_register_fn_swap,
|
||||
smooth_scroll=_register_smooth_scroll,
|
||||
dpi=_register_dpi,
|
||||
hand_detection=None,
|
||||
typing_illumination=None,
|
||||
)
|
||||
Feature = _SETTINGS_LIST(
|
||||
fn_swap=_feature_fn_swap,
|
||||
smooth_scroll=None,
|
||||
dpi=None,
|
||||
hand_detection=None,
|
||||
typing_illumination=None,
|
||||
)
|
||||
|
||||
del _SETTINGS_LIST
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def check_feature_settings(device, already_known):
|
||||
"""Try to auto-detect device settings by the HID++ 2.0 features they have."""
|
||||
if device.protocol is not None and device.protocol < 2.0:
|
||||
return
|
||||
if not any(s.name == _FN_SWAP[0] for s in already_known) and _hidpp20.FEATURE.FN_INVERSION in device.features:
|
||||
fn_swap = Feature.fn_swap()
|
||||
already_known.append(fn_swap(device))
|
||||
Loading…
Reference in New Issue