From 802da6a21ad61cb24644e8a45503b0e0b55b1763 Mon Sep 17 00:00:00 2001 From: Daniel Pavel Date: Fri, 2 Nov 2012 07:21:55 +0200 Subject: [PATCH] clean-ups --- app/receiver.py | 28 +++++++++++++----- app/solaar.py | 27 +++++++++-------- app/ui/main_window.py | 19 +++++++----- lib/logitech/unifying_receiver/api.py | 3 +- lib/logitech/unifying_receiver/listener.py | 4 +-- .../128x128/devices/Unifying Receiver.png | Bin 12038 -> 0 bytes 6 files changed, 49 insertions(+), 32 deletions(-) delete mode 100644 share/icons/hicolor/128x128/devices/Unifying Receiver.png diff --git a/app/receiver.py b/app/receiver.py index 2627bd20..ca3431af 100644 --- a/app/receiver.py +++ b/app/receiver.py @@ -4,6 +4,7 @@ from logging import getLogger as _Logger from struct import pack as _pack +from time import sleep as _sleep from logitech.unifying_receiver import base as _base from logitech.unifying_receiver import api as _api @@ -136,7 +137,7 @@ class DeviceInfo(_api.PairedDevice): self.LOG.debug("status %d => %d", self._status, new_status) urgent = new_status < STATUS.CONNECTED or self._status < STATUS.CONNECTED self._status = new_status - self._listener.status_changed_callback(self, urgent) + self._listener.status_changed(self, urgent) if new_status < STATUS.CONNECTED: self.props.clear() @@ -158,7 +159,11 @@ class DeviceInfo(_api.PairedDevice): @property def name(self): if self._name is None: - if self._status >= STATUS.CONNECTED: + if self._status < STATUS.CONNECTED: + codename = self.codename + if codename in NAMES: + self._name, self._kind = NAMES[codename] + else: self._name = _api.get_device_name(self.handle, self.number, self.features) return self._name or self.codename @@ -168,7 +173,7 @@ class DeviceInfo(_api.PairedDevice): if self._status < STATUS.CONNECTED: codename = self.codename if codename in NAMES: - self._kind = NAMES[codename][-1] + self._name, self._kind = NAMES[codename] else: self._kind = _api.get_device_kind(self.handle, self.number, self.features) return self._kind or '?' @@ -216,7 +221,7 @@ class DeviceInfo(_api.PairedDevice): self.props.update(status[1]) if self.status == status[0]: if p != self.props: - self._listener.status_changed_callback(self) + self._listener.status_changed(self) else: self.status = status[0] return True @@ -249,7 +254,7 @@ class ReceiverListener(_EventsListener): """Keeps the status of a Unifying Receiver. """ - def __init__(self, receiver, status_changed_callback): + def __init__(self, receiver, status_changed_callback=None): super(ReceiverListener, self).__init__(receiver.handle, self._events_handler) self.receiver = receiver @@ -258,7 +263,7 @@ class ReceiverListener(_EventsListener): self.events_filter = None self.events_handler = None - self.status_changed_callback = status_changed_callback or (lambda reason, urgent=False: None) + self.status_changed_callback = status_changed_callback receiver.kind = receiver.name receiver.devices = {} @@ -280,7 +285,11 @@ class ReceiverListener(_EventsListener): self.LOG.debug("status %d => %d", self.receiver.status, new_status) self.receiver.status = new_status self.receiver.status_text = _RECEIVER_STATUS_NAME[new_status] - self.status_changed_callback(self.receiver, True) + self.status_changed(None, True) + + def status_changed(self, device=None, urgent=False): + if self.status_changed_callback: + self.status_changed_callback(self.receiver, device, urgent) def _device_status_from(self, event): state_code = ord(event.data[2:3]) & 0xF0 @@ -337,7 +346,7 @@ class ReceiverListener(_EventsListener): dev = DeviceInfo(self, event.devnumber, event.data[4:5], status) self.LOG.info("new device %s", dev) - self.status_changed_callback(dev, True) + self.status_changed(dev, True) return dev def unpair_device(self, device): @@ -362,6 +371,8 @@ class ReceiverListener(_EventsListener): if receiver: rl = ReceiverListener(receiver, status_changed_callback) rl.start() + while not rl._active: + _sleep(0.1) return rl # @@ -369,6 +380,7 @@ class ReceiverListener(_EventsListener): # class _DUMMY_RECEIVER(object): + __slots__ = ['name', 'max_devices', 'status', 'status_text', 'devices'] name = _api.Receiver.name max_devices = _api.Receiver.max_devices status = STATUS.UNAVAILABLE diff --git a/app/solaar.py b/app/solaar.py index b7af4bf1..acc231f2 100644 --- a/app/solaar.py +++ b/app/solaar.py @@ -69,18 +69,19 @@ if __name__ == '__main__': listener = None notify_missing = True - def status_changed(reason, urgent=False): - global listener - receiver = DUMMY if listener is None else listener.receiver - ui.update(receiver, icon, window, reason) + def status_changed(receiver, device=None, urgent=False): + ui.update(receiver, icon, window, device) if ui.notify.available and urgent: - ui.notify.show(reason) - if not listener: - listener = None - GObject.timeout_add(3000, check_for_listener) + ui.notify.show(device or receiver) - def check_for_listener(): + global listener + if not listener: + GObject.timeout_add(5000, check_for_listener) + listener = None + + def check_for_listener(retry=True): global listener, notify_missing + if listener is None: listener = ReceiverListener.open(status_changed) if listener is None: @@ -88,14 +89,14 @@ if __name__ == '__main__': if notify_missing: status_changed(DUMMY, True) notify_missing = False - return True + return retry # print ("opened receiver", listener, listener.receiver) - pairing.state = pairing.State(listener) notify_missing = True - status_changed(listener.receiver, True) + pairing.state = pairing.State(listener) + status_changed(listener.receiver, None, True) - check_for_listener() + GObject.timeout_add(100, check_for_listener, False) Gtk.main() if listener is not None: diff --git a/app/ui/main_window.py b/app/ui/main_window.py index 5853cb2b..b241180a 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -37,10 +37,12 @@ def _toggle_info(action, label_widget, box_widget, frame): def _make_receiver_box(name): frame = Gtk.Frame() frame._device = None + frame.set_name(name) - icon = Gtk.Image.new_from_icon_name(name, _SMALL_DEVICE_ICON_SIZE) + icon_name = ui.get_icon(name, 'preferences-desktop-peripherals') + icon = Gtk.Image.new_from_icon_name(icon_name, _SMALL_DEVICE_ICON_SIZE) - label = Gtk.Label('Initializing...') + label = Gtk.Label('Scanning...') label.set_name('label') label.set_alignment(0, 0.5) @@ -83,8 +85,10 @@ def _make_receiver_box(name): def _make_device_box(index): frame = Gtk.Frame() frame._device = None + frame.set_name(_PLACEHOLDER) - icon = Gtk.Image.new_from_icon_name('image-missing', _DEVICE_ICON_SIZE) + icon_name = 'preferences-desktop-peripherals' + icon = Gtk.Image.new_from_icon_name(icon_name, _DEVICE_ICON_SIZE) icon.set_name('icon') icon.set_alignment(0.5, 0) @@ -222,7 +226,8 @@ def _update_device_box(frame, dev): if frame.get_name() != dev.name: frame.set_name(dev.name) - icon.set_from_icon_name(ui.get_icon(dev.name, dev.kind), _DEVICE_ICON_SIZE) + icon_name = ui.get_icon(dev.name, dev.kind) + icon.set_from_icon_name(icon_name, _DEVICE_ICON_SIZE) label.set_markup('' + dev.name + '') status = ui.find_children(frame, 'status') @@ -274,14 +279,14 @@ def _update_device_box(frame, dev): frame.set_visible(True) -def update(window, receiver, device): - print ("update", receiver, receiver.status, device) +def update(window, receiver, device=None): + # print ("update", receiver, receiver.status, device) window.set_icon_name(ui.appicon(receiver.status)) vbox = window.get_child() frames = list(vbox.get_children()) - if id(device) == id(receiver): + if device is None: _update_receiver_box(frames[0], receiver) if receiver.status < STATUS.CONNECTED: for frame in frames[1:]: diff --git a/lib/logitech/unifying_receiver/api.py b/lib/logitech/unifying_receiver/api.py index 263996ab..39aa36b0 100644 --- a/lib/logitech/unifying_receiver/api.py +++ b/lib/logitech/unifying_receiver/api.py @@ -77,8 +77,7 @@ class Receiver(object): self._firmware = None def close(self): - handle = self.handle - self.handle = 0 + handle, self.handle = self.handle, 0 return (handle and _base.close(handle)) @property diff --git a/lib/logitech/unifying_receiver/listener.py b/lib/logitech/unifying_receiver/listener.py index eaf171c9..0a3ef737 100644 --- a/lib/logitech/unifying_receiver/listener.py +++ b/lib/logitech/unifying_receiver/listener.py @@ -102,9 +102,9 @@ class EventsListener(_Thread): self._events.put(event) _base.request_context = None - _base.close(self._handle) + handle, self._handle = self._handle, 0 + _base.close(handle) _log.debug("stopped") - self._handle = 0 def stop(self): """Tells the listener to stop as soon as possible.""" diff --git a/share/icons/hicolor/128x128/devices/Unifying Receiver.png b/share/icons/hicolor/128x128/devices/Unifying Receiver.png deleted file mode 100644 index b08fff9eab0c22785687ea1130e35ed19691b3c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12038 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 MrTnqxgGuoJ0hsVR2><{9