yapf: adjust style to not indent closing brackets
Signed-off-by: Filipe Laíns <lains@archlinux.org>
This commit is contained in:
parent
bbaa144397
commit
27c90fa736
|
@ -36,8 +36,10 @@ def init_paths():
|
||||||
sys.path[0].encode(sys.getfilesystemencoding())
|
sys.path[0].encode(sys.getfilesystemencoding())
|
||||||
|
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
sys.stderr.write('ERROR: Solaar cannot recognize encoding of filesystem path, '
|
sys.stderr.write(
|
||||||
'this may happen because non UTF-8 characters in the pathname.\n')
|
'ERROR: Solaar cannot recognize encoding of filesystem path, '
|
||||||
|
'this may happen because non UTF-8 characters in the pathname.\n'
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
prefix = _path.normpath(_path.join(_path.realpath(decoded_path), '..'))
|
||||||
|
|
|
@ -80,11 +80,13 @@ def _print(marker, data, scroll=False):
|
||||||
|
|
||||||
if interactive and scroll:
|
if interactive and scroll:
|
||||||
# scroll the entire screen above the current line up by 1 line
|
# scroll the entire screen above the current line up by 1 line
|
||||||
sys.stdout.write('\033[s' # save cursor position
|
sys.stdout.write(
|
||||||
'\033[S' # scroll up
|
'\033[s' # save cursor position
|
||||||
'\033[A' # cursor up
|
'\033[S' # scroll up
|
||||||
'\033[L' # insert 1 line
|
'\033[A' # cursor up
|
||||||
'\033[G') # move cursor to column 1
|
'\033[L' # insert 1 line
|
||||||
|
'\033[G'
|
||||||
|
) # move cursor to column 1
|
||||||
sys.stdout.write(s)
|
sys.stdout.write(s)
|
||||||
if interactive and scroll:
|
if interactive and scroll:
|
||||||
# restore cursor position
|
# restore cursor position
|
||||||
|
@ -164,8 +166,10 @@ def _open(args):
|
||||||
if not handle:
|
if not handle:
|
||||||
sys.exit('!! Failed to open %s, aborting.' % device)
|
sys.exit('!! Failed to open %s, aborting.' % device)
|
||||||
|
|
||||||
print('.. Opened handle %r, vendor %r product %r serial %r.' %
|
print(
|
||||||
(handle, _hid.get_manufacturer(handle), _hid.get_product(handle), _hid.get_serial(handle)))
|
'.. Opened handle %r, vendor %r product %r serial %r.' %
|
||||||
|
(handle, _hid.get_manufacturer(handle), _hid.get_product(handle), _hid.get_serial(handle))
|
||||||
|
)
|
||||||
if args.hidpp:
|
if args.hidpp:
|
||||||
if _hid.get_manufacturer(handle) != b'Logitech':
|
if _hid.get_manufacturer(handle) != b'Logitech':
|
||||||
sys.exit('!! Only Logitech devices support the HID++ protocol.')
|
sys.exit('!! Only Logitech devices support the HID++ protocol.')
|
||||||
|
@ -188,10 +192,12 @@ def _parse_arguments():
|
||||||
arg_parser = argparse.ArgumentParser()
|
arg_parser = argparse.ArgumentParser()
|
||||||
arg_parser.add_argument('--history', help='history file (default ~/.hidconsole-history)')
|
arg_parser.add_argument('--history', help='history file (default ~/.hidconsole-history)')
|
||||||
arg_parser.add_argument('--hidpp', action='store_true', help='ensure input data is a valid HID++ request')
|
arg_parser.add_argument('--hidpp', action='store_true', help='ensure input data is a valid HID++ request')
|
||||||
arg_parser.add_argument('device',
|
arg_parser.add_argument(
|
||||||
nargs='?',
|
'device',
|
||||||
help='linux device to connect to (/dev/hidrawX); '
|
nargs='?',
|
||||||
'may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver')
|
help='linux device to connect to (/dev/hidrawX); '
|
||||||
|
'may be omitted if --hidpp is given, in which case it looks for the first Logitech receiver'
|
||||||
|
)
|
||||||
return arg_parser.parse_args()
|
return arg_parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,17 +42,19 @@ from pyudev import Monitor as _Monitor
|
||||||
|
|
||||||
native_implementation = 'udev'
|
native_implementation = 'udev'
|
||||||
|
|
||||||
DeviceInfo = namedtuple('DeviceInfo', [
|
DeviceInfo = namedtuple(
|
||||||
'path',
|
'DeviceInfo', [
|
||||||
'vendor_id',
|
'path',
|
||||||
'product_id',
|
'vendor_id',
|
||||||
'serial',
|
'product_id',
|
||||||
'release',
|
'serial',
|
||||||
'manufacturer',
|
'release',
|
||||||
'product',
|
'manufacturer',
|
||||||
'interface',
|
'product',
|
||||||
'driver',
|
'interface',
|
||||||
])
|
'driver',
|
||||||
|
]
|
||||||
|
)
|
||||||
del namedtuple
|
del namedtuple
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -121,29 +123,33 @@ def _match(action, device, filter):
|
||||||
return
|
return
|
||||||
|
|
||||||
attrs = usb_device.attributes
|
attrs = usb_device.attributes
|
||||||
d_info = DeviceInfo(path=device.device_node,
|
d_info = DeviceInfo(
|
||||||
vendor_id=vid[-4:],
|
path=device.device_node,
|
||||||
product_id=pid[-4:],
|
vendor_id=vid[-4:],
|
||||||
serial=hid_device.get('HID_UNIQ'),
|
product_id=pid[-4:],
|
||||||
release=attrs.get('bcdDevice'),
|
serial=hid_device.get('HID_UNIQ'),
|
||||||
manufacturer=attrs.get('manufacturer'),
|
release=attrs.get('bcdDevice'),
|
||||||
product=attrs.get('product'),
|
manufacturer=attrs.get('manufacturer'),
|
||||||
interface=usb_interface,
|
product=attrs.get('product'),
|
||||||
driver=hid_driver_name)
|
interface=usb_interface,
|
||||||
|
driver=hid_driver_name
|
||||||
|
)
|
||||||
return d_info
|
return d_info
|
||||||
|
|
||||||
elif action == 'remove':
|
elif action == 'remove':
|
||||||
# print (dict(device), dict(usb_device))
|
# print (dict(device), dict(usb_device))
|
||||||
|
|
||||||
d_info = DeviceInfo(path=device.device_node,
|
d_info = DeviceInfo(
|
||||||
vendor_id=vid[-4:],
|
path=device.device_node,
|
||||||
product_id=pid[-4:],
|
vendor_id=vid[-4:],
|
||||||
serial=None,
|
product_id=pid[-4:],
|
||||||
release=None,
|
serial=None,
|
||||||
manufacturer=None,
|
release=None,
|
||||||
product=None,
|
manufacturer=None,
|
||||||
interface=None,
|
product=None,
|
||||||
driver=None)
|
interface=None,
|
||||||
|
driver=None
|
||||||
|
)
|
||||||
return d_info
|
return d_info
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -287,20 +287,22 @@ def make_notification(devnumber, data):
|
||||||
|
|
||||||
address = ord(data[1:2])
|
address = ord(data[1:2])
|
||||||
if (
|
if (
|
||||||
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
# standard HID++ 1.0 notification, SubId may be 0x40 - 0x7F
|
||||||
(sub_id >= 0x40) or # noqa: E131
|
(sub_id >= 0x40) or # noqa: E131
|
||||||
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
# custom HID++1.0 battery events, where SubId is 0x07/0x0D
|
||||||
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00') or
|
(sub_id in (0x07, 0x0D) and len(data) == 5 and data[4:5] == b'\x00') or
|
||||||
# custom HID++1.0 illumination event, where SubId is 0x17
|
# custom HID++1.0 illumination event, where SubId is 0x17
|
||||||
(sub_id == 0x17 and len(data) == 5) or
|
(sub_id == 0x17 and len(data) == 5) or
|
||||||
# HID++ 2.0 feature notifications have the SoftwareID 0
|
# HID++ 2.0 feature notifications have the SoftwareID 0
|
||||||
(address & 0x0F == 0x00)): # noqa: E129
|
(address & 0x0F == 0x00)
|
||||||
|
): # noqa: E129
|
||||||
return _HIDPP_Notification(devnumber, sub_id, address, data[2:])
|
return _HIDPP_Notification(devnumber, sub_id, address, data[2:])
|
||||||
|
|
||||||
|
|
||||||
_HIDPP_Notification = namedtuple('_HIDPP_Notification', ('devnumber', 'sub_id', 'address', 'data'))
|
_HIDPP_Notification = namedtuple('_HIDPP_Notification', ('devnumber', 'sub_id', 'address', 'data'))
|
||||||
_HIDPP_Notification.__str__ = lambda self: 'Notification(%d,%02X,%02X,%s)' % (self.devnumber, self.sub_id, self.address,
|
_HIDPP_Notification.__str__ = lambda self: 'Notification(%d,%02X,%02X,%s)' % (
|
||||||
_strhex(self.data))
|
self.devnumber, self.sub_id, self.address, _strhex(self.data)
|
||||||
|
)
|
||||||
_HIDPP_Notification.__unicode__ = _HIDPP_Notification.__str__
|
_HIDPP_Notification.__unicode__ = _HIDPP_Notification.__str__
|
||||||
DJ_NOTIFICATION_LENGTH = _MEDIUM_MESSAGE_SIZE - 4 # to allow easy distinguishing of DJ notifications
|
DJ_NOTIFICATION_LENGTH = _MEDIUM_MESSAGE_SIZE - 4 # to allow easy distinguishing of DJ notifications
|
||||||
del namedtuple
|
del namedtuple
|
||||||
|
@ -374,15 +376,19 @@ def request(handle, devnumber, request_id, *params):
|
||||||
# raise NoSuchDevice(number=devnumber, request=request_id)
|
# raise NoSuchDevice(number=devnumber, request=request_id)
|
||||||
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug('(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
_log.debug(
|
||||||
_hidpp10.ERROR[error])
|
'(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
||||||
|
_hidpp10.ERROR[error]
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]:
|
if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]:
|
||||||
# a HID++ 2.0 feature call returned with an error
|
# a HID++ 2.0 feature call returned with an error
|
||||||
error = ord(reply_data[3:4])
|
error = ord(reply_data[3:4])
|
||||||
_log.error('(%s) device %d error on feature request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
_log.error(
|
||||||
_hidpp20.ERROR[error])
|
'(%s) device %d error on feature request {%04X}: %d = %s', handle, devnumber, request_id, error,
|
||||||
|
_hidpp20.ERROR[error]
|
||||||
|
)
|
||||||
raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params)
|
raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params)
|
||||||
|
|
||||||
if reply_data[:2] == request_data[:2]:
|
if reply_data[:2] == request_data[:2]:
|
||||||
|
@ -423,8 +429,10 @@ def request(handle, devnumber, request_id, *params):
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
# _log.debug("(%s) still waiting for reply, delta %f", handle, delta)
|
||||||
|
|
||||||
_log.warn('timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]', delta, timeout, devnumber, request_id,
|
_log.warn(
|
||||||
_strhex(params))
|
'timeout (%0.2f/%0.2f) on device %d request {%04X} params [%s]', delta, timeout, devnumber, request_id,
|
||||||
|
_strhex(params)
|
||||||
|
)
|
||||||
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
# raise DeviceUnreachable(number=devnumber, request=request_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,8 @@ FirmwareInfo = namedtuple('FirmwareInfo', ['kind', 'name', 'version', 'extras'])
|
||||||
"""Reprogrammable keys information."""
|
"""Reprogrammable keys information."""
|
||||||
ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', ['index', 'key', 'task', 'flags'])
|
ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', ['index', 'key', 'task', 'flags'])
|
||||||
|
|
||||||
ReprogrammableKeyInfoV4 = namedtuple('ReprogrammableKeyInfoV4',
|
ReprogrammableKeyInfoV4 = namedtuple(
|
||||||
['index', 'key', 'task', 'flags', 'pos', 'group', 'group_mask', 'remapped'])
|
'ReprogrammableKeyInfoV4', ['index', 'key', 'task', 'flags', 'pos', 'group', 'group_mask', 'remapped']
|
||||||
|
)
|
||||||
|
|
||||||
del namedtuple
|
del namedtuple
|
||||||
|
|
|
@ -31,8 +31,9 @@ from .settings_templates import RegisterSettings as _RS
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
|
_DeviceDescriptor = namedtuple(
|
||||||
('name', 'kind', 'wpid', 'codename', 'protocol', 'registers', 'settings', 'persister'))
|
'_DeviceDescriptor', ('name', 'kind', 'wpid', 'codename', 'protocol', 'registers', 'settings', 'persister')
|
||||||
|
)
|
||||||
del namedtuple
|
del namedtuple
|
||||||
|
|
||||||
DEVICES = {}
|
DEVICES = {}
|
||||||
|
@ -42,8 +43,10 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
|
||||||
assert name
|
assert name
|
||||||
|
|
||||||
if kind is None:
|
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
|
kind = (
|
||||||
else _DK.touchpad if 'Touchpad' in name else _DK.trackball if 'Trackball' in name else None)
|
_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
|
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
|
# heuristic: the codename is the last word in the device name
|
||||||
|
@ -70,14 +73,16 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
|
||||||
elif w[0:1] == '2':
|
elif w[0:1] == '2':
|
||||||
assert kind in (_DK.keyboard, _DK.numpad), '%s has protocol %0.1f, wpid %s' % (name, protocol, w)
|
assert kind in (_DK.keyboard, _DK.numpad), '%s has protocol %0.1f, wpid %s' % (name, protocol, w)
|
||||||
|
|
||||||
device_descriptor = _DeviceDescriptor(name=name,
|
device_descriptor = _DeviceDescriptor(
|
||||||
kind=kind,
|
name=name,
|
||||||
wpid=wpid,
|
kind=kind,
|
||||||
codename=codename,
|
wpid=wpid,
|
||||||
protocol=protocol,
|
codename=codename,
|
||||||
registers=registers,
|
protocol=protocol,
|
||||||
settings=settings,
|
registers=registers,
|
||||||
persister=persister)
|
settings=settings,
|
||||||
|
persister=persister
|
||||||
|
)
|
||||||
|
|
||||||
assert codename not in DEVICES, 'duplicate codename in device descriptors: %s' % (DEVICES[codename], )
|
assert codename not in DEVICES, 'duplicate codename in device descriptors: %s' % (DEVICES[codename], )
|
||||||
DEVICES[codename] = device_descriptor
|
DEVICES[codename] = device_descriptor
|
||||||
|
@ -293,24 +298,28 @@ _D(
|
||||||
|
|
||||||
_D('Wireless Mouse M150', protocol=2.0, wpid='4022')
|
_D('Wireless Mouse M150', protocol=2.0, wpid='4022')
|
||||||
_D('Wireless Mouse M175', protocol=2.0, wpid='4008')
|
_D('Wireless Mouse M175', protocol=2.0, wpid='4008')
|
||||||
_D('Wireless Mouse M185 new',
|
_D(
|
||||||
codename='M185n',
|
'Wireless Mouse M185 new',
|
||||||
protocol=4.5,
|
codename='M185n',
|
||||||
wpid='4054',
|
protocol=4.5,
|
||||||
settings=[
|
wpid='4054',
|
||||||
_FS.lowres_smooth_scroll(),
|
settings=[
|
||||||
_FS.pointer_speed(),
|
_FS.lowres_smooth_scroll(),
|
||||||
])
|
_FS.pointer_speed(),
|
||||||
|
]
|
||||||
|
)
|
||||||
# Apparently Logitech uses wpid 4055 for three different mice
|
# 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
|
# That's not so strange, as M185 is used on both Unifying-ready and non-Unifying-ready mice
|
||||||
_D('Wireless Mouse M185/M235/M310',
|
_D(
|
||||||
codename='M185/M235/M310',
|
'Wireless Mouse M185/M235/M310',
|
||||||
protocol=4.5,
|
codename='M185/M235/M310',
|
||||||
wpid='4055',
|
protocol=4.5,
|
||||||
settings=[
|
wpid='4055',
|
||||||
_FS.lowres_smooth_scroll(),
|
settings=[
|
||||||
_FS.pointer_speed(),
|
_FS.lowres_smooth_scroll(),
|
||||||
])
|
_FS.pointer_speed(),
|
||||||
|
]
|
||||||
|
)
|
||||||
_D('Wireless Mouse M185', protocol=2.0, wpid='4038')
|
_D('Wireless Mouse M185', protocol=2.0, wpid='4038')
|
||||||
_D('Wireless Mouse M187', protocol=2.0, wpid='4019')
|
_D('Wireless Mouse M187', protocol=2.0, wpid='4019')
|
||||||
_D('Wireless Mouse M215', protocol=1.0, wpid='1020')
|
_D('Wireless Mouse M215', protocol=1.0, wpid='1020')
|
||||||
|
@ -390,15 +399,17 @@ _D(
|
||||||
_RS.side_scroll(),
|
_RS.side_scroll(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
_D('Marathon Mouse M705 (M-R0073)',
|
_D(
|
||||||
codename='M705 (M-R0073)',
|
'Marathon Mouse M705 (M-R0073)',
|
||||||
protocol=4.5,
|
codename='M705 (M-R0073)',
|
||||||
wpid='406D',
|
protocol=4.5,
|
||||||
settings=[
|
wpid='406D',
|
||||||
_FS.hires_smooth_invert(),
|
settings=[
|
||||||
_FS.hires_smooth_resolution(),
|
_FS.hires_smooth_invert(),
|
||||||
_FS.pointer_speed(),
|
_FS.hires_smooth_resolution(),
|
||||||
])
|
_FS.pointer_speed(),
|
||||||
|
]
|
||||||
|
)
|
||||||
_D('Zone Touch Mouse T400')
|
_D('Zone Touch Mouse T400')
|
||||||
_D('Touch Mouse T620', protocol=2.0)
|
_D('Touch Mouse T620', protocol=2.0)
|
||||||
_D('Logitech Cube', kind=_DK.mouse, protocol=2.0)
|
_D('Logitech Cube', kind=_DK.mouse, protocol=2.0)
|
||||||
|
|
|
@ -38,17 +38,19 @@ del getLogger
|
||||||
|
|
||||||
DEVICE_KIND = _NamedInts(keyboard=0x01, mouse=0x02, numpad=0x03, presenter=0x04, trackball=0x08, touchpad=0x09)
|
DEVICE_KIND = _NamedInts(keyboard=0x01, mouse=0x02, numpad=0x03, presenter=0x04, trackball=0x08, touchpad=0x09)
|
||||||
|
|
||||||
POWER_SWITCH_LOCATION = _NamedInts(base=0x01,
|
POWER_SWITCH_LOCATION = _NamedInts(
|
||||||
top_case=0x02,
|
base=0x01,
|
||||||
edge_of_top_right_corner=0x03,
|
top_case=0x02,
|
||||||
top_left_corner=0x05,
|
edge_of_top_right_corner=0x03,
|
||||||
bottom_left_corner=0x06,
|
top_left_corner=0x05,
|
||||||
top_right_corner=0x07,
|
bottom_left_corner=0x06,
|
||||||
bottom_right_corner=0x08,
|
top_right_corner=0x07,
|
||||||
top_edge=0x09,
|
bottom_right_corner=0x08,
|
||||||
right_edge=0x0A,
|
top_edge=0x09,
|
||||||
left_edge=0x0B,
|
right_edge=0x0A,
|
||||||
bottom_edge=0x0C)
|
left_edge=0x0B,
|
||||||
|
bottom_edge=0x0C
|
||||||
|
)
|
||||||
|
|
||||||
# Some flags are used both by devices and receivers. The Logitech documentation
|
# Some flags are used both by devices and receivers. The Logitech documentation
|
||||||
# mentions that the first and last (third) byte are used for devices while the
|
# mentions that the first and last (third) byte are used for devices while the
|
||||||
|
@ -72,18 +74,20 @@ NOTIFICATION_FLAG = _NamedInts(
|
||||||
wireless=0x000100, # notify when the device wireless goes on/off-line
|
wireless=0x000100, # notify when the device wireless goes on/off-line
|
||||||
)
|
)
|
||||||
|
|
||||||
ERROR = _NamedInts(invalid_SubID__command=0x01,
|
ERROR = _NamedInts(
|
||||||
invalid_address=0x02,
|
invalid_SubID__command=0x01,
|
||||||
invalid_value=0x03,
|
invalid_address=0x02,
|
||||||
connection_request_failed=0x04,
|
invalid_value=0x03,
|
||||||
too_many_devices=0x05,
|
connection_request_failed=0x04,
|
||||||
already_exists=0x06,
|
too_many_devices=0x05,
|
||||||
busy=0x07,
|
already_exists=0x06,
|
||||||
unknown_device=0x08,
|
busy=0x07,
|
||||||
resource_error=0x09,
|
unknown_device=0x08,
|
||||||
request_unavailable=0x0A,
|
resource_error=0x09,
|
||||||
unsupported_parameter_value=0x0B,
|
request_unavailable=0x0A,
|
||||||
wrong_pin_code=0x0C)
|
unsupported_parameter_value=0x0B,
|
||||||
|
wrong_pin_code=0x0C
|
||||||
|
)
|
||||||
|
|
||||||
PAIRING_ERRORS = _NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06)
|
PAIRING_ERRORS = _NamedInts(device_timeout=0x01, device_not_supported=0x02, too_many_devices=0x03, sequence_timeout=0x06)
|
||||||
|
|
||||||
|
@ -167,8 +171,10 @@ def parse_battery_status(register, reply):
|
||||||
if register == REGISTERS.battery_charge:
|
if register == REGISTERS.battery_charge:
|
||||||
charge = ord(reply[:1])
|
charge = ord(reply[:1])
|
||||||
status_byte = ord(reply[2:3]) & 0xF0
|
status_byte = ord(reply[2:3]) & 0xF0
|
||||||
status_text = (BATTERY_STATUS.discharging if status_byte == 0x30 else BATTERY_STATUS.recharging
|
status_text = (
|
||||||
if status_byte == 0x50 else BATTERY_STATUS.full if status_byte == 0x90 else None)
|
BATTERY_STATUS.discharging if status_byte == 0x30 else
|
||||||
|
BATTERY_STATUS.recharging if status_byte == 0x50 else BATTERY_STATUS.full if status_byte == 0x90 else None
|
||||||
|
)
|
||||||
return charge, status_text, None
|
return charge, status_text, None
|
||||||
|
|
||||||
if register == REGISTERS.battery_status:
|
if register == REGISTERS.battery_status:
|
||||||
|
@ -179,7 +185,8 @@ def parse_battery_status(register, reply):
|
||||||
else BATTERY_APPOX.low if status_byte == 3 # low
|
else BATTERY_APPOX.low if status_byte == 3 # low
|
||||||
else BATTERY_APPOX.critical if status_byte == 1 # critical
|
else BATTERY_APPOX.critical if status_byte == 1 # critical
|
||||||
# pure 'charging' notifications may come without a status
|
# pure 'charging' notifications may come without a status
|
||||||
else BATTERY_APPOX.empty)
|
else BATTERY_APPOX.empty
|
||||||
|
)
|
||||||
|
|
||||||
charging_byte = ord(reply[1:2])
|
charging_byte = ord(reply[1:2])
|
||||||
if charging_byte == 0x00:
|
if charging_byte == 0x00:
|
||||||
|
|
|
@ -160,26 +160,23 @@ FEATURE._fallback = lambda x: 'unknown:%04X' % x
|
||||||
|
|
||||||
FEATURE_FLAG = _NamedInts(internal=0x20, hidden=0x40, obsolete=0x80)
|
FEATURE_FLAG = _NamedInts(internal=0x20, hidden=0x40, obsolete=0x80)
|
||||||
|
|
||||||
DEVICE_KIND = _NamedInts(keyboard=0x00,
|
DEVICE_KIND = _NamedInts(
|
||||||
remote_control=0x01,
|
keyboard=0x00, remote_control=0x01, numpad=0x02, mouse=0x03, touchpad=0x04, trackball=0x05, presenter=0x06, receiver=0x07
|
||||||
numpad=0x02,
|
)
|
||||||
mouse=0x03,
|
|
||||||
touchpad=0x04,
|
|
||||||
trackball=0x05,
|
|
||||||
presenter=0x06,
|
|
||||||
receiver=0x07)
|
|
||||||
|
|
||||||
FIRMWARE_KIND = _NamedInts(Firmware=0x00, Bootloader=0x01, Hardware=0x02, Other=0x03)
|
FIRMWARE_KIND = _NamedInts(Firmware=0x00, Bootloader=0x01, Hardware=0x02, Other=0x03)
|
||||||
|
|
||||||
BATTERY_OK = lambda status: status not in (BATTERY_STATUS.invalid_battery, BATTERY_STATUS.thermal_error)
|
BATTERY_OK = lambda status: status not in (BATTERY_STATUS.invalid_battery, BATTERY_STATUS.thermal_error)
|
||||||
|
|
||||||
BATTERY_STATUS = _NamedInts(discharging=0x00,
|
BATTERY_STATUS = _NamedInts(
|
||||||
recharging=0x01,
|
discharging=0x00,
|
||||||
almost_full=0x02,
|
recharging=0x01,
|
||||||
full=0x03,
|
almost_full=0x02,
|
||||||
slow_recharge=0x04,
|
full=0x03,
|
||||||
invalid_battery=0x05,
|
slow_recharge=0x04,
|
||||||
thermal_error=0x06)
|
invalid_battery=0x05,
|
||||||
|
thermal_error=0x06
|
||||||
|
)
|
||||||
|
|
||||||
CHARGE_STATUS = _NamedInts(charging=0x00, full=0x01, not_charging=0x02, error=0x07)
|
CHARGE_STATUS = _NamedInts(charging=0x00, full=0x01, not_charging=0x02, error=0x07)
|
||||||
|
|
||||||
|
@ -187,15 +184,17 @@ CHARGE_LEVEL = _NamedInts(average=50, full=90, critical=5)
|
||||||
|
|
||||||
CHARGE_TYPE = _NamedInts(standard=0x00, fast=0x01, slow=0x02)
|
CHARGE_TYPE = _NamedInts(standard=0x00, fast=0x01, slow=0x02)
|
||||||
|
|
||||||
ERROR = _NamedInts(unknown=0x01,
|
ERROR = _NamedInts(
|
||||||
invalid_argument=0x02,
|
unknown=0x01,
|
||||||
out_of_range=0x03,
|
invalid_argument=0x02,
|
||||||
hardware_error=0x04,
|
out_of_range=0x03,
|
||||||
logitech_internal=0x05,
|
hardware_error=0x04,
|
||||||
invalid_feature_index=0x06,
|
logitech_internal=0x05,
|
||||||
invalid_function=0x07,
|
invalid_feature_index=0x06,
|
||||||
busy=0x08,
|
invalid_function=0x07,
|
||||||
unsupported=0x09)
|
busy=0x08,
|
||||||
|
unsupported=0x09
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -388,8 +387,9 @@ class KeysArray(object):
|
||||||
self.keys[index] = _ReprogrammableKeyInfo(index, ctrl_id_text, ctrl_task_text, flags)
|
self.keys[index] = _ReprogrammableKeyInfo(index, ctrl_id_text, ctrl_task_text, flags)
|
||||||
if self.keyversion == 4:
|
if self.keyversion == 4:
|
||||||
try:
|
try:
|
||||||
mapped_data = feature_request(self.device, FEATURE.REPROG_CONTROLS_V4, 0x20, key & 0xff00,
|
mapped_data = feature_request(
|
||||||
key & 0xff)
|
self.device, FEATURE.REPROG_CONTROLS_V4, 0x20, key & 0xff00, key & 0xff
|
||||||
|
)
|
||||||
if mapped_data:
|
if mapped_data:
|
||||||
remap_key, remap_flag, remapped = _unpack('!HBH', mapped_data[:5])
|
remap_key, remap_flag, remapped = _unpack('!HBH', mapped_data[:5])
|
||||||
# if key not mapped map it to itself for display
|
# if key not mapped map it to itself for display
|
||||||
|
@ -401,8 +401,9 @@ class KeysArray(object):
|
||||||
# remap_flag = 0
|
# remap_flag = 0
|
||||||
|
|
||||||
remapped_text = special_keys.CONTROL[remapped]
|
remapped_text = special_keys.CONTROL[remapped]
|
||||||
self.keys[index] = _ReprogrammableKeyInfoV4(index, ctrl_id_text, ctrl_task_text, flags, pos, group,
|
self.keys[index] = _ReprogrammableKeyInfoV4(
|
||||||
gmask, remapped_text)
|
index, ctrl_id_text, ctrl_task_text, flags, pos, group, gmask, remapped_text
|
||||||
|
)
|
||||||
|
|
||||||
return self.keys[index]
|
return self.keys[index]
|
||||||
|
|
||||||
|
@ -517,8 +518,10 @@ def get_battery(device):
|
||||||
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
|
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
|
||||||
discharge = None if discharge == 0 else discharge
|
discharge = None if discharge == 0 else discharge
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug('device %d battery %d%% charged, next level %d%% charge, status %d = %s', device.number, discharge,
|
_log.debug(
|
||||||
dischargeNext, status, BATTERY_STATUS[status])
|
'device %d battery %d%% charged, next level %d%% charge, status %d = %s', device.number, discharge,
|
||||||
|
dischargeNext, status, BATTERY_STATUS[status]
|
||||||
|
)
|
||||||
return discharge, BATTERY_STATUS[status], dischargeNext
|
return discharge, BATTERY_STATUS[status], dischargeNext
|
||||||
|
|
||||||
|
|
||||||
|
@ -553,8 +556,10 @@ def decipher_voltage(voltage_report):
|
||||||
charge_lvl = CHARGE_LEVEL.critical
|
charge_lvl = CHARGE_LEVEL.critical
|
||||||
|
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug('device ???, battery voltage %d mV, charging = %s, charge status %d = %s, charge level %s, charge type %s',
|
_log.debug(
|
||||||
voltage, status, (flags & 0x03), charge_sts, charge_lvl, charge_type)
|
'device ???, battery voltage %d mV, charging = %s, charge status %d = %s, charge level %s, charge type %s',
|
||||||
|
voltage, status, (flags & 0x03), charge_sts, charge_lvl, charge_type
|
||||||
|
)
|
||||||
|
|
||||||
return charge_lvl, status, voltage, charge_sts, charge_type
|
return charge_lvl, status, voltage, charge_sts, charge_type
|
||||||
|
|
||||||
|
@ -587,8 +592,9 @@ def get_vertical_scrolling_info(device):
|
||||||
vertical_scrolling_info = feature_request(device, FEATURE.VERTICAL_SCROLLING)
|
vertical_scrolling_info = feature_request(device, FEATURE.VERTICAL_SCROLLING)
|
||||||
if vertical_scrolling_info:
|
if vertical_scrolling_info:
|
||||||
roller, ratchet, lines = _unpack('!BBB', vertical_scrolling_info[:3])
|
roller, ratchet, lines = _unpack('!BBB', vertical_scrolling_info[:3])
|
||||||
roller_type = ('reserved', 'standard', 'reserved', '3G', 'micro', 'normal touch pad', 'inverted touch pad',
|
roller_type = (
|
||||||
'reserved')[roller]
|
'reserved', 'standard', 'reserved', '3G', 'micro', 'normal touch pad', 'inverted touch pad', 'reserved'
|
||||||
|
)[roller]
|
||||||
return {'roller': roller_type, 'ratchet': ratchet, 'lines': lines}
|
return {'roller': roller_type, 'ratchet': ratchet, 'lines': lines}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -191,11 +191,13 @@ def _process_hidpp10_notification(device, status, n):
|
||||||
|
|
||||||
# wireless link notification
|
# wireless link notification
|
||||||
if n.sub_id == 0x41:
|
if n.sub_id == 0x41:
|
||||||
protocol_name = ('Bluetooth' if n.address == 0x01 else '27 MHz' if n.address == 0x02 else 'QUAD or eQUAD'
|
protocol_name = (
|
||||||
if n.address == 0x03 else 'eQUAD step 4 DJ' if n.address == 0x04 else 'DFU Lite' if n.address ==
|
'Bluetooth' if n.address == 0x01 else '27 MHz' if n.address == 0x02 else
|
||||||
0x05 else 'eQUAD step 4 Lite' if n.address == 0x06 else 'eQUAD step 4 Gaming' if n.address ==
|
'QUAD or eQUAD' if n.address == 0x03 else 'eQUAD step 4 DJ' if n.address == 0x04 else 'DFU Lite' if n.address ==
|
||||||
0x07 else 'eQUAD step 4 for gamepads' if n.address == 0x08 else 'eQUAD nano Lite' if n.address ==
|
0x05 else 'eQUAD step 4 Lite' if n.address == 0x06 else 'eQUAD step 4 Gaming' if n.address ==
|
||||||
0x0A else 'Lightspeed 1' if n.address == 0x0C else 'Lightspeed 1_1' if n.address == 0x0D else None)
|
0x07 else 'eQUAD step 4 for gamepads' if n.address == 0x08 else 'eQUAD nano Lite' if n.address ==
|
||||||
|
0x0A else 'Lightspeed 1' if n.address == 0x0C else 'Lightspeed 1_1' if n.address == 0x0D else None
|
||||||
|
)
|
||||||
if protocol_name:
|
if protocol_name:
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
wpid = _strhex(n.data[2:3] + n.data[1:2])
|
wpid = _strhex(n.data[2:3] + n.data[1:2])
|
||||||
|
@ -207,8 +209,10 @@ def _process_hidpp10_notification(device, status, n):
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
sw_present = bool(flags & 0x10)
|
sw_present = bool(flags & 0x10)
|
||||||
has_payload = bool(flags & 0x80)
|
has_payload = bool(flags & 0x80)
|
||||||
_log.debug('%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s', device,
|
_log.debug(
|
||||||
protocol_name, sw_present, link_encrypted, link_established, has_payload)
|
'%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s', device, protocol_name,
|
||||||
|
sw_present, link_encrypted, link_established, has_payload
|
||||||
|
)
|
||||||
status[_K.LINK_ENCRYPTED] = link_encrypted
|
status[_K.LINK_ENCRYPTED] = link_encrypted
|
||||||
status.changed(active=link_established)
|
status.changed(active=link_established)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -266,10 +266,12 @@ class PairedDevice(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if enable:
|
if enable:
|
||||||
set_flag_bits = (_hidpp10.NOTIFICATION_FLAG.battery_status
|
set_flag_bits = (
|
||||||
| _hidpp10.NOTIFICATION_FLAG.keyboard_illumination
|
_hidpp10.NOTIFICATION_FLAG.battery_status
|
||||||
| _hidpp10.NOTIFICATION_FLAG.wireless
|
| _hidpp10.NOTIFICATION_FLAG.keyboard_illumination
|
||||||
| _hidpp10.NOTIFICATION_FLAG.software_present)
|
| _hidpp10.NOTIFICATION_FLAG.wireless
|
||||||
|
| _hidpp10.NOTIFICATION_FLAG.software_present
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
set_flag_bits = 0
|
set_flag_bits = 0
|
||||||
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
|
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
|
||||||
|
@ -361,8 +363,9 @@ class Receiver(object):
|
||||||
|
|
||||||
self.name = product_info.get('name', '')
|
self.name = product_info.get('name', '')
|
||||||
self.re_pairs = product_info.get('re_pairs', False)
|
self.re_pairs = product_info.get('re_pairs', False)
|
||||||
self._str = '<%s(%s,%s%s)>' % (self.name.replace(
|
self._str = '<%s(%s,%s%s)>' % (
|
||||||
' ', ''), self.path, '' if isinstance(self.handle, int) else 'T', self.handle)
|
self.name.replace(' ', ''), self.path, '' if isinstance(self.handle, int) else 'T', self.handle
|
||||||
|
)
|
||||||
|
|
||||||
self._firmware = None
|
self._firmware = None
|
||||||
self._devices = {}
|
self._devices = {}
|
||||||
|
@ -398,9 +401,11 @@ class Receiver(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if enable:
|
if enable:
|
||||||
set_flag_bits = (_hidpp10.NOTIFICATION_FLAG.battery_status
|
set_flag_bits = (
|
||||||
| _hidpp10.NOTIFICATION_FLAG.wireless
|
_hidpp10.NOTIFICATION_FLAG.battery_status
|
||||||
| _hidpp10.NOTIFICATION_FLAG.software_present)
|
| _hidpp10.NOTIFICATION_FLAG.wireless
|
||||||
|
| _hidpp10.NOTIFICATION_FLAG.software_present
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
set_flag_bits = 0
|
set_flag_bits = 0
|
||||||
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
|
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
|
||||||
|
|
|
@ -167,8 +167,9 @@ class Setting(object):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if hasattr(self, '_value'):
|
if hasattr(self, '_value'):
|
||||||
assert hasattr(self, '_device')
|
assert hasattr(self, '_device')
|
||||||
return '<Setting([%s:%s] %s:%s=%s)>' % (self._rw.kind, self._validator.kind, self._device.codename, self.name,
|
return '<Setting([%s:%s] %s:%s=%s)>' % (
|
||||||
self._value)
|
self._rw.kind, self._validator.kind, self._device.codename, self.name, self._value
|
||||||
|
)
|
||||||
return '<Setting([%s:%s] %s)>' % (self._rw.kind, self._validator.kind, self.name)
|
return '<Setting([%s:%s] %s)>' % (self._rw.kind, self._validator.kind, self.name)
|
||||||
|
|
||||||
__unicode__ = __repr__ = __str__
|
__unicode__ = __repr__ = __str__
|
||||||
|
@ -531,8 +532,10 @@ class BooleanValidator(object):
|
||||||
return True
|
return True
|
||||||
if reply_value == self.false_value:
|
if reply_value == self.false_value:
|
||||||
return False
|
return False
|
||||||
_log.warn('BooleanValidator: reply %02X mismatched %02X/%02X/%02X', reply_value, self.true_value, self.false_value,
|
_log.warn(
|
||||||
self.mask)
|
'BooleanValidator: reply %02X mismatched %02X/%02X/%02X', reply_value, self.true_value, self.false_value,
|
||||||
|
self.mask
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
count = len(self.mask)
|
count = len(self.mask)
|
||||||
|
@ -701,8 +704,8 @@ class ChoicesMapValidator(ChoicesValidator):
|
||||||
# reprogrammable keys starts out as 0, which is not a choice, so don't use assert here
|
# reprogrammable keys starts out as 0, which is not a choice, so don't use assert here
|
||||||
if self.extra_default is not None and self.extra_default == reply_value:
|
if self.extra_default is not None and self.extra_default == reply_value:
|
||||||
return int(self.choices[key][0])
|
return int(self.choices[key][0])
|
||||||
assert reply_value in self.choices[key], '%s: failed to validate read value %02X' % (self.__class__.__name__,
|
assert reply_value in self.choices[
|
||||||
reply_value)
|
key], '%s: failed to validate read value %02X' % (self.__class__.__name__, reply_value)
|
||||||
return reply_value
|
return reply_value
|
||||||
|
|
||||||
def prepare_write(self, key, new_value):
|
def prepare_write(self, key, new_value):
|
||||||
|
|
|
@ -57,14 +57,16 @@ _F = _hidpp20.FEATURE
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def register_toggle(name,
|
def register_toggle(
|
||||||
register,
|
name,
|
||||||
true_value=_BooleanV.default_true,
|
register,
|
||||||
false_value=_BooleanV.default_false,
|
true_value=_BooleanV.default_true,
|
||||||
mask=_BooleanV.default_mask,
|
false_value=_BooleanV.default_false,
|
||||||
label=None,
|
mask=_BooleanV.default_mask,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
||||||
rw = _RegisterRW(register)
|
rw = _RegisterRW(register)
|
||||||
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
return _Setting(name, rw, validator, label=label, description=description, device_kind=device_kind)
|
||||||
|
@ -77,109 +79,114 @@ def register_choices(name, register, choices, kind=_KIND.choice, label=None, des
|
||||||
return _Setting(name, rw, validator, kind=kind, label=label, description=description, device_kind=device_kind)
|
return _Setting(name, rw, validator, kind=kind, label=label, description=description, device_kind=device_kind)
|
||||||
|
|
||||||
|
|
||||||
def feature_toggle(name,
|
def feature_toggle(
|
||||||
feature,
|
name,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
feature,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
read_function_id=_FeatureRW.default_read_fnid,
|
||||||
true_value=_BooleanV.default_true,
|
write_function_id=_FeatureRW.default_write_fnid,
|
||||||
false_value=_BooleanV.default_false,
|
true_value=_BooleanV.default_true,
|
||||||
mask=_BooleanV.default_mask,
|
false_value=_BooleanV.default_false,
|
||||||
label=None,
|
mask=_BooleanV.default_mask,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||||
return _Setting(name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind)
|
return _Setting(name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind)
|
||||||
|
|
||||||
|
|
||||||
def feature_bitfield_toggle(name,
|
def feature_bitfield_toggle(
|
||||||
feature,
|
name,
|
||||||
options,
|
feature,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
options,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
read_function_id=_FeatureRW.default_read_fnid,
|
||||||
label=None,
|
write_function_id=_FeatureRW.default_write_fnid,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
assert options
|
assert options
|
||||||
validator = _BitFieldV(options)
|
validator = _BitFieldV(options)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||||
return _BitFieldSetting(name,
|
return _BitFieldSetting(
|
||||||
rw,
|
name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind
|
||||||
validator,
|
)
|
||||||
feature=feature,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind)
|
|
||||||
|
|
||||||
|
|
||||||
def feature_bitfield_toggle_dynamic(name,
|
def feature_bitfield_toggle_dynamic(
|
||||||
feature,
|
name,
|
||||||
options_callback,
|
feature,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
options_callback,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
read_function_id=_FeatureRW.default_read_fnid,
|
||||||
label=None,
|
write_function_id=_FeatureRW.default_write_fnid,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
def instantiate(device):
|
def instantiate(device):
|
||||||
options = options_callback(device)
|
options = options_callback(device)
|
||||||
setting = feature_bitfield_toggle(name,
|
setting = feature_bitfield_toggle(
|
||||||
feature,
|
name,
|
||||||
options,
|
feature,
|
||||||
read_function_id=read_function_id,
|
options,
|
||||||
write_function_id=write_function_id,
|
read_function_id=read_function_id,
|
||||||
label=label,
|
write_function_id=write_function_id,
|
||||||
description=description,
|
label=label,
|
||||||
device_kind=device_kind)
|
description=description,
|
||||||
|
device_kind=device_kind
|
||||||
|
)
|
||||||
return setting(device)
|
return setting(device)
|
||||||
|
|
||||||
instantiate._rw_kind = _FeatureRW.kind
|
instantiate._rw_kind = _FeatureRW.kind
|
||||||
return instantiate
|
return instantiate
|
||||||
|
|
||||||
|
|
||||||
def feature_choices(name,
|
def feature_choices(
|
||||||
feature,
|
name,
|
||||||
choices,
|
feature,
|
||||||
read_function_id,
|
choices,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
bytes_count=None,
|
write_function_id,
|
||||||
label=None,
|
bytes_count=None,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
assert choices
|
assert choices
|
||||||
validator = _ChoicesV(choices, bytes_count=bytes_count)
|
validator = _ChoicesV(choices, bytes_count=bytes_count)
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||||
return _Setting(name,
|
return _Setting(
|
||||||
rw,
|
name, rw, validator, feature=feature, kind=_KIND.choice, label=label, description=description, device_kind=device_kind
|
||||||
validator,
|
)
|
||||||
feature=feature,
|
|
||||||
kind=_KIND.choice,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind)
|
|
||||||
|
|
||||||
|
|
||||||
def feature_choices_dynamic(name,
|
def feature_choices_dynamic(
|
||||||
feature,
|
name,
|
||||||
choices_callback,
|
feature,
|
||||||
read_function_id,
|
choices_callback,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
bytes_count=None,
|
write_function_id,
|
||||||
label=None,
|
bytes_count=None,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
# Proxy that obtains choices dynamically from a device
|
# Proxy that obtains choices dynamically from a device
|
||||||
def instantiate(device):
|
def instantiate(device):
|
||||||
# Obtain choices for this feature
|
# Obtain choices for this feature
|
||||||
choices = choices_callback(device)
|
choices = choices_callback(device)
|
||||||
setting = feature_choices(name,
|
setting = feature_choices(
|
||||||
feature,
|
name,
|
||||||
choices,
|
feature,
|
||||||
read_function_id,
|
choices,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
bytes_count=bytes_count,
|
write_function_id,
|
||||||
label=label,
|
bytes_count=bytes_count,
|
||||||
description=description,
|
label=label,
|
||||||
device_kind=device_kind)
|
description=description,
|
||||||
|
device_kind=device_kind
|
||||||
|
)
|
||||||
return setting(device)
|
return setting(device)
|
||||||
|
|
||||||
instantiate._rw_kind = _FeatureRW.kind
|
instantiate._rw_kind = _FeatureRW.kind
|
||||||
|
@ -189,92 +196,99 @@ def feature_choices_dynamic(name,
|
||||||
# maintain a mapping from keys (NamedInts) to one of a list of choices (NamedInts), default is first one
|
# maintain a mapping from keys (NamedInts) to one of a list of choices (NamedInts), default is first one
|
||||||
# the setting is stored as a JSON-compatible object mapping the key int (as a string) to the choice int
|
# the setting is stored as a JSON-compatible object mapping the key int (as a string) to the choice int
|
||||||
# extra_default is an extra value that comes from the device that also means the default
|
# extra_default is an extra value that comes from the device that also means the default
|
||||||
def feature_map_choices(name,
|
def feature_map_choices(
|
||||||
feature,
|
name,
|
||||||
choicesmap,
|
feature,
|
||||||
read_function_id,
|
choicesmap,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
key_bytes_count=None,
|
write_function_id,
|
||||||
skip_bytes_count=None,
|
key_bytes_count=None,
|
||||||
value_bytes_count=None,
|
skip_bytes_count=None,
|
||||||
label=None,
|
value_bytes_count=None,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None,
|
description=None,
|
||||||
extra_default=None):
|
device_kind=None,
|
||||||
|
extra_default=None
|
||||||
|
):
|
||||||
assert choicesmap
|
assert choicesmap
|
||||||
validator = _ChoicesMapV(choicesmap,
|
validator = _ChoicesMapV(
|
||||||
key_bytes_count=key_bytes_count,
|
choicesmap,
|
||||||
skip_bytes_count=skip_bytes_count,
|
key_bytes_count=key_bytes_count,
|
||||||
value_bytes_count=value_bytes_count,
|
skip_bytes_count=skip_bytes_count,
|
||||||
extra_default=extra_default)
|
value_bytes_count=value_bytes_count,
|
||||||
|
extra_default=extra_default
|
||||||
|
)
|
||||||
rw = _FeatureRWMap(feature, read_function_id, write_function_id, key_bytes=key_bytes_count)
|
rw = _FeatureRWMap(feature, read_function_id, write_function_id, key_bytes=key_bytes_count)
|
||||||
return _Settings(name,
|
return _Settings(
|
||||||
rw,
|
name,
|
||||||
validator,
|
rw,
|
||||||
feature=feature,
|
validator,
|
||||||
kind=_KIND.map_choice,
|
feature=feature,
|
||||||
label=label,
|
kind=_KIND.map_choice,
|
||||||
description=description,
|
label=label,
|
||||||
device_kind=device_kind)
|
description=description,
|
||||||
|
device_kind=device_kind
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def feature_map_choices_dynamic(name,
|
def feature_map_choices_dynamic(
|
||||||
feature,
|
name,
|
||||||
choices_callback,
|
feature,
|
||||||
read_function_id,
|
choices_callback,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
key_bytes_count=None,
|
write_function_id,
|
||||||
skip_bytes_count=None,
|
key_bytes_count=None,
|
||||||
value_bytes_count=None,
|
skip_bytes_count=None,
|
||||||
label=None,
|
value_bytes_count=None,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None,
|
description=None,
|
||||||
extra_default=None):
|
device_kind=None,
|
||||||
|
extra_default=None
|
||||||
|
):
|
||||||
# Proxy that obtains choices dynamically from a device
|
# Proxy that obtains choices dynamically from a device
|
||||||
def instantiate(device):
|
def instantiate(device):
|
||||||
choices = choices_callback(device)
|
choices = choices_callback(device)
|
||||||
if not choices: # no choices, so don't create a Setting
|
if not choices: # no choices, so don't create a Setting
|
||||||
return None
|
return None
|
||||||
setting = feature_map_choices(name,
|
setting = feature_map_choices(
|
||||||
feature,
|
name,
|
||||||
choices,
|
feature,
|
||||||
read_function_id,
|
choices,
|
||||||
write_function_id,
|
read_function_id,
|
||||||
key_bytes_count=key_bytes_count,
|
write_function_id,
|
||||||
skip_bytes_count=skip_bytes_count,
|
key_bytes_count=key_bytes_count,
|
||||||
value_bytes_count=value_bytes_count,
|
skip_bytes_count=skip_bytes_count,
|
||||||
label=label,
|
value_bytes_count=value_bytes_count,
|
||||||
description=description,
|
label=label,
|
||||||
device_kind=device_kind,
|
description=description,
|
||||||
extra_default=extra_default)
|
device_kind=device_kind,
|
||||||
|
extra_default=extra_default
|
||||||
|
)
|
||||||
return setting(device)
|
return setting(device)
|
||||||
|
|
||||||
instantiate._rw_kind = _FeatureRWMap.kind
|
instantiate._rw_kind = _FeatureRWMap.kind
|
||||||
return instantiate
|
return instantiate
|
||||||
|
|
||||||
|
|
||||||
def feature_range(name,
|
def feature_range(
|
||||||
feature,
|
name,
|
||||||
min_value,
|
feature,
|
||||||
max_value,
|
min_value,
|
||||||
read_function_id=_FeatureRW.default_read_fnid,
|
max_value,
|
||||||
write_function_id=_FeatureRW.default_write_fnid,
|
read_function_id=_FeatureRW.default_read_fnid,
|
||||||
rw=None,
|
write_function_id=_FeatureRW.default_write_fnid,
|
||||||
bytes_count=None,
|
rw=None,
|
||||||
label=None,
|
bytes_count=None,
|
||||||
description=None,
|
label=None,
|
||||||
device_kind=None):
|
description=None,
|
||||||
|
device_kind=None
|
||||||
|
):
|
||||||
validator = _RangeV(min_value, max_value, bytes_count=bytes_count)
|
validator = _RangeV(min_value, max_value, bytes_count=bytes_count)
|
||||||
if rw is None:
|
if rw is None:
|
||||||
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
rw = _FeatureRW(feature, read_function_id, write_function_id)
|
||||||
return _Setting(name,
|
return _Setting(
|
||||||
rw,
|
name, rw, validator, feature=feature, kind=_KIND.range, label=label, description=description, device_kind=device_kind
|
||||||
validator,
|
)
|
||||||
feature=feature,
|
|
||||||
kind=_KIND.range,
|
|
||||||
label=label,
|
|
||||||
description=description,
|
|
||||||
device_kind=device_kind)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -283,30 +297,50 @@ def feature_range(name,
|
||||||
|
|
||||||
_HAND_DETECTION = ('hand-detection', _('Hand Detection'), _('Turn on illumination when the hands hover over the keyboard.'))
|
_HAND_DETECTION = ('hand-detection', _('Hand Detection'), _('Turn on illumination when the hands hover over the keyboard.'))
|
||||||
_SMOOTH_SCROLL = ('smooth-scroll', _('Smooth Scrolling'), _('High-sensitivity mode for vertical scroll with the wheel.'))
|
_SMOOTH_SCROLL = ('smooth-scroll', _('Smooth Scrolling'), _('High-sensitivity mode for vertical scroll with the wheel.'))
|
||||||
_SIDE_SCROLL = ('side-scroll', _('Side Scrolling'),
|
_SIDE_SCROLL = (
|
||||||
_('When disabled, pushing the wheel sideways sends custom button events\n'
|
'side-scroll', _('Side Scrolling'),
|
||||||
'instead of the standard side-scrolling events.'))
|
_(
|
||||||
_HI_RES_SCROLL = ('hi-res-scroll', _('High Resolution Scrolling'),
|
'When disabled, pushing the wheel sideways sends custom button events\n'
|
||||||
_('High-sensitivity mode for vertical scroll with the wheel.'))
|
'instead of the standard side-scrolling events.'
|
||||||
_LOW_RES_SCROLL = ('lowres-smooth-scroll', _('HID++ Scrolling'), _('HID++ mode for vertical scroll with the wheel.') + '\n' +
|
)
|
||||||
_('Effectively turns off wheel scrolling in Linux.'))
|
)
|
||||||
_HIRES_INV = ('hires-smooth-invert', _('High Resolution Wheel Invert'),
|
_HI_RES_SCROLL = (
|
||||||
_('High-sensitivity wheel invert mode for vertical scroll.'))
|
'hi-res-scroll', _('High Resolution Scrolling'), _('High-sensitivity mode for vertical scroll with the wheel.')
|
||||||
|
)
|
||||||
|
_LOW_RES_SCROLL = (
|
||||||
|
'lowres-smooth-scroll', _('HID++ Scrolling'),
|
||||||
|
_('HID++ mode for vertical scroll with the wheel.') + '\n' + _('Effectively turns off wheel scrolling in Linux.')
|
||||||
|
)
|
||||||
|
_HIRES_INV = (
|
||||||
|
'hires-smooth-invert', _('High Resolution Wheel Invert'), _('High-sensitivity wheel invert mode for vertical scroll.')
|
||||||
|
)
|
||||||
_HIRES_RES = ('hires-smooth-resolution', _('Wheel Resolution'), _('High-sensitivity mode for vertical scroll with the wheel.'))
|
_HIRES_RES = ('hires-smooth-resolution', _('Wheel Resolution'), _('High-sensitivity mode for vertical scroll with the wheel.'))
|
||||||
_FN_SWAP = ('fn-swap', _('Swap Fx function'),
|
_FN_SWAP = (
|
||||||
_('When set, the F1..F12 keys will activate their special function,\n'
|
'fn-swap', _('Swap Fx function'),
|
||||||
'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'
|
'When set, the F1..F12 keys will activate their special function,\n'
|
||||||
'and you must hold the FN key to activate their special function.'))
|
'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.'
|
||||||
|
)
|
||||||
|
)
|
||||||
_DPI = ('dpi', _('Sensitivity (DPI)'), None)
|
_DPI = ('dpi', _('Sensitivity (DPI)'), None)
|
||||||
_POINTER_SPEED = ('pointer_speed', _('Sensitivity (Pointer Speed)'),
|
_POINTER_SPEED = (
|
||||||
_('Speed multiplier for mouse (256 is normal multiplier).'))
|
'pointer_speed', _('Sensitivity (Pointer Speed)'), _('Speed multiplier for mouse (256 is normal multiplier).')
|
||||||
_SMART_SHIFT = ('smart-shift', _('Smart Shift'),
|
)
|
||||||
_('Automatically switch the mouse wheel between ratchet and freespin mode.\n'
|
_SMART_SHIFT = (
|
||||||
'The mouse wheel is always free at 0, and always locked at 50'))
|
'smart-shift', _('Smart Shift'),
|
||||||
|
_(
|
||||||
|
'Automatically switch the mouse wheel between ratchet and freespin mode.\n'
|
||||||
|
'The mouse wheel is always free at 0, and always locked at 50'
|
||||||
|
)
|
||||||
|
)
|
||||||
_BACKLIGHT = ('backlight', _('Backlight'), _('Turn illumination on or off on keyboard.'))
|
_BACKLIGHT = ('backlight', _('Backlight'), _('Turn illumination on or off on keyboard.'))
|
||||||
_REPROGRAMMABLE_KEYS = ('reprogrammable-keys', _('Actions'), _('Change the action for the key or button.') + '\n' +
|
_REPROGRAMMABLE_KEYS = (
|
||||||
_('Changing important actions (such as for the left mouse button) can result in an unusable system.'))
|
'reprogrammable-keys', _('Actions'), _('Change the action for the key or button.') + '\n' +
|
||||||
|
_('Changing important actions (such as for the left mouse button) can result in an unusable system.')
|
||||||
|
)
|
||||||
_DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific keyboard keys.'))
|
_DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific keyboard keys.'))
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -314,135 +348,143 @@ _DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def _register_hand_detection(register=_R.keyboard_hand_detection,
|
def _register_hand_detection(
|
||||||
true_value=b'\x00\x00\x00',
|
register=_R.keyboard_hand_detection, true_value=b'\x00\x00\x00', false_value=b'\x00\x00\x30', mask=b'\x00\x00\xFF'
|
||||||
false_value=b'\x00\x00\x30',
|
):
|
||||||
mask=b'\x00\x00\xFF'):
|
return register_toggle(
|
||||||
return register_toggle(_HAND_DETECTION[0],
|
_HAND_DETECTION[0],
|
||||||
register,
|
register,
|
||||||
true_value=true_value,
|
true_value=true_value,
|
||||||
false_value=false_value,
|
false_value=false_value,
|
||||||
label=_HAND_DETECTION[1],
|
label=_HAND_DETECTION[1],
|
||||||
description=_HAND_DETECTION[2],
|
description=_HAND_DETECTION[2],
|
||||||
device_kind=(_DK.keyboard, ))
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _register_fn_swap(register=_R.keyboard_fn_swap, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
def _register_fn_swap(register=_R.keyboard_fn_swap, true_value=b'\x00\x01', mask=b'\x00\x01'):
|
||||||
return register_toggle(_FN_SWAP[0],
|
return register_toggle(
|
||||||
register,
|
_FN_SWAP[0],
|
||||||
true_value=true_value,
|
register,
|
||||||
mask=mask,
|
true_value=true_value,
|
||||||
label=_FN_SWAP[1],
|
mask=mask,
|
||||||
description=_FN_SWAP[2],
|
label=_FN_SWAP[1],
|
||||||
device_kind=(_DK.keyboard, ))
|
description=_FN_SWAP[2],
|
||||||
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _register_smooth_scroll(register=_R.mouse_button_flags, true_value=0x40, mask=0x40):
|
def _register_smooth_scroll(register=_R.mouse_button_flags, true_value=0x40, mask=0x40):
|
||||||
return register_toggle(_SMOOTH_SCROLL[0],
|
return register_toggle(
|
||||||
register,
|
_SMOOTH_SCROLL[0],
|
||||||
true_value=true_value,
|
register,
|
||||||
mask=mask,
|
true_value=true_value,
|
||||||
label=_SMOOTH_SCROLL[1],
|
mask=mask,
|
||||||
description=_SMOOTH_SCROLL[2],
|
label=_SMOOTH_SCROLL[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_SMOOTH_SCROLL[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _register_side_scroll(register=_R.mouse_button_flags, true_value=0x02, mask=0x02):
|
def _register_side_scroll(register=_R.mouse_button_flags, true_value=0x02, mask=0x02):
|
||||||
return register_toggle(_SIDE_SCROLL[0],
|
return register_toggle(
|
||||||
register,
|
_SIDE_SCROLL[0],
|
||||||
true_value=true_value,
|
register,
|
||||||
mask=mask,
|
true_value=true_value,
|
||||||
label=_SIDE_SCROLL[1],
|
mask=mask,
|
||||||
description=_SIDE_SCROLL[2],
|
label=_SIDE_SCROLL[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_SIDE_SCROLL[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _register_dpi(register=_R.mouse_dpi, choices=None):
|
def _register_dpi(register=_R.mouse_dpi, choices=None):
|
||||||
return register_choices(_DPI[0],
|
return register_choices(
|
||||||
register,
|
_DPI[0], register, choices, label=_DPI[1], description=_DPI[2], device_kind=(_DK.mouse, _DK.trackball)
|
||||||
choices,
|
)
|
||||||
label=_DPI[1],
|
|
||||||
description=_DPI[2],
|
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
|
||||||
|
|
||||||
|
|
||||||
def _feature_fn_swap():
|
def _feature_fn_swap():
|
||||||
return feature_toggle(_FN_SWAP[0],
|
return feature_toggle(
|
||||||
_F.FN_INVERSION,
|
_FN_SWAP[0], _F.FN_INVERSION, label=_FN_SWAP[1], description=_FN_SWAP[2], device_kind=(_DK.keyboard, )
|
||||||
label=_FN_SWAP[1],
|
)
|
||||||
description=_FN_SWAP[2],
|
|
||||||
device_kind=(_DK.keyboard, ))
|
|
||||||
|
|
||||||
|
|
||||||
# this might not be correct for this feature
|
# this might not be correct for this feature
|
||||||
def _feature_new_fn_swap():
|
def _feature_new_fn_swap():
|
||||||
return feature_toggle(_FN_SWAP[0],
|
return feature_toggle(
|
||||||
_F.NEW_FN_INVERSION,
|
_FN_SWAP[0], _F.NEW_FN_INVERSION, label=_FN_SWAP[1], description=_FN_SWAP[2], device_kind=(_DK.keyboard, )
|
||||||
label=_FN_SWAP[1],
|
)
|
||||||
description=_FN_SWAP[2],
|
|
||||||
device_kind=(_DK.keyboard, ))
|
|
||||||
|
|
||||||
|
|
||||||
# ignore the capabilities part of the feature - all devices should be able to swap Fn state
|
# ignore the capabilities part of the feature - all devices should be able to swap Fn state
|
||||||
# just use the current host (first byte = 0xFF) part of the feature to read and set the Fn state
|
# just use the current host (first byte = 0xFF) part of the feature to read and set the Fn state
|
||||||
def _feature_k375s_fn_swap():
|
def _feature_k375s_fn_swap():
|
||||||
return feature_toggle(_FN_SWAP[0],
|
return feature_toggle(
|
||||||
_F.K375S_FN_INVERSION,
|
_FN_SWAP[0],
|
||||||
label=_FN_SWAP[1],
|
_F.K375S_FN_INVERSION,
|
||||||
description=_FN_SWAP[2],
|
label=_FN_SWAP[1],
|
||||||
true_value=b'\xFF\x01',
|
description=_FN_SWAP[2],
|
||||||
false_value=b'\xFF\x00',
|
true_value=b'\xFF\x01',
|
||||||
device_kind=(_DK.keyboard, ))
|
false_value=b'\xFF\x00',
|
||||||
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# FIXME: This will enable all supported backlight settings,
|
# FIXME: This will enable all supported backlight settings,
|
||||||
# we should allow the users to select which settings they want to enable.
|
# we should allow the users to select which settings they want to enable.
|
||||||
def _feature_backlight2():
|
def _feature_backlight2():
|
||||||
return feature_toggle(_BACKLIGHT[0],
|
return feature_toggle(
|
||||||
_F.BACKLIGHT2,
|
_BACKLIGHT[0], _F.BACKLIGHT2, label=_BACKLIGHT[1], description=_BACKLIGHT[2], device_kind=(_DK.keyboard, )
|
||||||
label=_BACKLIGHT[1],
|
)
|
||||||
description=_BACKLIGHT[2],
|
|
||||||
device_kind=(_DK.keyboard, ))
|
|
||||||
|
|
||||||
|
|
||||||
def _feature_hi_res_scroll():
|
def _feature_hi_res_scroll():
|
||||||
return feature_toggle(_HI_RES_SCROLL[0],
|
return feature_toggle(
|
||||||
_F.HI_RES_SCROLLING,
|
_HI_RES_SCROLL[0],
|
||||||
label=_HI_RES_SCROLL[1],
|
_F.HI_RES_SCROLLING,
|
||||||
description=_HI_RES_SCROLL[2],
|
label=_HI_RES_SCROLL[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_HI_RES_SCROLL[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_lowres_smooth_scroll():
|
def _feature_lowres_smooth_scroll():
|
||||||
return feature_toggle(_LOW_RES_SCROLL[0],
|
return feature_toggle(
|
||||||
_F.LOWRES_WHEEL,
|
_LOW_RES_SCROLL[0],
|
||||||
label=_LOW_RES_SCROLL[1],
|
_F.LOWRES_WHEEL,
|
||||||
description=_LOW_RES_SCROLL[2],
|
label=_LOW_RES_SCROLL[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_LOW_RES_SCROLL[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_hires_smooth_invert():
|
def _feature_hires_smooth_invert():
|
||||||
return feature_toggle(_HIRES_INV[0],
|
return feature_toggle(
|
||||||
_F.HIRES_WHEEL,
|
_HIRES_INV[0],
|
||||||
read_function_id=0x10,
|
_F.HIRES_WHEEL,
|
||||||
write_function_id=0x20,
|
read_function_id=0x10,
|
||||||
true_value=0x04,
|
write_function_id=0x20,
|
||||||
mask=0x04,
|
true_value=0x04,
|
||||||
label=_HIRES_INV[1],
|
mask=0x04,
|
||||||
description=_HIRES_INV[2],
|
label=_HIRES_INV[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_HIRES_INV[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_hires_smooth_resolution():
|
def _feature_hires_smooth_resolution():
|
||||||
return feature_toggle(_HIRES_RES[0],
|
return feature_toggle(
|
||||||
_F.HIRES_WHEEL,
|
_HIRES_RES[0],
|
||||||
read_function_id=0x10,
|
_F.HIRES_WHEEL,
|
||||||
write_function_id=0x20,
|
read_function_id=0x10,
|
||||||
true_value=0x02,
|
write_function_id=0x20,
|
||||||
mask=0x02,
|
true_value=0x02,
|
||||||
label=_HIRES_RES[1],
|
mask=0x02,
|
||||||
description=_HIRES_RES[2],
|
label=_HIRES_RES[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_HIRES_RES[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_smart_shift():
|
def _feature_smart_shift():
|
||||||
|
@ -475,15 +517,17 @@ def _feature_smart_shift():
|
||||||
data = _int2bytes(mode, count=1) + _int2bytes(threshold, count=1) * 2
|
data = _int2bytes(mode, count=1) + _int2bytes(threshold, count=1) * 2
|
||||||
return super(_SmartShiftRW, self).write(device, data)
|
return super(_SmartShiftRW, self).write(device, data)
|
||||||
|
|
||||||
return feature_range(_SMART_SHIFT[0],
|
return feature_range(
|
||||||
_F.SMART_SHIFT,
|
_SMART_SHIFT[0],
|
||||||
_MIN_SMART_SHIFT_VALUE,
|
_F.SMART_SHIFT,
|
||||||
_MAX_SMART_SHIFT_VALUE,
|
_MIN_SMART_SHIFT_VALUE,
|
||||||
bytes_count=1,
|
_MAX_SMART_SHIFT_VALUE,
|
||||||
rw=_SmartShiftRW(_F.SMART_SHIFT),
|
bytes_count=1,
|
||||||
label=_SMART_SHIFT[1],
|
rw=_SmartShiftRW(_F.SMART_SHIFT),
|
||||||
description=_SMART_SHIFT[2],
|
label=_SMART_SHIFT[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_SMART_SHIFT[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_adjustable_dpi_choices(device):
|
def _feature_adjustable_dpi_choices(device):
|
||||||
|
@ -514,30 +558,34 @@ def _feature_adjustable_dpi():
|
||||||
# Assume sensorIdx 0 (there is only one sensor)
|
# Assume sensorIdx 0 (there is only one sensor)
|
||||||
# [2] getSensorDpi(sensorIdx) -> sensorIdx, dpiMSB, dpiLSB
|
# [2] getSensorDpi(sensorIdx) -> sensorIdx, dpiMSB, dpiLSB
|
||||||
# [3] setSensorDpi(sensorIdx, dpi)
|
# [3] setSensorDpi(sensorIdx, dpi)
|
||||||
return feature_choices_dynamic(_DPI[0],
|
return feature_choices_dynamic(
|
||||||
_F.ADJUSTABLE_DPI,
|
_DPI[0],
|
||||||
_feature_adjustable_dpi_choices,
|
_F.ADJUSTABLE_DPI,
|
||||||
read_function_id=0x20,
|
_feature_adjustable_dpi_choices,
|
||||||
write_function_id=0x30,
|
read_function_id=0x20,
|
||||||
bytes_count=3,
|
write_function_id=0x30,
|
||||||
label=_DPI[1],
|
bytes_count=3,
|
||||||
description=_DPI[2],
|
label=_DPI[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_DPI[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_pointer_speed():
|
def _feature_pointer_speed():
|
||||||
"""Pointer Speed feature"""
|
"""Pointer Speed feature"""
|
||||||
# min and max values taken from usb traces of Win software
|
# min and max values taken from usb traces of Win software
|
||||||
return feature_range(_POINTER_SPEED[0],
|
return feature_range(
|
||||||
_F.POINTER_SPEED,
|
_POINTER_SPEED[0],
|
||||||
0x002e,
|
_F.POINTER_SPEED,
|
||||||
0x01ff,
|
0x002e,
|
||||||
read_function_id=0x0,
|
0x01ff,
|
||||||
write_function_id=0x10,
|
read_function_id=0x0,
|
||||||
bytes_count=2,
|
write_function_id=0x10,
|
||||||
label=_POINTER_SPEED[1],
|
bytes_count=2,
|
||||||
description=_POINTER_SPEED[2],
|
label=_POINTER_SPEED[1],
|
||||||
device_kind=(_DK.mouse, _DK.trackball))
|
description=_POINTER_SPEED[2],
|
||||||
|
device_kind=(_DK.mouse, _DK.trackball)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# the keys for the choice map are Logitech controls (from special_keys)
|
# the keys for the choice map are Logitech controls (from special_keys)
|
||||||
|
@ -572,18 +620,20 @@ def _feature_reprogrammable_keys_choices(device):
|
||||||
|
|
||||||
|
|
||||||
def _feature_reprogrammable_keys():
|
def _feature_reprogrammable_keys():
|
||||||
return feature_map_choices_dynamic(_REPROGRAMMABLE_KEYS[0],
|
return feature_map_choices_dynamic(
|
||||||
_F.REPROG_CONTROLS_V4,
|
_REPROGRAMMABLE_KEYS[0],
|
||||||
_feature_reprogrammable_keys_choices,
|
_F.REPROG_CONTROLS_V4,
|
||||||
read_function_id=0x20,
|
_feature_reprogrammable_keys_choices,
|
||||||
write_function_id=0x30,
|
read_function_id=0x20,
|
||||||
key_bytes_count=2,
|
write_function_id=0x30,
|
||||||
skip_bytes_count=1,
|
key_bytes_count=2,
|
||||||
value_bytes_count=2,
|
skip_bytes_count=1,
|
||||||
label=_REPROGRAMMABLE_KEYS[1],
|
value_bytes_count=2,
|
||||||
description=_REPROGRAMMABLE_KEYS[2],
|
label=_REPROGRAMMABLE_KEYS[1],
|
||||||
device_kind=(_DK.keyboard, ),
|
description=_REPROGRAMMABLE_KEYS[2],
|
||||||
extra_default=0)
|
device_kind=(_DK.keyboard, ),
|
||||||
|
extra_default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _feature_disable_keyboard_keys_key_list(device):
|
def _feature_disable_keyboard_keys_key_list(device):
|
||||||
|
@ -593,14 +643,16 @@ def _feature_disable_keyboard_keys_key_list(device):
|
||||||
|
|
||||||
|
|
||||||
def _feature_disable_keyboard_keys():
|
def _feature_disable_keyboard_keys():
|
||||||
return feature_bitfield_toggle_dynamic(_DISABLE_KEYS[0],
|
return feature_bitfield_toggle_dynamic(
|
||||||
_F.KEYBOARD_DISABLE_KEYS,
|
_DISABLE_KEYS[0],
|
||||||
_feature_disable_keyboard_keys_key_list,
|
_F.KEYBOARD_DISABLE_KEYS,
|
||||||
read_function_id=0x10,
|
_feature_disable_keyboard_keys_key_list,
|
||||||
write_function_id=0x20,
|
read_function_id=0x10,
|
||||||
label=_DISABLE_KEYS[1],
|
write_function_id=0x20,
|
||||||
description=_DISABLE_KEYS[2],
|
label=_DISABLE_KEYS[1],
|
||||||
device_kind=(_DK.keyboard, ))
|
description=_DISABLE_KEYS[2],
|
||||||
|
device_kind=(_DK.keyboard, )
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -489,14 +489,16 @@ TASK = _NamedInts(
|
||||||
)
|
)
|
||||||
TASK._fallback = lambda x: 'unknown:%04X' % x
|
TASK._fallback = lambda x: 'unknown:%04X' % x
|
||||||
# hidpp 4.5 info from https://lekensteyn.nl/files/logitech/x1b04_specialkeysmsebuttons.html
|
# hidpp 4.5 info from https://lekensteyn.nl/files/logitech/x1b04_specialkeysmsebuttons.html
|
||||||
KEY_FLAG = _NamedInts(virtual=0x80,
|
KEY_FLAG = _NamedInts(
|
||||||
persistently_divertable=0x40,
|
virtual=0x80,
|
||||||
divertable=0x20,
|
persistently_divertable=0x40,
|
||||||
reprogrammable=0x10,
|
divertable=0x20,
|
||||||
FN_sensitive=0x08,
|
reprogrammable=0x10,
|
||||||
nonstandard=0x04,
|
FN_sensitive=0x08,
|
||||||
is_FN=0x02,
|
nonstandard=0x04,
|
||||||
mse=0x01)
|
is_FN=0x02,
|
||||||
|
mse=0x01
|
||||||
|
)
|
||||||
|
|
||||||
DISABLE = _NamedInts(
|
DISABLE = _NamedInts(
|
||||||
Caps_Lock=0x01,
|
Caps_Lock=0x01,
|
||||||
|
|
|
@ -102,10 +102,12 @@ class ReceiverStatus(dict):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
count = len(self._receiver)
|
count = len(self._receiver)
|
||||||
return (_('No paired devices.')
|
return (
|
||||||
if count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', count) % {
|
_('No paired devices.')
|
||||||
'count': count
|
if count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', count) % {
|
||||||
})
|
'count': count
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
__unicode__ = __str__
|
__unicode__ = __str__
|
||||||
|
|
||||||
|
@ -209,8 +211,10 @@ class DeviceStatus(dict):
|
||||||
if voltage is not None:
|
if voltage is not None:
|
||||||
self[KEYS.BATTERY_VOLTAGE] = voltage
|
self[KEYS.BATTERY_VOLTAGE] = voltage
|
||||||
|
|
||||||
charging = status in (_hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full,
|
charging = status in (
|
||||||
_hidpp20.BATTERY_STATUS.full, _hidpp20.BATTERY_STATUS.slow_recharge)
|
_hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full, _hidpp20.BATTERY_STATUS.full,
|
||||||
|
_hidpp20.BATTERY_STATUS.slow_recharge
|
||||||
|
)
|
||||||
old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
|
old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
|
||||||
|
|
||||||
changed = old_level != level or old_status != status or old_charging != charging
|
changed = old_level != level or old_status != status or old_charging != charging
|
||||||
|
|
|
@ -36,43 +36,54 @@ del getLogger
|
||||||
|
|
||||||
|
|
||||||
def _create_parser():
|
def _create_parser():
|
||||||
parser = _argparse.ArgumentParser(prog=NAME.lower(),
|
parser = _argparse.ArgumentParser(
|
||||||
add_help=False,
|
prog=NAME.lower(),
|
||||||
epilog='For details on individual actions, run `%s <action> --help`.' % NAME.lower())
|
add_help=False,
|
||||||
|
epilog='For details on individual actions, run `%s <action> --help`.' % NAME.lower()
|
||||||
|
)
|
||||||
subparsers = parser.add_subparsers(title='actions', help='optional action to perform')
|
subparsers = parser.add_subparsers(title='actions', help='optional action to perform')
|
||||||
|
|
||||||
sp = subparsers.add_parser('show', help='show information about devices')
|
sp = subparsers.add_parser('show', help='show information about devices')
|
||||||
sp.add_argument('device',
|
sp.add_argument(
|
||||||
nargs='?',
|
'device',
|
||||||
default='all',
|
nargs='?',
|
||||||
help='device to show information about; may be a device number (1..6), a serial, '
|
default='all',
|
||||||
'a substring of a device\'s name, or "all" (the default)')
|
help='device to show information about; may be a device number (1..6), a serial, '
|
||||||
|
'a substring of a device\'s name, or "all" (the default)'
|
||||||
|
)
|
||||||
sp.set_defaults(action='show')
|
sp.set_defaults(action='show')
|
||||||
|
|
||||||
sp = subparsers.add_parser('probe', help='probe a receiver (debugging use only)')
|
sp = subparsers.add_parser('probe', help='probe a receiver (debugging use only)')
|
||||||
sp.add_argument('receiver', nargs='?', help='select a certain receiver when more than one is present')
|
sp.add_argument('receiver', nargs='?', help='select a certain receiver when more than one is present')
|
||||||
sp.set_defaults(action='probe')
|
sp.set_defaults(action='probe')
|
||||||
|
|
||||||
sp = subparsers.add_parser('config',
|
sp = subparsers.add_parser(
|
||||||
help='read/write device-specific settings',
|
'config',
|
||||||
epilog='Please note that configuration only works on active devices.')
|
help='read/write device-specific settings',
|
||||||
sp.add_argument('device',
|
epilog='Please note that configuration only works on active devices.'
|
||||||
help='device to configure; may be a device number (1..6), a device serial, '
|
)
|
||||||
'or at least 3 characters of a device\'s name')
|
sp.add_argument(
|
||||||
|
'device',
|
||||||
|
help='device to configure; may be a device number (1..6), a device serial, '
|
||||||
|
'or at least 3 characters of a device\'s name'
|
||||||
|
)
|
||||||
sp.add_argument('setting', nargs='?', help='device-specific setting; leave empty to list available settings')
|
sp.add_argument('setting', nargs='?', help='device-specific setting; leave empty to list available settings')
|
||||||
sp.add_argument('value', nargs='?', help='new value for the setting')
|
sp.add_argument('value', nargs='?', help='new value for the setting')
|
||||||
sp.set_defaults(action='config')
|
sp.set_defaults(action='config')
|
||||||
|
|
||||||
sp = subparsers.add_parser('pair',
|
sp = subparsers.add_parser(
|
||||||
help='pair a new device',
|
'pair',
|
||||||
epilog='The Logitech Unifying Receiver supports up to 6 paired devices at the same time.')
|
help='pair a new device',
|
||||||
|
epilog='The Logitech Unifying Receiver supports up to 6 paired devices at the same time.'
|
||||||
|
)
|
||||||
sp.add_argument('receiver', nargs='?', help='select a certain receiver when more than one is present')
|
sp.add_argument('receiver', nargs='?', help='select a certain receiver when more than one is present')
|
||||||
sp.set_defaults(action='pair')
|
sp.set_defaults(action='pair')
|
||||||
|
|
||||||
sp = subparsers.add_parser('unpair', help='unpair a device')
|
sp = subparsers.add_parser('unpair', help='unpair a device')
|
||||||
sp.add_argument('device',
|
sp.add_argument(
|
||||||
help='device to unpair; may be a device number (1..6), a serial, '
|
'device', help='device to unpair; may be a device number (1..6), a serial, '
|
||||||
'or a substring of a device\'s name.')
|
'or a substring of a device\'s name.'
|
||||||
|
)
|
||||||
sp.set_defaults(action='unpair')
|
sp.set_defaults(action='unpair')
|
||||||
|
|
||||||
return parser, subparsers.choices
|
return parser, subparsers.choices
|
||||||
|
@ -130,8 +141,10 @@ def _find_device(receivers, name):
|
||||||
return dev
|
return dev
|
||||||
|
|
||||||
for dev in r:
|
for dev in r:
|
||||||
if (name == dev.serial.lower() or name == dev.codename.lower() or name == str(dev.kind).lower()
|
if (
|
||||||
or name in dev.name.lower()):
|
name == dev.serial.lower() or name == dev.codename.lower() or name == str(dev.kind).lower()
|
||||||
|
or name in dev.name.lower()
|
||||||
|
):
|
||||||
return dev
|
return dev
|
||||||
|
|
||||||
raise Exception("no device found matching '%s'" % name)
|
raise Exception("no device found matching '%s'" % name)
|
||||||
|
|
|
@ -31,8 +31,10 @@ def _print_setting(s, verbose=True):
|
||||||
if s.kind == _settings.KIND.toggle:
|
if s.kind == _settings.KIND.toggle:
|
||||||
print('# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0')
|
print('# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0')
|
||||||
elif s.choices:
|
elif s.choices:
|
||||||
print('# possible values: one of [', ', '.join(str(v) for v in s.choices),
|
print(
|
||||||
'], or higher/lower/highest/max/lowest/min')
|
'# possible values: one of [', ', '.join(str(v) for v in s.choices),
|
||||||
|
'], or higher/lower/highest/max/lowest/min'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# wtf?
|
# wtf?
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -45,22 +45,32 @@ def run(receivers, args, find_receiver, _ignore):
|
||||||
register = receiver.read_register(_R.notifications)
|
register = receiver.read_register(_R.notifications)
|
||||||
print(' Notification Register %#04x: %s' % (_R.notifications % 0x100, '0x' + _strhex(register) if register else 'None'))
|
print(' Notification Register %#04x: %s' % (_R.notifications % 0x100, '0x' + _strhex(register) if register else 'None'))
|
||||||
register = receiver.read_register(_R.receiver_connection)
|
register = receiver.read_register(_R.receiver_connection)
|
||||||
print(' Connection State %#04x: %s' %
|
print(
|
||||||
(_R.receiver_connection % 0x100, '0x' + _strhex(register) if register else 'None'))
|
' Connection State %#04x: %s' %
|
||||||
|
(_R.receiver_connection % 0x100, '0x' + _strhex(register) if register else 'None')
|
||||||
|
)
|
||||||
register = receiver.read_register(_R.devices_activity)
|
register = receiver.read_register(_R.devices_activity)
|
||||||
print(' Device Activity %#04x: %s' %
|
print(
|
||||||
(_R.devices_activity % 0x100, '0x' + _strhex(register) if register else 'None'))
|
' Device Activity %#04x: %s' %
|
||||||
|
(_R.devices_activity % 0x100, '0x' + _strhex(register) if register else 'None')
|
||||||
|
)
|
||||||
|
|
||||||
for device in range(0, 6):
|
for device in range(0, 6):
|
||||||
for sub_reg in [0x0, 0x10, 0x20, 0x30]:
|
for sub_reg in [0x0, 0x10, 0x20, 0x30]:
|
||||||
register = receiver.read_register(_R.receiver_info, sub_reg + device)
|
register = receiver.read_register(_R.receiver_info, sub_reg + device)
|
||||||
print(' Pairing Register %#04x %#04x: %s' %
|
print(
|
||||||
(_R.receiver_info % 0x100, sub_reg + device, '0x' + _strhex(register) if register else 'None'))
|
' Pairing Register %#04x %#04x: %s' %
|
||||||
|
(_R.receiver_info % 0x100, sub_reg + device, '0x' + _strhex(register) if register else 'None')
|
||||||
|
)
|
||||||
register = receiver.read_register(_R.receiver_info, 0x40 + device)
|
register = receiver.read_register(_R.receiver_info, 0x40 + device)
|
||||||
print(' Pairing Name %#04x %#02x: %s' %
|
print(
|
||||||
(_R.receiver_info % 0x100, 0x40 + device, register[2:2 + ord(register[1:2])] if register else 'None'))
|
' Pairing Name %#04x %#02x: %s' %
|
||||||
|
(_R.receiver_info % 0x100, 0x40 + device, register[2:2 + ord(register[1:2])] if register else 'None')
|
||||||
|
)
|
||||||
|
|
||||||
for sub_reg in range(0, 5):
|
for sub_reg in range(0, 5):
|
||||||
register = receiver.read_register(_R.firmware, sub_reg)
|
register = receiver.read_register(_R.firmware, sub_reg)
|
||||||
print(' Firmware %#04x %#04x: %s' %
|
print(
|
||||||
(_R.firmware % 0x100, sub_reg, '0x' + _strhex(register) if register else 'None'))
|
' Firmware %#04x %#04x: %s' %
|
||||||
|
(_R.firmware % 0x100, sub_reg, '0x' + _strhex(register) if register else 'None')
|
||||||
|
)
|
||||||
|
|
|
@ -48,22 +48,25 @@ prefer_symbolic_battery_icons = False
|
||||||
def _parse_arguments():
|
def _parse_arguments():
|
||||||
import argparse
|
import argparse
|
||||||
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
|
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
|
||||||
arg_parser.add_argument('-d',
|
arg_parser.add_argument(
|
||||||
'--debug',
|
'-d',
|
||||||
action='count',
|
'--debug',
|
||||||
default=0,
|
action='count',
|
||||||
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)')
|
default=0,
|
||||||
arg_parser.add_argument('-D',
|
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)'
|
||||||
'--hidraw',
|
)
|
||||||
action='store',
|
arg_parser.add_argument(
|
||||||
dest='hidraw_path',
|
'-D',
|
||||||
metavar='PATH',
|
'--hidraw',
|
||||||
help='unifying receiver to use; the first detected receiver if unspecified. Example: /dev/hidraw2')
|
action='store',
|
||||||
|
dest='hidraw_path',
|
||||||
|
metavar='PATH',
|
||||||
|
help='unifying receiver to use; the first detected receiver if unspecified. Example: /dev/hidraw2'
|
||||||
|
)
|
||||||
arg_parser.add_argument('--restart-on-wake-up', action='store_true', help='restart Solaar on sleep wake-up (experimental)')
|
arg_parser.add_argument('--restart-on-wake-up', action='store_true', help='restart Solaar on sleep wake-up (experimental)')
|
||||||
arg_parser.add_argument('-w',
|
arg_parser.add_argument(
|
||||||
'--window',
|
'-w', '--window', choices=('show', 'hide', 'only'), help='start with window showing / hidden / only (no tray icon)'
|
||||||
choices=('show', 'hide', 'only'),
|
)
|
||||||
help='start with window showing / hidden / only (no tray icon)')
|
|
||||||
arg_parser.add_argument('-b', '--battery-icons', choices=('regular', 'symbolic'), help='prefer regular / symbolic icons')
|
arg_parser.add_argument('-b', '--battery-icons', choices=('regular', 'symbolic'), help='prefer regular / symbolic icons')
|
||||||
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
|
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
|
||||||
arg_parser.add_argument('--help-actions', action='store_true', help='print help for the optional actions')
|
arg_parser.add_argument('--help-actions', action='store_true', help='print help for the optional actions')
|
||||||
|
|
|
@ -49,12 +49,9 @@ del namedtuple
|
||||||
|
|
||||||
|
|
||||||
def _ghost(device):
|
def _ghost(device):
|
||||||
return _GHOST_DEVICE(receiver=device.receiver,
|
return _GHOST_DEVICE(
|
||||||
number=device.number,
|
receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, status=None, online=False
|
||||||
name=device.name,
|
)
|
||||||
kind=device.kind,
|
|
||||||
status=None,
|
|
||||||
online=False)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -146,11 +143,15 @@ class ReceiverListener(_listener.EventsListener):
|
||||||
assert device is not None
|
assert device is not None
|
||||||
if _log.isEnabledFor(_INFO):
|
if _log.isEnabledFor(_INFO):
|
||||||
if device.kind is None:
|
if device.kind is None:
|
||||||
_log.info('status_changed %s: %s, %s (%X) %s', device, 'present' if bool(device) else 'removed', device.status,
|
_log.info(
|
||||||
alert, reason or '')
|
'status_changed %s: %s, %s (%X) %s', device, 'present' if bool(device) else 'removed', device.status,
|
||||||
|
alert, reason or ''
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
_log.info('status_changed %s: %s %s, %s (%X) %s', device, 'paired' if bool(device) else 'unpaired',
|
_log.info(
|
||||||
'online' if device.online else 'offline', device.status, alert, reason or '')
|
'status_changed %s: %s %s, %s (%X) %s', device, 'paired' if bool(device) else 'unpaired',
|
||||||
|
'online' if device.online else 'offline', device.status, alert, reason or ''
|
||||||
|
)
|
||||||
|
|
||||||
if device.kind is None:
|
if device.kind is None:
|
||||||
assert device == self.receiver
|
assert device == self.receiver
|
||||||
|
|
|
@ -47,12 +47,16 @@ def _error_dialog(reason, object):
|
||||||
|
|
||||||
if reason == 'permissions':
|
if reason == 'permissions':
|
||||||
title = _('Permissions error')
|
title = _('Permissions error')
|
||||||
text = (_('Found a Logitech Receiver (%s), but did not have permission to open it.') % object + '\n\n' +
|
text = (
|
||||||
_("If you've just installed Solaar, try removing the receiver and plugging it back in."))
|
_('Found a Logitech Receiver (%s), but did not have permission to open it.') % object + '\n\n' +
|
||||||
|
_("If you've just installed Solaar, try removing the receiver and plugging it back in.")
|
||||||
|
)
|
||||||
elif reason == 'unpair':
|
elif reason == 'unpair':
|
||||||
title = _('Unpairing failed')
|
title = _('Unpairing failed')
|
||||||
text = (_('Failed to unpair %{device} from %{receiver}.').format(device=object.name, receiver=object.receiver.name) +
|
text = (
|
||||||
'\n\n' + _('The receiver returned an error, with no further details.'))
|
_('Failed to unpair %{device} from %{receiver}.').format(device=object.name, receiver=object.receiver.name) +
|
||||||
|
'\n\n' + _('The receiver returned an error, with no further details.')
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object)
|
raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object)
|
||||||
|
|
||||||
|
|
|
@ -45,15 +45,19 @@ def _create():
|
||||||
about.set_authors(('Daniel Pavel http://github.com/pwr', ))
|
about.set_authors(('Daniel Pavel http://github.com/pwr', ))
|
||||||
try:
|
try:
|
||||||
about.add_credit_section(_('GUI design'), ('Julien Gascard', 'Daniel Pavel'))
|
about.add_credit_section(_('GUI design'), ('Julien Gascard', 'Daniel Pavel'))
|
||||||
about.add_credit_section(_('Testing'), (
|
about.add_credit_section(
|
||||||
'Douglas Wagner',
|
_('Testing'), (
|
||||||
'Julien Gascard',
|
'Douglas Wagner',
|
||||||
'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html',
|
'Julien Gascard',
|
||||||
))
|
'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html',
|
||||||
about.add_credit_section(_('Logitech documentation'), (
|
)
|
||||||
'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',
|
)
|
||||||
'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28',
|
about.add_credit_section(
|
||||||
))
|
_('Logitech documentation'), (
|
||||||
|
'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',
|
||||||
|
'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28',
|
||||||
|
)
|
||||||
|
)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# gtk3 < ~3.6.4 has incorrect gi bindings
|
# gtk3 < ~3.6.4 has incorrect gi bindings
|
||||||
import logging
|
import logging
|
||||||
|
@ -63,16 +67,18 @@ def _create():
|
||||||
import logging
|
import logging
|
||||||
logging.exception('failed to fully create the about dialog')
|
logging.exception('failed to fully create the about dialog')
|
||||||
|
|
||||||
about.set_translator_credits('\n'.join((
|
about.set_translator_credits(
|
||||||
'gogo (croatian)',
|
'\n'.join((
|
||||||
'Papoteur, David Geiger, Damien Lallement (français)',
|
'gogo (croatian)',
|
||||||
'Michele Olivo (italiano)',
|
'Papoteur, David Geiger, Damien Lallement (français)',
|
||||||
'Adrian Piotrowicz (polski)',
|
'Michele Olivo (italiano)',
|
||||||
'Drovetto, JrBenito (Portuguese-BR)',
|
'Adrian Piotrowicz (polski)',
|
||||||
'Daniel Pavel (română)',
|
'Drovetto, JrBenito (Portuguese-BR)',
|
||||||
'Daniel Zippert, Emelie Snecker (svensk)',
|
'Daniel Pavel (română)',
|
||||||
'Dimitriy Ryazantcev (Russian)',
|
'Daniel Zippert, Emelie Snecker (svensk)',
|
||||||
)))
|
'Dimitriy Ryazantcev (Russian)',
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
about.set_website('http://pwr-solaar.github.io/Solaar/')
|
about.set_website('http://pwr-solaar.github.io/Solaar/')
|
||||||
about.set_website_label(NAME)
|
about.set_website_label(NAME)
|
||||||
|
|
|
@ -91,8 +91,10 @@ def unpair(window, device):
|
||||||
assert device
|
assert device
|
||||||
assert device.kind is not None
|
assert device.kind is not None
|
||||||
|
|
||||||
qdialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
|
qdialog = Gtk.MessageDialog(
|
||||||
_('Unpair') + ' ' + device.name + ' ?')
|
window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
|
||||||
|
_('Unpair') + ' ' + device.name + ' ?'
|
||||||
|
)
|
||||||
qdialog.set_icon_name('remove')
|
qdialog.set_icon_name('remove')
|
||||||
qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||||
qdialog.add_button(_('Unpair'), Gtk.ResponseType.ACCEPT)
|
qdialog.add_button(_('Unpair'), Gtk.ResponseType.ACCEPT)
|
||||||
|
|
|
@ -64,7 +64,8 @@ def _look_for_application_icons():
|
||||||
del _sys
|
del _sys
|
||||||
|
|
||||||
share_solaar = [prefix_share] + list(
|
share_solaar = [prefix_share] + list(
|
||||||
_path.join(x, 'solaar') for x in [src_share, local_share, setuptools_share, repo_share] + data_dirs.split(':'))
|
_path.join(x, 'solaar') for x in [src_share, local_share, setuptools_share, repo_share] + data_dirs.split(':')
|
||||||
|
)
|
||||||
for location in share_solaar:
|
for location in share_solaar:
|
||||||
location = _path.join(location, 'icons')
|
location = _path.join(location, 'icons')
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
|
|
@ -208,8 +208,10 @@ def create(receiver):
|
||||||
page_text += _('\n\nThis receiver has %d pairing(s) remaining.') % receiver.remaining_pairings()
|
page_text += _('\n\nThis receiver has %d pairing(s) remaining.') % receiver.remaining_pairings()
|
||||||
page_text += _('\nCancelling at this point will not use up a pairing.')
|
page_text += _('\nCancelling at this point will not use up a pairing.')
|
||||||
|
|
||||||
page_intro = _create_page(assistant, Gtk.AssistantPageType.PROGRESS, _('Turn on the device you want to pair.'),
|
page_intro = _create_page(
|
||||||
'preferences-desktop-peripherals', page_text)
|
assistant, Gtk.AssistantPageType.PROGRESS, _('Turn on the device you want to pair.'),
|
||||||
|
'preferences-desktop-peripherals', page_text
|
||||||
|
)
|
||||||
spinner = Gtk.Spinner()
|
spinner = Gtk.Spinner()
|
||||||
spinner.set_visible(True)
|
spinner.set_visible(True)
|
||||||
page_intro.pack_end(spinner, True, True, 24)
|
page_intro.pack_end(spinner, True, True, 24)
|
||||||
|
|
|
@ -181,8 +181,9 @@ try:
|
||||||
def _create(menu):
|
def _create(menu):
|
||||||
theme_paths = Gtk.IconTheme.get_default().get_search_path()
|
theme_paths = Gtk.IconTheme.get_default().get_search_path()
|
||||||
|
|
||||||
ind = AppIndicator3.Indicator.new_with_path('indicator-solaar', _icon_file(_icons.TRAY_INIT),
|
ind = AppIndicator3.Indicator.new_with_path(
|
||||||
AppIndicator3.IndicatorCategory.HARDWARE, ':'.join(theme_paths))
|
'indicator-solaar', _icon_file(_icons.TRAY_INIT), AppIndicator3.IndicatorCategory.HARDWARE, ':'.join(theme_paths)
|
||||||
|
)
|
||||||
ind.set_title(NAME)
|
ind.set_title(NAME)
|
||||||
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
||||||
ind.set_attention_icon_full(_icon_file(_icons.TRAY_ATTENTION), '')
|
ind.set_attention_icon_full(_icon_file(_icons.TRAY_ATTENTION), '')
|
||||||
|
|
|
@ -168,12 +168,14 @@ def _create_buttons_box():
|
||||||
bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
|
bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
|
||||||
bb.set_layout(Gtk.ButtonBoxStyle.END)
|
bb.set_layout(Gtk.ButtonBoxStyle.END)
|
||||||
|
|
||||||
bb._details = _new_button(None,
|
bb._details = _new_button(
|
||||||
'dialog-information',
|
None,
|
||||||
_SMALL_BUTTON_ICON_SIZE,
|
'dialog-information',
|
||||||
tooltip=_('Show Technical Details'),
|
_SMALL_BUTTON_ICON_SIZE,
|
||||||
toggle=True,
|
tooltip=_('Show Technical Details'),
|
||||||
clicked=_update_details)
|
toggle=True,
|
||||||
|
clicked=_update_details
|
||||||
|
)
|
||||||
bb.add(bb._details)
|
bb.add(bb._details)
|
||||||
bb.set_child_secondary(bb._details, True)
|
bb.set_child_secondary(bb._details, True)
|
||||||
bb.set_child_non_homogeneous(bb._details, True)
|
bb.set_child_non_homogeneous(bb._details, True)
|
||||||
|
@ -319,10 +321,9 @@ def _create_window_layout():
|
||||||
bottom_buttons_box.set_spacing(20)
|
bottom_buttons_box.set_spacing(20)
|
||||||
quit_button = _new_button(_('Quit') + ' ' + NAME, 'application-exit', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=destroy)
|
quit_button = _new_button(_('Quit') + ' ' + NAME, 'application-exit', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=destroy)
|
||||||
bottom_buttons_box.add(quit_button)
|
bottom_buttons_box.add(quit_button)
|
||||||
about_button = _new_button(_('About') + ' ' + NAME,
|
about_button = _new_button(
|
||||||
'help-about',
|
_('About') + ' ' + NAME, 'help-about', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window
|
||||||
icon_size=_SMALL_BUTTON_ICON_SIZE,
|
)
|
||||||
clicked=_show_about_window)
|
|
||||||
bottom_buttons_box.add(about_button)
|
bottom_buttons_box.add(about_button)
|
||||||
|
|
||||||
# solaar_version = Gtk.Label()
|
# solaar_version = Gtk.Label()
|
||||||
|
@ -447,8 +448,9 @@ def _device_row(receiver_path, device_number, device=None):
|
||||||
icon_name = _icons.device_icon_name(device.name, device.kind)
|
icon_name = _icons.device_icon_name(device.name, device.kind)
|
||||||
status_text = None
|
status_text = None
|
||||||
status_icon = None
|
status_icon = None
|
||||||
row_data = (receiver_path, device_number, bool(device.online), device.codename, icon_name, status_text, status_icon,
|
row_data = (
|
||||||
device)
|
receiver_path, device_number, bool(device.online), device.codename, icon_name, status_text, status_icon, device
|
||||||
|
)
|
||||||
assert len(row_data) == len(_TREE_SEPATATOR)
|
assert len(row_data) == len(_TREE_SEPATATOR)
|
||||||
if _log.isEnabledFor(_DEBUG):
|
if _log.isEnabledFor(_DEBUG):
|
||||||
_log.debug('new device row %s at index %d', row_data, new_child_index)
|
_log.debug('new device row %s at index %d', row_data, new_child_index)
|
||||||
|
@ -533,10 +535,12 @@ def _update_details(button):
|
||||||
hid_version = device.protocol
|
hid_version = device.protocol
|
||||||
yield (_('Protocol'), 'HID++ %1.1f' % hid_version if hid_version else _('Unknown'))
|
yield (_('Protocol'), 'HID++ %1.1f' % hid_version if hid_version else _('Unknown'))
|
||||||
if read_all and device.polling_rate:
|
if read_all and device.polling_rate:
|
||||||
yield (_('Polling rate'), _('%(rate)d ms (%(rate_hz)dHz)') % {
|
yield (
|
||||||
'rate': device.polling_rate,
|
_('Polling rate'), _('%(rate)d ms (%(rate_hz)dHz)') % {
|
||||||
'rate_hz': 1000 // device.polling_rate
|
'rate': device.polling_rate,
|
||||||
})
|
'rate_hz': 1000 // device.polling_rate
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if read_all or not device.online:
|
if read_all or not device.online:
|
||||||
yield (_('Serial'), device.serial)
|
yield (_('Serial'), device.serial)
|
||||||
|
@ -589,17 +593,19 @@ def _update_receiver_panel(receiver, panel, buttons, full=False):
|
||||||
|
|
||||||
devices_count = len(receiver)
|
devices_count = len(receiver)
|
||||||
|
|
||||||
paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.',
|
paired_text = _(
|
||||||
'%(count)s paired devices.', devices_count) % {
|
'No device paired.'
|
||||||
'count': devices_count
|
) if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % {
|
||||||
}
|
'count': devices_count
|
||||||
|
}
|
||||||
|
|
||||||
if (receiver.max_devices > 0):
|
if (receiver.max_devices > 0):
|
||||||
paired_text += '\n\n<small>%s</small>' % ngettext('Up to %(max_count)s device can be paired to this receiver.',
|
paired_text += '\n\n<small>%s</small>' % ngettext(
|
||||||
'Up to %(max_count)s devices can be paired to this receiver.',
|
'Up to %(max_count)s device can be paired to this receiver.',
|
||||||
receiver.max_devices) % {
|
'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices
|
||||||
'max_count': receiver.max_devices
|
) % {
|
||||||
}
|
'max_count': receiver.max_devices
|
||||||
|
}
|
||||||
elif devices_count > 0:
|
elif devices_count > 0:
|
||||||
paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.')
|
paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.')
|
||||||
pairings = receiver.remaining_pairings(False)
|
pairings = receiver.remaining_pairings(False)
|
||||||
|
@ -684,12 +690,15 @@ def _update_device_panel(device, panel, buttons, full=False):
|
||||||
panel._secure._text.set_text(_('not encrypted'))
|
panel._secure._text.set_text(_('not encrypted'))
|
||||||
panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
|
panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
|
||||||
panel._secure.set_tooltip_text(
|
panel._secure.set_tooltip_text(
|
||||||
_('The wireless link between this device and its receiver is not encrypted.\n'
|
_(
|
||||||
'\n'
|
'The wireless link between this device and its receiver is not encrypted.\n'
|
||||||
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
|
'\n'
|
||||||
'\n'
|
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
|
||||||
'It is, however, a major security issue for text-input devices (keyboards, numpads),\n'
|
'\n'
|
||||||
'because typed text can be sniffed inconspicuously by 3rd parties within range.'))
|
'It is, however, a major security issue for text-input devices (keyboards, numpads),\n'
|
||||||
|
'because typed text can be sniffed inconspicuously by 3rd parties within range.'
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
panel._secure._text.set_text(_('encrypted'))
|
panel._secure._text.set_text(_('encrypted'))
|
||||||
panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)
|
panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)
|
||||||
|
|
Loading…
Reference in New Issue