1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qeventdispatcher_glib_p.h"
43 #include "qeventdispatcher_unix_p.h"
44
45 #include <private/qmutexpool_p.h>
46 #include <private/qthread_p.h>
47
48 #include "qcoreapplication.h"
49 #include "qsocketnotifier.h"
50
51 #include <QtCore/qhash.h>
52 #include <QtCore/qlist.h>
53 #include <QtCore/qpair.h>
54
55 #include <glib.h>
56
57 QT_BEGIN_NAMESPACE
58
59 struct GPollFDWithQSocketNotifier
60 {
61 GPollFD pollfd;
62 QSocketNotifier *socketNotifier;
63 };
64
65 struct GSocketNotifierSource
66 {
67 GSource source;
68 QList<GPollFDWithQSocketNotifier *> pollfds;
69 };
70
socketNotifierSourcePrepare(GSource *,gint * timeout)71 static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
72 {
73 if (timeout)
74 *timeout = -1;
75 return false;
76 }
77
socketNotifierSourceCheck(GSource * source)78 static gboolean socketNotifierSourceCheck(GSource *source)
79 {
80 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
81
82 bool pending = false;
83 for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
84 GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
85
86 if (p->pollfd.revents & G_IO_NVAL) {
87 // disable the invalid socket notifier
88 static const char *t[] = { "Read", "Write", "Exception" };
89 qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
90 p->pollfd.fd, t[int(p->socketNotifier->type())]);
91 // ### note, modifies src->pollfds!
92 p->socketNotifier->setEnabled(false);
93 }
94
95 pending = ((p->pollfd.revents & p->pollfd.events) != 0);
96 }
97
98 return pending;
99 }
100
socketNotifierSourceDispatch(GSource * source,GSourceFunc,gpointer)101 static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
102 {
103 QEvent event(QEvent::SockAct);
104
105 GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
106 for (int i = 0; i < src->pollfds.count(); ++i) {
107 GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
108
109 if ((p->pollfd.revents & p->pollfd.events) != 0)
110 QCoreApplication::sendEvent(p->socketNotifier, &event);
111 }
112
113 return true; // ??? don't remove, right?
114 }
115
116 static GSourceFuncs socketNotifierSourceFuncs = {
117 socketNotifierSourcePrepare,
118 socketNotifierSourceCheck,
119 socketNotifierSourceDispatch,
120 NULL,
121 NULL,
122 NULL
123 };
124
125 struct GTimerSource
126 {
127 GSource source;
128 QTimerInfoList timerList;
129 QEventLoop::ProcessEventsFlags processEventsFlags;
130 bool runWithIdlePriority;
131 };
132
timerSourcePrepareHelper(GTimerSource * src,gint * timeout)133 static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
134 {
135 timeval tv = { 0l, 0l };
136 if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
137 *timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
138 else
139 *timeout = -1;
140
141 return (*timeout == 0);
142 }
143
timerSourceCheckHelper(GTimerSource * src)144 static gboolean timerSourceCheckHelper(GTimerSource *src)
145 {
146 if (src->timerList.isEmpty()
147 || (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
148 return false;
149
150 if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout)
151 return false;
152
153 return true;
154 }
155
timerSourcePrepare(GSource * source,gint * timeout)156 static gboolean timerSourcePrepare(GSource *source, gint *timeout)
157 {
158 gint dummy;
159 if (!timeout)
160 timeout = &dummy;
161
162 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
163 if (src->runWithIdlePriority) {
164 if (timeout)
165 *timeout = -1;
166 return false;
167 }
168
169 return timerSourcePrepareHelper(src, timeout);
170 }
171
timerSourceCheck(GSource * source)172 static gboolean timerSourceCheck(GSource *source)
173 {
174 GTimerSource *src = reinterpret_cast<GTimerSource *>(source);
175 if (src->runWithIdlePriority)
176 return false;
177 return timerSourceCheckHelper(src);
178 }
179
timerSourceDispatch(GSource * source,GSourceFunc,gpointer)180 static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
181 {
182 GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);
183 if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)
184 return true;
185 timerSource->runWithIdlePriority = true;
186 (void) timerSource->timerList.activateTimers();
187 return true; // ??? don't remove, right again?
188 }
189
190 static GSourceFuncs timerSourceFuncs = {
191 timerSourcePrepare,
192 timerSourceCheck,
193 timerSourceDispatch,
194 NULL,
195 NULL,
196 NULL
197 };
198
199 struct GIdleTimerSource
200 {
201 GSource source;
202 GTimerSource *timerSource;
203 };
204
idleTimerSourcePrepare(GSource * source,gint * timeout)205 static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
206 {
207 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
208 GTimerSource *timerSource = idleTimerSource->timerSource;
209 if (!timerSource->runWithIdlePriority) {
210 // Yield to the normal priority timer source
211 if (timeout)
212 *timeout = -1;
213 return false;
214 }
215
216 return timerSourcePrepareHelper(timerSource, timeout);
217 }
218
idleTimerSourceCheck(GSource * source)219 static gboolean idleTimerSourceCheck(GSource *source)
220 {
221 GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
222 GTimerSource *timerSource = idleTimerSource->timerSource;
223 if (!timerSource->runWithIdlePriority) {
224 // Yield to the normal priority timer source
225 return false;
226 }
227 return timerSourceCheckHelper(timerSource);
228 }
229
idleTimerSourceDispatch(GSource * source,GSourceFunc,gpointer)230 static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
231 {
232 GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
233 (void) timerSourceDispatch(&timerSource->source, 0, 0);
234 return true;
235 }
236
237 static GSourceFuncs idleTimerSourceFuncs = {
238 idleTimerSourcePrepare,
239 idleTimerSourceCheck,
240 idleTimerSourceDispatch,
241 NULL,
242 NULL,
243 NULL
244 };
245
246 struct GPostEventSource
247 {
248 GSource source;
249 QAtomicInt serialNumber;
250 int lastSerialNumber;
251 QEventDispatcherGlibPrivate *d;
252 };
253
postEventSourcePrepare(GSource * s,gint * timeout)254 static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
255 {
256 QThreadData *data = QThreadData::current();
257 if (!data)
258 return false;
259
260 gint dummy;
261 if (!timeout)
262 timeout = &dummy;
263 const bool canWait = data->canWaitLocked();
264 *timeout = canWait ? -1 : 0;
265
266 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
267 return (!canWait
268 || (source->serialNumber != source->lastSerialNumber));
269 }
270
postEventSourceCheck(GSource * source)271 static gboolean postEventSourceCheck(GSource *source)
272 {
273 return postEventSourcePrepare(source, 0);
274 }
275
postEventSourceDispatch(GSource * s,GSourceFunc,gpointer)276 static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
277 {
278 GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
279 source->lastSerialNumber = source->serialNumber;
280 QCoreApplication::sendPostedEvents();
281 source->d->runTimersOnceWithNormalPriority();
282 return true; // i dunno, george...
283 }
284
285 static GSourceFuncs postEventSourceFuncs = {
286 postEventSourcePrepare,
287 postEventSourceCheck,
288 postEventSourceDispatch,
289 NULL,
290 NULL,
291 NULL
292 };
293
294
QEventDispatcherGlibPrivate(GMainContext * context)295 QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
296 : mainContext(context)
297 {
298 if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) {
299 static int dummyValue = 0; // only used for its address
300 QMutexLocker locker(QMutexPool::instance()->get(&dummyValue));
301 if (!g_thread_supported())
302 g_thread_init(NULL);
303 }
304
305 if (mainContext) {
306 g_main_context_ref(mainContext);
307 } else {
308 QCoreApplication *app = QCoreApplication::instance();
309 if (app && QThread::currentThread() == app->thread()) {
310 mainContext = g_main_context_default();
311 g_main_context_ref(mainContext);
312 } else {
313 mainContext = g_main_context_new();
314 }
315 }
316
317 #if GLIB_CHECK_VERSION (2, 22, 0)
318 g_main_context_push_thread_default (mainContext);
319 #endif
320
321 // setup post event source
322 postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
323 sizeof(GPostEventSource)));
324 postEventSource->serialNumber = 1;
325 postEventSource->d = this;
326 g_source_set_can_recurse(&postEventSource->source, true);
327 g_source_attach(&postEventSource->source, mainContext);
328
329 // setup socketNotifierSource
330 socketNotifierSource =
331 reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,
332 sizeof(GSocketNotifierSource)));
333 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
334 g_source_set_can_recurse(&socketNotifierSource->source, true);
335 g_source_attach(&socketNotifierSource->source, mainContext);
336
337 // setup normal and idle timer sources
338 timerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,
339 sizeof(GTimerSource)));
340 (void) new (&timerSource->timerList) QTimerInfoList();
341 timerSource->processEventsFlags = QEventLoop::AllEvents;
342 timerSource->runWithIdlePriority = false;
343 g_source_set_can_recurse(&timerSource->source, true);
344 g_source_attach(&timerSource->source, mainContext);
345
346 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,
347 sizeof(GIdleTimerSource)));
348 idleTimerSource->timerSource = timerSource;
349 g_source_set_can_recurse(&idleTimerSource->source, true);
350 g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
351 g_source_attach(&idleTimerSource->source, mainContext);
352 }
353
runTimersOnceWithNormalPriority()354 void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
355 {
356 timerSource->runWithIdlePriority = false;
357 }
358
QEventDispatcherGlib(QObject * parent)359 QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
360 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
361 {
362 }
363
QEventDispatcherGlib(GMainContext * mainContext,QObject * parent)364 QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
365 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
366 { }
367
~QEventDispatcherGlib()368 QEventDispatcherGlib::~QEventDispatcherGlib()
369 {
370 Q_D(QEventDispatcherGlib);
371
372 // destroy all timer sources
373 qDeleteAll(d->timerSource->timerList);
374 d->timerSource->timerList.~QTimerInfoList();
375 g_source_destroy(&d->timerSource->source);
376 g_source_unref(&d->timerSource->source);
377 d->timerSource = 0;
378 g_source_destroy(&d->idleTimerSource->source);
379 g_source_unref(&d->idleTimerSource->source);
380 d->idleTimerSource = 0;
381
382 // destroy socket notifier source
383 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
384 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
385 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
386 delete p;
387 }
388 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
389 g_source_destroy(&d->socketNotifierSource->source);
390 g_source_unref(&d->socketNotifierSource->source);
391 d->socketNotifierSource = 0;
392
393 // destroy post event source
394 g_source_destroy(&d->postEventSource->source);
395 g_source_unref(&d->postEventSource->source);
396 d->postEventSource = 0;
397
398 Q_ASSERT(d->mainContext != 0);
399 #if GLIB_CHECK_VERSION (2, 22, 0)
400 g_main_context_pop_thread_default (d->mainContext);
401 #endif
402 g_main_context_unref(d->mainContext);
403 d->mainContext = 0;
404 }
405
processEvents(QEventLoop::ProcessEventsFlags flags)406 bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
407 {
408 Q_D(QEventDispatcherGlib);
409
410 const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
411 if (canWait)
412 emit aboutToBlock();
413 else
414 emit awake();
415
416 // tell postEventSourcePrepare() and timerSource about any new flags
417 QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
418 d->timerSource->processEventsFlags = flags;
419
420 if (!(flags & QEventLoop::EventLoopExec)) {
421 // force timers to be sent at normal priority
422 d->timerSource->runWithIdlePriority = false;
423 }
424
425 bool result = g_main_context_iteration(d->mainContext, canWait);
426 while (!result && canWait)
427 result = g_main_context_iteration(d->mainContext, canWait);
428
429 d->timerSource->processEventsFlags = savedFlags;
430
431 if (canWait)
432 emit awake();
433
434 return result;
435 }
436
hasPendingEvents()437 bool QEventDispatcherGlib::hasPendingEvents()
438 {
439 Q_D(QEventDispatcherGlib);
440 return g_main_context_pending(d->mainContext);
441 }
442
registerSocketNotifier(QSocketNotifier * notifier)443 void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
444 {
445 Q_ASSERT(notifier);
446 int sockfd = notifier->socket();
447 int type = notifier->type();
448 #ifndef QT_NO_DEBUG
449 if (sockfd < 0) {
450 qWarning("QSocketNotifier: Internal error");
451 return;
452 } else if (notifier->thread() != thread()
453 || thread() != QThread::currentThread()) {
454 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
455 return;
456 }
457 #endif
458
459 Q_D(QEventDispatcherGlib);
460
461
462 GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
463 p->pollfd.fd = sockfd;
464 switch (type) {
465 case QSocketNotifier::Read:
466 p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
467 break;
468 case QSocketNotifier::Write:
469 p->pollfd.events = G_IO_OUT | G_IO_ERR;
470 break;
471 case QSocketNotifier::Exception:
472 p->pollfd.events = G_IO_PRI | G_IO_ERR;
473 break;
474 }
475 p->socketNotifier = notifier;
476
477 d->socketNotifierSource->pollfds.append(p);
478
479 g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
480 }
481
unregisterSocketNotifier(QSocketNotifier * notifier)482 void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
483 {
484 Q_ASSERT(notifier);
485 #ifndef QT_NO_DEBUG
486 int sockfd = notifier->socket();
487 if (sockfd < 0) {
488 qWarning("QSocketNotifier: Internal error");
489 return;
490 } else if (notifier->thread() != thread()
491 || thread() != QThread::currentThread()) {
492 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
493 return;
494 }
495 #endif
496
497 Q_D(QEventDispatcherGlib);
498
499 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
500 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
501 if (p->socketNotifier == notifier) {
502 // found it
503 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
504
505 d->socketNotifierSource->pollfds.removeAt(i);
506 delete p;
507
508 return;
509 }
510 }
511 }
512
registerTimer(int timerId,int interval,QObject * object)513 void QEventDispatcherGlib::registerTimer(int timerId, int interval, QObject *object)
514 {
515 #ifndef QT_NO_DEBUG
516 if (timerId < 1 || interval < 0 || !object) {
517 qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
518 return;
519 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
520 qWarning("QObject::startTimer: timers cannot be started from another thread");
521 return;
522 }
523 #endif
524
525 Q_D(QEventDispatcherGlib);
526 d->timerSource->timerList.registerTimer(timerId, interval, object);
527 }
528
unregisterTimer(int timerId)529 bool QEventDispatcherGlib::unregisterTimer(int timerId)
530 {
531 #ifndef QT_NO_DEBUG
532 if (timerId < 1) {
533 qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
534 return false;
535 } else if (thread() != QThread::currentThread()) {
536 qWarning("QObject::killTimer: timers cannot be stopped from another thread");
537 return false;
538 }
539 #endif
540
541 Q_D(QEventDispatcherGlib);
542 return d->timerSource->timerList.unregisterTimer(timerId);
543 }
544
unregisterTimers(QObject * object)545 bool QEventDispatcherGlib::unregisterTimers(QObject *object)
546 {
547 #ifndef QT_NO_DEBUG
548 if (!object) {
549 qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
550 return false;
551 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
552 qWarning("QObject::killTimers: timers cannot be stopped from another thread");
553 return false;
554 }
555 #endif
556
557 Q_D(QEventDispatcherGlib);
558 return d->timerSource->timerList.unregisterTimers(object);
559 }
560
registeredTimers(QObject * object) const561 QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
562 {
563 if (!object) {
564 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
565 return QList<TimerInfo>();
566 }
567
568 Q_D(const QEventDispatcherGlib);
569 return d->timerSource->timerList.registeredTimers(object);
570 }
571
interrupt()572 void QEventDispatcherGlib::interrupt()
573 {
574 wakeUp();
575 }
576
wakeUp()577 void QEventDispatcherGlib::wakeUp()
578 {
579 Q_D(QEventDispatcherGlib);
580 d->postEventSource->serialNumber.ref();
581 g_main_context_wakeup(d->mainContext);
582 }
583
flush()584 void QEventDispatcherGlib::flush()
585 {
586 }
587
versionSupported()588 bool QEventDispatcherGlib::versionSupported()
589 {
590 #if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
591 return false;
592 #else
593 return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
594 #endif
595 }
596
QEventDispatcherGlib(QEventDispatcherGlibPrivate & dd,QObject * parent)597 QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
598 : QAbstractEventDispatcher(dd, parent)
599 {
600 }
601
602 QT_END_NAMESPACE
603