improved support for some Nano receivers

This commit is contained in:
Daniel Pavel 2013-05-22 20:41:11 +03:00
parent 790fc7c04b
commit c3b73964d5
10 changed files with 139 additions and 158 deletions

View File

@ -67,9 +67,10 @@ class DeviceUnreachable(_KwException):
# #
# vendor_id, product_id, usb interface number, hid driver # vendor_id, product_id, usb interface number, hid driver
DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver') DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver')
DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver') DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver')
DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic') DEVICE_NANO_RECEIVER = (0x046d, 0xc52f, 1, 'hid-generic')
DEVICE_VXNANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic')
def receivers(): def receivers():
@ -78,8 +79,10 @@ def receivers():
yield d yield d
for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2): for d in _hid.enumerate(*DEVICE_UNIFYING_RECEIVER_2):
yield d yield d
#for d in _hid.enumerate(*DEVICE_NANO_RECEIVER): for d in _hid.enumerate(*DEVICE_NANO_RECEIVER):
# yield d yield d
for d in _hid.enumerate(*DEVICE_VXNANO_RECEIVER):
yield d
def notify_on_receivers(callback): def notify_on_receivers(callback):
@ -87,7 +90,8 @@ def notify_on_receivers(callback):
_hid.monitor_async(callback, _hid.monitor_async(callback,
DEVICE_UNIFYING_RECEIVER, DEVICE_UNIFYING_RECEIVER,
DEVICE_UNIFYING_RECEIVER_2, DEVICE_UNIFYING_RECEIVER_2,
# DEVICE_NANO_RECEIVER, DEVICE_NANO_RECEIVER,
DEVICE_VXNANO_RECEIVER,
) )

View File

@ -51,11 +51,11 @@ def check_features(device, already_known):
# #
_DeviceDescriptor = namedtuple('_DeviceDescriptor', _DeviceDescriptor = namedtuple('_DeviceDescriptor',
['name', 'kind', 'codename', 'registers', 'settings']) ['name', 'kind', 'product_id', 'codename', 'registers', 'settings'])
DEVICES = {} DEVICES = {}
def _D(name, codename=None, kind=None, registers=None, settings=None): def _D(name, codename=None, kind=None, product_id=None, registers=None, settings=None):
if kind is None: if kind is None:
kind = (_hidpp10.DEVICE_KIND.mouse if 'Mouse' in name kind = (_hidpp10.DEVICE_KIND.mouse if 'Mouse' in name
else _hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name else _hidpp10.DEVICE_KIND.keyboard if 'Keyboard' in name
@ -68,7 +68,9 @@ def _D(name, codename=None, kind=None, registers=None, settings=None):
codename = name.split(' ')[-1] codename = name.split(' ')[-1]
assert codename is not None assert codename is not None
DEVICES[codename] = _DeviceDescriptor(name, kind, codename, registers, settings) DEVICES[codename] = _DeviceDescriptor(name, kind, product_id, codename, registers, settings)
if product_id:
DEVICES[product_id] = DEVICES[codename]
# #
# #
@ -133,6 +135,13 @@ _D('Wireless Illuminated Keyboard K800',
# Mice # Mice
# _D('VX Nano Cordless Laser Mouse', product_id='c526', codename='VXNano',
# registers={'battery_charge': 0x0D},
# settings=[
# _register_smooth_scroll(0x01, true_value=0x40, mask=0x40),
# ],
# )
_D('Wireless Mouse M315') _D('Wireless Mouse M315')
_D('Wireless Mouse M325') _D('Wireless Mouse M325')
_D('Wireless Mouse M505') _D('Wireless Mouse M505')
@ -154,7 +163,7 @@ _D('Marathon Mouse M705',
) )
_D('Zone Touch Mouse T400') _D('Zone Touch Mouse T400')
_D('Touch Mouse T620') _D('Touch Mouse T620')
_D('Logitech Cube', kind='mouse') _D('Logitech Cube', kind=_hidpp10.DEVICE_KIND.mouse)
_D('Anywhere Mouse MX', codename='Anywhere MX', _D('Anywhere Mouse MX', codename='Anywhere MX',
# registers={'battery_charge': 0x0D}, # registers={'battery_charge': 0x0D},
# settings=[ # settings=[

View File

@ -153,7 +153,7 @@ def get_serial(device):
def get_firmware(device): def get_firmware(device):
firmware = [] firmware = [None, None]
reply = device.request(0x81F1, 0x01) reply = device.request(0x81F1, 0x01)
if reply: if reply:
@ -163,23 +163,17 @@ def get_firmware(device):
if reply: if reply:
fw_version += '.B' + _strhex(reply[1:3]) fw_version += '.B' + _strhex(reply[1:3])
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None) fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
firmware.append(fw) firmware[0] = fw
if device.kind is None and device.max_devices == 1:
# Nano receiver
return firmware
if device.kind is not None and not device._unifying:
# Nano device
return firmware
reply = device.request(0x81F1, 0x04) reply = device.request(0x81F1, 0x04)
if reply: if reply:
bl_version = _strhex(reply[1:3]) bl_version = _strhex(reply[1:3])
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4]) bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None) bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None)
firmware.append(bl) firmware[1] = bl
return tuple(firmware) if any(firmware):
return tuple(f for f in firmware if f)
def get_notification_flags(device): def get_notification_flags(device):

