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