diff --git a/lib/solaar/async.py b/lib/solaar/async.py new file mode 100644 index 00000000..2fbf3e6d --- /dev/null +++ b/lib/solaar/async.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- python-mode -*- +# -*- coding: UTF-8 -*- + +## Copyright (C) 2012-2013 Daniel Pavel +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from __future__ import absolute_import, division, print_function, unicode_literals + +from logging import getLogger, DEBUG as _DEBUG +_log = getLogger(__name__) +del getLogger + +from threading import Thread as _Thread + +try: + from Queue import Queue as _Queue +except ImportError: + from queue import Queue as _Queue + +# +# +# + +class TaskRunner(_Thread): + def __init__(self, name): + super(TaskRunner, self).__init__(name=name) + self.daemon = True + self.queue = _Queue(16) + self.alive = False + + def __call__(self, function, *args, **kwargs): + task = (function, args, kwargs) + self.queue.put(task) + + def stop(self): + self.alive = False + self.queue.put(None) + + def run(self): + self.alive = True + + if _log.isEnabledFor(_DEBUG): + _log.debug("started") + + while self.alive: + task = self.queue.get() + if task: + function, args, kwargs = task + assert function + try: + function(*args, **kwargs) + except: + _log.exception("calling %s", function) + + if _log.isEnabledFor(_DEBUG): + _log.debug("stopped") diff --git a/lib/solaar/gtk.py b/lib/solaar/gtk.py index 66a077f4..bdb5c1ab 100644 --- a/lib/solaar/gtk.py +++ b/lib/solaar/gtk.py @@ -71,16 +71,10 @@ def main(): try: import solaar.ui as ui - ui.init() - import solaar.listener as listener listener.setup_scanner(ui.status_changed, ui.error_dialog) - listener.start_all() - # main UI event loop - ui.run_loop() - - listener.stop_all() + ui.run_loop(listener.start_all, listener.stop_all) except Exception as e: import sys sys.exit('%s: error: %s' % (NAME.lower(), e)) diff --git a/lib/solaar/ui/__init__.py b/lib/solaar/ui/__init__.py index 5a0340e3..c23262bf 100644 --- a/lib/solaar/ui/__init__.py +++ b/lib/solaar/ui/__init__.py @@ -20,7 +20,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from logging import getLogger, DEBUG as _DEBUG, INFO as _INFO +from logging import getLogger, DEBUG as _DEBUG _log = getLogger(__name__) del getLogger @@ -37,27 +37,6 @@ assert Gtk.get_major_version() > 2, 'Solaar requires Gtk 3 python bindings' GLib.threads_init() -def _init_application(): - APP_ID = 'io.github.pwr.solaar' - app = Gtk.Application.new(APP_ID, 0) - # not sure this is necessary... - # app.set_property('register-session', True) - registered = app.register(None) - dbus_path = app.get_dbus_object_path() if hasattr(app, 'get_dbus_object_path') else APP_ID - if _log.isEnabledFor(_INFO): - _log.info("application %s, registered %s", dbus_path, registered) - # assert registered, "failed to register unique application %s" % app - - # if there is already a running instance, bail out - if app.get_is_remote(): - # pop up the window in the other instance - app.activate() - raise Exception("already running") - - return app - -application = _init_application() - # # # @@ -92,40 +71,13 @@ def error_dialog(reason, object): GLib.idle_add(_error_dialog, reason, object) # -# A separate thread is used to read/write from the device -# so as not to block the main (GUI) thread. +# # -try: - from Queue import Queue -except ImportError: - from queue import Queue -_task_queue = Queue(16) -del Queue - - -from threading import Thread, current_thread as _current_thread - -def _process_async_queue(): - t = _current_thread() - t.alive = True - while t.alive: - function, args, kwargs = _task_queue.get() - if function: - function(*args, **kwargs) - if _log.isEnabledFor(_DEBUG): - _log.debug("stopped") - -_queue_processor = Thread(name='AsyncUI', target=_process_async_queue) -_queue_processor.daemon = True -_queue_processor.alive = False -_queue_processor.start() - -del Thread - +_task_runner = None def async(function, *args, **kwargs): - task = (function, args, kwargs) - _task_queue.put(task) + if _task_runner: + _task_runner(function, *args, **kwargs) # # @@ -133,36 +85,73 @@ def async(function, *args, **kwargs): from . import notify, tray, window -def init(): + +def _startup(app, startup_hook): + if _log.isEnabledFor(_DEBUG): + _log.debug("startup registered=%s, remote=%s", app.get_is_registered(), app.get_is_remote()) + + from solaar.async import TaskRunner as _TaskRunner + global _task_runner + _task_runner = _TaskRunner('AsyncUI') + _task_runner.start() + notify.init() tray.init(lambda _ignore: window.destroy()) window.init() -def run_loop(): - def _activate(app): - assert app == application - if app.get_windows(): - window.popup() - else: - app.add_window(window._window) + startup_hook() - def _shutdown(app): - # stop the async UI processor - _queue_processor.alive = False - async(None) - tray.destroy() - notify.uninit() +def _activate(app): + if _log.isEnabledFor(_DEBUG): + _log.debug("activate") + if app.get_windows(): + window.popup() + else: + app.add_window(window._window) + +def _command_line(app, command_line): + if _log.isEnabledFor(_DEBUG): + _log.debug("command_line %s", command_line.get_arguments()) + + return 0 + + +def _shutdown(app, shutdown_hook): + if _log.isEnabledFor(_DEBUG): + _log.debug("shutdown") + + shutdown_hook() + + # stop the async UI processor + global _task_runner + _task_runner.stop() + _task_runner = None + + tray.destroy() + notify.uninit() + + +def run_loop(startup_hook, shutdown_hook, args=None): + # from gi.repository.Gio import ApplicationFlags as _ApplicationFlags + APP_ID = 'io.github.pwr.solaar' + application = Gtk.Application.new(APP_ID, 0) # _ApplicationFlags.HANDLES_COMMAND_LINE) + + application.connect('startup', _startup, startup_hook) + application.connect('command-line', _command_line) application.connect('activate', _activate) - application.connect('shutdown', _shutdown) - application.run(None) + application.connect('shutdown', _shutdown, shutdown_hook) + + application.run(args) # # # from logitech_receiver.status import ALERT + + def _status_changed(device, alert, reason): assert device is not None if _log.isEnabledFor(_DEBUG):