Merge remote-tracking branch 'upstream/tags/v4.8.0-rc1' into experimental
[qt:android-lighthouse.git] / src / gui / kernel / qeventdispatcher_qpa.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 QtGui 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 "qplatformdefs.h"
43 #include "qapplication.h"
44 #include "qeventdispatcher_qpa_p.h"
45 #include "private/qeventdispatcher_unix_p.h"
46 #include "qapplication_p.h"
47 #include "qplatformeventloopintegration_qpa.h"
48
49 #include <QWindowSystemInterface>
50 #include <QtCore/QElapsedTimer>
51 #include <QtCore/QAtomicInt>
52 #include <QtCore/QSemaphore>
53
54 #include <QtCore/QDebug>
55
56 #include <errno.h>
57
58 QT_BEGIN_NAMESPACE
59
60 QT_USE_NAMESPACE
61
62 class Rendezvous
63 {
64 public:
65     void checkpoint()
66     {
67         if (state.testAndSetOrdered(0,1)) {
68             semaphore.acquire();
69         } else if (state.testAndSetAcquire(1,0)) {
70             semaphore.release();
71         } else {
72             qWarning("Barrier internal error");
73         }
74     }
75 private:
76     QSemaphore semaphore;
77     QAtomicInt state;
78 };
79
80 class SelectWorker : public QThread
81 {
82 public:
83     SelectWorker(QEventDispatcherQPAPrivate *eventDispatcherPrivate)
84         : QThread(),
85           m_edPrivate(eventDispatcherPrivate),
86           m_retVal(0)
87     {
88     }
89
90     void setSelectValues(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
91     {
92         m_nfds = nfds;
93         m_readfds = readfds;
94         m_writefds = writefds;
95         m_exceptfds = exceptfds;
96
97
98     }
99
100     int retVal() const {
101         return m_retVal;
102     }
103
104 protected:
105     void run();
106
107 private:
108     QEventDispatcherQPAPrivate *m_edPrivate;
109     int m_retVal;
110
111     int m_nfds;
112     fd_set *m_readfds, *m_writefds, *m_exceptfds;
113 };
114
115 class QEventDispatcherQPAPrivate : public QEventDispatcherUNIXPrivate
116 {
117     Q_DECLARE_PUBLIC(QEventDispatcherQPA)
118 public:
119     QEventDispatcherQPAPrivate()
120         :  eventLoopIntegration(0),
121            barrierBeforeBlocking(0),
122            barrierReturnValue(0),
123            selectReturnMutex(0),
124            selectWorkerNeedsSync(true),
125            selectWorkerHasResult(false),
126            selectWorker(0),
127            m_integrationInitialised(false),
128            m_hasIntegration(false),
129            m_isEventLoopIntegrationRunning(false)
130     {
131
132     }
133
134     ~QEventDispatcherQPAPrivate()
135     {
136         delete selectWorker;
137         delete eventLoopIntegration;
138         delete barrierBeforeBlocking;
139         delete barrierReturnValue;
140         delete selectReturnMutex;
141     }
142
143     bool hasIntegration() const
144     {
145         if (!m_integrationInitialised) {
146             QEventDispatcherQPAPrivate *that = const_cast<QEventDispatcherQPAPrivate *>(this);
147             if (qApp && (qApp->thread() == QThread::currentThread())) { // guiThread
148                 if (QApplicationPrivate::platformIntegration()) {
149                     that->eventLoopIntegration = QApplicationPrivate::platformIntegration()->createEventLoopIntegration();
150                     if (that->eventLoopIntegration) {
151                         that->selectWorker = new SelectWorker(that);
152                         that->barrierBeforeBlocking = new Rendezvous;
153                         that->barrierReturnValue = new Rendezvous;
154                         that->selectReturnMutex = new QMutex;
155                         that->selectWorker->start();
156                         that->m_hasIntegration = true;
157                         if (!QElapsedTimer::isMonotonic())
158                             qWarning("Having eventloop integration without monotonic timers can lead to undefined behaviour");
159                     }
160                 }
161             }
162             that->m_integrationInitialised = true;
163         }
164         return m_hasIntegration;
165     }
166
167     bool isEventLoopIntegrationRunning() const
168     {
169         return m_isEventLoopIntegrationRunning;
170     }
171
172     void runEventLoopIntegration()
173     {
174         if (qApp && (qApp->thread() == QThread::currentThread())) {
175             m_isEventLoopIntegrationRunning = true;
176             eventLoopIntegration->startEventLoop();
177         }
178     }
179
180     QPlatformEventLoopIntegration *eventLoopIntegration;
181     Rendezvous *barrierBeforeBlocking;
182     Rendezvous *barrierReturnValue;
183
184     QMutex *selectReturnMutex;
185     bool selectWorkerNeedsSync;
186     bool selectWorkerHasResult;
187
188     SelectWorker *selectWorker;
189 private:
190     bool m_integrationInitialised;
191     bool m_hasIntegration;
192     bool m_isEventLoopIntegrationRunning;
193 };
194
195 QEventDispatcherQPA::QEventDispatcherQPA(QObject *parent)
196     : QEventDispatcherUNIX(*new QEventDispatcherQPAPrivate, parent)
197 { }
198
199 QEventDispatcherQPA::~QEventDispatcherQPA()
200 { }
201
202 bool QEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags)
203 {
204     Q_D(QEventDispatcherQPA);
205
206     if (d->hasIntegration()) {
207         if (!d->isEventLoopIntegrationRunning()) {
208             d->runEventLoopIntegration();
209         }
210         if (d->threadData->quitNow) {
211             d->eventLoopIntegration->quitEventLoop();
212             return false;
213         }
214     }
215     int nevents = 0;
216
217     // handle gui and posted events
218     d->interrupt = false;
219     if (flags & QEvent::DeferredDelete)
220         QApplication::sendPostedEvents(0, QEvent::DeferredDelete);
221     else
222         QApplication::sendPostedEvents();
223
224     while (!d->interrupt) {        // also flushes output buffer ###can be optimized
225         QWindowSystemInterfacePrivate::WindowSystemEvent *event;
226         if (!(flags & QEventLoop::ExcludeUserInputEvents)
227             && QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0) {
228             // process a pending user input event
229             event = QWindowSystemInterfacePrivate::getWindowSystemEvent();
230             if (!event)
231                 break;
232         } else {
233             break;
234         }
235
236         if (filterEvent(event)) {
237             delete event;
238             continue;
239         }
240         nevents++;
241
242         QApplicationPrivate::processWindowSystemEvent(event);
243         delete event;
244     }
245
246     if (!d->interrupt) {
247         if (QEventDispatcherUNIX::processEvents(flags)) {
248             QEventDispatcherUNIX::processEvents(flags);
249             return true;
250         }
251     }
252     return (nevents > 0);
253 }
254
255 bool QEventDispatcherQPA::hasPendingEvents()
256 {
257     extern uint qGlobalPostedEventsCount(); // from qapplication.cpp
258     return qGlobalPostedEventsCount() || QWindowSystemInterfacePrivate::windowSystemEventsQueued();
259 }
260
261 void QEventDispatcherQPA::registerSocketNotifier(QSocketNotifier *notifier)
262 {
263     Q_D(QEventDispatcherQPA);
264     QEventDispatcherUNIX::registerSocketNotifier(notifier);
265     if (d->hasIntegration())
266         wakeUp();
267
268 }
269
270 void QEventDispatcherQPA::unregisterSocketNotifier(QSocketNotifier *notifier)
271 {
272     Q_D(QEventDispatcherQPA);
273     QEventDispatcherUNIX::unregisterSocketNotifier(notifier);
274     if (d->hasIntegration())
275         wakeUp();
276 }
277
278 void QEventDispatcherQPA::flush()
279 {
280     if(qApp)
281         qApp->sendPostedEvents();
282 }
283
284 int QEventDispatcherQPA::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
285                                 timeval *timeout)
286 {
287     Q_D(QEventDispatcherQPA);
288     int retVal = 0;
289     if (d->hasIntegration()) {
290         qint64 timeoutmsec = 1000; // wait a second if we don't have timers
291         if (timeout)
292             timeoutmsec = timeout->tv_sec * 1000 + (timeout->tv_usec/1000);
293         d->selectReturnMutex->lock();
294         if (d->selectWorkerNeedsSync) {
295             if (d->selectWorkerHasResult) {
296                 retVal = d->selectWorker->retVal();
297                 d->selectWorkerHasResult = false;
298
299                 d->selectReturnMutex->unlock();
300                 d->barrierReturnValue->checkpoint();
301                 d->eventLoopIntegration->setNextTimerEvent(0);
302                 return retVal;
303             } else {
304                 d->selectWorkerNeedsSync = false;
305                 d->selectWorker->setSelectValues(nfds,readfds, writefds, exceptfds);
306                 d->barrierBeforeBlocking->checkpoint();
307             }
308         }
309         d->selectReturnMutex->unlock();
310         d->eventLoopIntegration->setNextTimerEvent(timeoutmsec);
311         retVal = 0; //is 0 if select has not returned
312     } else {
313         retVal = QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout);
314     }
315     return retVal;
316 }
317
318
319 void SelectWorker::run()
320 {
321
322     while(true) {
323         m_retVal = 0;
324         m_edPrivate->barrierBeforeBlocking->checkpoint(); // wait for mainthread
325         int tmpRet = qt_safe_select(m_nfds,m_readfds,m_writefds,m_exceptfds,0);
326         m_edPrivate->selectReturnMutex->lock();
327         m_edPrivate->eventLoopIntegration->qtNeedsToProcessEvents();
328
329         m_edPrivate->selectWorkerNeedsSync = true;
330         m_edPrivate->selectWorkerHasResult = true;
331         m_retVal = tmpRet;
332
333         m_edPrivate->selectReturnMutex->unlock();
334         m_edPrivate->barrierReturnValue->checkpoint();
335     }
336 }
337 QT_END_NAMESPACE