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