code clean-ups, the app starts faster now

This commit is contained in:
Daniel Pavel 2012-12-01 15:49:52 +02:00
parent 89c6904d69
commit 8f5fa0cf9a
12 changed files with 134 additions and 125 deletions

View File

@ -6,12 +6,13 @@ from logging import getLogger, DEBUG as _DEBUG
_log = getLogger('listener')
del getLogger
from logitech.unifying_receiver import (
Receiver, PairedDevice,
listener as _listener,
hidpp10 as _hidpp10,
hidpp20 as _hidpp20,
status as _status)
from types import MethodType as _MethodType
from logitech.unifying_receiver import (Receiver,
listener as _listener,
hidpp10 as _hidpp10,
hidpp20 as _hidpp20,
status as _status)
#
#
@ -35,16 +36,6 @@ DUMMY = _DUMMY_RECEIVER()
_DEVICE_STATUS_POLL = 30 # seconds
_DEVICE_TIMEOUT = 2 * _DEVICE_STATUS_POLL # seconds
# def _fake_device(listener):
# dev = _lur.PairedDevice(listener.receiver, 6)
# dev._wpid = '1234'
# dev._kind = 'touchpad'
# dev._codename = 'T650'
# dev._name = 'Wireless Rechargeable Touchpad T650'
# dev._serial = '0123456789'
# dev._protocol = 2.0
# dev.status = _lur.status.DeviceStatus(dev, listener._status_changed)
# return dev
class ReceiverListener(_listener.EventsListener):
"""Keeps the status of a Unifying Receiver.
@ -57,29 +48,23 @@ class ReceiverListener(_listener.EventsListener):
self.status_changed_callback = status_changed_callback
receiver.status = _status.ReceiverStatus(receiver, self._status_changed)
Receiver.create_device = self.create_device
def create_device(self, receiver, number):
if bool(self):
dev = PairedDevice(receiver, number)
if dev.wpid:
dev.status = _status.DeviceStatus(dev, self._status_changed)
_log.info("new device %s", dev)
return dev
# enhance the original register_new_device
def _register_with_status(r, number):
if bool(self):
dev = r.__register_new_device(number)
if dev is not None:
# read these as soon as possible, they will be used everywhere
dev.protocol, dev.codename
dev.status = _status.DeviceStatus(dev, self._status_changed)
return dev
receiver.__register_new_device = receiver.register_new_device
receiver.register_new_device = _MethodType(_register_with_status, receiver)
def has_started(self):
_log.info("events listener has started")
# self._status_changed(self.receiver)
self._status_changed(self.receiver)
self.receiver.enable_notifications()
for dev in self.receiver:
dev.codename, dev.kind, dev.name
# dev.status._changed(dev.protocol > 0)
# fake = _fake_device(self)
# self.receiver._devices[fake.number] = fake
# self._status_changed(fake, _status.ALERT.LOW)
self.receiver.notify_devices()
self._status_changed(self.receiver, _status.ALERT.LOW)
@ -88,13 +73,12 @@ class ReceiverListener(_listener.EventsListener):
if self.receiver:
self.receiver.enable_notifications(False)
self.receiver.close()
self.receiver = None
self._status_changed(None, alert=_status.ALERT.LOW)
def tick(self, timestamp):
if _log.isEnabledFor(_DEBUG):
_log.debug("tick: polling status: %s %s", self.receiver, self.receiver._devices)
_log.debug("tick: polling status: %s %s", self.receiver, list(iter(self.receiver)))
if self._last_tick > 0 and timestamp - self._last_tick > _DEVICE_STATUS_POLL * 2:
# if we missed a couple of polls, most likely the computer went into
@ -114,7 +98,7 @@ class ReceiverListener(_listener.EventsListener):
for dev in self.receiver:
if dev.status:
# read these in case they haven't been read already
dev.wpid, dev.serial, dev.protocol, dev.firmware
dev.protocol, dev.serial, dev.firmware
if _status.BATTERY_LEVEL not in dev.status:
battery = _hidpp20.get_battery(dev) or _hidpp10.get_battery(dev)
@ -130,12 +114,12 @@ class ReceiverListener(_listener.EventsListener):
if _log.isEnabledFor(_DEBUG):
_log.debug("status_changed %s: %s (%X) %s", device, None if device is None else device.status, alert, reason or '')
if self.status_changed_callback:
if device is None or device is self.receiver:
if device is None or device.kind is None:
self.status_changed_callback(self.receiver or DUMMY, None, alert, reason)
else:
self.status_changed_callback(self.receiver or DUMMY, device, alert, reason)
if device.status is None:
self.status_changed_callback(self.receiver, None)
# if device.status is None:
# self.status_changed_callback(self.receiver, None)
def _events_handler(self, event):
assert self.receiver

View File

@ -2,6 +2,8 @@
#
#
# from gi import pygtkcompat
# pygtkcompat.enable_gtk()
from gi.repository import GObject, Gtk
GObject.threads_init()

View File

@ -2,7 +2,7 @@
#
#
# from sys import version as PYTTHON_VERSION
# from sys import version as PYTHON_VERSION
from gi.repository import Gtk, Gdk
import ui
@ -46,13 +46,19 @@ def _show_about_window(action):
about.set_version(_VERSION)
about.set_comments('Shows status of devices connected\nto a Logitech Unifying Receiver.')
about.set_license_type(Gtk.License.GPL_2_0)
about.set_copyright(b'\xC2\xA9'.decode('utf-8') + ' 2012 Daniel Pavel')
about.set_license_type(Gtk.License.GPL_2_0)
about.set_authors(('Daniel Pavel http://github.com/pwr',))
try:
about.add_credit_section('Testing', ('Douglas Wagner',))
except Exception:
about.add_credit_section('Technical specifications\nprovided by',
('Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',))
except TypeError:
# gtk3 < 3.6 has incorrect gi bindings
pass
except:
# is the Gtk3 version too old?
pass
about.set_website('http://github.com/pwr/Solaar/wiki')
@ -73,6 +79,7 @@ def _pair_device(action, frame):
pair_dialog = ui.pair_window.create(action, frame._device)
pair_dialog.set_transient_for(window)
pair_dialog.set_destroy_with_parent(True)
pair_dialog.set_modal(True)
pair_dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
pair_dialog.set_position(Gtk.WindowPosition.CENTER)

View File

@ -268,7 +268,7 @@ def _update_receiver_info_label(label, dev):
def _toggle_info_box(action, label_widget, box_widget, frame, update_function):
if action.get_active():
box_widget.set_visible(True)
GObject.timeout_add(60, update_function, label_widget, frame._device)
GObject.timeout_add(50, update_function, label_widget, frame._device)
else:
box_widget.set_visible(False)
@ -375,8 +375,8 @@ def _update_device_box(frame, dev):
def update(window, receiver, device=None):
# print ("update", receiver, receiver.status, device)
assert receiver is not None
# print ("update", receiver, receiver.status, device, None if device is None else device.number)
window.set_icon_name(ui.appicon(receiver.status))
vbox = window._vbox

View File

@ -64,15 +64,13 @@ def _check_lock_state(assistant, receiver):
return False
if receiver.status.get(_status.ERROR):
# fake = _fake_device(receiver)
# receiver._devices[fake.number] = fake
# receiver.status.new_device = fake
# fake.status._changed()
# receiver.status.new_device = _fake_device(receiver)
_pairing_failed(assistant, receiver, receiver.status.pop(_status.ERROR))
return False
if receiver.status.new_device:
_pairing_succeeded(assistant, receiver)
device, receiver.status.new_device = receiver.status.new_device, None
_pairing_succeeded(assistant, receiver, device)
return False
if not receiver.status.lock_open:
@ -129,8 +127,7 @@ def _pairing_failed(assistant, receiver, error):
assistant.commit()
def _pairing_succeeded(assistant, receiver):
device, receiver.status.new_device = receiver.status.new_device, None
def _pairing_succeeded(assistant, receiver, device):
assert device
if _log.isEnabledFor(_DEBUG):
_log.debug("%s success: %s", receiver, device)
@ -173,7 +170,7 @@ def create(action, receiver):
assistant.set_title(action.get_label())
assistant.set_icon_name(action.get_icon_name())
assistant.set_size_request(420, 240)
assistant.set_size_request(400, 240)
assistant.set_resizable(False)
assistant.set_role('pair-device')

View File

@ -14,6 +14,7 @@ def create(window, menu_actions=None):
icon.set_title(name)
icon.set_name(name)
icon.set_from_icon_name(ui.appicon(False))
icon._devices = {}
icon.set_tooltip_text(name)
icon.connect('activate', window.toggle_visible)
@ -44,6 +45,7 @@ def _icon_with_battery(s):
assert mask
mask = GdkPixbuf.Pixbuf.new_from_file(mask)
assert mask.get_width() == 128 and mask.get_height() == 128
mask.saturate_and_pixelate(mask, 0.8, False)
battery = ui.icon_file(battery_icon, 128)
assert battery
@ -59,15 +61,22 @@ def _icon_with_battery(s):
return _PIXMAPS[name]
def update(icon, receiver, device=None):
# print "icon update", receiver, receiver._devices, device
# print ("icon update", receiver, receiver.status, len(receiver._devices), device)
battery_status = None
if device is not None:
icon._devices[device.number] = device
lines = [ui.NAME + ': ' + str(receiver.status), '']
if receiver and receiver._devices:
for dev in receiver:
if receiver:
for k in range(1, 1+ receiver.max_devices):
dev = icon._devices.get(k)
if dev is None:
continue
lines.append('<b>' + dev.name + '</b>')
assert dev.status is not None
assert hasattr(dev, 'status') and dev.status is not None
p = str(dev.status)
if p:
if not dev.status:
@ -86,6 +95,8 @@ def update(icon, receiver, device=None):
if battery_status is None and dev.status.get(_status.BATTERY_LEVEL):
battery_status = dev.status
else:
icon._devices.clear()
icon.set_tooltip_markup('\n'.join(lines).rstrip('\n'))

View File

@ -130,7 +130,7 @@ def write(handle, devnumber, data):
unloaded. The handle will be closed automatically.
"""
# the data is padded to either 5 or 18 bytes
if len(data) > _SHORT_MESSAGE_SIZE - 2:
if len(data) > _SHORT_MESSAGE_SIZE - 2 or data[:1] == b'\x82':
wdata = _pack('!BB18s', 0x11, devnumber, data)
else:
wdata = _pack('!BB5s', 0x10, devnumber, data)
@ -306,6 +306,13 @@ def request(handle, devnumber, request_id, *params):
raise _hidpp20.FeatureCallError(number=devnumber, request=request_id, error=error, params=params)
if reply_data[:2] == request_str:
if request_id & 0xFF00 == 0x8300:
# long registry r/w should return a long reply
assert report_id == 0x11
elif request_id & 0xF000 == 0x8000:
# short registry r/w should return a short reply
assert report_id == 0x10
if devnumber == 0xFF:
if request_id == 0x83B5 or request_id == 0x81F1:
# these replies have to match the first parameter as well

