From de67a2b84b0d8b8ef994e91c108af3273e2ac37f Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 26 Nov 2012 22:38:07 -0600 Subject: [PATCH] Add glib main loop support patches --- configure | 29 +- src/kernel/qeventloop.h | 20 + src/kernel/qeventloop_glib_p.h | 130 ++++++ src/kernel/qeventloop_p.h | 6 + src/kernel/qeventloop_unix_glib.cpp | 593 ++++++++++++++++++++++++++ src/kernel/qeventloop_x11_glib.cpp | 618 ++++++++++++++++++++++++++++ src/kernel/qt_kernel.pri | 22 +- src/qt.pro | 8 + 8 files changed, 1421 insertions(+), 5 deletions(-) create mode 100644 src/kernel/qeventloop_glib_p.h create mode 100644 src/kernel/qeventloop_unix_glib.cpp create mode 100644 src/kernel/qeventloop_x11_glib.cpp diff --git a/configure b/configure index 24a9489..076ec00 100755 --- a/configure +++ b/configure @@ -143,6 +143,8 @@ CFG_USER_BUILD_KEY= CFG_ACCESSIBILITY=auto CFG_DLOPEN_OPENGL=no CFG_ENDIAN=auto +CFG_GLIBMAINLOOP=no +CFG_LIBGLIBMAINLOOP= D_FLAGS= I_FLAGS= L_FLAGS= @@ -224,7 +226,7 @@ while [ "$#" -gt 0 ]; do VAL=no ;; #Qt style yes options - -incremental|-qvfb|-profile|-shared|-static|-sm|-thread|-xinerama|-xshape|-tablet|-pch|-stl|-ipv6|-freetype|-big-codecs|-xcursor|-xrandr|-xrender|-xft|-xkb|-nis|-cups|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-version-script|-dlopen-opengl) + -incremental|-qvfb|-profile|-shared|-static|-sm|-thread|-xinerama|-xshape|-tablet|-pch|-stl|-ipv6|-freetype|-big-codecs|-xcursor|-xrandr|-xrender|-xft|-xkb|-nis|-cups|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-version-script|-dlopen-opengl|-glibmainloop) VAR=`echo $1 | sed "s,^-\(.*\),\1,"` VAL=yes ;; @@ -729,6 +731,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + glibmainloop) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_GLIBMAINLOOP="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; nis) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_NIS="$VAL" @@ -2626,6 +2635,19 @@ if [ "$CFG_PNG" = "no" ]; then QMAKE_CONFIG="$QMAKE_CONFIG no-png" elif [ "$CFG_PNG" = "yes" ]; then QMAKE_CONFIG="$QMAKE_CONFIG png" +fi +if [ "$CFG_GLIBMAINLOOP" = "no" ]; then + QMAKE_CONFIG="$QMAKE_CONFIG no-glibmainloop" +elif [ "$CFG_GLIBMAINLOOP" = "yes" ]; then + QMAKE_CONFIG="$QMAKE_CONFIG glibmainloop" + + GLIB_CFLAGS="`pkg-config --cflags glib-2.0`" + QMAKE_VARS="$QMAKE_VARS \"QMAKE_CFLAGS_GLIB=$GLIB_CFLAGS\"" + + GLIB_LINKFLAG="`pkg-config --libs glib-2.0`" + QMAKE_VARS="$QMAKE_VARS \"QMAKE_LIBS_GLIB=$GLIB_LINKFLAG\"" + + fi if [ "$CFG_GIF" = "no" ]; then QMAKE_CONFIG="$QMAKE_CONFIG no-gif" @@ -3192,6 +3214,11 @@ if [ "$CFG_PNG" = "no" ]; then else echo "PNG support ......... $CFG_PNG ($CFG_LIBPNG)" fi +if [ "$CFG_GLIBMAINLOOP" = "no" ]; then + echo "Glib main loop support ......... $CFG_GLIBMAINLOOP" +else + echo "Glib main loop support ......... $CFG_GLIBMAINLOOP ($CFG_LIBGLIBMAINLOOP)" +fi echo "zlib support ........ $CFG_ZLIB" if [ "$PLATFORM_QWS" = "yes" ]; then echo "Embedded support .... $CFG_EMBEDDED" diff --git a/src/kernel/qeventloop.h b/src/kernel/qeventloop.h index 8d45c43..9a79f6c 100644 --- a/src/kernel/qeventloop.h +++ b/src/kernel/qeventloop.h @@ -44,6 +44,10 @@ #include "qsocketnotifier.h" #endif // QT_H +#if defined(QT_USE_GLIBMAINLOOP) +#include +#endif // QT_USE_GLIBMAINLOOP + class QEventLoopPrivate; class QSocketNotifier; class QTimer; @@ -95,6 +99,22 @@ public: virtual void wakeUp(); +#if defined(QT_USE_GLIBMAINLOOP) + + // glib main loop support + + /* internal: used to fit glib-main-loop gsource concept */ + + bool gsourcePrepare(GSource *gs, int * timeout); + bool gsourceCheck(GSource * gs); + bool gsourceDispatch(GSource * gs); + + bool processX11Events(); + + // end glib main loop support + +#endif //QT_USE_GLIBMAINLOOP + signals: void awake(); void aboutToBlock(); diff --git a/src/kernel/qeventloop_glib_p.h b/src/kernel/qeventloop_glib_p.h new file mode 100644 index 0000000..8d1ead9 --- /dev/null +++ b/src/kernel/qeventloop_glib_p.h @@ -0,0 +1,130 @@ +/** +** Qt->glib main event loop integration by Norbert Frese 2005 +** code based on qeventloop_p.h 3.3.5 +** +*/ + +/**************************************************************************** +** $Id: qt/qeventloop_glib_p.h +** +** Definition of QEventLoop class +** +** Copyright (C) 1992-2005 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses for Qt/Embedded may use this file in accordance with the +** Qt Embedded Commercial License Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QEVENTLOOP_GLIB_P_H +#define QEVENTLOOP_GLIB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include "qplatformdefs.h" +#endif // QT_H + +// SCO OpenServer redefines raise -> kill +#if defined(raise) +# undef raise +#endif + +#include "qeventloop.h" +#include "qwindowdefs.h" + +class QSocketNotifier; + +#include "qptrlist.h" + +#include + +// uncomment this for main loop related debug-output + +// #define DEBUG_QT_GLIBMAINLOOP 1 + +// Wrapper for QSocketNotifier Object and GPollFD + +struct QSockNotGPollFD +{ + QSocketNotifier *obj; + GPollFD gPollFD; + gushort events; // save events + bool pending; +}; + +class QEventLoopPrivate +{ +public: + QEventLoopPrivate() + { + reset(); + } + + void reset() { + looplevel = 0; + quitcode = 0; + quitnow = FALSE; + exitloop = FALSE; + shortcut = FALSE; + } + + int looplevel; + int quitcode; + unsigned int quitnow : 1; + unsigned int exitloop : 1; + unsigned int shortcut : 1; + +#if defined(Q_WS_X11) + int xfd; + + GPollFD x_gPollFD; + +#endif // Q_WS_X11 + + int thread_pipe[2]; + + GPollFD threadPipe_gPollFD; + + QPtrList sn_list; + // pending socket notifiers list + QPtrList sn_pending_list; + + // store flags for one iteration + uint pev_flags; + + // My GSource + + GSource * gSource; + +}; + +#endif // QEVENTLOOP_GLIB_P_H diff --git a/src/kernel/qeventloop_p.h b/src/kernel/qeventloop_p.h index 4e7e365..7935d73 100644 --- a/src/kernel/qeventloop_p.h +++ b/src/kernel/qeventloop_p.h @@ -49,6 +49,10 @@ // // +#if defined(QT_USE_GLIBMAINLOOP) +#include "qeventloop_glib_p.h" // use a different header file!!! +#else + #ifndef QT_H #include "qplatformdefs.h" #endif // QT_H @@ -147,4 +151,6 @@ public: }; +#endif // !QT_USE_GLIBMAINLOOP + #endif // QEVENTLOOP_P_H diff --git a/src/kernel/qeventloop_unix_glib.cpp b/src/kernel/qeventloop_unix_glib.cpp new file mode 100644 index 0000000..6852f6b --- /dev/null +++ b/src/kernel/qeventloop_unix_glib.cpp @@ -0,0 +1,593 @@ +/** +** Qt->glib main event loop integration by Norbert Frese 2005 +** code based on qeventloop_unix.cpp 3.3.5 +** +*/ + +/**************************************************************************** +** $Id: qt/qeventloop_unix_glib.cpp +** +** Implementation of QEventLoop class +** +** Copyright (C) 2000-2005 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses for Unix/X11 or for Qt/Embedded may use this file in accordance +** with the Qt Commercial License Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +#include "qeventloop_glib_p.h" // includes qplatformdefs.h +#include "qeventloop.h" +#include "qapplication.h" +#include "qbitarray.h" +#include +#include + +#include + +/***************************************************************************** + Timer handling; UNIX has no application timer support so we'll have to + make our own from scratch. + + NOTE: These functions are for internal use. QObject::startTimer() and + QObject::killTimer() are for public use. + The QTimer class provides a high-level interface which translates + timer events into signals. + + qStartTimer( interval, obj ) + Starts a timer which will run until it is killed with qKillTimer() + Arguments: + int interval timer interval in milliseconds + QObject *obj where to send the timer event + Returns: + int timer identifier, or zero if not successful + + qKillTimer( timerId ) + Stops a timer specified by a timer identifier. + Arguments: + int timerId timer identifier + Returns: + bool TRUE if successful + + qKillTimer( obj ) + Stops all timers that are sent to the specified object. + Arguments: + QObject *obj object receiving timer events + Returns: + bool TRUE if successful + *****************************************************************************/ + +// +// Internal data structure for timers +// + +struct TimerInfo { // internal timer info + int id; // - timer identifier + timeval interval; // - timer interval + timeval timeout; // - when to sent event + QObject *obj; // - object to receive event +}; + +typedef QPtrList TimerList; // list of TimerInfo structs + +static QBitArray *timerBitVec; // timer bit vector +static TimerList *timerList = 0; // timer list + +static void initTimers(); +void cleanupTimers(); +static timeval watchtime; // watch if time is turned back +timeval *qt_wait_timer(); +timeval *qt_wait_timer_max = 0; + +// +// Internal operator functions for timevals +// + +static inline bool operator<( const timeval &t1, const timeval &t2 ) +{ + return t1.tv_sec < t2.tv_sec || + (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); +} + +static inline bool operator==( const timeval &t1, const timeval &t2 ) +{ + return t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec; +} + +static inline timeval &operator+=( timeval &t1, const timeval &t2 ) +{ + t1.tv_sec += t2.tv_sec; + if ( (t1.tv_usec += t2.tv_usec) >= 1000000 ) { + t1.tv_sec++; + t1.tv_usec -= 1000000; + } + return t1; +} + +static inline timeval operator+( const timeval &t1, const timeval &t2 ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec + t2.tv_sec; + if ( (tmp.tv_usec = t1.tv_usec + t2.tv_usec) >= 1000000 ) { + tmp.tv_sec++; + tmp.tv_usec -= 1000000; + } + return tmp; +} + +static inline timeval operator-( const timeval &t1, const timeval &t2 ) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec - t2.tv_sec; + if ( (tmp.tv_usec = t1.tv_usec - t2.tv_usec) < 0 ) { + tmp.tv_sec--; + tmp.tv_usec += 1000000; + } + return tmp; +} + + +// +// Internal functions for manipulating timer data structures. +// The timerBitVec array is used for keeping track of timer identifiers. +// + +static int allocTimerId() // find avail timer identifier +{ + int i = timerBitVec->size()-1; + while ( i >= 0 && (*timerBitVec)[i] ) + i--; + if ( i < 0 ) { + i = timerBitVec->size(); + timerBitVec->resize( 4 * i ); + for( int j=timerBitVec->size()-1; j > i; j-- ) + timerBitVec->clearBit( j ); + } + timerBitVec->setBit( i ); + return i+1; +} + +static void insertTimer( const TimerInfo *ti ) // insert timer info into list +{ + TimerInfo *t = timerList->first(); + int index = 0; +#if defined(QT_DEBUG) + int dangerCount = 0; +#endif + while ( t && t->timeout < ti->timeout ) { // list is sorted by timeout +#if defined(QT_DEBUG) + if ( t->obj == ti->obj ) + dangerCount++; +#endif + t = timerList->next(); + index++; + } + timerList->insert( index, ti ); // inserts sorted +#if defined(QT_DEBUG) + if ( dangerCount > 16 ) + qDebug( "QObject: %d timers now exist for object %s::%s", + dangerCount, ti->obj->className(), ti->obj->name() ); +#endif +} + +static inline void getTime( timeval &t ) // get time of day +{ + gettimeofday( &t, 0 ); + while ( t.tv_usec >= 1000000 ) { // NTP-related fix + t.tv_usec -= 1000000; + t.tv_sec++; + } + while ( t.tv_usec < 0 ) { + if ( t.tv_sec > 0 ) { + t.tv_usec += 1000000; + t.tv_sec--; + } else { + t.tv_usec = 0; + break; + } + } +} + +static void repairTimer( const timeval &time ) // repair broken timer +{ + timeval diff = watchtime - time; + register TimerInfo *t = timerList->first(); + while ( t ) { // repair all timers + t->timeout = t->timeout - diff; + t = timerList->next(); + } +} + +// +// Timer activation functions (called from the event loop) +// + +/* + Returns the time to wait for the next timer, or null if no timers are + waiting. + + The result is bounded to qt_wait_timer_max if this exists. +*/ + +timeval *qt_wait_timer() +{ + static timeval tm; + bool first = TRUE; + timeval currentTime; + if ( timerList && timerList->count() ) { // there are waiting timers + getTime( currentTime ); + if ( first ) { + if ( currentTime < watchtime ) // clock was turned back + repairTimer( currentTime ); + first = FALSE; + watchtime = currentTime; + } + TimerInfo *t = timerList->first(); // first waiting timer + if ( currentTime < t->timeout ) { // time to wait + tm = t->timeout - currentTime; + } else { + tm.tv_sec = 0; // no time to wait + tm.tv_usec = 0; + } + if ( qt_wait_timer_max && *qt_wait_timer_max < tm ) + tm = *qt_wait_timer_max; + return &tm; + } + if ( qt_wait_timer_max ) { + tm = *qt_wait_timer_max; + return &tm; + } + return 0; // no timers +} + +// Timer initialization +static void initTimers() // initialize timers +{ + timerBitVec = new QBitArray( 128 ); + Q_CHECK_PTR( timerBitVec ); + int i = timerBitVec->size(); + while( i-- > 0 ) + timerBitVec->clearBit( i ); + timerList = new TimerList; + Q_CHECK_PTR( timerList ); + timerList->setAutoDelete( TRUE ); + gettimeofday( &watchtime, 0 ); +} + +// Timer cleanup +void cleanupTimers() +{ + delete timerList; + timerList = 0; + delete timerBitVec; + timerBitVec = 0; +} + +// Main timer functions for starting and killing timers +int qStartTimer( int interval, QObject *obj ) +{ + if ( !timerList ) // initialize timer data + initTimers(); + int id = allocTimerId(); // get free timer id + if ( id <= 0 || + id > (int)timerBitVec->size() || !obj )// cannot create timer + return 0; + timerBitVec->setBit( id-1 ); // set timer active + TimerInfo *t = new TimerInfo; // create timer + Q_CHECK_PTR( t ); + t->id = id; + t->interval.tv_sec = interval/1000; + t->interval.tv_usec = (interval%1000)*1000; + timeval currentTime; + getTime( currentTime ); + t->timeout = currentTime + t->interval; + t->obj = obj; + insertTimer( t ); // put timer in list + return id; +} + +bool qKillTimer( int id ) +{ + register TimerInfo *t; + if ( !timerList || id <= 0 || + id > (int)timerBitVec->size() || !timerBitVec->testBit( id-1 ) ) + return FALSE; // not init'd or invalid timer + t = timerList->first(); + while ( t && t->id != id ) // find timer info in list + t = timerList->next(); + if ( t ) { // id found + timerBitVec->clearBit( id-1 ); // set timer inactive + return timerList->remove(); + } + else // id not found + return FALSE; +} + +bool qKillTimer( QObject *obj ) +{ + register TimerInfo *t; + if ( !timerList ) // not initialized + return FALSE; + t = timerList->first(); + while ( t ) { // check all timers + if ( t->obj == obj ) { // object found + timerBitVec->clearBit( t->id-1 ); + timerList->remove(); + t = timerList->current(); + } else { + t = timerList->next(); + } + } + return TRUE; +} + + + + +/***************************************************************************** + QEventLoop implementations for Glib-Main-Loop + *****************************************************************************/ + + + + +void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier ) +{ + + + + int sockfd = notifier->socket(); + int type = notifier->type(); + if ( sockfd < 0 || type < 0 || type > 2 || notifier == 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QSocketNotifier: Internal error" ); +#endif + return; + } + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("register socket notifier %d\n", sockfd); + #endif + + QPtrList *list = &d->sn_list; + QSockNotGPollFD *sn; + + /* + if ( ! list ) { + // create new list, the QSockNotType destructor will delete it for us + list = new QPtrList; + Q_CHECK_PTR( list ); + list->setAutoDelete( TRUE ); + d->sn_list = list; + } + */ + + gushort events=0; + switch (type) { + case 0: + events = G_IO_IN | G_IO_HUP; + break; + case 1: + events = G_IO_OUT; + break; + case 2: + events = G_IO_PRI | G_IO_ERR | G_IO_NVAL; + break; + } + + sn = new QSockNotGPollFD; + Q_CHECK_PTR( sn ); + sn->obj = notifier; + sn->gPollFD.fd = sockfd; + sn->gPollFD.events = events; + sn->events = events; // save events! + sn->pending = FALSE; + + list->append( sn ); + + g_source_add_poll(d->gSource, &sn->gPollFD); + +} + +void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier ) +{ + int sockfd = notifier->socket(); + int type = notifier->type(); + if ( sockfd < 0 || type < 0 || type > 2 || notifier == 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QSocketNotifier: Internal error" ); +#endif + return; + } + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("unregister socket notifier %d\n", sockfd); + #endif + + QPtrList *list = &d->sn_list; + QSockNotGPollFD *sn; + if ( ! list ) + return; + sn = list->first(); + while ( sn && !(sn->obj == notifier) ) + sn = list->next(); + if ( !sn ) // not found + return; + + d->sn_pending_list.removeRef( sn ); + list->remove(); // remove notifier found above + g_source_remove_poll(d->gSource, &sn->gPollFD); + + delete sn; // we don't autodelete - lets do it manually + +} + +void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier ) +{ + int sockfd = notifier->socket(); + int type = notifier->type(); + if ( sockfd < 0 || type < 0 || type > 2 || notifier == 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QSocketNotifier: Internal error" ); +#endif + return; + } + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("set socket notifier pending %d\n", sockfd); + #endif + + QPtrList *list = &d->sn_list; + QSockNotGPollFD *sn; + if ( ! list ) + return; + sn = list->first(); + while ( sn && !(sn->obj == notifier) ) + sn = list->next(); + if ( ! sn ) { // not found + return; + } + + // We choose a random activation order to be more fair under high load. + // If a constant order is used and a peer early in the list can + // saturate the IO, it might grab our attention completely. + // Also, if we're using a straight list, the callback routines may + // delete other entries from the list before those other entries are + // processed. + if ( !sn->pending ) { + d->sn_pending_list.insert( (rand() & 0xff) % + (d->sn_pending_list.count()+1), sn ); + sn->pending = TRUE; // add it only once! + } +} + +void QEventLoop::wakeUp() +{ + /* + Apparently, there is not consistency among different operating + systems on how to use FIONREAD. + + FreeBSD, Linux and Solaris all expect the 3rd argument to + ioctl() to be an int, which is normally 32-bit even on 64-bit + machines. + + IRIX, on the other hand, expects a size_t, which is 64-bit on + 64-bit machines. + + So, the solution is to use size_t initialized to zero to make + sure all bits are set to zero, preventing underflow with the + FreeBSD/Linux/Solaris ioctls. + */ + size_t nbytes = 0; + char c = 0; + if ( ::ioctl( d->thread_pipe[0], FIONREAD, (char*)&nbytes ) >= 0 && nbytes == 0 ) { + ::write( d->thread_pipe[1], &c, 1 ); + } +} + +int QEventLoop::timeToWait() const +{ + timeval *tm = qt_wait_timer(); + if ( ! tm ) // no active timers + return -1; + return (tm->tv_sec*1000) + (tm->tv_usec/1000); +} + +int QEventLoop::activateTimers() +{ + if ( !timerList || !timerList->count() ) // no timers + return 0; + bool first = TRUE; + timeval currentTime; + int n_act = 0, maxCount = timerList->count(); + TimerInfo *begin = 0; + register TimerInfo *t; + + for ( ;; ) { + if ( ! maxCount-- ) + break; + getTime( currentTime ); // get current time + if ( first ) { + if ( currentTime < watchtime ) // clock was turned back + repairTimer( currentTime ); + first = FALSE; + watchtime = currentTime; + } + t = timerList->first(); + if ( !t || currentTime < t->timeout ) // no timer has expired + break; + if ( ! begin ) { + begin = t; + } else if ( begin == t ) { + // avoid sending the same timer multiple times + break; + } else if ( t->interval < begin->interval || t->interval == begin->interval ) { + begin = t; + } + timerList->take(); // unlink from list + t->timeout += t->interval; + if ( t->timeout < currentTime ) + t->timeout = currentTime + t->interval; + insertTimer( t ); // relink timer + if ( t->interval.tv_usec > 0 || t->interval.tv_sec > 0 ) + n_act++; + QTimerEvent e( t->id ); + QApplication::sendEvent( t->obj, &e ); // send event + if ( timerList->findRef( begin ) == -1 ) + begin = 0; + } + return n_act; +} + +int QEventLoop::activateSocketNotifiers() +{ + if ( d->sn_pending_list.isEmpty() ) + return 0; + + // activate entries + int n_act = 0; + QEvent event( QEvent::SockAct ); + QPtrListIterator it( d->sn_pending_list ); + QSockNotGPollFD *sn; + while ( (sn=it.current()) ) { + ++it; + d->sn_pending_list.removeRef( sn ); + if ( sn->pending ) { + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("activate sn : send event fd=%d\n", sn->gPollFD.fd ); + #endif + + + sn->pending = FALSE; + QApplication::sendEvent( sn->obj, &event ); + n_act++; + } + } + + return n_act; +} diff --git a/src/kernel/qeventloop_x11_glib.cpp b/src/kernel/qeventloop_x11_glib.cpp new file mode 100644 index 0000000..10ee007 --- /dev/null +++ b/src/kernel/qeventloop_x11_glib.cpp @@ -0,0 +1,618 @@ +/** +** Qt->glib main event loop integration by Norbert Frese 2005 +** code based on qeventloop_x11.cpp 3.3.5 +** +*/ + +/**************************************************************************** +** $Id: qt/qeventloop_x11_glib.cpp +** +** Implementation of QEventLoop class +** +** Copyright (C) 2000-2005 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses for Unix/X11 may use this file in accordance with the Qt Commercial +** License Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +#include "qeventloop_glib_p.h" // includes qplatformdefs.h +#include "qeventloop.h" +#include "qapplication.h" +#include "qbitarray.h" +#include "qcolor_p.h" +#include "qt_x11_p.h" + +#if defined(QT_THREAD_SUPPORT) +# include "qmutex.h" +#endif // QT_THREAD_SUPPORT + +#include + +#include + +// Qt-GSource Structure and Callbacks + +typedef struct { + GSource source; + QEventLoop * qeventLoop; +} QtGSource; + +static gboolean qt_gsource_prepare ( GSource *source, + gint *timeout ); +static gboolean qt_gsource_check ( GSource *source ); +static gboolean qt_gsource_dispatch ( GSource *source, + GSourceFunc callback, gpointer user_data ); + +static GSourceFuncs qt_gsource_funcs = { + qt_gsource_prepare, + qt_gsource_check, + qt_gsource_dispatch, + NULL, + NULL, + NULL +}; + +// forward main loop callbacks to QEventLoop methods! + +static gboolean qt_gsource_prepare ( GSource *source, + gint *timeout ) +{ + QtGSource * qtGSource; + qtGSource = (QtGSource*) source; + return qtGSource->qeventLoop->gsourcePrepare(source, timeout); +} + +static gboolean qt_gsource_check ( GSource *source ) +{ + QtGSource * qtGSource = (QtGSource*) source; + return qtGSource->qeventLoop->gsourceCheck(source); +} + +static gboolean qt_gsource_dispatch ( GSource *source, + GSourceFunc callback, gpointer user_data ) +{ + QtGSource * qtGSource = (QtGSource*) source; + return qtGSource->qeventLoop->gsourceDispatch(source); +} + + +// ------------------------------------------------- + +// resolve the conflict between X11's FocusIn and QEvent::FocusIn +#undef FocusOut +#undef FocusIn + +static const int XKeyPress = KeyPress; +static const int XKeyRelease = KeyRelease; +#undef KeyPress +#undef KeyRelease + +// from qapplication.cpp +extern bool qt_is_gui_used; + +// from qeventloop_unix.cpp +extern timeval *qt_wait_timer(); +extern void cleanupTimers(); + +// ### this needs to go away at some point... +typedef void (*VFPTR)(); +typedef QValueList QVFuncList; +void qt_install_preselect_handler( VFPTR ); +void qt_remove_preselect_handler( VFPTR ); +static QVFuncList *qt_preselect_handler = 0; +void qt_install_postselect_handler( VFPTR ); +void qt_remove_postselect_handler( VFPTR ); +static QVFuncList *qt_postselect_handler = 0; + +void qt_install_preselect_handler( VFPTR handler ) +{ + if ( !qt_preselect_handler ) + qt_preselect_handler = new QVFuncList; + qt_preselect_handler->append( handler ); +} +void qt_remove_preselect_handler( VFPTR handler ) +{ + if ( qt_preselect_handler ) { + QVFuncList::Iterator it = qt_preselect_handler->find( handler ); + if ( it != qt_preselect_handler->end() ) + qt_preselect_handler->remove( it ); + } +} +void qt_install_postselect_handler( VFPTR handler ) +{ + if ( !qt_postselect_handler ) + qt_postselect_handler = new QVFuncList; + qt_postselect_handler->prepend( handler ); +} +void qt_remove_postselect_handler( VFPTR handler ) +{ + if ( qt_postselect_handler ) { + QVFuncList::Iterator it = qt_postselect_handler->find( handler ); + if ( it != qt_postselect_handler->end() ) + qt_postselect_handler->remove( it ); + } +} + + +void QEventLoop::init() +{ + // initialize ProcessEventFlags (all events & wait for more) + + d->pev_flags = AllEvents | WaitForMore; + + // initialize the common parts of the event loop + pipe( d->thread_pipe ); + fcntl(d->thread_pipe[0], F_SETFD, FD_CLOEXEC); + fcntl(d->thread_pipe[1], F_SETFD, FD_CLOEXEC); + + // intitialize the X11 parts of the event loop + d->xfd = -1; + if ( qt_is_gui_used ) + d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); + + // new GSource + + QtGSource * qtGSource = (QtGSource*) g_source_new(&qt_gsource_funcs, + sizeof(QtGSource)); + + g_source_set_can_recurse ((GSource*)qtGSource, TRUE); + + qtGSource->qeventLoop = this; + + // init main loop and attach gsource + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside init(1)\n"); + #endif + + g_main_loop_new (NULL, 1); + + g_source_attach( (GSource*)qtGSource, NULL ); + + d->gSource = (GSource*) qtGSource; + + // poll for X11 events + + if ( qt_is_gui_used ) { + + + d->x_gPollFD.fd = d->xfd; + d->x_gPollFD.events = G_IO_IN | G_IO_HUP; + g_source_add_poll(d->gSource, &d->x_gPollFD); + } + + // poll thread-pipe + + d->threadPipe_gPollFD.fd = d->thread_pipe[0]; + d->threadPipe_gPollFD.events = G_IO_IN | G_IO_HUP; + + g_source_add_poll(d->gSource, &d->threadPipe_gPollFD); + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside init(2)\n"); + #endif + +} + +void QEventLoop::cleanup() +{ + // cleanup the common parts of the event loop + close( d->thread_pipe[0] ); + close( d->thread_pipe[1] ); + cleanupTimers(); + + // cleanup the X11 parts of the event loop + d->xfd = -1; + + // todo: destroy gsource +} + +bool QEventLoop::processEvents( ProcessEventsFlags flags ) +{ + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside processEvents(1) looplevel=%d\n", d->looplevel ); + #endif + ProcessEventsFlags save_flags; + int rval; + save_flags = d->pev_flags; + + d->pev_flags = flags; + + rval = g_main_context_iteration(NULL, flags & WaitForMore ? TRUE : FALSE); + + d->pev_flags = save_flags; + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside processEvents(2) looplevel=%d rval=%d\n", d->looplevel, rval ); + #endif + + return rval; // were events processed? +} + + +bool QEventLoop::processX11Events() +{ + ProcessEventsFlags flags = d->pev_flags; + // process events from the X server + XEvent event; + int nevents = 0; + +#if defined(QT_THREAD_SUPPORT) + QMutexLocker locker( QApplication::qt_mutex ); +#endif + + // handle gui and posted events + if ( qt_is_gui_used ) { + QApplication::sendPostedEvents(); + + // Two loops so that posted events accumulate + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + // also flushes output buffer + while ( XPending( QPaintDevice::x11AppDisplay() ) ) { + if ( d->shortcut ) { + return FALSE; + } + + XNextEvent( QPaintDevice::x11AppDisplay(), &event ); + + if ( flags & ExcludeUserInput ) { + switch ( event.type ) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case XKeyPress: + case XKeyRelease: + case EnterNotify: + case LeaveNotify: + continue; + + case ClientMessage: + { + // from qapplication_x11.cpp + extern Atom qt_wm_protocols; + extern Atom qt_wm_take_focus; + extern Atom qt_qt_scrolldone; + + // only keep the wm_take_focus and + // qt_qt_scrolldone protocols, discard all + // other client messages + if ( event.xclient.format != 32 ) + continue; + + if ( event.xclient.message_type == qt_wm_protocols || + (Atom) event.xclient.data.l[0] == qt_wm_take_focus ) + break; + if ( event.xclient.message_type == qt_qt_scrolldone ) + break; + } + + default: break; + } + } + + nevents++; + if ( qApp->x11ProcessEvent( &event ) == 1 ) + return TRUE; + } + } + } + + if ( d->shortcut ) { + return FALSE; + } + + QApplication::sendPostedEvents(); + + const uint exclude_all = ExcludeSocketNotifiers | 0x08; + // 0x08 == ExcludeTimers for X11 only + if ( nevents > 0 && ( flags & exclude_all ) == exclude_all && + ( flags & WaitForMore ) ) { + return TRUE; + } + return FALSE; +} + + +bool QEventLoop::gsourcePrepare(GSource *gs, int * timeout) +{ + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(1)\n"); + #endif + + ProcessEventsFlags flags = d->pev_flags; + +#if defined(QT_THREAD_SUPPORT) + QMutexLocker locker( QApplication::qt_mutex ); +#endif + + // don't block if exitLoop() or exit()/quit() has been called. + bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore); + + // Process timers and socket notifiers - the common UNIX stuff + + // return the maximum time we can wait for an event. + static timeval zerotm; + timeval *tm = 0; + if ( ! ( flags & 0x08 ) ) { // 0x08 == ExcludeTimers for X11 only + tm = qt_wait_timer(); // wait for timer or X event + if ( !canWait ) { + if ( !tm ) + tm = &zerotm; + tm->tv_sec = 0; // no time to wait + tm->tv_usec = 0; + } + } + + // include or exclude SocketNotifiers (by setting or cleaning poll events) + + if ( ! ( flags & ExcludeSocketNotifiers ) ) { + QPtrListIterator it( d->sn_list ); + QSockNotGPollFD *sn; + while ( (sn=it.current()) ) { + ++it; + sn->gPollFD.events = sn->events; // restore poll events + } + } else { + QPtrListIterator it( d->sn_list ); + QSockNotGPollFD *sn; + while ( (sn=it.current()) ) { + ++it; + sn->gPollFD.events = 0; // delete poll events + } + } + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(2) canwait=%d\n", canWait); + #endif + + if ( canWait ) + emit aboutToBlock(); + + + if ( qt_preselect_handler ) { + QVFuncList::Iterator it, end = qt_preselect_handler->end(); + for ( it = qt_preselect_handler->begin(); it != end; ++it ) + (**it)(); + } + + // unlock the GUI mutex and select. when we return from this function, there is + // something for us to do +#if defined(QT_THREAD_SUPPORT) + locker.mutex()->unlock(); +#endif + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(2.1) canwait=%d\n", canWait); + #endif + + // do we have to dispatch events? + if (hasPendingEvents()) { + *timeout = 0; // no time to stay in poll + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(3a)\n"); + #endif + + return FALSE; + } + + // stay in poll until something happens? + if (!tm) { // fixme + *timeout = -1; // wait forever + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(3b) timeout=%d \n", *timeout); + #endif + + + return FALSE; + } + + // else timeout >=0 + *timeout = tm->tv_sec * 1000 + tm->tv_usec/1000; + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourcePrepare(3c) timeout=%d \n", *timeout); + #endif + + + return FALSE; +} + + +bool QEventLoop::gsourceCheck(GSource *gs) { + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(1)\n"); + #endif + + + // Socketnotifier events? + + QPtrList *list = &d->sn_list; + + //if ( list ) { + + + QSockNotGPollFD *sn = list->first(); + while ( sn ) { + if ( sn->gPollFD.revents ) + return TRUE; + sn = list->next(); + } + //} + + if (d->x_gPollFD.revents) { + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) xfd!\n"); + #endif + + return TRUE; // we got events! + } + if (d->threadPipe_gPollFD.revents) { + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) threadpipe!!\n"); + #endif + + return TRUE; // we got events! + } + if (hasPendingEvents()) { + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) pendingEvents!\n"); + #endif + + return TRUE; // we got more X11 events! + } + // check if we have timers to activate? + + timeval * tm =qt_wait_timer(); + + if (tm && (tm->tv_sec == 0 && tm->tv_usec == 0 )) { + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) qtwaittimer!\n"); + #endif + + return TRUE; + } + + // nothing to dispatch + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceCheck(2) nothing to dispatch!\n"); + #endif + + return FALSE; +} + + +bool QEventLoop::gsourceDispatch(GSource *gs) { + + // relock the GUI mutex before processing any pending events +#if defined(QT_THREAD_SUPPORT) + QMutexLocker locker( QApplication::qt_mutex ); +#endif +#if defined(QT_THREAD_SUPPORT) + locker.mutex()->lock(); +#endif + + int nevents=0; + + ProcessEventsFlags flags = d->pev_flags; + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceDispatch(1)\n"); + #endif + + // we are awake, broadcast it + emit awake(); + emit qApp->guiThreadAwake(); + + // some other thread woke us up... consume the data on the thread pipe so that + // select doesn't immediately return next time + + if ( d->threadPipe_gPollFD.revents) { + char c; + ::read( d->thread_pipe[0], &c, 1 ); + } + + if ( qt_postselect_handler ) { + QVFuncList::Iterator it, end = qt_postselect_handler->end(); + for ( it = qt_postselect_handler->begin(); it != end; ++it ) + (**it)(); + } + + // activate socket notifiers + if ( ! ( flags & ExcludeSocketNotifiers )) { + // if select says data is ready on any socket, then set the socket notifier + // to pending + // if ( &d->sn_list ) { + + + QPtrList *list = &d->sn_list; + QSockNotGPollFD *sn = list->first(); + while ( sn ) { + if ( sn->gPollFD.revents ) + setSocketNotifierPending( sn->obj ); + sn = list->next(); + } + // } + + nevents += activateSocketNotifiers(); + } + + // activate timers + if ( ! ( flags & 0x08 ) ) { + // 0x08 == ExcludeTimers for X11 only + nevents += activateTimers(); + } + + + + // return true if we handled events, false otherwise + //return (nevents > 0); + + // now process x11 events! + + #ifdef DEBUG_QT_GLIBMAINLOOP + printf("inside gsourceDispatch(2) hasPendingEvents=%d\n", hasPendingEvents()); + #endif + + if (hasPendingEvents()) { + + // color approx. optimization - only on X11 + qt_reset_color_avail(); + + processX11Events(); + + } + +#if defined(QT_THREAD_SUPPORT) + locker.mutex()->unlock(); +#endif + + return TRUE; + +} + +bool QEventLoop::hasPendingEvents() const +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return ( qGlobalPostedEventsCount() || ( qt_is_gui_used ? XPending( QPaintDevice::x11AppDisplay() ) : 0)); +} + +void QEventLoop::appStartingUp() +{ + if ( qt_is_gui_used ) + d->xfd = XConnectionNumber( QPaintDevice::x11AppDisplay() ); +} + +void QEventLoop::appClosingDown() +{ + d->xfd = -1; +} diff --git a/src/kernel/qt_kernel.pri b/src/kernel/qt_kernel.pri index 5a89e08..b72220f 100644 --- a/src/kernel/qt_kernel.pri +++ b/src/kernel/qt_kernel.pri @@ -21,7 +21,7 @@ kernel { $$KERNEL_H/qdrawutil.h \ $$KERNEL_H/qdropsite.h \ $$KERNEL_H/qevent.h \ - $$KERNEL_H/qeventloop.h\ + $$KERNEL_H/qeventloop.h \ $$KERNEL_P/qeventloop_p.h \ $$KERNEL_H/qfocusdata.h \ $$KERNEL_H/qfont.h \ @@ -104,6 +104,10 @@ kernel { HEADERS += $$KERNEL_P/qinputcontext_p.h } + glibmainloop { + HEADERS+=$$KERNEL_P/qeventloop_glib_p.h + } + win32:SOURCES += $$KERNEL_CPP/qapplication_win.cpp \ $$KERNEL_CPP/qclipboard_win.cpp \ $$KERNEL_CPP/qcolor_win.cpp \ @@ -133,7 +137,6 @@ kernel { $$KERNEL_CPP/qcursor_x11.cpp \ $$KERNEL_CPP/qdnd_x11.cpp \ $$KERNEL_CPP/qdesktopwidget_x11.cpp \ - $$KERNEL_CPP/qeventloop_x11.cpp \ $$KERNEL_CPP/qfont_x11.cpp \ $$KERNEL_CPP/qinputcontext.cpp \ $$KERNEL_CPP/qinputcontext_x11.cpp \ @@ -146,6 +149,12 @@ kernel { $$KERNEL_CPP/qwidget_x11.cpp \ $$KERNEL_CPP/qwidgetcreate_x11.cpp \ $$KERNEL_CPP/qfontengine_x11.cpp + glibmainloop { + SOURCES += $$KERNEL_CPP/qeventloop_x11_glib.cpp + } else { + SOURCES += $$KERNEL_CPP/qeventloop_x11.cpp + } + } !x11:mac { @@ -172,8 +181,13 @@ kernel { DEFINES += QMAC_ONE_PIXEL_LOCK } else:unix { SOURCES += $$KERNEL_CPP/qprinter_unix.cpp \ - $$KERNEL_CPP/qpsprinter.cpp \ - $$KERNEL_CPP/qeventloop_unix.cpp + $$KERNEL_CPP/qpsprinter.cpp + glibmainloop { + SOURCES += $$KERNEL_CPP/qeventloop_unix_glib.cpp + } else { + SOURCES += $$KERNEL_CPP/qeventloop_unix.cpp + } + } unix:SOURCES += $$KERNEL_CPP/qprocess_unix.cpp \ $$KERNEL_CPP/qthread_unix.cpp diff --git a/src/qt.pro b/src/qt.pro index 2cc70cf..6f5037b 100644 --- a/src/qt.pro +++ b/src/qt.pro @@ -196,3 +196,11 @@ wince-* { $$CODECS_CPP/qtextcodecplugin.cpp \ $$WIDGETS_CPP/qwidgetplugin.cpp } + +glibmainloop { + DEFINES += QT_USE_GLIBMAINLOOP + QMAKE_CFLAGS += $$QMAKE_CFLAGS_GLIB + QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_GLIB + LIBS += $$QMAKE_LIBS_GLIB +} +