diff -r 000000000000 -r 1918ee327afb src/corelib/kernel/qeventdispatcher_glib.cpp --- /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 +#include + +#include "qcoreapplication.h" +#include "qsocketnotifier.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +struct GPollFDWithQSocketNotifier +{ + GPollFD pollfd; + QSocketNotifier *socketNotifier; +}; + +struct GSocketNotifierSource +{ + GSource source; + QList pollfds; +}; + +static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout) +{ + if (timeout) + *timeout = -1; + return false; +} + +static gboolean socketNotifierSourceCheck(GSource *source) +{ + GSocketNotifierSource *src = reinterpret_cast(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(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(source); + if (src->runWithIdlePriority) { + if (timeout) + *timeout = -1; + return false; + } + + return timerSourcePrepareHelper(src, timeout); +} + +static gboolean timerSourceCheck(GSource *source) +{ + GTimerSource *src = reinterpret_cast(source); + if (src->runWithIdlePriority) + return false; + return timerSourceCheckHelper(src); +} + +static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer) +{ + GTimerSource *timerSource = reinterpret_cast(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(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(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(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(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(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(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(g_source_new(&socketNotifierSourceFuncs, + sizeof(GSocketNotifierSource))); + (void) new (&socketNotifierSource->pollfds) QList(); + g_source_set_can_recurse(&socketNotifierSource->source, true); + g_source_attach(&socketNotifierSource->source, mainContext); + + // setup normal and idle timer sources + timerSource = reinterpret_cast(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(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(); + 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::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument"); + return QList(); + } + + 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