yapf: adjust style to not indent closing brackets

Signed-off-by: Filipe Laíns <lains@archlinux.org>
This commit is contained in:
Filipe Laíns 2020-07-02 17:01:05 +01:00 committed by Filipe Laíns
parent bbaa144397
commit 27c90fa736
27 changed files with 792 additions and 619 deletions

View File

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

View File

@ -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' # save cursor position
'\033[S' # scroll up '\033[S' # scroll up
'\033[A' # cursor up '\033[A' # cursor up
'\033[L' # insert 1 line '\033[L' # insert 1 line
'\033[G') # move cursor to column 1 '\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(
'device',
nargs='?', nargs='?',
help='linux device to connect to (/dev/hidrawX); ' 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') '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()

View File

@ -42,7 +42,8 @@ from pyudev import Monitor as _Monitor
native_implementation = 'udev' native_implementation = 'udev'
DeviceInfo = namedtuple('DeviceInfo', [ DeviceInfo = namedtuple(
'DeviceInfo', [
'path', 'path',
'vendor_id', 'vendor_id',
'product_id', 'product_id',
@ -52,7 +53,8 @@ DeviceInfo = namedtuple('DeviceInfo', [
'product', 'product',
'interface', 'interface',
'driver', 'driver',
]) ]
)
del namedtuple del namedtuple
# #
@ -121,7 +123,8 @@ 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(
path=device.device_node,
vendor_id=vid[-4:], vendor_id=vid[-4:],
product_id=pid[-4:], product_id=pid[-4:],
serial=hid_device.get('HID_UNIQ'), serial=hid_device.get('HID_UNIQ'),
@ -129,13 +132,15 @@ def _match(action, device, filter):
manufacturer=attrs.get('manufacturer'), manufacturer=attrs.get('manufacturer'),
product=attrs.get('product'), product=attrs.get('product'),
interface=usb_interface, interface=usb_interface,
driver=hid_driver_name) 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(
path=device.device_node,
vendor_id=vid[-4:], vendor_id=vid[-4:],
product_id=pid[-4:], product_id=pid[-4:],
serial=None, serial=None,
@ -143,7 +148,8 @@ def _match(action, device, filter):
manufacturer=None, manufacturer=None,
product=None, product=None,
interface=None, interface=None,
driver=None) driver=None
)
return d_info return d_info

View File

@ -294,13 +294,15 @@ def make_notification(devnumber, data):
# 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)

View File

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

View File

@ -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(
name=name,
kind=kind, kind=kind,
wpid=wpid, wpid=wpid,
codename=codename, codename=codename,
protocol=protocol, protocol=protocol,
registers=registers, registers=registers,
settings=settings, settings=settings,
persister=persister) 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(
'Wireless Mouse M185 new',
codename='M185n', codename='M185n',
protocol=4.5, protocol=4.5,
wpid='4054', wpid='4054',
settings=[ settings=[
_FS.lowres_smooth_scroll(), _FS.lowres_smooth_scroll(),
_FS.pointer_speed(), _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(
'Wireless Mouse M185/M235/M310',
codename='M185/M235/M310', codename='M185/M235/M310',
protocol=4.5, protocol=4.5,
wpid='4055', wpid='4055',
settings=[ settings=[
_FS.lowres_smooth_scroll(), _FS.lowres_smooth_scroll(),
_FS.pointer_speed(), _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,7 +399,8 @@ _D(
_RS.side_scroll(), _RS.side_scroll(),
], ],
) )
_D('Marathon Mouse M705 (M-R0073)', _D(
'Marathon Mouse M705 (M-R0073)',
codename='M705 (M-R0073)', codename='M705 (M-R0073)',
protocol=4.5, protocol=4.5,
wpid='406D', wpid='406D',
@ -398,7 +408,8 @@ _D('Marathon Mouse M705 (M-R0073)',
_FS.hires_smooth_invert(), _FS.hires_smooth_invert(),
_FS.hires_smooth_resolution(), _FS.hires_smooth_resolution(),
_FS.pointer_speed(), _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)

View File

@ -38,7 +38,8 @@ 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(
base=0x01,
top_case=0x02, top_case=0x02,
edge_of_top_right_corner=0x03, edge_of_top_right_corner=0x03,
top_left_corner=0x05, top_left_corner=0x05,
@ -48,7 +49,8 @@ POWER_SWITCH_LOCATION = _NamedInts(base=0x01,
top_edge=0x09, top_edge=0x09,
right_edge=0x0A, right_edge=0x0A,
left_edge=0x0B, left_edge=0x0B,
bottom_edge=0x0C) 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,7 +74,8 @@ 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_SubID__command=0x01,
invalid_address=0x02, invalid_address=0x02,
invalid_value=0x03, invalid_value=0x03,
connection_request_failed=0x04, connection_request_failed=0x04,
@ -83,7 +86,8 @@ ERROR = _NamedInts(invalid_SubID__command=0x01,
resource_error=0x09, resource_error=0x09,
request_unavailable=0x0A, request_unavailable=0x0A,
unsupported_parameter_value=0x0B, unsupported_parameter_value=0x0B,
wrong_pin_code=0x0C) 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:

View File

@ -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(
discharging=0x00,
recharging=0x01, recharging=0x01,
almost_full=0x02, almost_full=0x02,
full=0x03, full=0x03,
slow_recharge=0x04, slow_recharge=0x04,
invalid_battery=0x05, invalid_battery=0x05,
thermal_error=0x06) 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,7 +184,8 @@ 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(
unknown=0x01,
invalid_argument=0x02, invalid_argument=0x02,
out_of_range=0x03, out_of_range=0x03,
hardware_error=0x04, hardware_error=0x04,
@ -195,7 +193,8 @@ ERROR = _NamedInts(unknown=0x01,
invalid_feature_index=0x06, invalid_feature_index=0x06,
invalid_function=0x07, invalid_function=0x07,
busy=0x08, busy=0x08,
unsupported=0x09) 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}

View File

@ -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
'QUAD or eQUAD' if n.address == 0x03 else 'eQUAD step 4 DJ' if n.address == 0x04 else 'DFU Lite' if n.address ==
0x05 else 'eQUAD step 4 Lite' if n.address == 0x06 else 'eQUAD step 4 Gaming' if n.address == 0x05 else 'eQUAD step 4 Lite' if n.address == 0x06 else 'eQUAD step 4 Gaming' if n.address ==
0x07 else 'eQUAD step 4 for gamepads' if n.address == 0x08 else 'eQUAD nano Lite' if n.address == 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) 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:

View File

@ -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.battery_status
| _hidpp10.NOTIFICATION_FLAG.keyboard_illumination | _hidpp10.NOTIFICATION_FLAG.keyboard_illumination
| _hidpp10.NOTIFICATION_FLAG.wireless | _hidpp10.NOTIFICATION_FLAG.wireless
| _hidpp10.NOTIFICATION_FLAG.software_present) | _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.battery_status
| _hidpp10.NOTIFICATION_FLAG.wireless | _hidpp10.NOTIFICATION_FLAG.wireless
| _hidpp10.NOTIFICATION_FLAG.software_present) | _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)

View File

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

View File

@ -57,14 +57,16 @@ _F = _hidpp20.FEATURE
# #
def register_toggle(name, def register_toggle(
name,
register, register,
true_value=_BooleanV.default_true, true_value=_BooleanV.default_true,
false_value=_BooleanV.default_false, false_value=_BooleanV.default_false,
mask=_BooleanV.default_mask, mask=_BooleanV.default_mask,
label=None, label=None,
description=None, description=None,
device_kind=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,7 +79,8 @@ 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(
name,
feature, feature,
read_function_id=_FeatureRW.default_read_fnid, read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid, write_function_id=_FeatureRW.default_write_fnid,
@ -86,57 +89,61 @@ def feature_toggle(name,
mask=_BooleanV.default_mask, mask=_BooleanV.default_mask,
label=None, label=None,
description=None, description=None,
device_kind=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(
name,
feature, feature,
options, options,
read_function_id=_FeatureRW.default_read_fnid, read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid, write_function_id=_FeatureRW.default_write_fnid,
label=None, label=None,
description=None, description=None,
device_kind=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(
name,
feature, feature,
options_callback, options_callback,
read_function_id=_FeatureRW.default_read_fnid, read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid, write_function_id=_FeatureRW.default_write_fnid,
label=None, label=None,
description=None, description=None,
device_kind=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(
name,
feature, feature,
options, options,
read_function_id=read_function_id, read_function_id=read_function_id,
write_function_id=write_function_id, write_function_id=write_function_id,
label=label, label=label,
description=description, description=description,
device_kind=device_kind) 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(
name,
feature, feature,
choices, choices,
read_function_id, read_function_id,
@ -144,21 +151,18 @@ def feature_choices(name,
bytes_count=None, bytes_count=None,
label=None, label=None,
description=None, description=None,
device_kind=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(
name,
feature, feature,
choices_callback, choices_callback,
read_function_id, read_function_id,
@ -166,12 +170,14 @@ def feature_choices_dynamic(name,
bytes_count=None, bytes_count=None,
label=None, label=None,
description=None, description=None,
device_kind=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(
name,
feature, feature,
choices, choices,
read_function_id, read_function_id,
@ -179,7 +185,8 @@ def feature_choices_dynamic(name,
bytes_count=bytes_count, bytes_count=bytes_count,
label=label, label=label,
description=description, description=description,
device_kind=device_kind) device_kind=device_kind
)
return setting(device) return setting(device)
instantiate._rw_kind = _FeatureRW.kind instantiate._rw_kind = _FeatureRW.kind
@ -189,7 +196,8 @@ 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(
name,
feature, feature,
choicesmap, choicesmap,
read_function_id, read_function_id,
@ -200,25 +208,31 @@ def feature_map_choices(name,
label=None, label=None,
description=None, description=None,
device_kind=None, device_kind=None,
extra_default=None): extra_default=None
):
assert choicesmap assert choicesmap
validator = _ChoicesMapV(choicesmap, validator = _ChoicesMapV(
choicesmap,
key_bytes_count=key_bytes_count, key_bytes_count=key_bytes_count,
skip_bytes_count=skip_bytes_count, skip_bytes_count=skip_bytes_count,
value_bytes_count=value_bytes_count, value_bytes_count=value_bytes_count,
extra_default=extra_default) 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(
name,
rw, rw,
validator, validator,
feature=feature, feature=feature,
kind=_KIND.map_choice, kind=_KIND.map_choice,
label=label, label=label,
description=description, description=description,
device_kind=device_kind) device_kind=device_kind
)
def feature_map_choices_dynamic(name, def feature_map_choices_dynamic(
name,
feature, feature,
choices_callback, choices_callback,
read_function_id, read_function_id,
@ -229,13 +243,15 @@ def feature_map_choices_dynamic(name,
label=None, label=None,
description=None, description=None,
device_kind=None, device_kind=None,
extra_default=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(
name,
feature, feature,
choices, choices,
read_function_id, read_function_id,
@ -246,14 +262,16 @@ def feature_map_choices_dynamic(name,
label=label, label=label,
description=description, description=description,
device_kind=device_kind, device_kind=device_kind,
extra_default=extra_default) 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(
name,
feature, feature,
min_value, min_value,
max_value, max_value,
@ -263,18 +281,14 @@ def feature_range(name,
bytes_count=None, bytes_count=None,
label=None, label=None,
description=None, description=None,
device_kind=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,115 +348,120 @@ _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(
_FN_SWAP[0],
register, register,
true_value=true_value, true_value=true_value,
mask=mask, mask=mask,
label=_FN_SWAP[1], label=_FN_SWAP[1],
description=_FN_SWAP[2], description=_FN_SWAP[2],
device_kind=(_DK.keyboard, )) 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(
_SMOOTH_SCROLL[0],
register, register,
true_value=true_value, true_value=true_value,
mask=mask, mask=mask,
label=_SMOOTH_SCROLL[1], label=_SMOOTH_SCROLL[1],
description=_SMOOTH_SCROLL[2], description=_SMOOTH_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_SIDE_SCROLL[0],
register, register,
true_value=true_value, true_value=true_value,
mask=mask, mask=mask,
label=_SIDE_SCROLL[1], label=_SIDE_SCROLL[1],
description=_SIDE_SCROLL[2], description=_SIDE_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_FN_SWAP[0],
_F.K375S_FN_INVERSION, _F.K375S_FN_INVERSION,
label=_FN_SWAP[1], label=_FN_SWAP[1],
description=_FN_SWAP[2], description=_FN_SWAP[2],
true_value=b'\xFF\x01', true_value=b'\xFF\x01',
false_value=b'\xFF\x00', false_value=b'\xFF\x00',
device_kind=(_DK.keyboard, )) 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(
_HI_RES_SCROLL[0],
_F.HI_RES_SCROLLING, _F.HI_RES_SCROLLING,
label=_HI_RES_SCROLL[1], label=_HI_RES_SCROLL[1],
description=_HI_RES_SCROLL[2], description=_HI_RES_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_LOW_RES_SCROLL[0],
_F.LOWRES_WHEEL, _F.LOWRES_WHEEL,
label=_LOW_RES_SCROLL[1], label=_LOW_RES_SCROLL[1],
description=_LOW_RES_SCROLL[2], description=_LOW_RES_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_HIRES_INV[0],
_F.HIRES_WHEEL, _F.HIRES_WHEEL,
read_function_id=0x10, read_function_id=0x10,
write_function_id=0x20, write_function_id=0x20,
@ -430,11 +469,13 @@ def _feature_hires_smooth_invert():
mask=0x04, mask=0x04,
label=_HIRES_INV[1], label=_HIRES_INV[1],
description=_HIRES_INV[2], description=_HIRES_INV[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_HIRES_RES[0],
_F.HIRES_WHEEL, _F.HIRES_WHEEL,
read_function_id=0x10, read_function_id=0x10,
write_function_id=0x20, write_function_id=0x20,
@ -442,7 +483,8 @@ def _feature_hires_smooth_resolution():
mask=0x02, mask=0x02,
label=_HIRES_RES[1], label=_HIRES_RES[1],
description=_HIRES_RES[2], description=_HIRES_RES[2],
device_kind=(_DK.mouse, _DK.trackball)) device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_smart_shift(): def _feature_smart_shift():
@ -475,7 +517,8 @@ 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(
_SMART_SHIFT[0],
_F.SMART_SHIFT, _F.SMART_SHIFT,
_MIN_SMART_SHIFT_VALUE, _MIN_SMART_SHIFT_VALUE,
_MAX_SMART_SHIFT_VALUE, _MAX_SMART_SHIFT_VALUE,
@ -483,7 +526,8 @@ def _feature_smart_shift():
rw=_SmartShiftRW(_F.SMART_SHIFT), rw=_SmartShiftRW(_F.SMART_SHIFT),
label=_SMART_SHIFT[1], label=_SMART_SHIFT[1],
description=_SMART_SHIFT[2], description=_SMART_SHIFT[2],
device_kind=(_DK.mouse, _DK.trackball)) device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_adjustable_dpi_choices(device): def _feature_adjustable_dpi_choices(device):
@ -514,7 +558,8 @@ 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(
_DPI[0],
_F.ADJUSTABLE_DPI, _F.ADJUSTABLE_DPI,
_feature_adjustable_dpi_choices, _feature_adjustable_dpi_choices,
read_function_id=0x20, read_function_id=0x20,
@ -522,13 +567,15 @@ def _feature_adjustable_dpi():
bytes_count=3, bytes_count=3,
label=_DPI[1], label=_DPI[1],
description=_DPI[2], description=_DPI[2],
device_kind=(_DK.mouse, _DK.trackball)) 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(
_POINTER_SPEED[0],
_F.POINTER_SPEED, _F.POINTER_SPEED,
0x002e, 0x002e,
0x01ff, 0x01ff,
@ -537,7 +584,8 @@ def _feature_pointer_speed():
bytes_count=2, bytes_count=2,
label=_POINTER_SPEED[1], label=_POINTER_SPEED[1],
description=_POINTER_SPEED[2], description=_POINTER_SPEED[2],
device_kind=(_DK.mouse, _DK.trackball)) 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,7 +620,8 @@ 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(
_REPROGRAMMABLE_KEYS[0],
_F.REPROG_CONTROLS_V4, _F.REPROG_CONTROLS_V4,
_feature_reprogrammable_keys_choices, _feature_reprogrammable_keys_choices,
read_function_id=0x20, read_function_id=0x20,
@ -583,7 +632,8 @@ def _feature_reprogrammable_keys():
label=_REPROGRAMMABLE_KEYS[1], label=_REPROGRAMMABLE_KEYS[1],
description=_REPROGRAMMABLE_KEYS[2], description=_REPROGRAMMABLE_KEYS[2],
device_kind=(_DK.keyboard, ), device_kind=(_DK.keyboard, ),
extra_default=0) 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(
_DISABLE_KEYS[0],
_F.KEYBOARD_DISABLE_KEYS, _F.KEYBOARD_DISABLE_KEYS,
_feature_disable_keyboard_keys_key_list, _feature_disable_keyboard_keys_key_list,
read_function_id=0x10, read_function_id=0x10,
write_function_id=0x20, write_function_id=0x20,
label=_DISABLE_KEYS[1], label=_DISABLE_KEYS[1],
description=_DISABLE_KEYS[2], description=_DISABLE_KEYS[2],
device_kind=(_DK.keyboard, )) device_kind=(_DK.keyboard, )
)
# #

View File

@ -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(
virtual=0x80,
persistently_divertable=0x40, persistently_divertable=0x40,
divertable=0x20, divertable=0x20,
reprogrammable=0x10, reprogrammable=0x10,
FN_sensitive=0x08, FN_sensitive=0x08,
nonstandard=0x04, nonstandard=0x04,
is_FN=0x02, is_FN=0x02,
mse=0x01) mse=0x01
)
DISABLE = _NamedInts( DISABLE = _NamedInts(
Caps_Lock=0x01, Caps_Lock=0x01,

View File

@ -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 (
_('No paired devices.')
if count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', count) % { if count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', count) % {
'count': 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

View File

@ -36,43 +36,54 @@ del getLogger
def _create_parser(): def _create_parser():
parser = _argparse.ArgumentParser(prog=NAME.lower(), parser = _argparse.ArgumentParser(
prog=NAME.lower(),
add_help=False, add_help=False,
epilog='For details on individual actions, run `%s <action> --help`.' % NAME.lower()) 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(
'device',
nargs='?', nargs='?',
default='all', default='all',
help='device to show information about; may be a device number (1..6), a serial, ' 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)') '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(
'config',
help='read/write device-specific settings', help='read/write device-specific settings',
epilog='Please note that configuration only works on active devices.') epilog='Please note that configuration only works on active devices.'
sp.add_argument('device', )
sp.add_argument(
'device',
help='device to configure; may be a device number (1..6), a device serial, ' help='device to configure; may be a device number (1..6), a device serial, '
'or at least 3 characters of a device\'s name') '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(
'pair',
help='pair a new device', help='pair a new device',
epilog='The Logitech Unifying Receiver supports up to 6 paired devices at the same time.') 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)

View File

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

View File

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

View File

@ -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(
'-d',
'--debug', '--debug',
action='count', action='count',
default=0, default=0,
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)') help='print logging messages, for debugging purposes (may be repeated for extra verbosity)'
arg_parser.add_argument('-D', )
arg_parser.add_argument(
'-D',
'--hidraw', '--hidraw',
action='store', action='store',
dest='hidraw_path', dest='hidraw_path',
metavar='PATH', metavar='PATH',
help='unifying receiver to use; the first detected receiver if unspecified. Example: /dev/hidraw2') 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')

View File

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

View File

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

View File

@ -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(
_('Testing'), (
'Douglas Wagner', 'Douglas Wagner',
'Julien Gascard', 'Julien Gascard',
'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html', 'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html',
)) )
about.add_credit_section(_('Logitech documentation'), ( )
about.add_credit_section(
_('Logitech documentation'), (
'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower', 'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',
'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28', '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,7 +67,8 @@ 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(
'\n'.join((
'gogo (croatian)', 'gogo (croatian)',
'Papoteur, David Geiger, Damien Lallement (français)', 'Papoteur, David Geiger, Damien Lallement (français)',
'Michele Olivo (italiano)', 'Michele Olivo (italiano)',
@ -72,7 +77,8 @@ def _create():
'Daniel Pavel (română)', 'Daniel Pavel (română)',
'Daniel Zippert, Emelie Snecker (svensk)', 'Daniel Zippert, Emelie Snecker (svensk)',
'Dimitriy Ryazantcev (Russian)', '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)

View File

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

View File

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

View File

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

View File

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

View File

@ -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(
None,
'dialog-information', 'dialog-information',
_SMALL_BUTTON_ICON_SIZE, _SMALL_BUTTON_ICON_SIZE,
tooltip=_('Show Technical Details'), tooltip=_('Show Technical Details'),
toggle=True, toggle=True,
clicked=_update_details) 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 (
_('Polling rate'), _('%(rate)d ms (%(rate_hz)dHz)') % {
'rate': device.polling_rate, 'rate': device.polling_rate,
'rate_hz': 1000 // 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,15 +593,17 @@ 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.'
) if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % {
'count': 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:
@ -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' _(
'The wireless link between this device and its receiver is not encrypted.\n'
'\n' '\n'
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n' 'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
'\n' '\n'
'It is, however, a major security issue for text-input devices (keyboards, numpads),\n' '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.')) '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)

View File

@ -5,6 +5,8 @@ min-python-version = 3.5
[yapf] [yapf]
column_limit = 127 column_limit = 127
dedent_closing_brackets = True
coalesce_brackets = True
[isort] [isort]
line_length = 127 line_length = 127