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
53QT_BEGIN_NAMESPACE
54
55struct GPollFDWithQSocketNotifier
56{
57 GPollFD pollfd;
58 QSocketNotifier *socketNotifier;
59};
60
61struct GSocketNotifierSource
62{
63 GSource source;
64 QList<GPollFDWithQSocketNotifier *> pollfds;
65};
66
67static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
68{
69 if (timeout)
70 *timeout = -1;
71 return false;
72}
73
74static 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
98static 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
113static GSourceFuncs socketNotifierSourceFuncs = {
114 socketNotifierSourcePrepare,
115 socketNotifierSourceCheck,
116 socketNotifierSourceDispatch,
117 nullptr,
118 nullptr,
119 nullptr
120};
121
122struct GTimerSource
123{
124 GSource source;
125 QTimerInfoList timerList;
126 QEventLoop::ProcessEventsFlags processEventsFlags;
127 bool runWithIdlePriority;
128};
129
130static 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
141static 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
153static 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
169static 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
177static 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
187static GSourceFuncs timerSourceFuncs = {
188 timerSourcePrepare,
189 timerSourceCheck,
190 timerSourceDispatch,
191 nullptr,
192 nullptr,
193 nullptr
194};
195
196struct GIdleTimerSource
197{
198 GSource source;
199 GTimerSource *timerSource;
200};
201
202static 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
216static 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
227static 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
234static GSourceFuncs idleTimerSourceFuncs = {
235 idleTimerSourcePrepare,
236 idleTimerSourceCheck,
237 idleTimerSourceDispatch,
238 nullptr,
239 nullptr,
240 nullptr
241};
242
243struct GPostEventSource
244{
245 GSource source;
246 QAtomicInt serialNumber;
247 int lastSerialNumber;
248 QEventDispatcherGlibPrivate *d;
249};
250
251static 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
268static gboolean postEventSourceCheck(GSource *source)
269{
270 return postEventSourcePrepare(source, nullptr);
271}
272
273static 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
282static GSourceFuncs postEventSourceFuncs = {
283 postEventSourcePrepare,
284 postEventSourceCheck,
285 postEventSourceDispatch,
286 nullptr,
287 nullptr,
288 nullptr
289};
290
291
292QEventDispatcherGlibPrivate::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 GSource *source = g_source_new(&postEventSourceFuncs, sizeof(GPostEventSource));
322 g_source_set_name(source, "[Qt] GPostEventSource");
323 postEventSource = reinterpret_cast<GPostEventSource *>(source);
324
325 postEventSource->serialNumber.storeRelaxed(1);
326 postEventSource->d = this;
327 g_source_set_can_recurse(&postEventSource->source, true);
328 g_source_attach(&postEventSource->source, mainContext);
329
330 // setup socketNotifierSource
331 source = g_source_new(&socketNotifierSourceFuncs, sizeof(GSocketNotifierSource));
332 g_source_set_name(source, "[Qt] GSocketNotifierSource");
333 socketNotifierSource = reinterpret_cast<GSocketNotifierSource *>(source);
334 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
335 g_source_set_can_recurse(&socketNotifierSource->source, true);
336 g_source_attach(&socketNotifierSource->source, mainContext);
337
338 // setup normal and idle timer sources
339 source = g_source_new(&timerSourceFuncs, sizeof(GTimerSource));
340 g_source_set_name(source, "[Qt] GTimerSource");
341 timerSource = reinterpret_cast<GTimerSource *>(source);
342 (void) new (&timerSource->timerList) QTimerInfoList();
343 timerSource->processEventsFlags = QEventLoop::AllEvents;
344 timerSource->runWithIdlePriority = false;
345 g_source_set_can_recurse(&timerSource->source, true);
346 g_source_attach(&timerSource->source, mainContext);
347
348 source = g_source_new(&idleTimerSourceFuncs, sizeof(GIdleTimerSource));
349 g_source_set_name(source, "[Qt] GIdleTimerSource");
350 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);
351 idleTimerSource->timerSource = timerSource;
352 g_source_set_can_recurse(&idleTimerSource->source, true);
353 g_source_attach(&idleTimerSource->source, mainContext);
354}
355
356void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
357{
358 timerSource->runWithIdlePriority = false;
359}
360
361QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
362 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
363{
364}
365
366QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
367 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
368{ }
369
370QEventDispatcherGlib::~QEventDispatcherGlib()
371{
372 Q_D(QEventDispatcherGlib);
373
374 // destroy all timer sources
375 qDeleteAll(d->timerSource->timerList);
376 d->timerSource->timerList.~QTimerInfoList();
377 g_source_destroy(&d->timerSource->source);
378 g_source_unref(&d->timerSource->source);
379 d->timerSource = nullptr;
380 g_source_destroy(&d->idleTimerSource->source);
381 g_source_unref(&d->idleTimerSource->source);
382 d->idleTimerSource = nullptr;
383
384 // destroy socket notifier source
385 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
386 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
387 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
388 delete p;
389 }
390 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
391 g_source_destroy(&d->socketNotifierSource->source);
392 g_source_unref(&d->socketNotifierSource->source);
393 d->socketNotifierSource = nullptr;
394
395 // destroy post event source
396 g_source_destroy(&d->postEventSource->source);
397 g_source_unref(&d->postEventSource->source);
398 d->postEventSource = nullptr;
399
400 Q_ASSERT(d->mainContext != nullptr);
401#if GLIB_CHECK_VERSION (2, 22, 0)
402 g_main_context_pop_thread_default (d->mainContext);
403#endif
404 g_main_context_unref(d->mainContext);
405 d->mainContext = nullptr;
406}
407
408bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
409{
410 Q_D(QEventDispatcherGlib);
411
412 const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
413 if (canWait)
414 emit aboutToBlock();
415 else
416 emit awake();
417
418 // tell postEventSourcePrepare() and timerSource about any new flags
419 QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
420 d->timerSource->processEventsFlags = flags;
421
422 if (!(flags & QEventLoop::EventLoopExec)) {
423 // force timers to be sent at normal priority
424 d->timerSource->runWithIdlePriority = false;
425 }
426
427 bool result = g_main_context_iteration(d->mainContext, canWait);
428 while (!result && canWait)
429 result = g_main_context_iteration(d->mainContext, canWait);
430
431 d->timerSource->processEventsFlags = savedFlags;
432
433 if (canWait)
434 emit awake();
435
436 return result;
437}
438
439void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
440{
441 Q_ASSERT(notifier);
442 int sockfd = notifier->socket();
443 int type = notifier->type();
444#ifndef QT_NO_DEBUG
445 if (sockfd < 0) {
446 qWarning("QSocketNotifier: Internal error");
447 return;
448 } else if (notifier->thread() != thread()
449 || thread() != QThread::currentThread()) {
450 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
451 return;
452 }
453#endif
454
455 Q_D(QEventDispatcherGlib);
456
457
458 GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
459 p->pollfd.fd = sockfd;
460 switch (type) {
461 case QSocketNotifier::Read:
462 p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
463 break;
464 case QSocketNotifier::Write:
465 p->pollfd.events = G_IO_OUT | G_IO_ERR;
466 break;
467 case QSocketNotifier::Exception:
468 p->pollfd.events = G_IO_PRI | G_IO_ERR;
469 break;
470 }
471 p->socketNotifier = notifier;
472
473 d->socketNotifierSource->pollfds.append(p);
474
475 g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
476}
477
478void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
479{
480 Q_ASSERT(notifier);
481#ifndef QT_NO_DEBUG
482 int sockfd = notifier->socket();
483 if (sockfd < 0) {
484 qWarning("QSocketNotifier: Internal error");
485 return;
486 } else if (notifier->thread() != thread()
487 || thread() != QThread::currentThread()) {
488 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
489 return;
490 }
491#endif
492
493 Q_D(QEventDispatcherGlib);
494
495 for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
496 GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
497 if (p->socketNotifier == notifier) {
498 // found it
499 g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
500
501 d->socketNotifierSource->pollfds.removeAt(i);
502 delete p;
503
504 return;
505 }
506 }
507}
508
509void QEventDispatcherGlib::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
510{
511#ifndef QT_NO_DEBUG
512 if (timerId < 1 || interval < 0 || !object) {
513 qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
514 return;
515 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
516 qWarning("QEventDispatcherGlib::registerTimer: timers cannot be started from another thread");
517 return;
518 }
519#endif
520
521 Q_D(QEventDispatcherGlib);
522 d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
523}
524
525bool QEventDispatcherGlib::unregisterTimer(int timerId)
526{
527#ifndef QT_NO_DEBUG
528 if (timerId < 1) {
529 qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
530 return false;
531 } else if (thread() != QThread::currentThread()) {
532 qWarning("QEventDispatcherGlib::unregisterTimer: timers cannot be stopped from another thread");
533 return false;
534 }
535#endif
536
537 Q_D(QEventDispatcherGlib);
538 return d->timerSource->timerList.unregisterTimer(timerId);
539}
540
541bool QEventDispatcherGlib::unregisterTimers(QObject *object)
542{
543#ifndef QT_NO_DEBUG
544 if (!object) {
545 qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
546 return false;
547 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
548 qWarning("QEventDispatcherGlib::unregisterTimers: timers cannot be stopped from another thread");
549 return false;
550 }
551#endif
552
553 Q_D(QEventDispatcherGlib);
554 return d->timerSource->timerList.unregisterTimers(object);
555}
556
557QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
558{
559 if (!object) {
560 qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
561 return QList<TimerInfo>();
562 }
563
564 Q_D(const QEventDispatcherGlib);
565 return d->timerSource->timerList.registeredTimers(object);
566}
567
568int QEventDispatcherGlib::remainingTime(int timerId)
569{
570#ifndef QT_NO_DEBUG
571 if (timerId < 1) {
572 qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
573 return -1;
574 }
575#endif
576
577 Q_D(QEventDispatcherGlib);
578 return d->timerSource->timerList.timerRemainingTime(timerId);
579}
580
581void QEventDispatcherGlib::interrupt()
582{
583 wakeUp();
584}
585
586void QEventDispatcherGlib::wakeUp()
587{
588 Q_D(QEventDispatcherGlib);
589 d->postEventSource->serialNumber.ref();
590 g_main_context_wakeup(d->mainContext);
591}
592
593bool QEventDispatcherGlib::versionSupported()
594{
595#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
596 return false;
597#else
598 return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
599#endif
600}
601
602QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
603 : QAbstractEventDispatcher(dd, parent)
604{
605}
606
607QT_END_NAMESPACE
608
609#include "moc_qeventdispatcher_glib_p.cpp"
610