/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qplatformdefs.h" #include "qapplication.h" #include "qeventdispatcher_qpa_p.h" #include "private/qeventdispatcher_unix_p.h" #include "qapplication_p.h" #include "qplatformeventloopintegration_qpa.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE QT_USE_NAMESPACE class Rendezvous { public: void checkpoint() { if (state.testAndSetOrdered(0,1)) { semaphore.acquire(); } else if (state.testAndSetAcquire(1,0)) { semaphore.release(); } else { qWarning("Barrier internal error"); } } private: QSemaphore semaphore; QAtomicInt state; }; class SelectWorker : public QThread { public: SelectWorker(QEventDispatcherQPAPrivate *eventDispatcherPrivate) : QThread(), m_edPrivate(eventDispatcherPrivate), m_retVal(0) { } void setSelectValues(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) { m_nfds = nfds; m_readfds = readfds; m_writefds = writefds; m_exceptfds = exceptfds; } int retVal() const { return m_retVal; } protected: void run(); private: QEventDispatcherQPAPrivate *m_edPrivate; int m_retVal; int m_nfds; fd_set *m_readfds, *m_writefds, *m_exceptfds; }; class QEventDispatcherQPAPrivate : public QEventDispatcherUNIXPrivate { Q_DECLARE_PUBLIC(QEventDispatcherQPA) public: QEventDispatcherQPAPrivate() : eventLoopIntegration(0), barrierBeforeBlocking(0), barrierReturnValue(0), selectReturnMutex(0), selectWorkerNeedsSync(true), selectWorkerHasResult(false), selectWorker(0), m_integrationInitialised(false), m_hasIntegration(false), m_isEventLoopIntegrationRunning(false) { } ~QEventDispatcherQPAPrivate() { delete selectWorker; delete eventLoopIntegration; delete barrierBeforeBlocking; delete barrierReturnValue; delete selectReturnMutex; } bool hasIntegration() const { if (!m_integrationInitialised) { QEventDispatcherQPAPrivate *that = const_cast(this); if (qApp && (qApp->thread() == QThread::currentThread())) { // guiThread if (QApplicationPrivate::platformIntegration()) { that->eventLoopIntegration = QApplicationPrivate::platformIntegration()->createEventLoopIntegration(); if (that->eventLoopIntegration) { that->selectWorker = new SelectWorker(that); that->barrierBeforeBlocking = new Rendezvous; that->barrierReturnValue = new Rendezvous; that->selectReturnMutex = new QMutex; that->selectWorker->start(); that->m_hasIntegration = true; if (!QElapsedTimer::isMonotonic()) qWarning("Having eventloop integration without monotonic timers can lead to undefined behaviour"); } } } that->m_integrationInitialised = true; } return m_hasIntegration; } bool isEventLoopIntegrationRunning() const { return m_isEventLoopIntegrationRunning; } void runEventLoopIntegration() { if (qApp && (qApp->thread() == QThread::currentThread())) { m_isEventLoopIntegrationRunning = true; eventLoopIntegration->startEventLoop(); } } QPlatformEventLoopIntegration *eventLoopIntegration; Rendezvous *barrierBeforeBlocking; Rendezvous *barrierReturnValue; QMutex *selectReturnMutex; bool selectWorkerNeedsSync; bool selectWorkerHasResult; SelectWorker *selectWorker; private: bool m_integrationInitialised; bool m_hasIntegration; bool m_isEventLoopIntegrationRunning; }; QEventDispatcherQPA::QEventDispatcherQPA(QObject *parent) : QEventDispatcherUNIX(*new QEventDispatcherQPAPrivate, parent) { } QEventDispatcherQPA::~QEventDispatcherQPA() { } bool QEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherQPA); if (d->hasIntegration()) { if (!d->isEventLoopIntegrationRunning()) { d->runEventLoopIntegration(); } if (d->threadData->quitNow) { d->eventLoopIntegration->quitEventLoop(); return false; } } int nevents = 0; // handle gui and posted events d->interrupt = false; QApplication::sendPostedEvents(); while (!d->interrupt) { // also flushes output buffer ###can be optimized QWindowSystemInterfacePrivate::WindowSystemEvent *event; if (!(flags & QEventLoop::ExcludeUserInputEvents) && QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0) { // process a pending user input event event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); if (!event) break; } else { break; } if (filterEvent(event)) { delete event; continue; } nevents++; QApplicationPrivate::processWindowSystemEvent(event); delete event; } if (!d->interrupt) { if (QEventDispatcherUNIX::processEvents(flags)) return true; } return (nevents > 0); } bool QEventDispatcherQPA::hasPendingEvents() { extern uint qGlobalPostedEventsCount(); // from qapplication.cpp return qGlobalPostedEventsCount() || QWindowSystemInterfacePrivate::windowSystemEventsQueued(); } void QEventDispatcherQPA::registerSocketNotifier(QSocketNotifier *notifier) { Q_D(QEventDispatcherQPA); QEventDispatcherUNIX::registerSocketNotifier(notifier); if (d->hasIntegration()) wakeUp(); } void QEventDispatcherQPA::unregisterSocketNotifier(QSocketNotifier *notifier) { Q_D(QEventDispatcherQPA); QEventDispatcherUNIX::unregisterSocketNotifier(notifier); if (d->hasIntegration()) wakeUp(); } void QEventDispatcherQPA::flush() { if(qApp) qApp->sendPostedEvents(); } int QEventDispatcherQPA::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timeval *timeout) { Q_D(QEventDispatcherQPA); int retVal = 0; if (d->hasIntegration()) { qint64 timeoutmsec = LONG_MAX; // wait if we don't have timers if (timeout) timeoutmsec = timeout->tv_sec * 1000 + (timeout->tv_usec/1000); d->selectReturnMutex->lock(); if (d->selectWorkerNeedsSync) { if (d->selectWorkerHasResult) { retVal = d->selectWorker->retVal(); d->selectWorkerHasResult = false; d->selectReturnMutex->unlock(); d->barrierReturnValue->checkpoint(); d->eventLoopIntegration->setNextTimerEvent(0); return retVal; } else { d->selectWorkerNeedsSync = false; d->selectWorker->setSelectValues(nfds,readfds, writefds, exceptfds); d->barrierBeforeBlocking->checkpoint(); } } d->selectReturnMutex->unlock(); d->eventLoopIntegration->setNextTimerEvent(timeoutmsec); retVal = 0; //is 0 if select has not returned } else { retVal = QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout); } return retVal; } void SelectWorker::run() { while(true) { m_retVal = 0; m_edPrivate->barrierBeforeBlocking->checkpoint(); // wait for mainthread int tmpRet = qt_safe_select(m_nfds,m_readfds,m_writefds,m_exceptfds,0); m_edPrivate->selectReturnMutex->lock(); m_edPrivate->eventLoopIntegration->qtNeedsToProcessEvents(); m_edPrivate->selectWorkerNeedsSync = true; m_edPrivate->selectWorkerHasResult = true; m_retVal = tmpRet; m_edPrivate->selectReturnMutex->unlock(); m_edPrivate->barrierReturnValue->checkpoint(); } } QT_END_NAMESPACE