From d5374b9f51a8eac6ac20e2c154ff48ae44b8248c Mon Sep 17 00:00:00 2001 From: Daniel Pavel Date: Sun, 23 Jun 2013 18:11:42 +0200 Subject: [PATCH] optionally listen for upower suspend/resume events to stop/start all receiver listeners --- lib/hidapi/udev.py | 14 ++++----- lib/solaar/gtk.py | 4 ++- lib/solaar/listener.py | 29 ++++++++++++++----- lib/solaar/upower.py | 64 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 lib/solaar/upower.py diff --git a/lib/hidapi/udev.py b/lib/hidapi/udev.py index 426d546d..d18af1e0 100644 --- a/lib/hidapi/udev.py +++ b/lib/hidapi/udev.py @@ -120,13 +120,13 @@ def monitor_glib(callback, *device_filters): c = _Context() # already existing devices - for device in c.list_devices(subsystem='hidraw'): - # print (device, dict(device), dict(device.attributes)) - for filter in device_filters: - d_info = _match('add', device, *filter) - if d_info: - GLib.idle_add(callback, 'add', d_info) - break + # for device in c.list_devices(subsystem='hidraw'): + # # print (device, dict(device), dict(device.attributes)) + # for filter in device_filters: + # d_info = _match('add', device, *filter) + # if d_info: + # GLib.idle_add(callback, 'add', d_info) + # break m = _Monitor.from_netlink(c) m.filter_by(subsystem='hidraw') diff --git a/lib/solaar/gtk.py b/lib/solaar/gtk.py index afead645..ead8efb8 100644 --- a/lib/solaar/gtk.py +++ b/lib/solaar/gtk.py @@ -44,7 +44,9 @@ def _run(args): ui.init() import solaar.listener as listener - listener.start_scanner(ui.status_changed, ui.error_dialog) + listener.setup_scanner(ui.status_changed, ui.error_dialog) + + listener.start_all() # main UI event loop ui.run_loop() diff --git a/lib/solaar/listener.py b/lib/solaar/listener.py index e12bf3b1..7a4c4b8f 100644 --- a/lib/solaar/listener.py +++ b/lib/solaar/listener.py @@ -201,11 +201,11 @@ class ReceiverListener(_listener.EventsListener): _all_listeners = {} -def start(device_info, status_changed_callback): - assert status_changed_callback +def _start(device_info): + assert _status_callback receiver = Receiver.open(device_info) if receiver: - rl = ReceiverListener(receiver, status_changed_callback) + rl = ReceiverListener(receiver, _status_callback) rl.start() _all_listeners[device_info.path] = rl return rl @@ -213,12 +213,21 @@ def start(device_info, status_changed_callback): _log.warn("failed to open %s", device_info) +def start_all(): + # just in case this it called twice in a row... + stop_all() + + _log.info("starting receiver listening threads") + for device_info in _base.receivers(): + _process_receiver_event('add', device_info) + + def stop_all(): listeners = list(_all_listeners.values()) _all_listeners.clear() if listeners: - _log.info("stopping %s", listeners) + _log.info("stopping receiver listening threads %s", listeners) for l in listeners: l.stop() @@ -230,10 +239,16 @@ def stop_all(): l.join() +# stop/start all receiver threads on suspend/resume events, if possible +from . import upower +upower.watch(start_all, stop_all) + + +from logitech.unifying_receiver import base as _base _status_callback = None _error_callback = None -def start_scanner(status_changed_callback, error_callback): +def setup_scanner(status_changed_callback, error_callback): global _status_callback, _error_callback if _status_callback: raise Exception("scanner was already set-up") @@ -241,7 +256,6 @@ def start_scanner(status_changed_callback, error_callback): _status_callback = status_changed_callback _error_callback = error_callback - from logitech.unifying_receiver import base as _base _base.notify_on_receivers_glib(_process_receiver_event) @@ -249,6 +263,7 @@ def start_scanner(status_changed_callback, error_callback): def _process_receiver_event(action, device_info): assert action is not None assert device_info is not None + assert _error_callback _log.info("receiver event %s %s", action, device_info) @@ -261,7 +276,7 @@ def _process_receiver_event(action, device_info): if action == 'add': # a new receiver device was detected try: - start(device_info, _status_callback) + _start(device_info) except OSError: # permission error, ignore this path for now _error_callback('permissions', device_info.path) diff --git a/lib/solaar/upower.py b/lib/solaar/upower.py new file mode 100644 index 00000000..88d1254d --- /dev/null +++ b/lib/solaar/upower.py @@ -0,0 +1,64 @@ +# +# +# + +from __future__ import absolute_import, division, print_function, unicode_literals + +from logging import getLogger +_log = getLogger(__name__) +del getLogger + +# +# As suggested here: http://stackoverflow.com/a/13548984 +# + + +_suspend_callback = None +def _suspend(): + if _suspend_callback: + _log.info("received suspend event from UPower") + _suspend_callback() + + +_resume_callback = None +def _resume(): + if _resume_callback: + _log.info("received resume event from UPower") + _resume_callback() + + +def watch(on_resume_callback, on_suspend_callback): + """Register callback for suspend/resume events. + They are called only if the system DBus is running, and the UPower daemon is available.""" + global _resume_callback, _suspend_callback + _suspend_callback = on_suspend_callback + _resume_callback = on_resume_callback + + +try: + import dbus # for dbus communication (obviously) + + _UPOWER_BUS = 'org.freedesktop.UPower' + _UPOWER_INTERFACE = 'org.freedesktop.UPower' + + # integration into the main GLib loop + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + assert bus + + bus.add_signal_receiver(_suspend, signal_name='Sleeping', + dbus_interface=_UPOWER_INTERFACE, bus_name=_UPOWER_BUS) + + bus.add_signal_receiver(_resume, signal_name='Resuming', + dbus_interface=_UPOWER_INTERFACE, bus_name=_UPOWER_BUS) + + _log.info("connected to system dbus, watching for suspend/resume events") + +except: + # Either: + # - the dbus library is not available + # - the system dbus is not running + _log.warn("failed to register suspend/resume callbacks") + pass