View File

@ -32,19 +32,40 @@ class PairedDevice(object):
assert number > 0 and number <= receiver.max_devices assert number > 0 and number <= receiver.max_devices
self.number = number self.number = number
self._unifying = receiver.max_devices > 1 self.wpid = None
self._protocol = None if self._unifying else 1.0 self.polling_rate = 0
self._wpid = None
self._power_switch = None self._kind = None
self._polling_rate = None if self._unifying else 0
self._codename = None self._codename = None
self._name = None self._name = None
self._kind = None
self._serial = None if self._unifying else receiver.serial self._protocol = None if self.receiver.unifying_supported else 1.0
self._power_switch = None if self.receiver.unifying_supported else '(unknown)'
self._serial = None if self.receiver.unifying_supported else self.receiver.serial
if self.receiver.unifying_supported:
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
if pair_info:
self.wpid = _strhex(pair_info[3:5])
kind = ord(pair_info[7:8]) & 0x0F
self._kind = _hidpp10.DEVICE_KIND[kind]
self.polling_rate = ord(pair_info[2:3])
# else:
# # guesswork...
# descriptor = _descriptors.DEVICES.get(self.receiver.product_id)
# if descriptor:
# self._kind = descriptor.kind
# self._codename = descriptor.codename
# self._name = descriptor.name
# device_info = self.receiver.request(0x83B5, 0x04)
# if device_info:
# self.wpid = _strhex(device_info[3:5])
self._firmware = None self._firmware = None
self._keys = None self._keys = None
self.features = _hidpp20.FeaturesArray(self) if self._unifying else None self.features = _hidpp20.FeaturesArray(self) if self.receiver.unifying_supported else None
self._registers = None self._registers = None
self._settings = None self._settings = None
@ -55,66 +76,36 @@ class PairedDevice(object):
# _log.debug("device %d protocol %s", self.number, self._protocol) # _log.debug("device %d protocol %s", self.number, self._protocol)
return self._protocol or 0 return self._protocol or 0
@property
def wpid(self):
if self._wpid is None:
if self._unifying:
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
if pair_info:
self._wpid = _strhex(pair_info[3:5])
if self._kind is None:
kind = ord(pair_info[7:8]) & 0x0F
self._kind = _hidpp10.DEVICE_KIND[kind]
if self._polling_rate is None:
self._polling_rate = ord(pair_info[2:3])
else:
# guesswork...
device_info = self.receiver.request(0x83B5, 0x04)
self.wpid = _strhex(device_info[3:5])
return self._wpid
@property
def polling_rate(self):
if self._polling_rate is None:
if self._unifying:
self.wpid, 0
else:
self._polling_rate = 0
return self._polling_rate
@property @property
def power_switch_location(self): def power_switch_location(self):
if self._power_switch is None: if self._power_switch is None:
if self._unifying: ps = self.receiver.request(0x83B5, 0x30 + self.number - 1)
ps = self.receiver.request(0x83B5, 0x30 + self.number - 1) if ps:
if ps: ps = ord(ps[9:10]) & 0x0F
ps = ord(ps[9:10]) & 0x0F self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps]
return self._power_switch return self._power_switch
@property @property
def codename(self): def codename(self):
if self._codename is None: if self._codename is None:
if self._unifying: codename = self.receiver.request(0x83B5, 0x40 + self.number - 1)
codename = self.receiver.request(0x83B5, 0x40 + self.number - 1) if codename:
if codename: self._codename = codename[2:].rstrip(b'\x00').decode('utf-8')
self._codename = codename[2:].rstrip(b'\x00').decode('utf-8') # _log.debug("device %d codename %s", self.number, self._codename)
# _log.debug("device %d codename %s", self.number, self._codename)
return self._codename return self._codename
@property @property
def name(self): def name(self):
if self._name is None: if self._name is None:
if self._unifying: if self.codename in _descriptors.DEVICES:
if self.codename in _descriptors.DEVICES: self._name, self._kind = _descriptors.DEVICES[self._codename][:2]
self._name, self._kind = _descriptors.DEVICES[self._codename][:2] elif self.protocol >= 2.0:
elif self.protocol >= 2.0: self._name = _hidpp20.get_name(self)
self._name = _hidpp20.get_name(self)
return self._name or self.codename or '?' return self._name or self.codename or '?'
@property @property
def kind(self): def kind(self):
if self._kind is None and self._unifying: if self._kind is None and self.receiver.unifying_supported:
pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1) pair_info = self.receiver.request(0x83B5, 0x20 + self.number - 1)
if pair_info: if pair_info:
kind = ord(pair_info[7:8]) & 0x0F kind = ord(pair_info[7:8]) & 0x0F
@ -146,7 +137,7 @@ class PairedDevice(object):
@property @property
def keys(self): def keys(self):
if self._keys is None: if self._keys is None:
if self._unifying: if self.protocol >= 2.0:
self._keys = _hidpp20.get_keys(self) or () self._keys = _hidpp20.get_keys(self) or ()
return self._keys return self._keys
@ -201,7 +192,7 @@ class PairedDevice(object):
return _base.request(self.receiver.handle, self.number, request_id, *params) return _base.request(self.receiver.handle, self.number, request_id, *params)
def feature_request(self, feature, function=0x00, *params): def feature_request(self, feature, function=0x00, *params):
if self._unifying: if self.protocol >= 2.0:
return _hidpp20.feature_request(self, feature, function, *params) return _hidpp20.feature_request(self, feature, function, *params)
def ping(self): def ping(self):
@ -236,15 +227,18 @@ class Receiver(object):
number = 0xFF number = 0xFF
kind = None kind = None
def __init__(self, handle, path=None): def __init__(self, handle, device_info):
assert handle assert handle
self.handle = handle self.handle = handle
assert path assert device_info
self.path = path self.path = device_info.path
self.product_id = device_info.product_id
# read the serial immediately, so we can find out max_devices
# this will tell us if it's a Unifying or Nano receiver
serial_reply = self.request(0x83B5, 0x03) serial_reply = self.request(0x83B5, 0x03)
assert serial_reply assert serial_reply
self._serial = _strhex(serial_reply[1:5]) self.serial = _strhex(serial_reply[1:5])
self.max_devices = ord(serial_reply[6:7]) self.max_devices = ord(serial_reply[6:7])
if self.max_devices == 1: if self.max_devices == 1:
@ -255,6 +249,9 @@ class Receiver(object):
raise Exception("unknown receiver type") raise Exception("unknown receiver type")
self._str = '<%s(%s,%s%s)>' % (self.name.replace(' ', ''), self.path, '' if type(self.handle) == int else 'T', self.handle) self._str = '<%s(%s,%s%s)>' % (self.name.replace(' ', ''), self.path, '' if type(self.handle) == int else 'T', self.handle)
old_equad_reply = self.request(0x83B5, 0x04)
self.unifying_supported = old_equad_reply is None
self._firmware = None self._firmware = None
self._devices = {} self._devices = {}
@ -266,13 +263,6 @@ class Receiver(object):
def __del__(self): def __del__(self):
self.close() self.close()
@property
def serial(self):
assert self._serial
# if self._serial is None and self.handle:
# self._serial = _hidpp10.get_serial(self)
return self._serial
@property @property
def firmware(self): def firmware(self):
if self._firmware is None and self.handle: if self._firmware is None and self.handle:
@ -308,21 +298,13 @@ class Receiver(object):
def register_new_device(self, number): def register_new_device(self, number):
if self._devices.get(number) is not None: if self._devices.get(number) is not None:
raise IndexError("%s: device number %d already registered" % (self, number)) raise IndexError("%s: device number %d already registered" % (self, number))
dev = PairedDevice(self, number) dev = PairedDevice(self, number)
# create a device object, but only use it if the receiver knows about it
# Nano receiver
if self.max_devices == 1 and number == 1:
# the Nano receiver does not provide the wpid
_log.info("%s: found Nano device %d (%s)", self, number, dev.serial)
# dev._wpid = self.serial + ':1'
self._devices[number] = dev
return dev
if dev.wpid: if dev.wpid:
_log.info("%s: found Unifying device %d (%s)", self, number, dev.wpid) _log.info("%s: found device %d (%s)", self, number, dev.wpid)
self._devices[number] = dev self._devices[number] = dev
return dev return dev
self._devices[number] = None self._devices[number] = None
def set_lock(self, lock_closed=True, device=0, timeout=0): def set_lock(self, lock_closed=True, device=0, timeout=0):
@ -403,18 +385,18 @@ class Receiver(object):
__bool__ = __nonzero__ = lambda self: self.handle is not None __bool__ = __nonzero__ = lambda self: self.handle is not None
@classmethod @classmethod
def open(self, path): def open(self, device_info):
"""Opens a Logitech Receiver found attached to the machine, by Linux device path. """Opens a Logitech Receiver found attached to the machine, by Linux device path.
:returns: An open file handle for the found receiver, or ``None``. :returns: An open file handle for the found receiver, or ``None``.
""" """
try: try:
handle = _base.open_path(path) handle = _base.open_path(device_info.path)
if handle: if handle:
return Receiver(handle, path) return Receiver(handle, device_info)
except OSError as e: except OSError as e:
_log.exception("open %s", path) _log.exception("open %s", device_info)
if e.errno == _errno.EACCES: if e.errno == _errno.EACCES:
raise raise
except: except:
_log.exception("open %s", path) _log.exception("open %s", device_info)

