diff --git a/app/__init__.py b/app/__init__.py index 093969d6..d9b677d9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -29,15 +29,15 @@ def _status_updated(watcher, icon, window): # pass -def run(): +def run(config): GObject.threads_init() - ui.notify.start(APP_TITLE) + ui.notify.init(APP_TITLE, config.notifications) watcher = WatcherThread(ui.notify.show) watcher.start() - window = ui.window.create(APP_TITLE, watcher.devices[0]) + window = ui.window.create(APP_TITLE, watcher.devices[0], not config.start_hidden, config.close_to_tray) menu_actions = [('Scan all devices', watcher.full_scan), # ('Pair new device', _pair_new_device, watcher), @@ -53,4 +53,4 @@ def run(): Gtk.main() watcher.stop() - ui.notify.stop() + ui.notify.set_active(False) diff --git a/app/ui/notify.py b/app/ui/notify.py index 8fa43fd2..46759394 100644 --- a/app/ui/notify.py +++ b/app/ui/notify.py @@ -2,35 +2,62 @@ # Optional desktop notifications. # +import logging + + try: import notify2 as _notify - - available = True + available = True # assumed to be working since the import succeeded + _active = False # not yet active + _app_title = None _notifications = {} - def start(app_title): + def init(app_title, active=True): """Init the notifications system.""" - _notify.init(app_title) - return True + global _app_title, _active + _app_title = app_title + return set_active(active) - def stop(): - """Stop the notifications system.""" - for n in list(_notifications.values()): - try: - n.close() - except Exception: - # DBUS - pass - _notifications.clear() - _notify.uninit() + def set_active(active=True): + global available, _active + if available: + if active: + if not _active: + try: + _notify.init(_app_title) + _active = True + except: + logging.exception("initializing desktop notifications") + available = False + else: + if _active: + for n in list(_notifications.values()): + try: + n.close() + except Exception: + logging.exception("closing open notification %s", n) + # DBUS + pass + _notifications.clear() + try: + _notify.uninit() + except: + logging.exception("stopping desktop notifications") + available = False + _active = False + return _active - def show(status_code, title, text, icon=None): + def active(): + return _active + + + def show(status_code, title, text='', icon=None): """Show a notification with title and text.""" - if not available: + if not available or not _active: return if title in _notifications: @@ -45,17 +72,20 @@ try: icon = icon or title notification.update(title, text, title) try: - notification.show() + if text: + notification.show() + else: + notification.close() except Exception: - # DBUS - pass + logging.exception("showing notification %s", notification) except ImportError: - import logging logging.exception("ouch") logging.warn("python-notify2 not found, desktop notifications are disabled") available = False - def start(app_title): pass - def stop(): pass + active = False + def init(app_title, active=True): return False + def active(): return False + def set_active(active=True): return False def show(status_code, title, text, icon=None): pass diff --git a/app/ui/window.py b/app/ui/window.py index ac18f334..57b50a7c 100644 --- a/app/ui/window.py +++ b/app/ui/window.py @@ -21,7 +21,10 @@ def update(window, devices): first = controls[0].get_child() icon, label = first.get_children() rstatus = devices[0] - label.set_markup('%s\n%s' % (rstatus.name, rstatus.text)) + if rstatus.text: + label.set_markup('%s\n%s' % (rstatus.name, rstatus.text)) + else: + label.set_markup('%s' % rstatus.name) for index in range(1, 1 + _MAX_DEVICES): devstatus = devices.get(index) @@ -120,7 +123,7 @@ def _device_box(title): return frame -def create(title, rstatus): +def create(title, rstatus, show=True, close_to_tray=False): vbox = Gtk.VBox(homogeneous=False, spacing=4) vbox.set_border_width(4) @@ -130,6 +133,8 @@ def create(title, rstatus): vbox.set_visible(True) window = Gtk.Window() + window.add(vbox) + window.set_title(title) window.set_icon_name(title) window.set_keep_above(True) @@ -137,16 +142,21 @@ def create(title, rstatus): # window.set_skip_pager_hint(True) window.set_deletable(False) window.set_resizable(False) + window.set_position(Gtk.WindowPosition.MOUSE) window.set_type_hint(Gdk.WindowTypeHint.UTILITY) + window.set_wmclass(title, 'status-window') window.set_role('status-window') - window.connect('window-state-event', _state_event) - window.connect('delete-event', lambda w, e: toggle(None, window) or True) + if close_to_tray: + window.connect('window-state-event', _state_event) + window.connect('delete-event', lambda w, e: toggle(None, window) or True) + else: + window.connect('delete-event', Gtk.main_quit) - window.add(vbox) - window.present() + if show: + window.present() return window diff --git a/app/watcher.py b/app/watcher.py index 1d78c162..beb3290a 100644 --- a/app/watcher.py +++ b/app/watcher.py @@ -17,15 +17,15 @@ _THREAD_SLEEP = 5 # seconds _UNIFYING_RECEIVER = 'Unifying Receiver' _NO_DEVICES = 'No devices attached.' -_SCANNING = 'Initializing...' +_INITIALIZING = 'Initializing...' +_SCANNING = 'Scanning...' _NO_RECEIVER = 'not found' -_FOUND_RECEIVER = 'found' class _DevStatus(api.AttachedDeviceInfo): timestamp = time.time() code = devices.STATUS.UNKNOWN - text = '' + text = _INITIALIZING refresh = None @@ -48,20 +48,23 @@ class WatcherThread(threading.Thread): def run(self): self.active = True - self._notify(0, _UNIFYING_RECEIVER, _SCANNING) while self.active: if self.listener is None: + self._device_status_changed(self.rstatus, (devices.STATUS.UNKNOWN, _INITIALIZING)) + self._update_status_text() + receiver = api.open() if receiver: - self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _FOUND_RECEIVER)) + self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _SCANNING)) + self._update_status_text() for devinfo in api.list_devices(receiver): self._new_device(devinfo) - - if len(self.devices) == 1: + if len(self.devices) > 1: + self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, '')) + else: self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _NO_DEVICES)) - self._update_status_text() self.listener = EventsListener(receiver, self._events_callback) @@ -163,20 +166,30 @@ class WatcherThread(threading.Thread): devstatus.timestamp = time.time() if type(status) == int: - devstatus.code = status - if devstatus.code in devices.STATUS_NAME: - devstatus.text = devices.STATUS_NAME[devstatus.code] + status_code = status + if status_code in devices.STATUS_NAME: + status_text = devices.STATUS_NAME[status_code] else: - devstatus.code = status[0] + status_code = status[0] if isinstance(status[1], str): - devstatus.text = status[1] + status_text = status[1] elif isinstance(status[1], dict): + status_text = '' for key, value in status[1].items(): - setattr(devstatus, key, value) + if key == 'text': + status_text = value + else: + setattr(devstatus, key, value) + else: + status_code = devices.STATUS.UNKNOWN + status_text = '' - if old_status_code != devstatus.code: - logging.debug("%s: device '%s' status changed %s => %s: %s", time.asctime(), devstatus.name, old_status_code, devstatus.code, devstatus.text) - if devstatus.code // 256 != old_status_code // 256: + if not (status_code == 0 and old_status_code > 0): + devstatus.code = status_code + devstatus.text = status_text + logging.debug("%s: device '%s' status update %s => %s: %s", time.asctime(), devstatus.name, old_status_code, status_code, status_text) + + if status_code == 0 or old_status_code != status_code: self._notify(devstatus.code, devstatus.name, devstatus.text) return True diff --git a/lib/hidapi/__init__.py b/lib/hidapi/__init__.py index 4e5973ec..e0a2b217 100644 --- a/lib/hidapi/__init__.py +++ b/lib/hidapi/__init__.py @@ -1,5 +1,8 @@ -"""Generic Human Interface Device API. -""" +"""Generic Human Interface Device API.""" + +__author__ = "Daniel Pavel" +__license__ = "GPL" +__version__ = "0.3" # # In case a future pure-Python implementation is feasible. diff --git a/lib/logitech/__init__.py b/lib/logitech/__init__.py index cfe18a79..e5a3437e 100644 --- a/lib/logitech/__init__.py +++ b/lib/logitech/__init__.py @@ -1 +1,5 @@ -# pass +# + +__author__ = "Daniel Pavel" +__license__ = "GPL" +__version__ = "0.4" diff --git a/solaar.py b/solaar.py index 2ed7aeab..566c7a8a 100644 --- a/solaar.py +++ b/solaar.py @@ -6,18 +6,23 @@ __version__ = '0.4' # # -import logging - - if __name__ == '__main__': import argparse - arg_parser = argparse.ArgumentParser() + arg_parser = argparse.ArgumentParser(prog='Solaar') arg_parser.add_argument('-v', '--verbose', action='count', default=0, help='increase the logger verbosity') + arg_parser.add_argument('-N', '--disable-notifications', action='store_false', dest='notifications', + help='disable desktop notifications') + arg_parser.add_argument('-H', '--start-hidden', action='store_true', dest='start_hidden', + help='hide the application window on start') + arg_parser.add_argument('-t', '--close-to-tray', action='store_true', + help='closing the application window hides it') + arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__) args = arg_parser.parse_args() + import logging log_level = logging.root.level - 10 * args.verbose logging.basicConfig(level=log_level if log_level > 0 else 1) import app - app.run() + app.run(args)