// This is the SIP interface definition for the Qt support for the standard // Python DBus bindings. // // Copyright (c) 2007 Phil Thompson // // Licensed under the Academic Free License version 2.1 // // 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include #include #include "helper.h" // The callback to add a watch. extern "C" {static dbus_bool_t add_watch(DBusWatch *watch, void *data);} static dbus_bool_t add_watch(DBusWatch *watch, void *data) { pyqtDBusHelper *hlp = reinterpret_cast(data); int fd = dbus_watch_get_fd(watch); unsigned int flags = dbus_watch_get_flags(watch); dbus_bool_t enabled = dbus_watch_get_enabled(watch); pyqtDBusHelper::Watcher watcher; watcher.watch = watch; if (flags & DBUS_WATCH_READABLE) { watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, hlp); watcher.read->setEnabled(enabled); hlp->connect(watcher.read, SIGNAL(activated(int)), SLOT(readSocket(int))); } if (flags & DBUS_WATCH_WRITABLE) { watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, hlp); watcher.write->setEnabled(enabled); hlp->connect(watcher.write, SIGNAL(activated(int)), SLOT(writeSocket(int))); } hlp->watchers.insertMulti(fd, watcher); return true; } // The callback to remove a watch. extern "C" {static void remove_watch(DBusWatch *watch, void *data);} static void remove_watch(DBusWatch *watch, void *data) { pyqtDBusHelper *hlp = reinterpret_cast(data); int fd = dbus_watch_get_fd(watch); pyqtDBusHelper::Watchers::iterator it = hlp->watchers.find(fd); while (it != hlp->watchers.end() && it.key() == fd) { pyqtDBusHelper::Watcher &watcher = it.value(); if (watcher.watch == watch) { if (watcher.read) delete watcher.read; if (watcher.write) delete watcher.write; hlp->watchers.erase(it); return; } ++it; } } // The callback to toggle a watch. extern "C" {static void toggle_watch(DBusWatch *watch, void *data);} static void toggle_watch(DBusWatch *watch, void *data) { pyqtDBusHelper *hlp = reinterpret_cast(data); int fd = dbus_watch_get_fd(watch); unsigned int flags = dbus_watch_get_flags(watch); dbus_bool_t enabled = dbus_watch_get_enabled(watch); pyqtDBusHelper::Watchers::const_iterator it = hlp->watchers.find(fd); while (it != hlp->watchers.end() && it.key() == fd) { const pyqtDBusHelper::Watcher &watcher = it.value(); if (watcher.watch == watch) { if (flags & DBUS_WATCH_READABLE && watcher.read) watcher.read->setEnabled(enabled); if (flags & DBUS_WATCH_WRITABLE && watcher.write) watcher.write->setEnabled(enabled); return; } ++it; } } // The callback to add a timeout. extern "C" {static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data);} static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { // Nothing to do if the timeout is disabled. if (!dbus_timeout_get_enabled(timeout)) return true; // Pretend it is successful if there is no application instance. This can // happen if the global application gets garbage collected before the // dbus-python main loop does. if (!QCoreApplication::instance()) return true; pyqtDBusHelper *hlp = reinterpret_cast(data); int id = hlp->startTimer(dbus_timeout_get_interval(timeout)); if (!id) return false; hlp->timeouts[id] = timeout; return true; } // The callback to remove a timeout. extern "C" {static void remove_timeout(DBusTimeout *timeout, void *data);} static void remove_timeout(DBusTimeout *timeout, void *data) { pyqtDBusHelper *hlp = reinterpret_cast(data); pyqtDBusHelper::Timeouts::iterator it = hlp->timeouts.begin(); while (it != hlp->timeouts.end()) if (it.value() == timeout) { hlp->killTimer(it.key()); it = hlp->timeouts.erase(it); } else ++it; } // The callback to toggle a timeout. extern "C" {static void toggle_timeout(DBusTimeout *timeout, void *data);} static void toggle_timeout(DBusTimeout *timeout, void *data) { remove_timeout(timeout, data); add_timeout(timeout, data); } // The callback to delete a helper instance. extern "C" {static void dbus_qt_delete_helper(void *data);} static void dbus_qt_delete_helper(void *data) { delete reinterpret_cast(data); } // The callback to wakeup the event loop. extern "C" {static void wakeup_main(void *data);} static void wakeup_main(void *data) { pyqtDBusHelper *hlp = reinterpret_cast(data); // This all seems to work (with responses coming at the same time as with // GLib) but it doesn't seem right. In effect this is being used as a // polling function and the QSocketNotifiers never get fired. if (dbus_connection_get_dispatch_status(hlp->connection) == DBUS_DISPATCH_DATA_REMAINS) QMetaObject::invokeMethod(hlp, "dispatch", Qt::QueuedConnection); } // The callback to set up a DBus connection. extern "C" {static dbus_bool_t dbus_qt_conn(DBusConnection *conn, void *data);} static dbus_bool_t dbus_qt_conn(DBusConnection *conn, void *data) { bool rc; Py_BEGIN_ALLOW_THREADS pyqtDBusHelper *hlp = reinterpret_cast(data); hlp->connection = conn; if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, data, 0)) rc = false; else if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, data, 0)) rc = false; else rc = true; dbus_connection_set_wakeup_main_function(conn, wakeup_main, hlp, 0); Py_END_ALLOW_THREADS return rc; } // The callback to set up a DBus server. extern "C" {static dbus_bool_t dbus_qt_srv(DBusServer *srv, void *data);} static dbus_bool_t dbus_qt_srv(DBusServer *srv, void *data) { bool rc; Py_BEGIN_ALLOW_THREADS if (!dbus_server_set_watch_functions(srv, add_watch, remove_watch, toggle_watch, data, 0)) rc = false; else if (!dbus_server_set_timeout_functions(srv, add_timeout, remove_timeout, toggle_timeout, data, 0)) rc = false; else rc = true; Py_END_ALLOW_THREADS return rc; } // Create a new helper instance. pyqtDBusHelper::pyqtDBusHelper() : QObject(), connection(0) { } // Handle a socket being ready to read. void pyqtDBusHelper::readSocket(int fd) { Watchers::const_iterator it = watchers.find(fd); while (it != watchers.end() && it.key() == fd) { const Watcher &watcher = it.value(); if (watcher.read && watcher.read->isEnabled()) dbus_watch_handle(watcher.watch, DBUS_WATCH_READABLE); ++it; } if (connection) dispatch(); } // Handle a socket being ready to write. void pyqtDBusHelper::writeSocket(int fd) { Watchers::const_iterator it = watchers.find(fd); while (it != watchers.end() && it.key() == fd) { const Watcher &watcher = it.value(); if (watcher.write && watcher.write->isEnabled()) dbus_watch_handle(watcher.watch, DBUS_WATCH_WRITABLE); ++it; } } // Handoff to the connection dispatcher while there is data. void pyqtDBusHelper::dispatch() { while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; } // Reimplemented to handle timer events. void pyqtDBusHelper::timerEvent(QTimerEvent *e) { DBusTimeout *timeout = timeouts.value(e->timerId()); if (timeout) dbus_timeout_handle(timeout); } PyDoc_STRVAR(DBusQtMainLoop__doc__, "DBusQtMainLoop([set_as_default=False]) -> NativeMainLoop\n" "\n" "Return a NativeMainLoop object.\n" "\n" "If the keyword argument set_as_default is given and is True, set the new\n" "main loop as the default for all new Connection or Bus instances.\n"); extern "C" {static PyObject *DBusQtMainLoop(PyObject *, PyObject *args, PyObject *kwargs);} static PyObject *DBusQtMainLoop(PyObject *, PyObject *args, PyObject *kwargs) { if (PyTuple_Size(args) != 0) { PyErr_SetString(PyExc_TypeError, "DBusQtMainLoop() takes no positional arguments"); return 0; } int set_as_default = 0; static char *argnames[] = {"set_as_default", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", argnames, &set_as_default)) return 0; pyqtDBusHelper *hlp = new pyqtDBusHelper; PyObject *mainloop = DBusPyNativeMainLoop_New4(dbus_qt_conn, dbus_qt_srv, dbus_qt_delete_helper, hlp); if (!mainloop) { delete hlp; return 0; } if (set_as_default) { PyObject *func = PyObject_GetAttrString(_dbus_bindings_module, "set_default_main_loop"); if (!func) { Py_DECREF(mainloop); return 0; } PyObject *res = PyObject_CallFunctionObjArgs(func, mainloop, 0); Py_DECREF(func); if (!res) { Py_DECREF(mainloop); return 0; } Py_DECREF(res); } return mainloop; } // The table of module functions. static PyMethodDef module_functions[] = { {"DBusQtMainLoop", (PyCFunction)DBusQtMainLoop, METH_VARARGS|METH_KEYWORDS, DBusQtMainLoop__doc__}, {0, 0, 0, 0} }; // The module entry point. PyMODINIT_FUNC initqt() { // Import the generic part of the Python DBus bindings. if (import_dbus_bindings("dbus.mainloop.qt") < 0) return; Py_InitModule("qt", module_functions); }