View File

@ -2,7 +2,10 @@
#
#
from .common import NamedInts as _NamedInts
from .common import (strhex as _strhex,
NamedInts as _NamedInts,
FirmwareInfo as _FirmwareInfo)
from .hidpp20 import FIRMWARE_KIND
#
# constants
@ -64,3 +67,29 @@ def get_battery(device):
if reply:
charge = ord(reply[:1])
return charge, None
def get_receiver_serial(receiver):
serial = receiver.request(0x83B5, 0x03)
if serial:
return _strhex(serial[1:5])
def get_receiver_firmware(receiver):
firmware = []
reply = receiver.request(0x83B5, 0x02)
if reply:
fw_version = _strhex(reply[1:5])
fw_version = '%s.%s.B%s' % (fw_version[0:2], fw_version[2:4], fw_version[4:8])
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, '', fw_version, None)
firmware.append(fw)
reply = receiver.request(0x81F1, 0x04)
if reply:
bl_version = _strhex(reply[1:3])
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, '', bl_version, None)
firmware.append(bl)
return tuple(firmware)

View File

@ -3,6 +3,7 @@
#
from struct import pack as _pack, unpack as _unpack
from weakref import proxy as _proxy
from logging import getLogger, DEBUG as _DEBUG
_log = getLogger('LUR').getChild('hidpp20')
@ -123,14 +124,12 @@ class FeaturesArray(object):
def __init__(self, device):
assert device is not None
self.device = device
self.device = _proxy(device)
self.supported = True
self.features = None
def __del__(self):
self.supported = False
self.features = None
self.device = None
def _check(self):
# print ("%s check" % self.device)
@ -251,13 +250,9 @@ class KeysArray(object):
def __init__(self, device, count):
assert device is not None
self.device = device
self.device = _proxy(device)
self.keys = [None] * count
def __del__(self):
self.keys = None
self.device = None
def __getitem__(self, index):
assert type(index) == int
if index < 0 or index >= len(self.keys):

