1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtCore module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
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
71
static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
72
{
73
    if (timeout)
74
        *timeout = -1;
75
    return false;
76
}
77
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
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
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
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
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
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
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
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
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
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
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
    *timeout = data->canWait ? -1 : 0;
264
265
    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
266
    return (!data->canWait
267
            || (source->serialNumber != source->lastSerialNumber));
268
}
269
270
static gboolean postEventSourceCheck(GSource *source)
271
{
272
    return postEventSourcePrepare(source, 0);
273
}
274
275
static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
276
{
277
    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
278
    source->lastSerialNumber = source->serialNumber;
279
    QCoreApplication::sendPostedEvents();
280
    source->d->runTimersOnceWithNormalPriority();
281
    return true; // i dunno, george...
282
}
283
284
static GSourceFuncs postEventSourceFuncs = {
285
    postEventSourcePrepare,
286
    postEventSourceCheck,
287
    postEventSourceDispatch,
288
    NULL,
289
    NULL,
290
    NULL
291
};
292
293
294
QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
295
    : mainContext(context)
296
{
297
    if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) {
298
        static int dummyValue = 0; // only used for its address
299
        QMutexLocker locker(QMutexPool::instance()->get(&dummyValue));
300
        if (!g_thread_supported())
301
            g_thread_init(NULL);
302
    }
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 = 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_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE);
350
    g_source_attach(&idleTimerSource->source, mainContext);
351
}
352
353
void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
354
{
355
    timerSource->runWithIdlePriority = false;
356
}
357
358
QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
359
    : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
360
{
361
}
362
363
QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
364
    : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
365
{ }
366
367
QEventDispatcherGlib::~QEventDispatcherGlib()
368
{
369
    Q_D(QEventDispatcherGlib);
370
371
    // destroy all timer sources
372
    qDeleteAll(d->timerSource->timerList);
373
    d->timerSource->timerList.~QTimerInfoList();
374
    g_source_destroy(&d->timerSource->source);
375
    g_source_unref(&d->timerSource->source);
376
    d->timerSource = 0;
377
    g_source_destroy(&d->idleTimerSource->source);
378
    g_source_unref(&d->idleTimerSource->source);
379
    d->idleTimerSource = 0;
380
381
    // destroy socket notifier source
382
    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
383
        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
384
        g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
385
        delete p;
386
    }
387
    d->socketNotifierSource->pollfds.~QList<GPollFDWithQSocketNotifier *>();
388
    g_source_destroy(&d->socketNotifierSource->source);
389
    g_source_unref(&d->socketNotifierSource->source);
390
    d->socketNotifierSource = 0;
391
392
    // destroy post event source
393
    g_source_destroy(&d->postEventSource->source);
394
    g_source_unref(&d->postEventSource->source);
395
    d->postEventSource = 0;
396
397
    Q_ASSERT(d->mainContext != 0);
398
#if GLIB_CHECK_VERSION (2, 22, 0)
399
    g_main_context_pop_thread_default (d->mainContext);
400
#endif
401
    g_main_context_unref(d->mainContext);
402
    d->mainContext = 0;
403
}
404
405
bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
406
{
407
    Q_D(QEventDispatcherGlib);
408
409
    const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
410
    if (canWait)
411
        emit aboutToBlock();
412
    else
413
        emit awake();
414
415
    // tell postEventSourcePrepare() and timerSource about any new flags
416
    QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
417
    d->timerSource->processEventsFlags = flags;
418
419
    if (!(flags & QEventLoop::EventLoopExec)) {
420
        // force timers to be sent at normal priority
421
        d->timerSource->runWithIdlePriority = false;
422
    }
423
424
    bool result = g_main_context_iteration(d->mainContext, canWait);
425
    while (!result && canWait)
426
        result = g_main_context_iteration(d->mainContext, canWait);
427
428
    d->timerSource->processEventsFlags = savedFlags;
429
430
    if (canWait)
431
        emit awake();
432
433
    return result;
434
}
435
436
bool QEventDispatcherGlib::hasPendingEvents()
437
{
438
    Q_D(QEventDispatcherGlib);
439
    return g_main_context_pending(d->mainContext);
440
}
441
442
void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
443
{
444
    Q_ASSERT(notifier);
445
    int sockfd = notifier->socket();
446
    int type = notifier->type();
447
#ifndef QT_NO_DEBUG
448
    if (sockfd < 0) {
449
        qWarning("QSocketNotifier: Internal error");
450
        return;
451
    } else if (notifier->thread() != thread()
452
               || thread() != QThread::currentThread()) {
453
        qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
454
        return;
455
    }
456
#endif
457
458
    Q_D(QEventDispatcherGlib);
459
460
461
    GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier;
462
    p->pollfd.fd = sockfd;
463
    switch (type) {
464
    case QSocketNotifier::Read:
465
        p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
466
        break;
467
    case QSocketNotifier::Write:
468
        p->pollfd.events = G_IO_OUT | G_IO_ERR;
469
        break;
470
    case QSocketNotifier::Exception:
471
        p->pollfd.events = G_IO_PRI | G_IO_ERR;
472
        break;
473
    }
474
    p->socketNotifier = notifier;
475
476
    d->socketNotifierSource->pollfds.append(p);
477
478
    g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd);
