diff options
Diffstat (limited to 'src/kernel/qeventloop_x11.cpp')
-rw-r--r-- | src/kernel/qeventloop_x11.cpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/src/kernel/qeventloop_x11.cpp b/src/kernel/qeventloop_x11.cpp new file mode 100644 index 000000000..76d2e62e0 --- /dev/null +++ b/src/kernel/qeventloop_x11.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Implementation of TQEventLoop class +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qeventloop_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 <errno.h> + + +// resolve the conflict between X11's FocusIn and TQEvent::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 TQValueList<VFPTR> TQVFuncList; +void qt_install_preselect_handler( VFPTR ); +void qt_remove_preselect_handler( VFPTR ); +static TQVFuncList *qt_preselect_handler = 0; +void qt_install_postselect_handler( VFPTR ); +void qt_remove_postselect_handler( VFPTR ); +static TQVFuncList *qt_postselect_handler = 0; + +void qt_install_preselect_handler( VFPTR handler ) +{ + if ( !qt_preselect_handler ) + qt_preselect_handler = new TQVFuncList; + qt_preselect_handler->append( handler ); +} +void qt_remove_preselect_handler( VFPTR handler ) +{ + if ( qt_preselect_handler ) { + TQVFuncList::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 TQVFuncList; + qt_postselect_handler->prepend( handler ); +} +void qt_remove_postselect_handler( VFPTR handler ) +{ + if ( qt_postselect_handler ) { + TQVFuncList::Iterator it = qt_postselect_handler->find( handler ); + if ( it != qt_postselect_handler->end() ) + qt_postselect_handler->remove( it ); + } +} + + +void TQEventLoop::init() +{ + // 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); + + d->sn_highest = -1; + + // intitialize the X11 parts of the event loop + d->xfd = -1; + if ( qt_is_gui_used ) + d->xfd = XConnectionNumber( TQPaintDevice::x11AppDisplay() ); +} + +void TQEventLoop::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; +} + +bool TQEventLoop::processEvents( ProcessEventsFlags flags ) +{ + // process events from the X server + XEvent event; + int nevents = 0; + +#if defined(QT_THREAD_SUPPORT) + TQMutexLocker locker( TQApplication::qt_mutex ); +#endif + + // handle gui and posted events + if ( qt_is_gui_used ) { + TQApplication::sendPostedEvents(); + + // Two loops so that posted events accumulate + while ( XPending( TQPaintDevice::x11AppDisplay() ) ) { + // also flushes output buffer + while ( XPending( TQPaintDevice::x11AppDisplay() ) ) { + if ( d->shortcut ) { + return FALSE; + } + + XNextEvent( TQPaintDevice::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; + } + + TQApplication::sendPostedEvents(); + + const uint exclude_all = ExcludeSocketNotifiers | 0x08; + // 0x08 == ExcludeTimers for X11 only + if ( nevents > 0 && ( flags & exclude_all ) == exclude_all && + ( flags & WaitForMore ) ) { + return TRUE; + } + + // don't block if exitLoop() or exit()/tquit() has been called. + bool canWait = d->exitloop || d->tquitnow ? 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; + } + } + + int highest = 0; + if ( ! ( flags & ExcludeSocketNotifiers ) ) { + // return the highest fd we can wait for input on + if ( d->sn_highest >= 0 ) { // has socket notifier(s) + if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() ) + d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds; + else + FD_ZERO( &d->sn_vec[0].select_fds ); + + if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() ) + d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds; + else + FD_ZERO( &d->sn_vec[1].select_fds ); + + if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() ) + d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds; + else + FD_ZERO( &d->sn_vec[2].select_fds ); + } else { + FD_ZERO( &d->sn_vec[0].select_fds ); + + FD_ZERO( &d->sn_vec[1].select_fds ); + FD_ZERO( &d->sn_vec[2].select_fds ); + } + + highest = d->sn_highest; + } else { + FD_ZERO( &d->sn_vec[0].select_fds ); + FD_ZERO( &d->sn_vec[1].select_fds ); + FD_ZERO( &d->sn_vec[2].select_fds ); + } + + if ( qt_is_gui_used ) { + // select for events on the event socket - only on X11 + FD_SET( d->xfd, &d->sn_vec[0].select_fds ); + highest = TQMAX( highest, d->xfd ); + } + + FD_SET( d->thread_pipe[0], &d->sn_vec[0].select_fds ); + highest = TQMAX( highest, d->thread_pipe[0] ); + + if ( canWait ) + emit aboutToBlock(); + + if ( qt_preselect_handler ) { + TQVFuncList::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 + + int nsel; + do { + nsel = select( highest + 1, + &d->sn_vec[0].select_fds, + &d->sn_vec[1].select_fds, + &d->sn_vec[2].select_fds, + tm ); + } while (nsel == -1 && (errno == EINTR || errno == EAGAIN)); + + // relock the GUI mutex before processing any pending events +#if defined(QT_THREAD_SUPPORT) + locker.mutex()->lock(); +#endif + + // we are awake, broadcast it + emit awake(); + emit qApp->guiThreadAwake(); + + if (nsel == -1) { + if (errno == EBADF) { + // it seems a socket notifier has a bad fd... find out + // which one it is and disable it + fd_set fdset; + zerotm.tv_sec = zerotm.tv_usec = 0l; + + for (int type = 0; type < 3; ++type) { + TQPtrList<TQSockNot> *list = d->sn_vec[type].list; + if (!list) continue; + + TQSockNot *sn = list->first(); + while (sn) { + FD_ZERO(&fdset); + FD_SET(sn->fd, &fdset); + + int ret = -1; + do { + switch (type) { + case 0: // read + ret = select(sn->fd + 1, &fdset, 0, 0, &zerotm); + break; + case 1: // write + ret = select(sn->fd + 1, 0, &fdset, 0, &zerotm); + break; + case 2: // except + ret = select(sn->fd + 1, 0, 0, &fdset, &zerotm); + break; + } + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + if (ret == -1 && errno == EBADF) { + // disable the invalid socket notifier + static const char *t[] = { "Read", "Write", "Exception" }; + qWarning("TQSocketNotifier: invalid socket %d and type '%s', disabling...", + sn->fd, t[type]); + sn->obj->setEnabled(FALSE); + } + + sn = list->next(); + } + } + } else { + // EINVAL... shouldn't happen, so let's complain to stderr + // and hope someone sends us a bug report + perror( "select" ); + } + } + + // some other thread woke us up... consume the data on the thread pipe so that + // select doesn't immediately return next time + if ( nsel > 0 && FD_ISSET( d->thread_pipe[0], &d->sn_vec[0].select_fds ) ) { + char c; + ::read( d->thread_pipe[0], &c, 1 ); + } + + if ( qt_postselect_handler ) { + TQVFuncList::Iterator it, end = qt_postselect_handler->end(); + for ( it = qt_postselect_handler->begin(); it != end; ++it ) + (**it)(); + } + + // activate socket notifiers + if ( ! ( flags & ExcludeSocketNotifiers ) && nsel > 0 && d->sn_highest >= 0 ) { + // if select says data is ready on any socket, then set the socket notifier + // to pending + int i; + for ( i=0; i<3; i++ ) { + if ( ! d->sn_vec[i].list ) + continue; + + TQPtrList<TQSockNot> *list = d->sn_vec[i].list; + TQSockNot *sn = list->first(); + while ( sn ) { + if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) + setSocketNotifierPending( sn->obj ); + sn = list->next(); + } + } + + nevents += activateSocketNotifiers(); + } + + // activate timers + if ( ! ( flags & 0x08 ) ) { + // 0x08 == ExcludeTimers for X11 only + nevents += activateTimers(); + } + + // color approx. optimization - only on X11 + qt_reset_color_avail(); + + // return true if we handled events, false otherwise + return (nevents > 0); +} + +bool TQEventLoop::hasPendingEvents() const +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return ( qGlobalPostedEventsCount() || XPending( TQPaintDevice::x11AppDisplay() ) ); +} + +void TQEventLoop::appStartingUp() +{ + if ( qt_is_gui_used ) + d->xfd = XConnectionNumber( TQPaintDevice::x11AppDisplay() ); +} + +void TQEventLoop::appClosingDown() +{ + d->xfd = -1; +} |