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