479
}
480
481
void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
482
{
483
    Q_ASSERT(notifier);
484
#ifndef QT_NO_DEBUG
485
    int sockfd = notifier->socket();
486
    if (sockfd < 0) {
487
        qWarning("QSocketNotifier: Internal error");
488
        return;
489
    } else if (notifier->thread() != thread()
490
               || thread() != QThread::currentThread()) {
491
        qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
492
        return;
493
    }
494
#endif
495
496
    Q_D(QEventDispatcherGlib);
497
498
    for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
499
        GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
500
        if (p->socketNotifier == notifier) {
501
            // found it
502
            g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
503
504
            d->socketNotifierSource->pollfds.removeAt(i);
505
            delete p;
506
507
            return;
508
        }
509
    }
510
}
511
512
void QEventDispatcherGlib::registerTimer(int timerId, int interval, QObject *object)
513
{
514
#ifndef QT_NO_DEBUG
515
    if (timerId < 1 || interval < 0 || !object) {
516
        qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
517
        return;
518
    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
519
        qWarning("QObject::startTimer: timers cannot be started from another thread");
520
        return;
521
    }
522
#endif
523
524
    Q_D(QEventDispatcherGlib);
525
    d->timerSource->timerList.registerTimer(timerId, interval, object);
526
}
527
528
bool QEventDispatcherGlib::unregisterTimer(int timerId)
529
{
530
#ifndef QT_NO_DEBUG
531
    if (timerId < 1) {
532
        qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
533
        return false;
534
    } else if (thread() != QThread::currentThread()) {
535
        qWarning("QObject::killTimer: timers cannot be stopped from another thread");
536
        return false;
537
    }
538
#endif
539
540
    Q_D(QEventDispatcherGlib);
541
    return d->timerSource->timerList.unregisterTimer(timerId);
542
}
543
544
bool QEventDispatcherGlib::unregisterTimers(QObject *object)
545
{
546
#ifndef QT_NO_DEBUG
547
    if (!object) {
548
        qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument");
549
        return false;
550
    } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
551
        qWarning("QObject::killTimers: timers cannot be stopped from another thread");
552
        return false;
553
    }
554
#endif
555
556
    Q_D(QEventDispatcherGlib);
557
    return d->timerSource->timerList.unregisterTimers(object);
558
}
559
560
QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
561
{
562
    if (!object) {
563
        qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
564
        return QList<TimerInfo>();
565
    }
566
567
    Q_D(const QEventDispatcherGlib);
568
    return d->timerSource->timerList.registeredTimers(object);
569
}
570
571
void QEventDispatcherGlib::interrupt()
572
{
573
    wakeUp();
574
}
575
576
void QEventDispatcherGlib::wakeUp()
577
{
578
    Q_D(QEventDispatcherGlib);
579
    d->postEventSource->serialNumber.ref();
580
    g_main_context_wakeup(d->mainContext);
581
}
582
583
void QEventDispatcherGlib::flush()
584
{
585
}
586
587
bool QEventDispatcherGlib::versionSupported()
588
{
589
#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION)
590
    return false;
591
#else
592
    return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301;
593
#endif
594
}
595
596
QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
597
    : QAbstractEventDispatcher(dd, parent)
598
{
599
}
600
601
QT_END_NAMESPACE