enable notifications for peripherals; fixes #27

This commit is contained in:
Daniel Pavel 2013-05-17 23:06:24 +03:00
parent e2cf9255ac
commit a5eeac6e5a
6 changed files with 80 additions and 24 deletions

View File

@ -85,7 +85,7 @@ class NamedInts(object):
if the value already exists in the set (int or string), ValueError will be
raised.
"""
__slots__ = ['__dict__', '_values', '_indexed', '_fallback']
__slots__ = ['__dict__', '_values', '_indexed', '_fallback', '_all_bits']
def __init__(self, **kwargs):
def _readable_name(n):
@ -99,6 +99,7 @@ class NamedInts(object):
self._values = sorted(list(values.values()))
self._indexed = {int(v): v for v in self._values}
self._fallback = None
self._all_bits = sum(self._values)
@classmethod
def range(cls, from_value, to_value, name_generator=lambda x: str(x), step=1):
@ -116,6 +117,9 @@ class NamedInts(object):
if unknown_bits:
yield 'unknown:%06X' % unknown_bits
def all_bits(self):
return self._all_bits
def __getitem__(self, index):
if isinstance(index, int):
if index in self._indexed:

View File

@ -40,6 +40,7 @@ POWER_SWITCH_LOCATION = _NamedInts(
NOTIFICATION_FLAG = _NamedInts(
battery_status=0x100000,
# reserved_r1b4=0x001000, unknown
wireless=0x000100,
software_present=0x000800)
@ -76,7 +77,8 @@ def get_register(device, name, default_number=-1):
return reply
if not known_register and device.ping():
_log.warn("%s: failed to read '%s' from default register 0x%02X, blacklisting", device, name, default_number)
_log.warn("%s: failed to read '%s' from default register 0x%02X, blacklisting",
device, name, default_number)
device.registers[name] = -default_number
@ -160,3 +162,16 @@ def get_firmware(device):
firmware.append(bl)
return tuple(firmware)
def get_notification_flags(device):
flags = device.request(0x8100)
if flags is not None:
assert len(flags) == 3
return ord(flags[0:1]) << 16 | ord(flags[1:2]) << 8 | ord(flags[2:3])
def set_notification_flags(device, *flag_bits):
flag_bits = sum(int(b) for b in flag_bits)
result = device.request(0x8000, 0xFF & (flag_bits >> 16), 0xFF & (flag_bits >> 8), 0xFF & flag_bits)
return result is not None

View File

@ -165,6 +165,25 @@ class PairedDevice(object):
_descriptors.check_features(self, self._settings)
return self._settings
def enable_notifications(self, enable=True):
"""Enable or disable device (dis)connection notifications on this
receiver."""
if not self.receiver or not self.receiver.handle:
return False
set_flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
flag_bits = _hidpp10.get_notification_flags(self)
if flag_bits is not None:
flag_bits = tuple(_hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits))
if ok:
_log.info("%s: device notifications %s %s", self, 'enabled' if enable else 'disabled', flag_bits)
else:
_log.warn("%s: failed to %s device notifications %s", self, 'enable' if enable else 'disable', flag_bits)
return ok
def request(self, request_id, *params):
return _base.request(self.receiver.handle, self.number, request_id, *params)
@ -252,22 +271,18 @@ class Receiver(object):
receiver."""
if not self.handle:
return False
if enable:
# set all possible flags
ok = self.request(0x8000, 0xFF, 0xFF, 0xFF)
else:
# clear out all possible flags
ok = self.request(0x8000)
flags = self.request(0x8100)
if flags:
flags = ord(flags[0:1]) << 16 | ord(flags[1:2]) << 8 | ord(flags[2:3])
flags = tuple(_hidpp10.NOTIFICATION_FLAG.flag_names(flags))
flag_bits = _hidpp10.NOTIFICATION_FLAG.all_bits() if enable else 0
ok = _hidpp10.set_notification_flags(self, flag_bits)
flag_bits = _hidpp10.get_notification_flags(self)
if flag_bits is not None:
flag_bits = tuple(_hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits))
if ok:
_log.info("%s: device notifications %s %s", self, 'enabled' if enable else 'disabled', flags)
_log.info("%s: device notifications %s %s", self, 'enabled' if enable else 'disabled', flag_bits)
else:
_log.warn("%s: failed to %s device notifications %s", self, 'enable' if enable else 'disable', flags)
_log.warn("%s: failed to %s device notifications %s", self, 'enable' if enable else 'disable', flag_bits)
return ok
def notify_devices(self):
@ -375,7 +390,7 @@ class Receiver(object):
@classmethod
def open(self, path):
"""Opens a Logitech Receiver found attached to the machine, by 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``.
"""

View File

@ -173,8 +173,11 @@ class DeviceStatus(dict):
def _changed(self, active=True, alert=ALERT.NONE, reason=None, timestamp=None):
assert self._changed_callback
self._active = active
if not active:
was_active, self._active = self._active, active
if active:
if not was_active:
self._device.enable_notifications()
else:
battery = self.get(BATTERY_LEVEL)
self.clear()
if battery is not None:

View File

@ -59,7 +59,8 @@ class ReceiverListener(_listener.EventsListener):
r.status = 'The device was unplugged.'
if r:
try:
r.enable_notifications(False)
pass
# r.enable_notifications(False)
except:
_log.exception("disabling notifications on receiver %s" % r)
finally:

View File

@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from gi.repository import Gtk, Gdk, GLib
from solaar import NAME
from logitech.unifying_receiver import status as _status
from logitech.unifying_receiver import status as _status, hidpp10 as _hidpp10
from . import config_panel as _config_panel
from . import action as _action, icons as _icons
@ -67,10 +67,18 @@ def _make_receiver_box(receiver):
def _update_info_label(f):
device = f._device
if 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)] + \
[(fw.kind, fw.version) for fw in device.firmware]
f._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-13s: %s' % item for item in items))
notification_flags = _hidpp10.get_notification_flags(device)
if notification_flags:
notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
else:
notification_flags = ('(none)',)
items.append(('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_sensitive(True)
def _toggle_info_label(action, f):
@ -170,11 +178,11 @@ def _make_device_box(index):
frame._info_label = info_label
def _update_info_label(f):
if frame._info_label.get_text().count('\n') < 4:
if True: # frame._info_label.get_text().count('\n') < 4:
device = f._device
assert device
items = [None, None, None, None, None, None, None, None]
items = [None, None, None, None, None, None, None, None, None]
hid = device.protocol
items[0] = ('Protocol', 'HID++ %1.1f' % hid if hid else 'unknown')
items[1] = ('Polling rate', '%d ms' % device.polling_rate)
@ -184,7 +192,17 @@ def _make_device_box(index):
if firmware:
items[4:] = [(fw.kind, (fw.name + ' ' + fw.version).strip()) for fw in firmware]
frame._info_label.set_markup('<small><tt>%s</tt></small>' % '\n'.join('%-13s: %s' % i for i in items if i))
if device.status:
notification_flags = _hidpp10.get_notification_flags(device)
if notification_flags:
notification_flags = _hidpp10.NOTIFICATION_FLAG.flag_names(notification_flags)
else:
notification_flags = ('(none)',)
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_sensitive(True)
def _toggle_info_label(action, f):