moved settings templates into separate .py

This commit is contained in:
Daniel Pavel 2013-07-01 15:24:30 +02:00
parent b1e9480f5a
commit ceba698678
3 changed files with 184 additions and 99 deletions

View File

@ -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(),
],
)

View File

@ -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')

View File

@ -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))