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(msg: "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(receiver: p->socketNotifier, event: &event);
108 }
109
110 return true; // ??? don't remove, right?
111}
112
113static GSourceFuncs socketNotifierSourceFuncs = {
114 .prepare: socketNotifierSourcePrepare,
115 .check: socketNotifierSourceCheck,
116 .dispatch: socketNotifierSourceDispatch,
117 .finalize: nullptr,
118 .closure_callback: nullptr,
119 .closure_marshal: 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 = { .tv_sec: 0l, .tv_nsec: 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 .prepare: timerSourcePrepare,
189 .check: timerSourceCheck,
190 .dispatch: timerSourceDispatch,
191 .finalize: nullptr,
192 .closure_callback: nullptr,
193 .closure_marshal: 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(src: 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(src: timerSource);
225}
226
227static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
228{
229 GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;
230 (void) timerSourceDispatch(source: &timerSource->source, nullptr, nullptr);
231 return true;
232}
233
234static GSourceFuncs idleTimerSourceFuncs = {
235 .prepare: idleTimerSourcePrepare,
236 .check: idleTimerSourceCheck,
237 .dispatch: idleTimerSourceDispatch,
238 .finalize: nullptr,
239 .closure_callback: nullptr,
240 .closure_marshal: 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(s: source, timeout: 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 .prepare: postEventSourcePrepare,
284 .check: postEventSourceCheck,
285 .dispatch: postEventSourceDispatch,
286 .finalize: nullptr,
287 .closure_callback: nullptr,
288 .closure_marshal: 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(context: 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(context: 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 (context: mainContext);
318#endif
319
320 // setup post event source
321 postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(source_funcs: &postEventSourceFuncs,
322 struct_size: sizeof(GPostEventSource)));
323 postEventSource->serialNumber.storeRelaxed(newValue: 1);
324 postEventSource->d = this;
325 g_source_set_can_recurse(source: &postEventSource->source, can_recurse: true);
326 g_source_attach(source: &postEventSource->source, context: mainContext);
327
328 // setup socketNotifierSource
329 socketNotifierSource =
330 reinterpret_cast<GSocketNotifierSource *>(g_source_new(source_funcs: &socketNotifierSourceFuncs,
331 struct_size: sizeof(GSocketNotifierSource)));
332 (void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();
333 g_source_set_can_recurse(source: &socketNotifierSource->source, can_recurse: true);
334 g_source_attach(source: &socketNotifierSource->source, context: mainContext);
335
336 // setup normal and idle timer sources
337 timerSource = reinterpret_cast<GTimerSource *>(g_source_new(source_funcs: &timerSourceFuncs,
338 struct_size: sizeof(GTimerSource)));
339 (void) new (&timerSource->timerList) QTimerInfoList();
340 timerSource->processEventsFlags = QEventLoop::AllEvents;
341 timerSource->runWithIdlePriority = false;
342 g_source_set_can_recurse(source: &timerSource->source, can_recurse: true);
343 g_source_attach(source: &timerSource->source, context: mainContext);
344
345 idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(source_funcs: &idleTimerSourceFuncs,
346 struct_size: sizeof(GIdleTimerSource)));
347 idleTimerSource->timerSource = timerSource;
348 g_source_set_can_recurse(source: &idleTimerSource->source, can_recurse: true);
349 g_source_attach(source: &idleTimerSource->source, context: mainContext);
350}
351
352void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
353{
354 timerSource->runWithIdlePriority = false;
355}
356
357QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
358 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
359{
360}
361
362QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
363 : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
364{ }
365
366QEventDispatcherGlib::~QEventDispatcherGlib()
367{
368 Q_D(QEventDispatcherGlib);
369
370 // destroy all timer sources
371 qDeleteAll(c: d->timerSource->timerList);
372 d->timerSource->timerList.~QTimerInfoList();
373 g_source_destroy(source: &d->timerSource->source);
374 g_source_unref(source: &d->timerSource->source);
375 d->timerSource = nullptr;
376 g_source_destroy(source: &d->idleTimerSource->source);
377 g_source_unref(source: &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(source: &d->socketNotifierSource->source, fd: &p->pollfd);
384 delete p;
385 }
386 d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
387 g_source_destroy(source: &d->socketNotifierSource->source);
388 g_source_unref(source: &d->socketNotifierSource->source);
389 d->socketNotifierSource = nullptr;
390
391 // destroy post event source
392 g_source_destroy(source: &d->postEventSource->source);
393 g_source_unref(source: &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 (context: d->mainContext);
399#endif
400 g_main_context_unref(context: d->mainContext);
401 d->mainContext = nullptr;
402}
403
404bool 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(context: d->mainContext, may_block: canWait);
424 while (!result && canWait)
425 result = g_main_context_iteration(context: d->mainContext, may_block: canWait);
426
427 d->timerSource->processEventsFlags = savedFlags;
428
429 if (canWait)
430 emit awake();
431
432 return result;
433}
434
435bool QEventDispatcherGlib::hasPendingEvents()
436{
437 Q_D(QEventDispatcherGlib);
438 return g_main_context_pending(context: d->mainContext);
439}
440
441void 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(msg: "QSocketNotifier: Internal error");
449 return;
450 } else if (notifier->thread() != thread()
451 || thread() != QThread::currentThread()) {
452 qWarning(msg: "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(t: p);
476
477 g_source_add_poll(source: &d->socketNotifierSource->source, fd: &p->pollfd);
478}
479
480void 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(msg: "QSocketNotifier: Internal error");
487 return;
488 } else if (notifier->thread() != thread()
489 || thread() != QThread::currentThread()) {
490 qWarning(msg: "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(source: &d->socketNotifierSource->source, fd: &p->pollfd);
502
503 d->socketNotifierSource->pollfds.removeAt(i);
504 delete p;
505
506 return;
507 }
508 }
509}
510
511void 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(msg: "QEventDispatcherGlib::registerTimer: invalid arguments");
516 return;
517 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
518 qWarning(msg: "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
527bool QEventDispatcherGlib::unregisterTimer(int timerId)
528{
529#ifndef QT_NO_DEBUG
530 if (timerId < 1) {
531 qWarning(msg: "QEventDispatcherGlib::unregisterTimer: invalid argument");
532 return false;
533 } else if (thread() != QThread::currentThread()) {
534 qWarning(msg: "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
543bool QEventDispatcherGlib::unregisterTimers(QObject *object)
544{
545#ifndef QT_NO_DEBUG
546 if (!object) {
547 qWarning(msg: "QEventDispatcherGlib::unregisterTimers: invalid argument");
548 return false;
549 } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
550 qWarning(msg: "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
559QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
560{
561 if (!object) {
562 qWarning(msg: "QEventDispatcherUNIX:registeredTimers: invalid argument");
563 return QList<TimerInfo>();
564 }
565
566 Q_D(const QEventDispatcherGlib);
567 return d->timerSource->timerList.registeredTimers(object);
568}
569
570int QEventDispatcherGlib::remainingTime(int timerId)
571{
572#ifndef QT_NO_DEBUG
573 if (timerId < 1) {
574 qWarning(msg: "QEventDispatcherGlib::remainingTimeTime: invalid argument");
575 return -1;
576 }
577#endif
578
579 Q_D(QEventDispatcherGlib);
580 return d->timerSource->timerList.timerRemainingTime(timerId);
581}
582
583void QEventDispatcherGlib::interrupt()
584{
585 wakeUp();
586}
587
588void QEventDispatcherGlib::wakeUp()
589{
590 Q_D(QEventDispatcherGlib);
591 d->postEventSource->serialNumber.ref();
592 g_main_context_wakeup(context: d->mainContext);
593}
594
595void QEventDispatcherGlib::flush()
596{
597}
598
599bool 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
608QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
609 : QAbstractEventDispatcher(dd, parent)
610{
611}
612
613QT_END_NAMESPACE
614
615#include "moc_qeventdispatcher_glib_p.cpp"
616

source code of qtbase/src/corelib/kernel/qeventdispatcher_glib.cpp