View File

@ -273,6 +273,7 @@ class DeviceStatus(dict):
_log.warn("%s: unrecognized %s", self._device, n) _log.warn("%s: unrecognized %s", self._device, n)
def _process_hidpp10_notification(self, n): def _process_hidpp10_notification(self, n):
# unpair notification
if n.sub_id == 0x40: if n.sub_id == 0x40:
if n.address == 0x02: if n.address == 0x02:
# device un-paired # device un-paired
@ -283,44 +284,33 @@ class DeviceStatus(dict):
_log.warn("%s: disconnection with unknown type %02X: %s", self._device, n.address, n) _log.warn("%s: disconnection with unknown type %02X: %s", self._device, n.address, n)
return True return True
# wireless link notification
if n.sub_id == 0x41: if n.sub_id == 0x41:
if n.address == 0x04: # unifying protocol protocol_name = ('unifying (eQuad DJ)' if n.address == 0x04
# wpid = _strhex(n.data[4:5] + n.data[3:4]) else 'eQuad' if n.address == 0x03
# assert wpid == device.wpid else None)
if protocol_name:
flags = ord(n.data[:1]) & 0xF0 flags = ord(n.data[:1]) & 0xF0
link_encrypyed = bool(flags & 0x20) link_encrypyed = bool(flags & 0x20)
link_established = not (flags & 0x40) link_established = not (flags & 0x40)
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: unifying connection notification: software=%s, encrypted=%s, link=%s, payload=%s", _log.debug("%s: %s connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
self._device, sw_present, link_encrypyed, link_established, has_payload) self._device, protocol_name, sw_present, link_encrypyed, link_established, has_payload)
self[ENCRYPTED] = link_encrypyed
self._changed(link_established)
elif n.address == 0x03: # eQuad protocol
# Nano devices might not have been initialized fully
if self._device._kind is None:
kind = ord(n.data[:1]) & 0x0F
self._device._kind = _hidpp10.DEVICE_KIND[kind]
if self._device._wpid is None:
self._device._wpid = _strhex(n.data[2:3] + n.data[1:2])
flags = ord(n.data[:1]) & 0xF0
link_encrypyed = bool(flags & 0x20)
link_established = not (flags & 0x40)
if _log.isEnabledFor(_DEBUG):
sw_present = bool(flags & 0x10)
has_payload = bool(flags & 0x80)
_log.debug("%s: eQuad connection notification: software=%s, encrypted=%s, link=%s, payload=%s",
self._device, sw_present, link_encrypyed, link_established, has_payload)
self[ENCRYPTED] = link_encrypyed self[ENCRYPTED] = link_encrypyed
self._changed(link_established) self._changed(link_established)
if protocol_name == 'eQuad':
# some Nano devices might not have been initialized fully
if self._device._kind is None:
kind = ord(n.data[:1]) & 0x0F
self._device._kind = _hidpp10.DEVICE_KIND[kind]
assert self._device.wpid == _strhex(n.data[2:3] + n.data[1:2])
else: else:
_log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n) _log.warn("%s: connection notification with unknown protocol %02X: %s", self._device.number, n.address, n)
# if the device just came online, read the battery charge
if self._active: # and BATTERY_LEVEL not in self: if self._active: # and BATTERY_LEVEL not in self:
self.read_battery() self.read_battery()
@ -332,6 +322,7 @@ class DeviceStatus(dict):
# if n.address == 0x03, it's an actual input event # if n.address == 0x03, it's an actual input event
return True return True
# power notification
if n.sub_id == 0x4B: if n.sub_id == 0x4B:
if n.address == 0x01: if n.address == 0x01:
if _log.isEnabledFor(_DEBUG): if _log.isEnabledFor(_DEBUG):