View File

@ -83,6 +83,7 @@ class ThreadedHandle(object):
#
_EVENT_READ_TIMEOUT = 500
_IDLE_READS = 5
class EventsListener(_threading.Thread):
@ -111,6 +112,7 @@ class EventsListener(_threading.Thread):
self.has_started()
last_tick = 0
idle_reads = 0
while self._active:
if self._queued_events.empty():
@ -136,10 +138,13 @@ class EventsListener(_threading.Thread):
except:
_log.exception("processing event %s", event)
elif self.tick_period:
now = _timestamp()
if now - last_tick >= self.tick_period:
last_tick = now
self.tick(now)
idle_reads += 1
if idle_reads % _IDLE_READS == 0:
idle_reads = 0
now = _timestamp()
if now - last_tick >= self.tick_period:
last_tick = now
self.tick(now)
_base.unhandled_hook = None
del self._queued_events

View File

@ -3,6 +3,7 @@
#
import errno as _errno
from weakref import proxy as _proxy
from logging import getLogger
_log = getLogger('LUR').getChild('receiver')
@ -25,7 +26,7 @@ MAX_PAIRED_DEVICES = 6
class PairedDevice(object):
def __init__(self, receiver, number):
assert receiver
self.receiver = receiver
self.receiver = _proxy(receiver)
assert number > 0 and number <= MAX_PAIRED_DEVICES
self.number = number
@ -41,11 +42,6 @@ class PairedDevice(object):
self.features = _hidpp20.FeaturesArray(self)
def __del__(self):
del self.receiver
del self.features
del self._keys
@property
def protocol(self):
if self._protocol is None:
@ -82,10 +78,9 @@ class PairedDevice(object):
@property
def name(self):
if self._name is None:
if self.protocol < 2.0:
if self.codename in _DEVICES:
_, self._name, self._kind = _DEVICES[self._codename]
else:
if self.codename in _DEVICES:
_, self._name, self._kind = _DEVICES[self._codename]
elif self.protocol >= 2.0:
self._name = _hidpp20.get_name(self)
return self._name or self.codename or '?'
@ -99,10 +94,9 @@ class PairedDevice(object):
if self._wpid is None:
self._wpid = _strhex(pair_info[3:5])
if self._kind is None:
if self.protocol < 2.0:
if self.codename in _DEVICES:
_, self._name, self._kind = _DEVICES[self._codename]
else:
if self.codename in _DEVICES:
_, self._name, self._kind = _DEVICES[self._codename]
elif self.protocol >= 2.0:
self._kind = _hidpp20.get_kind(self)
return self._kind or '?'
@ -168,7 +162,6 @@ class Receiver(object):
name = 'Unifying Receiver'
kind = None
max_devices = MAX_PAIRED_DEVICES
create_device = PairedDevice
def __init__(self, handle, path=None):
assert handle
@ -192,30 +185,13 @@ class Receiver(object):
@property
def serial(self):
if self._serial is None and self.handle:
serial = self.request(0x83B5, 0x03)
if serial:
self._serial = _strhex(serial[1:5])
self._serial = _hidpp10.get_receiver_serial(self)
return self._serial
@property
def firmware(self):
if self._firmware is None and self.handle:
firmware = []
reply = self.request(0x83B5, 0x02)
if reply:
fw_version = _strhex(reply[1:5])
fw_version = '%s.%s.B%s' % (fw_version[0:2], fw_version[2:4], fw_version[4:8])
firmware.append(_FirmwareInfo(_hidpp20.FIRMWARE_KIND.Firmware, '', fw_version, None))
reply = self.request(0x81F1, 0x04)
if reply:
bl_version = _strhex(reply[1:3])
bl_version = '%s.%s' % (bl_version[0:2], bl_version[2:4])
firmware.append(_FirmwareInfo(_hidpp20.FIRMWARE_KIND.Bootloader, '', bl_version, None))
self._firmware = tuple(firmware)
self._firmware = _hidpp10.get_receiver_firmware(self)
return self._firmware
def enable_notifications(self, enable=True):
@ -241,6 +217,16 @@ class Receiver(object):
if not self.request(0x8002, 0x02):
_log.warn("failed to trigger device events")
def register_new_device(self, number):
if self._devices.get(number) is not None:
raise IndexError("device number %d already registered" % number)
dev = PairedDevice(self, number)
if dev.wpid:
_log.info("registered new device %d (%s)", number, dev.wpid)
self._devices[number] = dev
return dev
self._devices[number] = None
def set_lock(self, lock_closed=True, device=0, timeout=0):
if self.handle:
lock = 0x02 if lock_closed else 0x01
@ -271,13 +257,7 @@ class Receiver(object):
if key < 1 or key > MAX_PAIRED_DEVICES:
raise IndexError(key)
dev = Receiver.create_device(self, key)
if dev is not None and dev.wpid:
self._devices[key] = dev
return dev
# no paired device at this index
self._devices[key] = None
return self.register_new_device(key)
def __delitem__(self, key):
if self._devices.get(key) is None:

View File

@ -4,6 +4,7 @@
from time import time as _timestamp
from struct import unpack as _unpack
from weakref import proxy as _proxy
from logging import getLogger, DEBUG as _DEBUG
_log = getLogger('LUR.status')
@ -33,7 +34,7 @@ ERROR='error'
class ReceiverStatus(dict):
def __init__(self, receiver, changed_callback):
assert receiver
self._receiver = receiver
self._receiver = _proxy(receiver)
assert changed_callback
self._changed_callback = changed_callback
@ -52,9 +53,7 @@ class ReceiverStatus(dict):
def device_paired(self, number):
_log.info("new device paired")
dev = self._receiver.create_device(self._receiver, number)
self._receiver._devices[number] = dev
self.new_device = dev
dev = self.new_device = self._receiver.register_new_device(number)
return dev
def _changed(self, alert=ALERT.LOW, reason=None):
@ -88,7 +87,7 @@ class ReceiverStatus(dict):
class DeviceStatus(dict):
def __init__(self, device, changed_callback):
assert device
self._device = device
self._device = _proxy(device)
assert changed_callback
self._changed_callback = changed_callback
@ -126,13 +125,6 @@ class DeviceStatus(dict):
# _log.debug("device %d changed: active=%s %s", self._device.number, self._active, dict(self))
self._changed_callback(self._device, alert, reason)
# @property
# def battery(self):
# battery = _hidpp10.get_battery_level(self)
# if battery is None:
# battery = _hidpp20.get_battery_level(self)
# return battery
def process_event(self, event):
if event.sub_id == 0x40:
if event.address == 0x02:
@ -241,7 +233,7 @@ class DeviceStatus(dict):
else:
self._changed()
else:
_log.warn("SOLAR_CHARGE event not GOOD? %s", event)
_log.warn("SOLAR CHARGE event not GOOD? %s", event)
return True
if feature == _hidpp20.FEATURE.TOUCH_MOUSE: