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())
except UnicodeError:
sys.stderr.write('ERROR: Solaar cannot recognize encoding of filesystem path, '
'this may happen because non UTF-8 characters in the pathname.\n')
sys.stderr.write(
'ERROR: Solaar cannot recognize encoding of filesystem path, '
'this may happen because non UTF-8 characters in the pathname.\n'
)
sys.exit(1)
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:
# 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[A' # cursor up
'\033[L' # insert 1 line
'\033[G') # move cursor to column 1
'\033[G'
) # move cursor to column 1
sys.stdout.write(s)
if interactive and scroll:
# restore cursor position
@ -164,8 +166,10 @@ def _open(args):
if not handle:
sys.exit('!! Failed to open %s, aborting.' % device)
print('.. Opened handle %r, vendor %r product %r serial %r.' %
(handle, _hid.get_manufacturer(handle), _hid.get_product(handle), _hid.get_serial(handle)))
print(
'.. 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 _hid.get_manufacturer(handle) != b'Logitech':
sys.exit('!! Only Logitech devices support the HID++ protocol.')
@ -188,10 +192,12 @@ def _parse_arguments():
arg_parser = argparse.ArgumentParser()
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('device',
arg_parser.add_argument(
'device',
nargs='?',
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()

View File

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

View File

@ -294,13 +294,15 @@ def make_notification(devnumber, data):
# custom HID++1.0 illumination event, where SubId is 0x17
(sub_id == 0x17 and len(data) == 5) or
# 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:])
_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,
_strhex(self.data))
_HIDPP_Notification.__str__ = lambda self: 'Notification(%d,%02X,%02X,%s)' % (
self.devnumber, self.sub_id, self.address, _strhex(self.data)
)
_HIDPP_Notification.__unicode__ = _HIDPP_Notification.__str__
DJ_NOTIFICATION_LENGTH = _MEDIUM_MESSAGE_SIZE - 4 # to allow easy distinguishing of DJ notifications
del namedtuple
@ -374,15 +376,19 @@ def request(handle, devnumber, request_id, *params):
# raise NoSuchDevice(number=devnumber, request=request_id)
if _log.isEnabledFor(_DEBUG):
_log.debug('(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error,
_hidpp10.ERROR[error])
_log.debug(
'(%s) device 0x%02X error on request {%04X}: %d = %s', handle, devnumber, request_id, error,
_hidpp10.ERROR[error]
)
return
if reply_data[:1] == b'\xFF' and reply_data[1:3] == request_data[:2]:
# a HID++ 2.0 feature call returned with an error
error = ord(reply_data[3:4])
_log.error('(%s) device %d error on feature request {%04X}: %d = %s', handle, devnumber, request_id, error,
_hidpp20.ERROR[error])
_log.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)
if reply_data[:2] == request_data[:2]:
@ -423,8 +429,10 @@ def request(handle, devnumber, request_id, *params):
# if _log.isEnabledFor(_DEBUG):
# _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,
_strhex(params))
_log.warn(
'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)

View File

@ -272,7 +272,8 @@ FirmwareInfo = namedtuple('FirmwareInfo', ['kind', 'name', 'version', 'extras'])
"""Reprogrammable keys information."""
ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', ['index', 'key', 'task', 'flags'])
ReprogrammableKeyInfoV4 = namedtuple('ReprogrammableKeyInfoV4',
['index', 'key', 'task', 'flags', 'pos', 'group', 'group_mask', 'remapped'])
ReprogrammableKeyInfoV4 = namedtuple(
'ReprogrammableKeyInfoV4', ['index', 'key', 'task', 'flags', 'pos', 'group', 'group_mask', 'remapped']
)
del namedtuple

View File

@ -31,8 +31,9 @@ from .settings_templates import RegisterSettings as _RS
#
#
_DeviceDescriptor = namedtuple('_DeviceDescriptor',
('name', 'kind', 'wpid', 'codename', 'protocol', 'registers', 'settings', 'persister'))
_DeviceDescriptor = namedtuple(
'_DeviceDescriptor', ('name', 'kind', 'wpid', 'codename', 'protocol', 'registers', 'settings', 'persister')
)
del namedtuple
DEVICES = {}
@ -42,8 +43,10 @@ def _D(name, codename=None, kind=None, wpid=None, protocol=None, registers=None,
assert name
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
else _DK.touchpad if 'Touchpad' in name else _DK.trackball if 'Trackball' in name else None)
kind = (
_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
# 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':
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,
wpid=wpid,
codename=codename,
protocol=protocol,
registers=registers,
settings=settings,
persister=persister)
persister=persister
)
assert codename not in DEVICES, 'duplicate codename in device descriptors: %s' % (DEVICES[codename], )
DEVICES[codename] = device_descriptor
@ -293,24 +298,28 @@ _D(
_D('Wireless Mouse M150', protocol=2.0, wpid='4022')
_D('Wireless Mouse M175', protocol=2.0, wpid='4008')
_D('Wireless Mouse M185 new',
_D(
'Wireless Mouse M185 new',
codename='M185n',
protocol=4.5,
wpid='4054',
settings=[
_FS.lowres_smooth_scroll(),
_FS.pointer_speed(),
])
]
)
# 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
_D('Wireless Mouse M185/M235/M310',
_D(
'Wireless Mouse M185/M235/M310',
codename='M185/M235/M310',
protocol=4.5,
wpid='4055',
settings=[
_FS.lowres_smooth_scroll(),
_FS.pointer_speed(),
])
]
)
_D('Wireless Mouse M185', protocol=2.0, wpid='4038')
_D('Wireless Mouse M187', protocol=2.0, wpid='4019')
_D('Wireless Mouse M215', protocol=1.0, wpid='1020')
@ -390,7 +399,8 @@ _D(
_RS.side_scroll(),
],
)
_D('Marathon Mouse M705 (M-R0073)',
_D(
'Marathon Mouse M705 (M-R0073)',
codename='M705 (M-R0073)',
protocol=4.5,
wpid='406D',
@ -398,7 +408,8 @@ _D('Marathon Mouse M705 (M-R0073)',
_FS.hires_smooth_invert(),
_FS.hires_smooth_resolution(),
_FS.pointer_speed(),
])
]
)
_D('Zone Touch Mouse T400')
_D('Touch Mouse T620', 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)
POWER_SWITCH_LOCATION = _NamedInts(base=0x01,
POWER_SWITCH_LOCATION = _NamedInts(
base=0x01,
top_case=0x02,
edge_of_top_right_corner=0x03,
top_left_corner=0x05,
@ -48,7 +49,8 @@ POWER_SWITCH_LOCATION = _NamedInts(base=0x01,
top_edge=0x09,
right_edge=0x0A,
left_edge=0x0B,
bottom_edge=0x0C)
bottom_edge=0x0C
)
# 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
@ -72,7 +74,8 @@ NOTIFICATION_FLAG = _NamedInts(
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_value=0x03,
connection_request_failed=0x04,
@ -83,7 +86,8 @@ ERROR = _NamedInts(invalid_SubID__command=0x01,
resource_error=0x09,
request_unavailable=0x0A,
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)
@ -167,8 +171,10 @@ def parse_battery_status(register, reply):
if register == REGISTERS.battery_charge:
charge = ord(reply[:1])
status_byte = ord(reply[2:3]) & 0xF0
status_text = (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)
status_text = (
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
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.critical if status_byte == 1 # critical
# pure 'charging' notifications may come without a status
else BATTERY_APPOX.empty)
else BATTERY_APPOX.empty
)
charging_byte = ord(reply[1:2])
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)
DEVICE_KIND = _NamedInts(keyboard=0x00,
remote_control=0x01,
numpad=0x02,
mouse=0x03,
touchpad=0x04,
trackball=0x05,
presenter=0x06,
receiver=0x07)
DEVICE_KIND = _NamedInts(
keyboard=0x00, remote_control=0x01, numpad=0x02, mouse=0x03, touchpad=0x04, trackball=0x05, presenter=0x06, receiver=0x07
)
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_STATUS = _NamedInts(discharging=0x00,
BATTERY_STATUS = _NamedInts(
discharging=0x00,
recharging=0x01,
almost_full=0x02,
full=0x03,
slow_recharge=0x04,
invalid_battery=0x05,
thermal_error=0x06)
thermal_error=0x06
)
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)
ERROR = _NamedInts(unknown=0x01,
ERROR = _NamedInts(
unknown=0x01,
invalid_argument=0x02,
out_of_range=0x03,
hardware_error=0x04,
@ -195,7 +193,8 @@ ERROR = _NamedInts(unknown=0x01,
invalid_feature_index=0x06,
invalid_function=0x07,
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)
if self.keyversion == 4:
try:
mapped_data = feature_request(self.device, FEATURE.REPROG_CONTROLS_V4, 0x20, key & 0xff00,
key & 0xff)
mapped_data = feature_request(
self.device, FEATURE.REPROG_CONTROLS_V4, 0x20, key & 0xff00, key & 0xff
)
if mapped_data:
remap_key, remap_flag, remapped = _unpack('!HBH', mapped_data[:5])
# if key not mapped map it to itself for display
@ -401,8 +401,9 @@ class KeysArray(object):
# remap_flag = 0
remapped_text = special_keys.CONTROL[remapped]
self.keys[index] = _ReprogrammableKeyInfoV4(index, ctrl_id_text, ctrl_task_text, flags, pos, group,
gmask, remapped_text)
self.keys[index] = _ReprogrammableKeyInfoV4(
index, ctrl_id_text, ctrl_task_text, flags, pos, group, gmask, remapped_text
)
return self.keys[index]
@ -517,8 +518,10 @@ def get_battery(device):
discharge, dischargeNext, status = _unpack('!BBB', battery[:3])
discharge = None if discharge == 0 else discharge
if _log.isEnabledFor(_DEBUG):
_log.debug('device %d battery %d%% charged, next level %d%% charge, status %d = %s', device.number, discharge,
dischargeNext, status, BATTERY_STATUS[status])
_log.debug(
'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
@ -553,8 +556,10 @@ def decipher_voltage(voltage_report):
charge_lvl = CHARGE_LEVEL.critical
if _log.isEnabledFor(_DEBUG):
_log.debug('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)
_log.debug(
'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
@ -587,8 +592,9 @@ def get_vertical_scrolling_info(device):
vertical_scrolling_info = feature_request(device, FEATURE.VERTICAL_SCROLLING)
if vertical_scrolling_info:
roller, ratchet, lines = _unpack('!BBB', vertical_scrolling_info[:3])
roller_type = ('reserved', 'standard', 'reserved', '3G', 'micro', 'normal touch pad', 'inverted touch pad',
'reserved')[roller]
roller_type = (
'reserved', 'standard', 'reserved', '3G', 'micro', 'normal touch pad', 'inverted touch pad', 'reserved'
)[roller]
return {'roller': roller_type, 'ratchet': ratchet, 'lines': lines}

View File

@ -191,11 +191,13 @@ def _process_hidpp10_notification(device, status, n):
# wireless link notification
if n.sub_id == 0x41:
protocol_name = ('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 ==
protocol_name = (
'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 ==
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 _log.isEnabledFor(_DEBUG):
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):
sw_present = bool(flags & 0x10)
has_payload = bool(flags & 0x80)
_log.debug('%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s', device,
protocol_name, sw_present, link_encrypted, link_established, has_payload)
_log.debug(
'%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.changed(active=link_established)
else:

View File

@ -266,10 +266,12 @@ class PairedDevice(object):
return False
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.wireless
| _hidpp10.NOTIFICATION_FLAG.software_present)
| _hidpp10.NOTIFICATION_FLAG.software_present
)
else:
set_flag_bits = 0
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
@ -361,8 +363,9 @@ class Receiver(object):
self.name = product_info.get('name', '')
self.re_pairs = product_info.get('re_pairs', False)
self._str = '<%s(%s,%s%s)>' % (self.name.replace(
' ', ''), self.path, '' if isinstance(self.handle, int) else 'T', self.handle)
self._str = '<%s(%s,%s%s)>' % (
self.name.replace(' ', ''), self.path, '' if isinstance(self.handle, int) else 'T', self.handle
)
self._firmware = None
self._devices = {}
@ -398,9 +401,11 @@ class Receiver(object):
return False
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.software_present)
| _hidpp10.NOTIFICATION_FLAG.software_present
)
else:
set_flag_bits = 0
ok = _hidpp10.set_notification_flags(self, set_flag_bits)

View File

@ -167,8 +167,9 @@ class Setting(object):
def __str__(self):
if hasattr(self, '_value'):
assert hasattr(self, '_device')
return '<Setting([%s:%s] %s:%s=%s)>' % (self._rw.kind, self._validator.kind, self._device.codename, self.name,
self._value)
return '<Setting([%s:%s] %s:%s=%s)>' % (
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)
__unicode__ = __repr__ = __str__
@ -531,8 +532,10 @@ class BooleanValidator(object):
return True
if reply_value == self.false_value:
return False
_log.warn('BooleanValidator: reply %02X mismatched %02X/%02X/%02X', reply_value, self.true_value, self.false_value,
self.mask)
_log.warn(
'BooleanValidator: reply %02X mismatched %02X/%02X/%02X', reply_value, self.true_value, self.false_value,
self.mask
)
return False
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
if self.extra_default is not None and self.extra_default == reply_value:
return int(self.choices[key][0])
assert reply_value in self.choices[key], '%s: failed to validate read value %02X' % (self.__class__.__name__,
reply_value)
assert reply_value in self.choices[
key], '%s: failed to validate read value %02X' % (self.__class__.__name__, reply_value)
return reply_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,
true_value=_BooleanV.default_true,
false_value=_BooleanV.default_false,
mask=_BooleanV.default_mask,
label=None,
description=None,
device_kind=None):
device_kind=None
):
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
rw = _RegisterRW(register)
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)
def feature_toggle(name,
def feature_toggle(
name,
feature,
read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid,
@ -86,57 +89,61 @@ def feature_toggle(name,
mask=_BooleanV.default_mask,
label=None,
description=None,
device_kind=None):
device_kind=None
):
validator = _BooleanV(true_value=true_value, false_value=false_value, mask=mask)
rw = _FeatureRW(feature, read_function_id, write_function_id)
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,
options,
read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid,
label=None,
description=None,
device_kind=None):
device_kind=None
):
assert options
validator = _BitFieldV(options)
rw = _FeatureRW(feature, read_function_id, write_function_id)
return _BitFieldSetting(name,
rw,
validator,
feature=feature,
label=label,
description=description,
device_kind=device_kind)
return _BitFieldSetting(
name, rw, validator, feature=feature, label=label, description=description, device_kind=device_kind
)
def feature_bitfield_toggle_dynamic(name,
def feature_bitfield_toggle_dynamic(
name,
feature,
options_callback,
read_function_id=_FeatureRW.default_read_fnid,
write_function_id=_FeatureRW.default_write_fnid,
label=None,
description=None,
device_kind=None):
device_kind=None
):
def instantiate(device):
options = options_callback(device)
setting = feature_bitfield_toggle(name,
setting = feature_bitfield_toggle(
name,
feature,
options,
read_function_id=read_function_id,
write_function_id=write_function_id,
label=label,
description=description,
device_kind=device_kind)
device_kind=device_kind
)
return setting(device)
instantiate._rw_kind = _FeatureRW.kind
return instantiate
def feature_choices(name,
def feature_choices(
name,
feature,
choices,
read_function_id,
@ -144,21 +151,18 @@ def feature_choices(name,
bytes_count=None,
label=None,
description=None,
device_kind=None):
device_kind=None
):
assert choices
validator = _ChoicesV(choices, bytes_count=bytes_count)
rw = _FeatureRW(feature, read_function_id, write_function_id)
return _Setting(name,
rw,
validator,
feature=feature,
kind=_KIND.choice,
label=label,
description=description,
device_kind=device_kind)
return _Setting(
name, rw, 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,
choices_callback,
read_function_id,
@ -166,12 +170,14 @@ def feature_choices_dynamic(name,
bytes_count=None,
label=None,
description=None,
device_kind=None):
device_kind=None
):
# Proxy that obtains choices dynamically from a device
def instantiate(device):
# Obtain choices for this feature
choices = choices_callback(device)
setting = feature_choices(name,
setting = feature_choices(
name,
feature,
choices,
read_function_id,
@ -179,7 +185,8 @@ def feature_choices_dynamic(name,
bytes_count=bytes_count,
label=label,
description=description,
device_kind=device_kind)
device_kind=device_kind
)
return setting(device)
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
# 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
def feature_map_choices(name,
def feature_map_choices(
name,
feature,
choicesmap,
read_function_id,
@ -200,25 +208,31 @@ def feature_map_choices(name,
label=None,
description=None,
device_kind=None,
extra_default=None):
extra_default=None
):
assert choicesmap
validator = _ChoicesMapV(choicesmap,
validator = _ChoicesMapV(
choicesmap,
key_bytes_count=key_bytes_count,
skip_bytes_count=skip_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)
return _Settings(name,
return _Settings(
name,
rw,
validator,
feature=feature,
kind=_KIND.map_choice,
label=label,
description=description,
device_kind=device_kind)
device_kind=device_kind
)
def feature_map_choices_dynamic(name,
def feature_map_choices_dynamic(
name,
feature,
choices_callback,
read_function_id,
@ -229,13 +243,15 @@ def feature_map_choices_dynamic(name,
label=None,
description=None,
device_kind=None,
extra_default=None):
extra_default=None
):
# Proxy that obtains choices dynamically from a device
def instantiate(device):
choices = choices_callback(device)
if not choices: # no choices, so don't create a Setting
return None
setting = feature_map_choices(name,
setting = feature_map_choices(
name,
feature,
choices,
read_function_id,
@ -246,14 +262,16 @@ def feature_map_choices_dynamic(name,
label=label,
description=description,
device_kind=device_kind,
extra_default=extra_default)
extra_default=extra_default
)
return setting(device)
instantiate._rw_kind = _FeatureRWMap.kind
return instantiate
def feature_range(name,
def feature_range(
name,
feature,
min_value,
max_value,
@ -263,18 +281,14 @@ def feature_range(name,
bytes_count=None,
label=None,
description=None,
device_kind=None):
device_kind=None
):
validator = _RangeV(min_value, max_value, bytes_count=bytes_count)
if rw is None:
rw = _FeatureRW(feature, read_function_id, write_function_id)
return _Setting(name,
rw,
validator,
feature=feature,
kind=_KIND.range,
label=label,
description=description,
device_kind=device_kind)
return _Setting(
name, rw, 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.'))
_SMOOTH_SCROLL = ('smooth-scroll', _('Smooth Scrolling'), _('High-sensitivity mode for vertical scroll with the wheel.'))
_SIDE_SCROLL = ('side-scroll', _('Side Scrolling'),
_('When disabled, pushing the wheel sideways sends custom button events\n'
'instead of the standard side-scrolling events.'))
_HI_RES_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.'))
_SIDE_SCROLL = (
'side-scroll', _('Side Scrolling'),
_(
'When disabled, pushing the wheel sideways sends custom button events\n'
'instead of the standard side-scrolling events.'
)
)
_HI_RES_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.'))
_FN_SWAP = ('fn-swap', _('Swap Fx function'),
_('When set, the F1..F12 keys will activate their special function,\n'
'and you must hold the FN key to activate their standard function.') + '\n\n' +
_('When unset, the F1..F12 keys will activate their standard function,\n'
'and you must hold the FN key to activate their special function.'))
_FN_SWAP = (
'fn-swap', _('Swap Fx function'),
_(
'When set, the F1..F12 keys will activate their special function,\n'
'and you must hold the FN key to activate their standard function.'
) + '\n\n' + _(
'When unset, the F1..F12 keys will activate their standard function,\n'
'and you must hold the FN key to activate their special function.'
)
)
_DPI = ('dpi', _('Sensitivity (DPI)'), None)
_POINTER_SPEED = ('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'
'The mouse wheel is always free at 0, and always locked at 50'))
_POINTER_SPEED = (
'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'
'The mouse wheel is always free at 0, and always locked at 50'
)
)
_BACKLIGHT = ('backlight', _('Backlight'), _('Turn illumination on or off on keyboard.'))
_REPROGRAMMABLE_KEYS = ('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.'))
_REPROGRAMMABLE_KEYS = (
'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.'))
#
@ -314,115 +348,120 @@ _DISABLE_KEYS = ('disable-keyboard-keys', _('Disable keys'), _('Disable specific
#
def _register_hand_detection(register=_R.keyboard_hand_detection,
true_value=b'\x00\x00\x00',
false_value=b'\x00\x00\x30',
mask=b'\x00\x00\xFF'):
return register_toggle(_HAND_DETECTION[0],
def _register_hand_detection(
register=_R.keyboard_hand_detection, true_value=b'\x00\x00\x00', false_value=b'\x00\x00\x30', mask=b'\x00\x00\xFF'
):
return register_toggle(
_HAND_DETECTION[0],
register,
true_value=true_value,
false_value=false_value,
label=_HAND_DETECTION[1],
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'):
return register_toggle(_FN_SWAP[0],
return register_toggle(
_FN_SWAP[0],
register,
true_value=true_value,
mask=mask,
label=_FN_SWAP[1],
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):
return register_toggle(_SMOOTH_SCROLL[0],
return register_toggle(
_SMOOTH_SCROLL[0],
register,
true_value=true_value,
mask=mask,
label=_SMOOTH_SCROLL[1],
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):
return register_toggle(_SIDE_SCROLL[0],
return register_toggle(
_SIDE_SCROLL[0],
register,
true_value=true_value,
mask=mask,
label=_SIDE_SCROLL[1],
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):
return register_choices(_DPI[0],
register,
choices,
label=_DPI[1],
description=_DPI[2],
device_kind=(_DK.mouse, _DK.trackball))
return register_choices(
_DPI[0], register, choices, label=_DPI[1], description=_DPI[2], device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_fn_swap():
return feature_toggle(_FN_SWAP[0],
_F.FN_INVERSION,
label=_FN_SWAP[1],
description=_FN_SWAP[2],
device_kind=(_DK.keyboard, ))
return feature_toggle(
_FN_SWAP[0], _F.FN_INVERSION, label=_FN_SWAP[1], description=_FN_SWAP[2], device_kind=(_DK.keyboard, )
)
# this might not be correct for this feature
def _feature_new_fn_swap():
return feature_toggle(_FN_SWAP[0],
_F.NEW_FN_INVERSION,
label=_FN_SWAP[1],
description=_FN_SWAP[2],
device_kind=(_DK.keyboard, ))
return feature_toggle(
_FN_SWAP[0], _F.NEW_FN_INVERSION, 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
# just use the current host (first byte = 0xFF) part of the feature to read and set the Fn state
def _feature_k375s_fn_swap():
return feature_toggle(_FN_SWAP[0],
return feature_toggle(
_FN_SWAP[0],
_F.K375S_FN_INVERSION,
label=_FN_SWAP[1],
description=_FN_SWAP[2],
true_value=b'\xFF\x01',
false_value=b'\xFF\x00',
device_kind=(_DK.keyboard, ))
device_kind=(_DK.keyboard, )
)
# FIXME: This will enable all supported backlight settings,
# we should allow the users to select which settings they want to enable.
def _feature_backlight2():
return feature_toggle(_BACKLIGHT[0],
_F.BACKLIGHT2,
label=_BACKLIGHT[1],
description=_BACKLIGHT[2],
device_kind=(_DK.keyboard, ))
return feature_toggle(
_BACKLIGHT[0], _F.BACKLIGHT2, label=_BACKLIGHT[1], description=_BACKLIGHT[2], device_kind=(_DK.keyboard, )
)
def _feature_hi_res_scroll():
return feature_toggle(_HI_RES_SCROLL[0],
return feature_toggle(
_HI_RES_SCROLL[0],
_F.HI_RES_SCROLLING,
label=_HI_RES_SCROLL[1],
description=_HI_RES_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_lowres_smooth_scroll():
return feature_toggle(_LOW_RES_SCROLL[0],
return feature_toggle(
_LOW_RES_SCROLL[0],
_F.LOWRES_WHEEL,
label=_LOW_RES_SCROLL[1],
description=_LOW_RES_SCROLL[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_hires_smooth_invert():
return feature_toggle(_HIRES_INV[0],
return feature_toggle(
_HIRES_INV[0],
_F.HIRES_WHEEL,
read_function_id=0x10,
write_function_id=0x20,
@ -430,11 +469,13 @@ def _feature_hires_smooth_invert():
mask=0x04,
label=_HIRES_INV[1],
description=_HIRES_INV[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_hires_smooth_resolution():
return feature_toggle(_HIRES_RES[0],
return feature_toggle(
_HIRES_RES[0],
_F.HIRES_WHEEL,
read_function_id=0x10,
write_function_id=0x20,
@ -442,7 +483,8 @@ def _feature_hires_smooth_resolution():
mask=0x02,
label=_HIRES_RES[1],
description=_HIRES_RES[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_smart_shift():
@ -475,7 +517,8 @@ def _feature_smart_shift():
data = _int2bytes(mode, count=1) + _int2bytes(threshold, count=1) * 2
return super(_SmartShiftRW, self).write(device, data)
return feature_range(_SMART_SHIFT[0],
return feature_range(
_SMART_SHIFT[0],
_F.SMART_SHIFT,
_MIN_SMART_SHIFT_VALUE,
_MAX_SMART_SHIFT_VALUE,
@ -483,7 +526,8 @@ def _feature_smart_shift():
rw=_SmartShiftRW(_F.SMART_SHIFT),
label=_SMART_SHIFT[1],
description=_SMART_SHIFT[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_adjustable_dpi_choices(device):
@ -514,7 +558,8 @@ def _feature_adjustable_dpi():
# Assume sensorIdx 0 (there is only one sensor)
# [2] getSensorDpi(sensorIdx) -> sensorIdx, dpiMSB, dpiLSB
# [3] setSensorDpi(sensorIdx, dpi)
return feature_choices_dynamic(_DPI[0],
return feature_choices_dynamic(
_DPI[0],
_F.ADJUSTABLE_DPI,
_feature_adjustable_dpi_choices,
read_function_id=0x20,
@ -522,13 +567,15 @@ def _feature_adjustable_dpi():
bytes_count=3,
label=_DPI[1],
description=_DPI[2],
device_kind=(_DK.mouse, _DK.trackball))
device_kind=(_DK.mouse, _DK.trackball)
)
def _feature_pointer_speed():
"""Pointer Speed feature"""
# 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,
0x002e,
0x01ff,
@ -537,7 +584,8 @@ def _feature_pointer_speed():
bytes_count=2,
label=_POINTER_SPEED[1],
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)
@ -572,7 +620,8 @@ def _feature_reprogrammable_keys_choices(device):
def _feature_reprogrammable_keys():
return feature_map_choices_dynamic(_REPROGRAMMABLE_KEYS[0],
return feature_map_choices_dynamic(
_REPROGRAMMABLE_KEYS[0],
_F.REPROG_CONTROLS_V4,
_feature_reprogrammable_keys_choices,
read_function_id=0x20,
@ -583,7 +632,8 @@ def _feature_reprogrammable_keys():
label=_REPROGRAMMABLE_KEYS[1],
description=_REPROGRAMMABLE_KEYS[2],
device_kind=(_DK.keyboard, ),
extra_default=0)
extra_default=0
)
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():
return feature_bitfield_toggle_dynamic(_DISABLE_KEYS[0],
return feature_bitfield_toggle_dynamic(
_DISABLE_KEYS[0],
_F.KEYBOARD_DISABLE_KEYS,
_feature_disable_keyboard_keys_key_list,
read_function_id=0x10,
write_function_id=0x20,
label=_DISABLE_KEYS[1],
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
# 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,
divertable=0x20,
reprogrammable=0x10,
FN_sensitive=0x08,
nonstandard=0x04,
is_FN=0x02,
mse=0x01)
mse=0x01
)
DISABLE = _NamedInts(
Caps_Lock=0x01,

View File

@ -102,10 +102,12 @@ class ReceiverStatus(dict):
def __str__(self):
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) % {
'count': count
})
}
)
__unicode__ = __str__
@ -209,8 +211,10 @@ class DeviceStatus(dict):
if voltage is not None:
self[KEYS.BATTERY_VOLTAGE] = voltage
charging = status in (_hidpp20.BATTERY_STATUS.recharging, _hidpp20.BATTERY_STATUS.almost_full,
_hidpp20.BATTERY_STATUS.full, _hidpp20.BATTERY_STATUS.slow_recharge)
charging = status in (
_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
changed = old_level != level or old_status != status or old_charging != charging

View File

@ -36,43 +36,54 @@ del getLogger
def _create_parser():
parser = _argparse.ArgumentParser(prog=NAME.lower(),
parser = _argparse.ArgumentParser(
prog=NAME.lower(),
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')
sp = subparsers.add_parser('show', help='show information about devices')
sp.add_argument('device',
sp.add_argument(
'device',
nargs='?',
default='all',
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 = 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.set_defaults(action='probe')
sp = subparsers.add_parser('config',
sp = subparsers.add_parser(
'config',
help='read/write device-specific settings',
epilog='Please note that configuration only works on active devices.')
sp.add_argument('device',
epilog='Please note that configuration only works on active devices.'
)
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')
'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('value', nargs='?', help='new value for the setting')
sp.set_defaults(action='config')
sp = subparsers.add_parser('pair',
sp = subparsers.add_parser(
'pair',
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.set_defaults(action='pair')
sp = subparsers.add_parser('unpair', help='unpair a device')
sp.add_argument('device',
help='device to unpair; may be a device number (1..6), a serial, '
'or a substring of a device\'s name.')
sp.add_argument(
'device', help='device to unpair; may be a device number (1..6), a serial, '
'or a substring of a device\'s name.'
)
sp.set_defaults(action='unpair')
return parser, subparsers.choices
@ -130,8 +141,10 @@ def _find_device(receivers, name):
return dev
for dev in r:
if (name == dev.serial.lower() or name == dev.codename.lower() or name == str(dev.kind).lower()
or name in dev.name.lower()):
if (
name == dev.serial.lower() or name == dev.codename.lower() or name == str(dev.kind).lower()
or name in dev.name.lower()
):
return dev
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:
print('# possible values: on/true/t/yes/y/1 or off/false/f/no/n/0')
elif s.choices:
print('# possible values: one of [', ', '.join(str(v) for v in s.choices),
'], or higher/lower/highest/max/lowest/min')
print(
'# possible values: one of [', ', '.join(str(v) for v in s.choices),
'], or higher/lower/highest/max/lowest/min'
)
else:
# wtf?
pass

View File

@ -45,22 +45,32 @@ def run(receivers, args, find_receiver, _ignore):
register = receiver.read_register(_R.notifications)
print(' Notification Register %#04x: %s' % (_R.notifications % 0x100, '0x' + _strhex(register) if register else 'None'))
register = receiver.read_register(_R.receiver_connection)
print(' Connection State %#04x: %s' %
(_R.receiver_connection % 0x100, '0x' + _strhex(register) if register else 'None'))
print(
' Connection State %#04x: %s' %
(_R.receiver_connection % 0x100, '0x' + _strhex(register) if register else 'None')
)
register = receiver.read_register(_R.devices_activity)
print(' Device Activity %#04x: %s' %
(_R.devices_activity % 0x100, '0x' + _strhex(register) if register else 'None'))
print(
' Device Activity %#04x: %s' %
(_R.devices_activity % 0x100, '0x' + _strhex(register) if register else 'None')
)
for device in range(0, 6):
for sub_reg in [0x0, 0x10, 0x20, 0x30]:
register = receiver.read_register(_R.receiver_info, sub_reg + device)
print(' Pairing Register %#04x %#04x: %s' %
(_R.receiver_info % 0x100, sub_reg + device, '0x' + _strhex(register) if register else 'None'))
print(
' 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)
print(' Pairing Name %#04x %#02x: %s' %
(_R.receiver_info % 0x100, 0x40 + device, register[2:2 + ord(register[1:2])] if register else 'None'))
print(
' 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):
register = receiver.read_register(_R.firmware, sub_reg)
print(' Firmware %#04x %#04x: %s' %
(_R.firmware % 0x100, sub_reg, '0x' + _strhex(register) if register else 'None'))
print(
' 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():
import argparse
arg_parser = argparse.ArgumentParser(prog=NAME.lower())
arg_parser.add_argument('-d',
arg_parser.add_argument(
'-d',
'--debug',
action='count',
default=0,
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)')
arg_parser.add_argument('-D',
help='print logging messages, for debugging purposes (may be repeated for extra verbosity)'
)
arg_parser.add_argument(
'-D',
'--hidraw',
action='store',
dest='hidraw_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('-w',
'--window',
choices=('show', 'hide', 'only'),
help='start with window showing / hidden / only (no tray icon)')
arg_parser.add_argument(
'-w', '--window', 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('-V', '--version', action='version', version='%(prog)s ' + __version__)
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):
return _GHOST_DEVICE(receiver=device.receiver,
number=device.number,
name=device.name,
kind=device.kind,
status=None,
online=False)
return _GHOST_DEVICE(
receiver=device.receiver, number=device.number, name=device.name, kind=device.kind, status=None, online=False
)
#
@ -146,11 +143,15 @@ class ReceiverListener(_listener.EventsListener):
assert device is not None
if _log.isEnabledFor(_INFO):
if device.kind is None:
_log.info('status_changed %s: %s, %s (%X) %s', device, 'present' if bool(device) else 'removed', device.status,
alert, reason or '')
_log.info(
'status_changed %s: %s, %s (%X) %s', device, 'present' if bool(device) else 'removed', device.status,
alert, reason or ''
)
else:
_log.info('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 '')
_log.info(
'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:
assert device == self.receiver

View File

@ -47,12 +47,16 @@ def _error_dialog(reason, object):
if reason == 'permissions':
title = _('Permissions error')
text = (_('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."))
text = (
_('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':
title = _('Unpairing failed')
text = (_('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.'))
text = (
_('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:
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', ))
try:
about.add_credit_section(_('GUI design'), ('Julien Gascard', 'Daniel Pavel'))
about.add_credit_section(_('Testing'), (
about.add_credit_section(
_('Testing'), (
'Douglas Wagner',
'Julien Gascard',
'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',
'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28',
))
)
)
except TypeError:
# gtk3 < ~3.6.4 has incorrect gi bindings
import logging
@ -63,7 +67,8 @@ def _create():
import logging
logging.exception('failed to fully create the about dialog')
about.set_translator_credits('\n'.join((
about.set_translator_credits(
'\n'.join((
'gogo (croatian)',
'Papoteur, David Geiger, Damien Lallement (français)',
'Michele Olivo (italiano)',
@ -72,7 +77,8 @@ def _create():
'Daniel Pavel (română)',
'Daniel Zippert, Emelie Snecker (svensk)',
'Dimitriy Ryazantcev (Russian)',
)))
))
)
about.set_website('http://pwr-solaar.github.io/Solaar/')
about.set_website_label(NAME)

View File

@ -91,8 +91,10 @@ def unpair(window, device):
assert device
assert device.kind is not None
qdialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
_('Unpair') + ' ' + device.name + ' ?')
qdialog = Gtk.MessageDialog(
window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
_('Unpair') + ' ' + device.name + ' ?'
)
qdialog.set_icon_name('remove')
qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
qdialog.add_button(_('Unpair'), Gtk.ResponseType.ACCEPT)

View File

@ -64,7 +64,8 @@ def _look_for_application_icons():
del _sys
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:
location = _path.join(location, 'icons')
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 += _('\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.'),
'preferences-desktop-peripherals', page_text)
page_intro = _create_page(
assistant, Gtk.AssistantPageType.PROGRESS, _('Turn on the device you want to pair.'),
'preferences-desktop-peripherals', page_text
)
spinner = Gtk.Spinner()
spinner.set_visible(True)
page_intro.pack_end(spinner, True, True, 24)

View File

@ -181,8 +181,9 @@ try:
def _create(menu):
theme_paths = Gtk.IconTheme.get_default().get_search_path()
ind = AppIndicator3.Indicator.new_with_path('indicator-solaar', _icon_file(_icons.TRAY_INIT),
AppIndicator3.IndicatorCategory.HARDWARE, ':'.join(theme_paths))
ind = AppIndicator3.Indicator.new_with_path(
'indicator-solaar', _icon_file(_icons.TRAY_INIT), AppIndicator3.IndicatorCategory.HARDWARE, ':'.join(theme_paths)
)
ind.set_title(NAME)
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
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.set_layout(Gtk.ButtonBoxStyle.END)
bb._details = _new_button(None,
bb._details = _new_button(
None,
'dialog-information',
_SMALL_BUTTON_ICON_SIZE,
tooltip=_('Show Technical Details'),
toggle=True,
clicked=_update_details)
clicked=_update_details
)
bb.add(bb._details)
bb.set_child_secondary(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)
quit_button = _new_button(_('Quit') + ' ' + NAME, 'application-exit', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=destroy)
bottom_buttons_box.add(quit_button)
about_button = _new_button(_('About') + ' ' + NAME,
'help-about',
icon_size=_SMALL_BUTTON_ICON_SIZE,
clicked=_show_about_window)
about_button = _new_button(
_('About') + ' ' + NAME, 'help-about', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window
)
bottom_buttons_box.add(about_button)
# 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)
status_text = None
status_icon = None
row_data = (receiver_path, device_number, bool(device.online), device.codename, icon_name, status_text, status_icon,
device)
row_data = (
receiver_path, device_number, bool(device.online), device.codename, icon_name, status_text, status_icon, device
)
assert len(row_data) == len(_TREE_SEPATATOR)
if _log.isEnabledFor(_DEBUG):
_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
yield (_('Protocol'), 'HID++ %1.1f' % hid_version if hid_version else _('Unknown'))
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_hz': 1000 // device.polling_rate
})
}
)
if read_all or not device.online:
yield (_('Serial'), device.serial)
@ -589,15 +593,17 @@ def _update_receiver_panel(receiver, panel, buttons, full=False):
devices_count = len(receiver)
paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.',
'%(count)s paired devices.', devices_count) % {
paired_text = _(
'No device paired.'
) if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % {
'count': devices_count
}
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.',
'Up to %(max_count)s devices can be paired to this receiver.',
receiver.max_devices) % {
paired_text += '\n\n<small>%s</small>' % ngettext(
'Up to %(max_count)s device can be paired to this receiver.',
'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices
) % {
'max_count': receiver.max_devices
}
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._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
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'
'For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n'
'\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:
panel._secure._text.set_text(_('encrypted'))
panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)

View File

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