diff --git a/app/solaar.py b/app/solaar.py index 8966d82c..9bda2645 100644 --- a/app/solaar.py +++ b/app/solaar.py @@ -23,7 +23,7 @@ if __name__ == '__main__': arg_parser = argparse.ArgumentParser(prog=APP_TITLE) arg_parser.add_argument('-v', '--verbose', action='count', default=0, help='increase the logger verbosity (may be repeated)') - arg_parser.add_argument('-s', '--systray', action='store_true', + arg_parser.add_argument('-S', '--no-systray', action='store_false', dest='systray', help='embed the application into the systray') arg_parser.add_argument('-N', '--no-notifications', action='store_false', dest='notifications', help='disable desktop notifications (if systray is enabled)') @@ -67,5 +67,4 @@ if __name__ == '__main__': Gtk.main() watcher.stop() - if args.notifications: - ui.notify.set_active(False) + ui.notify.set_active(False) diff --git a/app/watcher.py b/app/watcher.py index cf2594c0..9ed447c1 100644 --- a/app/watcher.py +++ b/app/watcher.py @@ -132,7 +132,7 @@ class Watcher(Thread): return None if type(dev) == int: - # assert self.listener + assert self.listener dev = self.listener.request(api.get_device_info, dev) if dev: @@ -177,33 +177,33 @@ class Watcher(Thread): devstatus.code = status_code devstatus.text = status_text - _l.debug("%s update %s => %s: %s", devstatus, old_status_code, status_code, status_text) + _l.debug("%s update %s => %s: %s", devstatus, old_status_code, status_code, status_text) if self.notify and (status_code <= C.STATUS.CONNECTED or status_code != old_status_code): self.notify(devstatus.code, devstatus.name, devstatus.text) return True - def _events_callback(self, code, devnumber, data): - # _l.debug("event %s", (code, devnumber, data)) + def _events_callback(self, event): + # _l.debug("event %s", event) updated = False - if devnumber in self.devices: - devstatus = self.devices[devnumber] - if code == 0x10 and data[:1] == b'\x8F': + if event.devnumber in self.devices: + devstatus = self.devices[event.devnumber] + if event.code == 0x10 and event.data[:1] == b'\x8F': updated = True self._device_status_changed(devstatus, C.STATUS.UNAVAILABLE) - elif code == 0x11: - status = devices.process_event(devstatus, data, self.listener) + elif event.code == 0x11: + status = devices.process_event(devstatus, event.data, self.listener) updated |= self._device_status_changed(devstatus, status) else: - _l.warn("unknown event code %02x", code) - elif devnumber: - self._new_device(devnumber) + _l.warn("unknown event %s", event) + elif event.devnumber: + self._new_device(event.devnumber) updated = True else: - _l.warn("don't know how to handle event %s", (code, devnumber, data)) + _l.warn("don't know how to handle event %s", event) if updated: self._update_status_text() diff --git a/lib/logitech/unifying_receiver/common.py b/lib/logitech/unifying_receiver/common.py index 26ac84a4..15aac84a 100644 --- a/lib/logitech/unifying_receiver/common.py +++ b/lib/logitech/unifying_receiver/common.py @@ -2,6 +2,9 @@ # Some common functions and types. # +from binascii import hexlify as _hexlify + + class FallbackDict(dict): def __init__(self, fallback_function=lambda x: None, *args, **kwargs): super(FallbackDict, self).__init__(*args, **kwargs) @@ -46,4 +49,8 @@ ReprogrammableKeyInfo = namedtuple('ReprogrammableKeyInfo', [ 'task_name', 'flags']) +class Packet(namedtuple('Packet', ['code', 'devnumber', 'data'])): + def __str__(self): + return 'Packet(0x%02x,%d,%s)' % (self.code, self.devnumber, _hexlify(self.data)) + del namedtuple diff --git a/lib/logitech/unifying_receiver/listener.py b/lib/logitech/unifying_receiver/listener.py index 4066266f..6bb76bbd 100644 --- a/lib/logitech/unifying_receiver/listener.py +++ b/lib/logitech/unifying_receiver/listener.py @@ -4,10 +4,11 @@ from logging import getLogger as _Logger from threading import (Thread, Event, Lock) -from time import sleep as _sleep +# from time import sleep as _sleep from . import base as _base from . import exceptions as E +from .common import Packet # for both Python 2 and 3 try: @@ -21,16 +22,15 @@ _l = _Logger('lur.listener') _READ_EVENT_TIMEOUT = int(_base.DEFAULT_TIMEOUT / 5) # ms -_IDLE_SLEEP = _base.DEFAULT_TIMEOUT / 5 # ms def _callback_caller(listener, callback): # _l.log(_LOG_LEVEL, "%s starting callback caller", listener) - while listener._active: + while listener._active or not listener.events.empty(): event = listener.events.get() _l.log(_LOG_LEVEL, "%s delivering event %s", listener, event) try: - callback.__call__(*event) + callback.__call__(event) except: _l.exception("callback for %s", event) # _l.log(_LOG_LEVEL, "%s stopped callback caller", listener) @@ -55,11 +55,10 @@ class EventsListener(Thread): self.task = None self.task_processing = Lock() - self.task_reply = None self.task_done = Event() - self.events = Queue(16) + self.events = Queue(32) self.event_caller = Thread(group='Unifying Receiver', name='Callback-%x' % receiver, target=_callback_caller, args=(self, events_callback)) self.event_caller.daemon = True @@ -77,23 +76,22 @@ class EventsListener(Thread): _base.unhandled_hook = self._unhandled while self._active: + event = None try: event = _base.read(self.receiver, _READ_EVENT_TIMEOUT) except E.NoReceiver: _l.warn("%s receiver disconnected", self) + self.events.put(Packet(0xFF, 0xFF, None)) self._active = False - if self._active: - if event: - # _l.log(_LOG_LEVEL, "%s queueing event %s", self, event) - self.events.put(event) + if event: + _l.log(_LOG_LEVEL, "%s queueing event %s", self, event) + self.events.put(Packet(*event)) - if self.task is None: - # _l.log(_LOG_LEVEL, "%s idle sleep", self) - _sleep(_IDLE_SLEEP / 1000.0) - else: - self.task_reply = self._make_request(*self.task) - self.task_done.set() + if self.task: + task, self.task = self.task, None + self.task_reply = self._make_request(*task) + self.task_done.set() _base.close(self.receiver) self.__str_cached = 'Events(%x)' % self.receiver @@ -102,9 +100,10 @@ class EventsListener(Thread): def stop(self): """Tells the listener to stop as soon as possible.""" - _l.log(_LOG_LEVEL, "%s stopping", self) - self._active = False - self.join() + if self._active: + _l.log(_LOG_LEVEL, "stopping %s", self) + self._active = False + self.join() def request(self, api_function, *args, **kwargs): """Make an UR API request through this listener's receiver. @@ -114,14 +113,14 @@ class EventsListener(Thread): """ # _l.log(_LOG_LEVEL, "%s request '%s.%s' with %s, %s", self, api_function.__module__, api_function.__name__, args, kwargs) - self.task_processing.acquire() - self.task_done.clear() - self.task = (api_function, args, kwargs) + if not self._active: + return None - self.task_done.wait() - reply = self.task_reply - self.task = self.task_reply = None - self.task_processing.release() + with self.task_processing: + self.task_done.clear() + self.task = (api_function, args, kwargs) + self.task_done.wait() + reply, self.task_reply = self.task_reply, None # _l.log(_LOG_LEVEL, "%s request '%s.%s' => %s", self, api_function.__module__, api_function.__name__, repr(reply)) if isinstance(reply, Exception): @@ -139,8 +138,8 @@ class EventsListener(Thread): self.task_reply = e def _unhandled(self, reply_code, devnumber, data): - event = (reply_code, devnumber, data) - _l.log(_LOG_LEVEL, "%s queueing unhandled event %s", self, event) + event = Packet(reply_code, devnumber, data) + # _l.log(_LOG_LEVEL, "%s queueing unhandled event %s", self, event) self.events.put(event) def __str__(self):