View File

@ -36,7 +36,7 @@ def _receiver():
from logitech.unifying_receiver.base import receivers from logitech.unifying_receiver.base import receivers
for dev_info in receivers(): for dev_info in receivers():
try: try:
r = Receiver.open(dev_info.path) r = Receiver.open(dev_info)
if r: if r:
return r return r
except Exception as e: except Exception as e:
@ -99,7 +99,7 @@ def _print_receiver(receiver, verbose=False):
else: else:
print (" All notifications disabled") print (" All notifications disabled")
if paired_count > 0: if receiver.unifying_supported:
activity = receiver.request(0x83B3) activity = receiver.request(0x83B3)
if activity: if activity:
activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)] activity = [(d, ord(activity[d - 1:d])) for d in range(1, receiver.max_devices)]

View File

@ -50,12 +50,12 @@ def _run(args):
listeners = {} listeners = {}
from solaar.listener import ReceiverListener from solaar.listener import ReceiverListener
def handle_receivers_events(action, device): def handle_receivers_events(action, device_info):
assert action is not None assert action is not None
assert device is not None assert device_info is not None
# whatever the action, stop any previous receivers at this path # whatever the action, stop any previous receivers at this path
l = listeners.pop(device.path, None) l = listeners.pop(device_info.path, None)
if l is not None: if l is not None:
assert isinstance(l, ReceiverListener) assert isinstance(l, ReceiverListener)
l.stop() l.stop()
@ -63,18 +63,17 @@ def _run(args):
if action == 'add': if action == 'add':
# a new receiver device was detected # a new receiver device was detected
try: try:
l = ReceiverListener.open(device.path, status_changed) l = ReceiverListener.open(device_info, status_changed)
if l is not None: if l is not None:
listeners[device.path] = l listeners[device_info.path] = l
except OSError: except OSError:
# permission error, blacklist this path for now # permission error, blacklist this path for now
listeners.pop(device.path, None) listeners.pop(device_info.path, None)
GLib.idle_add(ui.error_dialog, 'Permissions error', GLib.idle_add(ui.error_dialog, 'Permissions error',
'Found a Logitech Unifying Receiver device,\n' 'Found a Logitech Receiver, but did not have permission to open it.\n'
'but did not have permission to open it.\n'
'\n' '\n'
'If you\'ve just installed Solaar, try removing\n' 'If you\'ve just installed Solaar, try removing the receiver\n'
'the receiver and plugging it back in.') 'and plugging it back in.')
# elif action == 'remove': # elif action == 'remove':
# # we'll be receiving remove events for any hidraw devices, # # we'll be receiving remove events for any hidraw devices,

