src/corelib/kernel/qeventdispatcher_glib.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/kernel/qeventdispatcher_glib.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,584 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventdispatcher_glib_p.h"
+#include "qeventdispatcher_unix_p.h"
+
+#include <private/qmutexpool_p.h>
+#include <private/qthread_p.h>
+
+#include "qcoreapplication.h"
+#include "qsocketnotifier.h"
+
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+
+#include <glib.h>
+
+QT_BEGIN_NAMESPACE
+
+struct GPollFDWithQSocketNotifier
+{
+    GPollFD pollfd;
+    QSocketNotifier *socketNotifier;
+};
+
+struct GSocketNotifierSource
+{
+    GSource source;
+    QList<GPollFDWithQSocketNotifier *> pollfds;
+};
+
+static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
+{
+    if (timeout)
+        *timeout = -1;
+    return false;
+}
+
+static gboolean socketNotifierSourceCheck(GSource *source)
+{
+    GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
+
+    bool pending = false;
+    for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
+        GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
+
+        if (p->pollfd.revents & G_IO_NVAL) {
+            // disable the invalid socket notifier
+            static const char *t[] = { "Read", "Write", "Exception" };
+            qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
+                     p->pollfd.fd, t[int(p->socketNotifier->type())]);
+            // ### note, modifies src->pollfds!
+            p->socketNotifier->setEnabled(false);
+        }
+
+        pending = ((p->pollfd.revents & p->pollfd.events) != 0);
+    }
+
+    return pending;
+}
+
+static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+    QEvent event(QEvent::SockAct);
+
+    GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
+    for (int i = 0; i < src->pollfds.count(); ++i) {
+        GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
+
+        if ((p->pollfd.revents & p->pollfd.events) != 0)
+            QCoreApplication::sendEvent(p->socketNotifier, &event);
+    }
+
+    return true; // ??? don't remove, right?
+}
+
+static GSourceFuncs socketNotifierSourceFuncs = {
+    socketNotifierSourcePrepare,
+    socketNotifierSourceCheck,
+    socketNotifierSourceDispatch,
+    NULL,
+    NULL,
+    NULL
+};
+
+struct GTimerSource
+{
+    GSource source;
+    QTimerInfoList timerList;
+    QEventLoop::ProcessEventsFlags processEventsFlags;
+    bool runWithIdlePriority;
+};
+
+static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
+{
+    timeval tv = { 0l, 0l };
+    if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
+        *timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+    else
+        *timeout = -1;
+
+    return (*timeout == 0);
+}
+
+static gboolean timerSourceCheckHelper(GTimerSource *src)
+{
+    if (src->timerList.isEmpty()
+        || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
+        return false;
+
+    if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout)
+        return false;
+
+    return true;
+}
+
+static gboolean timerSourcePrepare(GSource *source, gint *timeout)
+{
+    gint dummy;
+    if (!timeout)
+        timeout = &dummy;
+
+    GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
+    if (src->runWithIdlePriority) {
+        if (timeout)
+            *timeout = -1;
+        return false;
+    }
+
+    return timerSourcePrepareHelper(src, timeout);
+}
+
+static gboolean timerSourceCheck(GSource *source)
+{
+    GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
+    if (src->runWithIdlePriority)
+        return false;
+    return timerSourceCheckHelper(src);
+}
+
+static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+    GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
+    timerSource->runWithIdlePriority = true;
+    (void) timerSource->timerList.activateTimers();
+    return true; // ??? don't remove, right again?
+}
+
+static GSourceFuncs timerSourceFuncs = {
+    timerSourcePrepare,
+    timerSourceCheck,
+    timerSourceDispatch,
+    NULL,
+    NULL,
+    NULL
+};
+
+struct GIdleTimerSource
+{
+    GSource source;
+    GTimerSource *timerSource;
+};
+
+static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
+{
+    GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
+    GTimerSource *timerSource = idleTimerSource->timerSource;
+    if (!timerSource->runWithIdlePriority) {
+        // Yield to the normal priority timer source
+        if (timeout)
+            *timeout = -1;
+        return false;
+    }
+
+    return timerSourcePrepareHelper(timerSource, timeout);
+}
+
+static gboolean idleTimerSourceCheck(GSource *source)
+{
+    GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
+    GTimerSource *timerSource = idleTimerSource->timerSource;
+    if (!timerSource->runWithIdlePriority) {
+        // Yield to the normal priority timer source
+        return false;
+    }
+    return timerSourceCheckHelper(timerSource);
+}
+
+static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+    GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
+    (void) timerSourceDispatch(&timerSource->source, 0, 0);
+    return true;
+}
+
+static GSourceFuncs idleTimerSourceFuncs = {
+    idleTimerSourcePrepare,
+    idleTimerSourceCheck,
+    idleTimerSourceDispatch,
+    NULL,
+    NULL,
+    NULL
+};
+
+struct GPostEventSource
+{
+    GSource source;
+    QAtomicInt serialNumber;
+    int lastSerialNumber;
+};
+
+static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
+{
+    QThreadData *data = QThreadData::current();
+    if (!data)
+        return false;
+
+    gint dummy;
+    if (!timeout)
+        timeout = &dummy;
+    *timeout = data->canWait ? -1 : 0;
+
+    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
+    return (!data->canWait
+            || (source->serialNumber != source->lastSerialNumber));
+}
+
+static gboolean postEventSourceCheck(GSource *source)
+{
+    return postEventSourcePrepare(source, 0);
+}
+
+static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
+{
+    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
+    source->lastSerialNumber = source->serialNumber;
+    QCoreApplication::sendPostedEvents();
+    return true; // i dunno, george...
+}
+
+static GSourceFuncs postEventSourceFuncs = {
+    postEventSourcePrepare,
+    postEventSourceCheck,
+    postEventSourceDispatch,
+    NULL,
+    NULL,
+    NULL
+};
+
+
+QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
+    : mainContext(context)
+{
+    if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) {
+        static int dummyValue = 0; // only used for its address
+        QMutexLocker locker(QMutexPool::instance()->get(&dummyValue));
+        if (!g_thread_supported())
+            g_thread_init(NULL);
+    }
+
+    if (mainContext) {
+        g_main_context_ref(mainContext);
+    } else {
+        QCoreApplication *app = QCoreApplication::instance();
+        if (app && QThread::currentThread() == app->thread()) {
+            mainContext = g_main_context_default();
+            g_main_context_ref(mainContext);
+        } else {
+            mainContext = g_main_context_new();
+        }
+    }
+
+    // setup post event source
+    postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
+                                                                        sizeof(GPostEventSource)));
+    postEventSource->serialNumber = 1;
+    g_source_set_can_recurse(&postEventSource->source, true);
+    g_source_attach(&postEventSource->source, mainContext);
+
+    // setup socketNotifierSource
+    socketNotifierSource =
+        reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
+                                                               sizeof(GSocketNotifierSource)));
+    (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
+    g_source_set_can_recurse(&socketNotifierSource->source, true);
+    g_source_attach(&socketNotifierSource->source, mainContext);
+
+    // setup normal and idle timer sources
+    timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
+                                                                sizeof(GTimerSource)));
+    (void) new (&timerSource->timerList) QTimerInfoList();
+    timerSource->processEventsFlags = QEventLoop::AllEvents;
+    timerSource->runWithIdlePriority = false;
+    g_source_set_can_recurse(&timerSource->source, true);
+    g_source_attach(&timerSource->source, mainContext);
+
+    idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
+                                                                        sizeof(GIdleTimerSource)));
+    idleTimerSource->timerSource = timerSource;
+    g_source_set_can_recurse(&idleTimerSource->source, true);
+    g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
+    g_source_attach(&idleTimerSource->source, mainContext);
+}
+
+QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
+    : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
+{
+}
+
+QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
+    : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
+{ }
+
+QEventDispatcherGlib::~QEventDispatcherGlib()
+{
+    Q_D(QEventDispatcherGlib);
+
+    // destroy all timer sources
+    qDeleteAll(d->timerSource->timerList);
+    d->timerSource->timerList.~QTimerInfoList();
+    g_source_destroy(&d->timerSource->source);
+    g_source_unref(&d->timerSource->source);
+    d->timerSource = 0;
+    g_source_destroy(&d->idleTimerSource->source);
+    g_source_unref(&d->idleTimerSource->source);
+    d->idleTimerSource = 0;
+
+    // destroy socket notifier source
+    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
+        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
+        g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
+        delete p;
+    }
+    d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
+    g_source_destroy(&d->socketNotifierSource->source);
+    g_source_unref(&d->socketNotifierSource->source);
+    d->socketNotifierSource = 0;
+
+    // destroy post event source
+    g_source_destroy(&d->postEventSource->source);
+    g_source_unref(&d->postEventSource->source);
+    d->postEventSource = 0;
+
+    Q_ASSERT(d->mainContext != 0);
+    g_main_context_unref(d->mainContext);
+    d->mainContext = 0;
+}
+
+bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+    Q_D(QEventDispatcherGlib);
+
+    const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
+    if (canWait)
+        emit aboutToBlock();
+    else
+        emit awake();
+
+    // tell postEventSourcePrepare() and timerSource about any new flags
+    QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
+    d->timerSource->processEventsFlags = flags;
+
+    if (!(flags & QEventLoop::EventLoopExec)) {
+        // force timers to be sent at normal priority
+        d->timerSource->runWithIdlePriority = false;
+    }
+
+    bool result = g_main_context_iteration(d->mainContext, canWait);
+    while (!result && canWait)
+        result = g_main_context_iteration(d->mainContext, canWait);
+
+    d->timerSource->processEventsFlags = savedFlags;
+
+    if (canWait)
+        emit awake();
+
+    return result;
+}
+
+bool QEventDispatcherGlib::hasPendingEvents()
+{
+    Q_D(QEventDispatcherGlib);
+    return g_main_context_pending(d->mainContext);
+}
+
+void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
+{
+    Q_ASSERT(notifier);
+    int sockfd = notifier->socket();
+    int type = notifier->type();
+#ifndef QT_NO_DEBUG
+    if (sockfd < 0) {
+        qWarning("QSocketNotifier: Internal error");
+        return;
+    } else if (notifier->thread() != thread()
+               || thread() != QThread::currentThread()) {
+        qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
+        return;
+    }
+#endif
+
+    Q_D(QEventDispatcherGlib);
+
+
+    GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
+    p->pollfd.fd = sockfd;
+    switch (type) {
+    case QSocketNotifier::Read:
+        p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+        break;
+    case QSocketNotifier::Write:
+        p->pollfd.events = G_IO_OUT | G_IO_ERR;
+        break;
+    case QSocketNotifier::Exception:
+        p->pollfd.events = G_IO_PRI | G_IO_ERR;
+        break;
+    }
+    p->socketNotifier = notifier;
+
+    d->socketNotifierSource->pollfds.append(p);
+
+    g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
+}
+
+void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+    Q_ASSERT(notifier);
+#ifndef QT_NO_DEBUG
+    int sockfd = notifier->socket();
+    if (sockfd < 0) {
+        qWarning("QSocketNotifier: Internal error");
+        return;
+    } else if (notifier->thread() != thread()
+               || thread() != QThread::currentThread()) {
+        qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
+        return;
+    }
+#endif
+
+    Q_D(QEventDispatcherGlib);
+
+    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
+        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
+        if (p->socketNotifier == notifier) {
+            // found it
+            g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
+
+            d->socketNotifierSource->pollfds.removeAt(i);
+            delete p;
+
+            return;
+        }
+    }
+}
+
+void QEventDispatcherGlib::registerTimer(int timerId, int interval, QObject *object)
+{
+#ifndef QT_NO_DEBUG
+    if (timerId < 1 || interval < 0 || !object) {
+        qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
+        return;
+    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+        qWarning("QObject::startTimer: timers cannot be started from another thread");
+        return;
+    }
+#endif
+
+    Q_D(QEventDispatcherGlib);
+    d->timerSource->timerList.registerTimer(timerId, interval, object);
+}
+
+bool QEventDispatcherGlib::unregisterTimer(int timerId)
+{
+#ifndef QT_NO_DEBUG
+    if (timerId < 1) {
+        qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
+        return false;
+    } else if (thread() != QThread::currentThread()) {
+        qWarning("QObject::killTimer: timers cannot be stopped from another thread");
+        return false;
+    }
+#endif
+
+    Q_D(QEventDispatcherGlib);
+    return d->timerSource->timerList.unregisterTimer(timerId);
+}
+
+bool QEventDispatcherGlib::unregisterTimers(QObject *object)
+{
+#ifndef QT_NO_DEBUG
+    if (!object) {
+        qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument");
+        return false;
+    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+        qWarning("QObject::killTimers: timers cannot be stopped from another thread");
+        return false;
+    }
+#endif
+
+    Q_D(QEventDispatcherGlib);
+    return d->timerSource->timerList.unregisterTimers(object);
+}
+
+QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
+{
+    if (!object) {
+        qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
+        return QList<TimerInfo>();
+    }
+
+    Q_D(const QEventDispatcherGlib);
+    return d->timerSource->timerList.registeredTimers(object);
+}
+
+void QEventDispatcherGlib::interrupt()
+{
+    wakeUp();
+}
+
+void QEventDispatcherGlib::wakeUp()
+{
+    Q_D(QEventDispatcherGlib);
+    d->postEventSource->serialNumber.ref();
+    g_main_context_wakeup(d->mainContext);
+}
+
+void QEventDispatcherGlib::flush()
+{
+}
+
+bool QEventDispatcherGlib::versionSupported()
+{
+#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
+    return false;
+#else
+    return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
+#endif
+}
+
+QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
+    : QAbstractEventDispatcher(dd, parent)
+{
+}
+
+QT_END_NAMESPACE