diff --git a/lib/hidapi/udev.py b/lib/hidapi/udev.py index 1ad26889..bcdd80cd 100644 --- a/lib/hidapi/udev.py +++ b/lib/hidapi/udev.py @@ -57,9 +57,9 @@ def exit(): return True -def _match(action, device, vendor_id=None, product_id=None, interface_number=None, driver=None): +def _match(action, device, vendor_id=None, product_id=None, interface_number=None, hid_driver=None): usb_device = device.find_parent('usb', 'usb_device') - # print (action, device, "usb:", usb_device) + # print ("* parent", action, device, "usb:", usb_device) if not usb_device: return @@ -71,15 +71,15 @@ def _match(action, device, vendor_id=None, product_id=None, interface_number=Non if action == 'add': hid_device = device.find_parent('hid') - # print (action, device, "hid:", usb_device) + # print ("** found hid", action, device, "hid:", hid_device, hid_device['DRIVER']) if not hid_device: return hid_driver_name = hid_device['DRIVER'] - if driver is not None and driver != hid_driver_name: + if hid_driver is not None and hid_driver != hid_driver_name: return intf_device = device.find_parent('usb', 'usb_interface') - # print (action, device, "usb_interface:", usb_device) + # print ("*** usb interface", action, device, "usb_interface:", intf_device) if interface_number is None: usb_interface = None if intf_device is None else intf_device.attributes.asint('bInterfaceNumber') else: @@ -163,7 +163,7 @@ def monitor(callback, *device_filters): raise -def enumerate(vendor_id=None, product_id=None, interface_number=None, driver=None): +def enumerate(vendor_id=None, product_id=None, interface_number=None, hid_driver=None): """Enumerate the HID Devices. List all the HID devices attached to the system, optionally filtering by @@ -172,7 +172,7 @@ def enumerate(vendor_id=None, product_id=None, interface_number=None, driver=Non :returns: a list of matching ``DeviceInfo`` tuples. """ for dev in _Context().list_devices(subsystem='hidraw'): - dev_info = _match('add', dev, vendor_id, product_id, interface_number, driver) + dev_info = _match('add', dev, vendor_id, product_id, interface_number, hid_driver) if dev_info: yield dev_info diff --git a/lib/logitech/unifying_receiver/base.py b/lib/logitech/unifying_receiver/base.py index 2d4b006a..1c64df8b 100644 --- a/lib/logitech/unifying_receiver/base.py +++ b/lib/logitech/unifying_receiver/base.py @@ -66,10 +66,10 @@ class DeviceUnreachable(_KwException): # # -# vendor_id, product_id, interface number, driver +# vendor_id, product_id, usb interface number, hid driver DEVICE_UNIFYING_RECEIVER = (0x046d, 0xc52b, 2, 'logitech-djreceiver') DEVICE_UNIFYING_RECEIVER_2 = (0x046d, 0xc532, 2, 'logitech-djreceiver') -DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'generic-usb') +DEVICE_NANO_RECEIVER = (0x046d, 0xc526, 1, 'hid-generic') def receivers(): diff --git a/lib/logitech/unifying_receiver/receiver.py b/lib/logitech/unifying_receiver/receiver.py index a4acbd5a..b0e0ffa3 100644 --- a/lib/logitech/unifying_receiver/receiver.py +++ b/lib/logitech/unifying_receiver/receiver.py @@ -33,14 +33,14 @@ class PairedDevice(object): self.number = number self._unifying = receiver.max_devices > 1 - self._protocol = None + self._protocol = None if self._unifying else 1.0 self._wpid = None self._power_switch = None - self._polling_rate = None + self._polling_rate = None if self._unifying else 0 self._codename = None self._name = None self._kind = None - self._serial = None + self._serial = None if self._unifying else receiver.serial self._firmware = None self._keys = None @@ -67,42 +67,49 @@ class PairedDevice(object): self._kind = _hidpp10.DEVICE_KIND[kind] if self._polling_rate is None: self._polling_rate = ord(pair_info[2:3]) - # else: - # device_info = self.receiver.request(0x83B5, 0x04) - # self.wpid = _strhex(device_info[3:5]) + 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 and self._unifying: - self.wpid, 0 + if self._polling_rate is None: + if self._unifying: + self.wpid, 0 + else: + self._polling_rate = 0 return self._polling_rate @property def power_switch_location(self): - if self._power_switch is None and self._unifying: - ps = self.receiver.request(0x83B5, 0x30 + self.number - 1) - if ps: - ps = ord(ps[9:10]) & 0x0F - self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps] + if self._power_switch is None: + if self._unifying: + ps = self.receiver.request(0x83B5, 0x30 + self.number - 1) + if ps: + ps = ord(ps[9:10]) & 0x0F + self._power_switch = _hidpp10.POWER_SWITCH_LOCATION[ps] return self._power_switch @property def codename(self): - if self._codename is None and self._unifying: - codename = self.receiver.request(0x83B5, 0x40 + self.number - 1) - if codename: - self._codename = codename[2:].rstrip(b'\x00').decode('utf-8') - # _log.debug("device %d codename %s", self.number, self._codename) + if self._codename is None: + if self._unifying: + codename = self.receiver.request(0x83B5, 0x40 + self.number - 1) + if codename: + self._codename = codename[2:].rstrip(b'\x00').decode('utf-8') + # _log.debug("device %d codename %s", self.number, self._codename) return self._codename @property def name(self): - if self._name is None and self._unifying: - if self.codename in _descriptors.DEVICES: - self._name, self._kind = _descriptors.DEVICES[self._codename][:2] - elif self.protocol >= 2.0: - self._name = _hidpp20.get_name(self) + if self._name is None: + if self._unifying: + if self.codename in _descriptors.DEVICES: + self._name, self._kind = _descriptors.DEVICES[self._codename][:2] + elif self.protocol >= 2.0: + self._name = _hidpp20.get_name(self) return self._name or self.codename or '?' @property @@ -132,14 +139,15 @@ class PairedDevice(object): @property def serial(self): - if self._serial is None and self._unifying: + if self._serial is None: self._serial = _hidpp10.get_serial(self) return self._serial or '?' @property def keys(self): - if self._keys is None and self._unifying: - self._keys = _hidpp20.get_keys(self) or () + if self._keys is None: + if self._unifying: + self._keys = _hidpp20.get_keys(self) or () return self._keys @property @@ -277,7 +285,8 @@ class Receiver(object): if not self.handle: return False - flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0 + # flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0 + flag_bits = 0xFFFFFF if enable else 0 ok = _hidpp10.set_notification_flags(self, flag_bits) flag_bits = _hidpp10.get_notification_flags(self) @@ -303,12 +312,12 @@ class Receiver(object): # 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 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: _log.info("%s: found Unifying device %d (%s)", self, number, dev.wpid) diff --git a/lib/solaar/ui/main_window.py b/lib/solaar/ui/main_window.py index 6ffbded1..b79a5a4b 100644 --- a/lib/solaar/ui/main_window.py +++ b/lib/solaar/ui/main_window.py @@ -29,6 +29,7 @@ _MAX_DEVICES = 7 def _make_receiver_box(receiver): frame = Gtk.Frame() + # frame.set_shadow_type(Gtk.ShadowType.NONE) frame._device = receiver icon_set = _icons.device_icon_set(receiver.name) @@ -92,8 +93,11 @@ def _make_receiver_box(receiver): toggle_info_action = _action.make_toggle('dialog-information', 'Details', _toggle_info_label, frame) toolbar.insert(toggle_info_action.create_tool_item(), 0) - toolbar.insert(_action.pair(frame).create_tool_item(), -1) - # toolbar.insert(ui.action.about.create_tool_item(), -1) + pair_action = _action.pair(frame) + if receiver.max_devices == 1: + pair_action.set_sensitive(False) + pair_action.set_tooltip('Pairing not supported by this receiver') + toolbar.insert(pair_action.create_tool_item(), -1) vbox = Gtk.VBox(homogeneous=False, spacing=2) vbox.set_border_width(2) @@ -137,7 +141,7 @@ def _make_device_box(index): not_encrypted_icon = Gtk.Image.new_from_icon_name('security-low', _STATUS_ICON_SIZE) not_encrypted_icon.set_name('not-encrypted') - not_encrypted_icon.set_tooltip_text('The wireless link between this device and the Unifying Receiver is not encrypted.\n' + not_encrypted_icon.set_tooltip_text('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' @@ -182,15 +186,16 @@ def _make_device_box(index): device = f._device assert device - items = [None, None, None, None, None, None, None, None, None] + items = [None] * 8 hid = device.protocol items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown') - items[1] = ('Polling rate', '%d ms' % device.polling_rate) + items[1] = ('Polling rate', '%d ms' % device.polling_rate) if device.polling_rate else None items[2] = ('Wireless PID', device.wpid) items[3] = ('Serial', device.serial) firmware = device.firmware if firmware: - items[4:] = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware] + firmware = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware] + items[4:4+len(firmware)] = firmware if device.status: notification_flags = _hidpp10.get_notification_flags(device) @@ -417,6 +422,11 @@ def _update_device_box(frame, dev): for i in frame._toolbar.get_children(): i.set_active(False) + if dev.receiver.max_devices == 1: + unpair_button = frame.get_child().get_children()[-1] + unpair_button.set_sensitive(False) + unpair_button.set_tooltip_text('Unpairing not supported by this device') + battery_icon, battery_label, light_icon, light_label, not_encrypted_icon, _ = frame._status_icons battery_level = dev.status.get(_status.BATTERY_LEVEL) @@ -425,7 +435,7 @@ def _update_device_box(frame, dev): if battery_level is None: battery_icon.set_sensitive(False) - battery_icon.set_from_icon_name(_icons.battery(-1), _STATUS_ICON_SIZE) + battery_icon.set_from_icon_name(_icons.battery(None), _STATUS_ICON_SIZE) battery_label.set_markup('no status') battery_label.set_sensitive(True) else: