1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtCore module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qeventdispatcher_glib_p.h"
43 #include "qeventdispatcher_unix_p.h"
45 #include <private/qthread_p.h>
47 #include "qcoreapplication.h"
48 #include "qsocketnotifier.h"
50 #include <QtCore/qhash.h>
51 #include <QtCore/qlist.h>
52 #include <QtCore/qpair.h>
58 struct GPollFDWithQSocketNotifier
61 QSocketNotifier *socketNotifier;
64 struct GSocketNotifierSource
67 QList<GPollFDWithQSocketNotifier *> pollfds;
70 static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
77 static gboolean socketNotifierSourceCheck(GSource *source)
79 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
82 for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
83 GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
85 if (p->pollfd.revents & G_IO_NVAL) {
86 // disable the invalid socket notifier
87 static const char *t[] = { "Read", "Write", "Exception" };
88 qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
89 p->pollfd.fd, t[int(p->socketNotifier->type())]);
90 // ### note, modifies src->pollfds!
91 p->socketNotifier->setEnabled(false);
94 pending = ((p->pollfd.revents & p->pollfd.events) != 0);
100 static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
102 QEvent event(QEvent::SockAct);
104 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
105 for (int i = 0; i < src->pollfds.count(); ++i) {
106 GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
108 if ((p->pollfd.revents & p->pollfd.events) != 0)
109 QCoreApplication::sendEvent(p->socketNotifier, &event);
112 return true; // ??? don't remove, right?
115 static GSourceFuncs socketNotifierSourceFuncs = {
116 socketNotifierSourcePrepare,
117 socketNotifierSourceCheck,
118 socketNotifierSourceDispatch,
127 QTimerInfoList timerList;
128 QEventLoop::ProcessEventsFlags processEventsFlags;
129 bool runWithIdlePriority;
132 static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
134 timeval tv = { 0l, 0l };
135 if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
136 *timeout = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000);
140 return (*timeout == 0);
143 static gboolean timerSourceCheckHelper(GTimerSource *src)
145 if (src->timerList.isEmpty()
146 || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
149 if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout)
155 static gboolean timerSourcePrepare(GSource *source, gint *timeout)
161 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
162 if (src->runWithIdlePriority) {
168 return timerSourcePrepareHelper(src, timeout);
171 static gboolean timerSourceCheck(GSource *source)
173 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
174 if (src->runWithIdlePriority)
176 return timerSourceCheckHelper(src);
179 static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
181 GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
182 if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
184 timerSource->runWithIdlePriority = true;
185 (void) timerSource->timerList.activateTimers();
186 return true; // ??? don't remove, right again?
189 static GSourceFuncs timerSourceFuncs = {
198 struct GIdleTimerSource
201 GTimerSource *timerSource;
204 static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
206 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
207 GTimerSource *timerSource = idleTimerSource->timerSource;
208 if (!timerSource->runWithIdlePriority) {
209 // Yield to the normal priority timer source
215 return timerSourcePrepareHelper(timerSource, timeout);
218 static gboolean idleTimerSourceCheck(GSource *source)
220 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
221 GTimerSource *timerSource = idleTimerSource->timerSource;
222 if (!timerSource->runWithIdlePriority) {
223 // Yield to the normal priority timer source
226 return timerSourceCheckHelper(timerSource);
229 static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
231 GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
232 (void) timerSourceDispatch(&timerSource->source, 0, 0);
236 static GSourceFuncs idleTimerSourceFuncs = {
237 idleTimerSourcePrepare,
238 idleTimerSourceCheck,
239 idleTimerSourceDispatch,
245 struct GPostEventSource
248 QAtomicInt serialNumber;
249 int lastSerialNumber;
250 QEventDispatcherGlibPrivate *d;
253 static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
255 QThreadData *data = QThreadData::current();
262 *timeout = data->canWait ? -1 : 0;
264 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
265 return (!data->canWait
266 || (source->serialNumber.load() != source->lastSerialNumber));
269 static gboolean postEventSourceCheck(GSource *source)
271 return postEventSourcePrepare(source, 0);
274 static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
276 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
277 source->lastSerialNumber = source->serialNumber.load();
278 QCoreApplication::sendPostedEvents();
279 source->d->runTimersOnceWithNormalPriority();
280 return true; // i dunno, george...
283 static GSourceFuncs postEventSourceFuncs = {
284 postEventSourcePrepare,
285 postEventSourceCheck,
286 postEventSourceDispatch,
293 QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
294 : mainContext(context)
296 if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) {
297 static QBasicMutex mutex;
298 QMutexLocker locker(&mutex);
299 if (!g_thread_supported())
304 g_main_context_ref(mainContext);
306 QCoreApplication *app = QCoreApplication::instance();
307 if (app && QThread::currentThread() == app->thread()) {
308 mainContext = g_main_context_default();
309 g_main_context_ref(mainContext);
311 mainContext = g_main_context_new();
315 #if GLIB_CHECK_VERSION (2, 22, 0)
316 g_main_context_push_thread_default (mainContext);
319 // setup post event source
320 postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
321 sizeof(GPostEventSource)));
322 postEventSource->serialNumber.store(1);
323 postEventSource->d = this;
324 g_source_set_can_recurse(&postEventSource->source, true);
325 g_source_attach(&postEventSource->source, mainContext);
327 // setup socketNotifierSource
328 socketNotifierSource =
329 reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
330 sizeof(GSocketNotifierSource)));
331 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
332 g_source_set_can_recurse(&socketNotifierSource->source, true);
333 g_source_attach(&socketNotifierSource->source, mainContext);
335 // setup normal and idle timer sources
336 timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
337 sizeof(GTimerSource)));
338 (void) new (&timerSource->timerList) QTimerInfoList();
339 timerSource->processEventsFlags = QEventLoop::AllEvents;
340 timerSource->runWithIdlePriority = false;
341 g_source_set_can_recurse(&timerSource->source, true);
342 g_source_attach(&timerSource->source, mainContext);
344 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
345 sizeof(GIdleTimerSource)));
346 idleTimerSource->timerSource = timerSource;
347 g_source_set_can_recurse(&idleTimerSource->source, true);
348 g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
349 g_source_attach(&idleTimerSource->source, mainContext);
352 void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
354 timerSource->runWithIdlePriority = false;
357 QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
358 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
362 QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
363 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
366 QEventDispatcherGlib::~QEventDispatcherGlib()
368 Q_D(QEventDispatcherGlib);
370 // destroy all timer sources
371 qDeleteAll(d->timerSource->timerList);
372 d->timerSource->timerList.~QTimerInfoList();
373 g_source_destroy(&d->timerSource->source);
374 g_source_unref(&d->timerSource->source);
376 g_source_destroy(&d->idleTimerSource->source);
377 g_source_unref(&d->idleTimerSource->source);
378 d->idleTimerSource = 0;
380 // destroy socket notifier source
381 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
382 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
383 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
386 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
387 g_source_destroy(&d->socketNotifierSource->source);
388 g_source_unref(&d->socketNotifierSource->source);
389 d->socketNotifierSource = 0;
391 // destroy post event source
392 g_source_destroy(&d->postEventSource->source);
393 g_source_unref(&d->postEventSource->source);
394 d->postEventSource = 0;
396 Q_ASSERT(d->mainContext != 0);
397 #if GLIB_CHECK_VERSION (2, 22, 0)
398 g_main_context_pop_thread_default (d->mainContext);
400 g_main_context_unref(d->mainContext);
404 bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
406 Q_D(QEventDispatcherGlib);
408 const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
414 // tell postEventSourcePrepare() and timerSource about any new flags
415 QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
416 d->timerSource->processEventsFlags = flags;
418 if (!(flags & QEventLoop::EventLoopExec)) {
419 // force timers to be sent at normal priority
420 d->timerSource->runWithIdlePriority = false;
423 bool result = g_main_context_iteration(d->mainContext, canWait);
424 while (!result && canWait)
425 result = g_main_context_iteration(d->mainContext, canWait);
427 d->timerSource->processEventsFlags = savedFlags;
435 bool QEventDispatcherGlib::hasPendingEvents()
437 Q_D(QEventDispatcherGlib);
438 return g_main_context_pending(d->mainContext);
441 void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
444 int sockfd = notifier->socket();
445 int type = notifier->type();
448 qWarning("QSocketNotifier: Internal error");
450 } else if (notifier->thread() != thread()
451 || thread() != QThread::currentThread()) {
452 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
457 Q_D(QEventDispatcherGlib);
460 GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
461 p->pollfd.fd = sockfd;
463 case QSocketNotifier::Read:
464 p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
466 case QSocketNotifier::Write:
467 p->pollfd.events = G_IO_OUT | G_IO_ERR;
469 case QSocketNotifier::Exception:
470 p->pollfd.events = G_IO_PRI | G_IO_ERR;
473 p->socketNotifier = notifier;
475 d->socketNotifierSource->pollfds.append(p);
477 g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
480 void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
484 int sockfd = notifier->socket();
486 qWarning("QSocketNotifier: Internal error");
488 } else if (notifier->thread() != thread()
489 || thread() != QThread::currentThread()) {
490 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
495 Q_D(QEventDispatcherGlib);
497 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
498 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
499 if (p->socketNotifier == notifier) {
501 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
503 d->socketNotifierSource->pollfds.removeAt(i);
511 void QEventDispatcherGlib::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
514 if (timerId < 1 || interval < 0 || !object) {
515 qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
517 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
518 qWarning("QObject::startTimer: timers cannot be started from another thread");
523 Q_D(QEventDispatcherGlib);
524 d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
527 bool QEventDispatcherGlib::unregisterTimer(int timerId)
531 qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
533 } else if (thread() != QThread::currentThread()) {
534 qWarning("QObject::killTimer: timers cannot be stopped from another thread");
539 Q_D(QEventDispatcherGlib);
540 return d->timerSource->timerList.unregisterTimer(timerId);
543 bool QEventDispatcherGlib::unregisterTimers(QObject *object)
547 qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
549 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
550 qWarning("QObject::killTimers: timers cannot be stopped from another thread");
555 Q_D(QEventDispatcherGlib);
556 return d->timerSource->timerList.unregisterTimers(object);
559 QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
562 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
563 return QList<TimerInfo>();
566 Q_D(const QEventDispatcherGlib);
567 return d->timerSource->timerList.registeredTimers(object);
570 int QEventDispatcherGlib::remainingTime(int timerId)
574 qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
579 Q_D(QEventDispatcherGlib);
580 return d->timerSource->timerList.timerRemainingTime(timerId);
583 void QEventDispatcherGlib::interrupt()
588 void QEventDispatcherGlib::wakeUp()
590 Q_D(QEventDispatcherGlib);
591 d->postEventSource->serialNumber.ref();
592 g_main_context_wakeup(d->mainContext);
595 void QEventDispatcherGlib::flush()
599 bool QEventDispatcherGlib::versionSupported()
601 #if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
604 return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
608 QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
609 : QAbstractEventDispatcher(dd, parent)