View File

@ -161,12 +161,12 @@ class ReceiverListener(_listener.EventsListener):
__unicode__ = __str__ __unicode__ = __str__
@classmethod @classmethod
def open(self, path, status_changed_callback): def open(self, device_info, status_changed_callback):
assert status_changed_callback assert status_changed_callback
receiver = Receiver.open(path) receiver = Receiver.open(device_info)
if receiver: if receiver:
rl = ReceiverListener(receiver, status_changed_callback) rl = ReceiverListener(receiver, status_changed_callback)
rl.start() rl.start()
return rl return rl
else: else:
_log.warn("failed to open %s", path) _log.warn("failed to open %s", device_info)

View File

@ -70,16 +70,17 @@ def _make_receiver_box(receiver):
device = f._device device = f._device
if True: # f._info_label.get_visible() and '\n' not in f._info_label.get_text(): if True: # f._info_label.get_visible() and '\n' not in f._info_label.get_text():
items = [('Path', device.path), ('Serial', device.serial)] + \ items = [('Path', device.path), ('Serial', device.serial)] + \
[(fw.kind, fw.version) for fw in device.firmware] list((fw.kind, fw.version) for fw in device.firmware) + \
[None]
notification_flags = _hidpp10.get_notification_flags(device) notification_flags = _hidpp10.get_notification_flags(device)
if notification_flags: if notification_flags:
notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags) notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
else: else:
notification_flags = ('(none)',) notification_flags = ('(none)',)
items.append(('Notifications', ('\n%16s' % ' ').join(notification_flags))) items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags))
f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % item for item in items)) f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i))
f._info_label.set_sensitive(True) f._info_label.set_sensitive(True)
def _toggle_info_label(action, f): def _toggle_info_label(action, f):
@ -94,7 +95,7 @@ def _make_receiver_box(receiver):
toggle_info_action = _action.make_toggle('dialog-information', 'Details', _toggle_info_label, frame) toggle_info_action = _action.make_toggle('dialog-information', 'Details', _toggle_info_label, frame)
toolbar.insert(toggle_info_action.create_tool_item(), 0) toolbar.insert(toggle_info_action.create_tool_item(), 0)
pair_action = _action.pair(frame) pair_action = _action.pair(frame)
if receiver.max_devices == 1: if not receiver.unifying_supported:
pair_action.set_sensitive(False) pair_action.set_sensitive(False)
pair_action.set_tooltip('Pairing not supported by this receiver') pair_action.set_tooltip('Pairing not supported by this receiver')
toolbar.insert(pair_action.create_tool_item(), -1) toolbar.insert(pair_action.create_tool_item(), -1)
@ -186,16 +187,14 @@ def _make_device_box(index):
device = f._device device = f._device
assert device assert device
items = [None] * 8
hid = device.protocol hid = device.protocol
items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown') items = [
items[1] = ('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown'),
items[2] = ('Wireless PID', device.wpid) ('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None,
items[3] = ('Serial', device.serial) ('Wireless PID', device.wpid),
firmware = device.firmware ('Serial', device.serial) ] + \
if firmware: list((fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in device.firmware) + \
firmware = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware] [None]
items[4:4+len(firmware)] = firmware
if device.status: if device.status:
notification_flags = _hidpp10.get_notification_flags(device) notification_flags = _hidpp10.get_notification_flags(device)
@ -204,8 +203,6 @@ def _make_device_box(index):
else: else:
notification_flags = ('(none)',) notification_flags = ('(none)',)
items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags)) items[-1] = ('Notifications', ('\n%16s' % ' ').join(notification_flags))
else:
items[-1] = None
frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i)) frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-14s: %s' % i for i in items if i))
frame._info_label.set_sensitive(True) frame._info_label.set_sensitive(True)
@ -422,7 +419,7 @@ def _update_device_box(frame, dev):
for i in frame._toolbar.get_children(): for i in frame._toolbar.get_children():
i.set_active(False) i.set_active(False)
if dev.receiver.max_devices == 1: if not dev.receiver.unifying_supported:
unpair_button = frame.get_child().get_children()[-1] unpair_button = frame.get_child().get_children()[-1]
unpair_button.set_sensitive(False) unpair_button.set_sensitive(False)
unpair_button.set_tooltip_text('Unpairing not supported by this device') unpair_button.set_tooltip_text('Unpairing not supported by this device')

View File

@ -8,8 +8,13 @@
# HIDAPI/hidraw for Logitech Unifying Receiver # HIDAPI/hidraw for Logitech Unifying Receiver
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660" ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", GROUP="plugdev", MODE="0660"
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c532", GROUP="plugdev", MODE="0660"
# HIDAPI/hidraw for Logitech Nano Receiver # HIDAPI/hidraw for Logitech Nano Receiver, "Unifying ready"
ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52f", GROUP="plugdev", MODE="0660"
# HIDAPI/hidraw for VX Nano receiver
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c51a", GROUP="plugdev", MODE="0660"
#ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660" #ACTION=="add", KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c526", GROUP="plugdev", MODE="0660"
# vim: ft=udevrules # vim: ft=udevrules