/****************************************************************************
** $Id: qeventloop_pm.cpp 8 2005-11-16 19:36:46Z dmik $
**
** Implementation of OS/2 startup routines and event handling
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
** Copyright (C) 2004 Norman ASA.  Initial OS/2 Port.
** Copyright (C) 2005 netlabs.org.  Further OS/2 Development.
**
** 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 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_p.h"
#include "qeventloop.h"
#include "qapplication.h"

#include <sys/socket.h>

#if defined(QT_THREAD_SUPPORT)
#  include "qmutex.h"
#  include "qthread.h"
#  include "qsemaphore.h"
#endif // QT_THREAD_SUPPORT

extern uint qGlobalPostedEventsCount();
extern bool qt_pmEventFilter( QMSG* msg, MRESULT &result );

static HAB qt_gui_hab = 0;
static HMQ qt_gui_queue = 0;
//@@TODO (dmik): later
//// Simpler timers are needed when Qt does not have the event loop,
//// such as for plugins.
//#ifndef Q_OS_TEMP
//Q_EXPORT bool	qt_win_use_simple_timers = TRUE;
//#else
//Q_EXPORT bool	qt_win_use_simple_timers = FALSE;
//#endif
//void CALLBACK   qt_simple_timer_func( HWND, UINT, UINT, DWORD );

static void	initTimers();
static void	cleanupTimers();
static bool	dispatchTimer( uint, QMSG * );
static bool	activateTimer( uint );
static void	activateZeroTimers();

static int	 numZeroTimers	= 0;		// number of full-speed timers

// a flag to disable the warning when the console process dynamically
// switches istelf to PM. Currently used by the UIC tool.
bool qt_suppress_morph_warning = false;

// Check that the current process is in PM mode. This is called by QEventLoop
// and QApplication initialization routines and by initTimers() to ensure
// that we are in PM mode and therefore it is possible to create the message
// queue. "Morphing" to PM leaves the console attahed which can be used for
// debugging.
void qt_ensure_pm()
{
    PPIB ppib;
    DosGetInfoBlocks( NULL, &ppib );
    if( ppib->pib_ultype != 3 ) {
#if defined(QT_CHECK_STATE)
        if( !qt_suppress_morph_warning )
            qWarning(
                "Qt: the program has not been linked as the Presentation Manager application\n"
                "    but it uses GUI capabilities. Switching to PM mode dynamically."
            );
#endif
        ppib->pib_ultype = 3;
    }
}

/*****************************************************************************
  Safe configuration (move,resize,setGeometry) mechanism to avoid
  recursion when processing messages.
 *****************************************************************************/

#include "qptrqueue.h"

struct QPMConfigRequest {
    WId	 id;					// widget to be configured
    int	 req;					// 0=move, 1=resize, 2=setGeo
    int	 x, y, w, h;				// request parameters
};

static QPtrQueue<QPMConfigRequest> *configRequests = 0;

void qPMRequestConfig( WId id, int req, int x, int y, int w, int h )
{
    if ( !configRequests )			// create queue
	configRequests = new QPtrQueue<QPMConfigRequest>;
    QPMConfigRequest *r = new QPMConfigRequest;
    r->id = id;					// create new request
    r->req = req;
    r->x = x;
    r->y = y;
    r->w = w;
    r->h = h;
    configRequests->enqueue( r );		// store request in queue
}

Q_EXPORT void qPMProcessConfigRequests()		// perform requests in queue
{
    if ( !configRequests )
	return;
    QPMConfigRequest *r;
    for ( ;; ) {
	if ( configRequests->isEmpty() )
	    break;
	r = configRequests->dequeue();
	QWidget *w = QWidget::find( r->id );
	if ( w ) {				// widget exists
	    if ( w->testWState(Qt::WState_ConfigPending) )
		return;				// biting our tail
	    if ( r->req == 0 )
		w->move( r->x, r->y );
	    else if ( r->req == 1 )
		w->resize( r->w, r->h );
	    else
		w->setGeometry( r->x, r->y, r->w, r->h );
	}
	delete r;
    }
    delete configRequests;
    configRequests = 0;
}

/*****************************************************************************
  Timer handling; Our routines depend on OS/2 PM timer functions, but we
  need some extra handling to activate objects at timeout.

  Implementation note: There are two types of timer identifiers. PM
  timer ids (internal use) are stored in TimerInfo.  Qt timer ids are
  indexes (+1) into the timerVec vector.

  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
//

#include "qptrvector.h"
#include "qintdict.h"

struct TimerInfo {				// internal timer info
    uint     ind;				// - Qt timer identifier - 1
    ULONG    id;				// - PM timer identifier
    bool     zero;				// - zero timing
    QObject *obj;				// - object to receive events
};
typedef QPtrVector<TimerInfo>  TimerVec;		// vector of TimerInfo structs
typedef QIntDict<TimerInfo> TimerDict;		// fast dict of timers

static TimerVec  *timerVec  = 0;		// timer vector
static TimerDict *timerDict = 0;		// timer dict


//@@TODO (dmik): later (needed for plugin support)
//void CALLBACK qt_simple_timer_func( HWND, UINT, UINT idEvent, DWORD )
//{
//    dispatchTimer( idEvent, 0 );
//}


// Activate a timer, used by both event-loop based and simple timers.

static bool dispatchTimer( uint timerId, QMSG *msg )
{
    MRESULT res = NULL;
    if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
	return activateTimer( timerId );
    return TRUE;
}


//
// Timer activation (called from the event loop when WM_TIMER arrives)
//

static bool activateTimer( uint id )		// activate timer
{
    if ( !timerVec )				// should never happen
	return FALSE;
    register TimerInfo *t = timerDict->find( id );
    if ( !t )					// no such timer id
	return FALSE;
    QTimerEvent e( t->ind + 1 );
    QApplication::sendEvent( t->obj, &e );	// send event
    return TRUE;				// timer event was processed
}

static void activateZeroTimers()		// activate full-speed timers
{
    if ( !timerVec )
	return;
    uint i=0;
    register TimerInfo *t = 0;
    int n = numZeroTimers;
    while ( n-- ) {
	for ( ;; ) {
	    t = timerVec->at(i++);
	    if ( t && t->zero )
		break;
	    else if ( i == timerVec->size() )		// should not happen
		return;
	}
	QTimerEvent e( t->ind + 1 );
	QApplication::sendEvent( t->obj, &e );
    }
}


//
// Timer initialization and cleanup routines
//

static void initTimers()			// initialize timers
{
//@@TODO (dmik): qt_ensure_pm() should not be called when simple timers are
//  implemented to be used by plugins.
    qt_ensure_pm();
    timerVec = new TimerVec( 128 );
    Q_CHECK_PTR( timerVec );
    timerVec->setAutoDelete( TRUE );
    timerDict = new TimerDict( 29 );
    Q_CHECK_PTR( timerDict );
}

static void cleanupTimers()			// remove pending timers
{
    register TimerInfo *t;
    if ( !timerVec )				// no timers were used
	return;
    for ( uint i=0; i<timerVec->size(); i++ ) {		// kill all pending timers
	t = timerVec->at( i );
	if ( t && !t->zero )
	    WinStopTimer( 0, 0, t->id );
    }
    delete timerDict;
    timerDict = 0;
    delete timerVec;
    timerVec  = 0;

//@@TODO (dmik): later (needed for plugin support)
//    if ( qt_win_use_simple_timers ) {
//	// Dangerous to leave WM_TIMER events in the queue if they have our
//	// timerproc (eg. Qt-based DLL plugins may be unloaded)
//	MSG msg;
//	while (winPeekMessage( &msg, (HWND)-1, WM_TIMER, WM_TIMER, PM_REMOVE ))
//	    continue;
//    }
}


//
// Main timer functions for starting and killing timers
//


int qStartTimer( int interval, QObject *obj )
{
    register TimerInfo *t;
    if ( !timerVec )				// initialize timer data
	initTimers();
    int ind = timerVec->findRef( 0 );		// get free timer
    if ( ind == -1 || !obj ) {
	ind = timerVec->size();			// increase the size
	timerVec->resize( ind * 4 );
    }
    t = new TimerInfo;				// create timer entry
    Q_CHECK_PTR( t );
    t->ind  = ind;
    t->obj  = obj;

//@@TODO (dmik): later (needed for plugin support)
//    if ( qt_win_use_simple_timers ) {
//	t->zero = FALSE;
//	t->id = SetTimer( 0, 0, (uint)interval,
//			  (TIMERPROC)qt_simple_timer_func );
//    } else {
	t->zero = interval == 0;
	if ( t->zero ) {			// add zero timer
            t->id = 0;
	    numZeroTimers++;
            // post WM_NULL to our message queue to let
            // QEventLoop::processEvents() handle a newly created zero
            // timer as soon as possible; otherwise we could get quite a
            // noticeable delay between starting and emitting the first
            // signal (longer than if we had started a regular 1 ms timer),
            // due to the internal implementation of the WinGetMsg() function.
            WinPostMsg( 0, WM_NULL, 0, 0 );
	} else {
            t->id = WinStartTimer( 0, 0, 0, (ULONG) interval );
	}
//    }
    if ( !t->zero && t->id == 0 ) {
#if defined(QT_CHECK_STATE)
	qSystemWarning( "qStartTimer: Failed to create a timer." );
#endif
	delete t;				// could not set timer
	return 0;
    }
    timerVec->insert( ind, t );			// store in timer vector
    if ( !t->zero )
        timerDict->insert( t->id, t );		// store in dict
    return ind + 1;				// return index in vector
}

bool qKillTimer( int ind )
{
    if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
	return FALSE;
    register TimerInfo *t = timerVec->at(ind-1);
    if ( !t )
	return FALSE;
    if ( t->zero ) {
	numZeroTimers--;
    } else {
	WinStopTimer( 0, 0, t->id );
        timerDict->remove( t->id );
    }
    timerVec->remove( ind-1 );
    return TRUE;
}

bool qKillTimer( QObject *obj )
{
    if ( !timerVec )
	return FALSE;
    register TimerInfo *t;
    for ( uint i=0; i<timerVec->size(); i++ ) {
	t = timerVec->at( i );
	if ( t && t->obj == obj ) {		// object found
	    if ( t->zero ) {
		numZeroTimers--;
	    } else {
		WinStopTimer( 0, 0, t->id );
                timerDict->remove( t->id );
            }
	    timerVec->remove( i );
	}
    }
    return TRUE;
}

/*****************************************************************************
 Socket notifier type
 *****************************************************************************/

QSockNotType::QSockNotType()
    : list( 0 )
{
    FD_ZERO( &select_fds );
    FD_ZERO( &enabled_fds );
    FD_ZERO( &pending_fds );
}

QSockNotType::~QSockNotType()
{
    if ( list )
	delete list;
    list = 0;
}

/*****************************************************************************
 socket select() thread
 *****************************************************************************/

#if defined(QT_THREAD_SUPPORT)

static class QSockSelectThread : public QThread
{
public:
    QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
    void run();
    void cancelSelectOrIdle( bool terminate = FALSE );
private:
    QEventLoopPrivate *d;
    bool exit;
} *ss_thread = 0;

// recursive mutex to serialize access to socket notifier data
static QMutex ss_mutex( TRUE );
// flag to indicate a presence of sockets to do select() (we use QSemaphore
// instead of QWaitCondition because we need the "level-triggered" semantics,
// the "edge-triggered" one can produce deadlocks)
static QSemaphore ss_flag( 1 );

void QSockSelectThread::run()
{
    while ( !exit ) {
        ss_mutex.lock();
        if ( d->sn_highest >= 0 ) {		// has socket notifier(s)
            // read
            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 );
            // write
            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 );
            // except
            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 );
            // do select
            int nfds = d->sn_highest + 1;
            ss_mutex.unlock();
            int nsel = ::select( nfds,
                               &d->sn_vec[0].select_fds,
                               &d->sn_vec[1].select_fds,
                               &d->sn_vec[2].select_fds,
                               NULL );
            if ( nsel > 0 ) {
                ss_mutex.lock();
                // 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;
                    QPtrList<QSockNot> *list = d->sn_vec[i].list;
                    QSockNot *sn = list->first();
                    while ( sn ) {
                        if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
                            // see comments inside QEventLoop::setSocketNotifierPending()
                            if ( !FD_ISSET( sn->fd, sn->queue ) ) {
                                d->sn_pending_list.insert(
                                    (rand() & 0xff) % (d->sn_pending_list.count()+1),
                                    sn
                                );
                                FD_SET( sn->fd, sn->queue );
                            }
                        }
                        sn = list->next();
                    }
                }
                ss_mutex.unlock();
                // wake up gui thread to let it activate notifiers
                WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
            }
        } else {
            // no sockets to select(), go to the idle state
            ss_mutex.unlock();
            // wait for the ability to capture the flag
            ss_flag ++;
            // release the flag before termination
            if ( exit )
                ss_flag --;
        }
    }
}

void QSockSelectThread::cancelSelectOrIdle( bool terminate )
{
    exit = terminate;
    if ( d->sn_highest >= 0 ) {
        // terminate select() execution
        ::so_cancel( d->sn_highest );
    } else {
        // terminate the idle state by releasing the flag
        if ( !ss_flag.available() )
            ss_flag --;
    }
    // wait for this thread to end if the termination is requested
    if ( exit )
        wait();
}

static void ss_cleanup()
{
    ss_thread->cancelSelectOrIdle( TRUE );
    delete ss_thread;
}

static void ss_init( QEventLoopPrivate *d )
{
    if ( ss_thread ) {
        // the user has created a second QEventLoop instance, it should
        // completely replace the previous (see also QEventLoop::QEventLoop()).
        ss_cleanup();
    } else {
        qAddPostRoutine( ss_cleanup );
    }
    // capture the flag initially
    ss_flag ++;
    ss_thread = new QSockSelectThread( d );
    ss_thread->start();
}

#endif // QT_THREAD_SUPPORT

/*****************************************************************************
 QEventLoop Implementation for OS/2 PM
 *****************************************************************************/

void QEventLoop::init()
{
    d->sn_highest = -1;

    qt_ensure_pm();
    d->hab = WinInitialize( 0 );
    d->hmq = WinCreateMsgQueue( d->hab, 0 );
    qt_gui_hab = d->hab;
    qt_gui_queue = d->hmq;

#if defined(QT_THREAD_SUPPORT)
    ss_init( d );
#else
#if defined(QT_CHECK_STATE)
    qWarning(
        "QEventLoop::init: socket notifiers are not supported "
        "for a single-threaded version of Qt."
    );
#endif
#endif // QT_THREAD_SUPPORT
}

void QEventLoop::cleanup()
{
    cleanupTimers();

    WinDestroyMsgQueue( d->hmq );
    WinTerminate( d->hab );
    qt_gui_queue = 0;
    qt_gui_hab = 0;
}

void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
{
#if defined(QT_THREAD_SUPPORT)
    int sockfd = -1;
    int type = -1;
    if ( notifier ) {
        sockfd = notifier->socket();
        type = notifier->type();
    }
    if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
#if defined(QT_CHECK_RANGE)
	qWarning( "QSocketNotifier: Internal error" );
#endif
	return;
    }

    QMutexLocker locker( &ss_mutex );
    ss_thread->cancelSelectOrIdle();

    QPtrList<QSockNot>  *list = d->sn_vec[type].list;
    fd_set *fds  = &d->sn_vec[type].enabled_fds;
    QSockNot *sn;

    if ( ! list ) {
	// create new list, the QSockNotType destructor will delete it for us
	list = new QPtrList<QSockNot>;
	Q_CHECK_PTR( list );
	list->setAutoDelete( TRUE );
	d->sn_vec[type].list = list;
    }

    sn = new QSockNot;
    Q_CHECK_PTR( sn );
    sn->obj = notifier;
    sn->fd = sockfd;
    sn->queue = &d->sn_vec[type].pending_fds;

    if ( list->isEmpty() ) {
	list->insert( 0, sn );
    } else {				// sort list by fd, decreasing
	QSockNot *p = list->first();
	while ( p && p->fd > sockfd )
	    p = list->next();
#if defined(QT_CHECK_STATE)
	if ( p && p->fd == sockfd ) {
	    static const char *t[] = { "read", "write", "exception" };
	    qWarning( "QSocketNotifier: Multiple socket notifiers for "
		      "same socket %d and type %s", sockfd, t[type] );
	}
#endif
	if ( p )
	    list->insert( list->at(), sn );
	else
	    list->append( sn );
    }

    FD_SET( sockfd, fds );
    d->sn_highest = QMAX( d->sn_highest, sockfd );
#endif // QT_THREAD_SUPPORT
}

void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
{
#if defined(QT_THREAD_SUPPORT)
    int sockfd = -1;
    int type = -1;
    if ( notifier ) {
        sockfd = notifier->socket();
        type = notifier->type();
    }
    if ( sockfd < 0 || type < 0 || type > 2 ) {
#if defined(QT_CHECK_RANGE)
	qWarning( "QSocketNotifier: Internal error" );
#endif
	return;
    }

    QMutexLocker locker( &ss_mutex );
    ss_thread->cancelSelectOrIdle();

    QPtrList<QSockNot> *list = d->sn_vec[type].list;
    fd_set *fds  =  &d->sn_vec[type].enabled_fds;
    QSockNot *sn;
    if ( ! list )
	return;
    sn = list->first();
    while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
	sn = list->next();
    if ( !sn ) // not found
	return;

    FD_CLR( sockfd, fds );			// clear fd bit
    FD_CLR( sockfd, sn->queue );
    d->sn_pending_list.removeRef( sn );		// remove from activation list
    list->remove();				// remove notifier found above

    if ( d->sn_highest == sockfd ) {		// find highest fd
	d->sn_highest = -1;
	for ( int i=0; i<3; i++ ) {
	    if ( d->sn_vec[i].list && ! d->sn_vec[i].list->isEmpty() )
		d->sn_highest = QMAX( d->sn_highest,  // list is fd-sorted
				      d->sn_vec[i].list->getFirst()->fd );
	}
    }
#endif // QT_THREAD_SUPPORT
}

void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
{
#if defined(QT_THREAD_SUPPORT)
    int sockfd = -1;
    int type = -1;
    if ( notifier ) {
        sockfd = notifier->socket();
        type = notifier->type();
    }
    if ( sockfd < 0 || type < 0 || type > 2 ) {
#if defined(QT_CHECK_RANGE)
	qWarning( "QSocketNotifier: Internal error" );
#endif
	return;
    }

    QMutexLocker locker( &ss_mutex );

    QPtrList<QSockNot> *list = d->sn_vec[type].list;
    QSockNot *sn;
    if ( ! list )
	return;
    sn = list->first();
    while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
	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 ( !FD_ISSET( sn->fd, sn->queue ) ) {
	d->sn_pending_list.insert(
            (rand() & 0xff) % (d->sn_pending_list.count()+1),
            sn
        );
	FD_SET( sn->fd, sn->queue );
    }
#endif // QT_THREAD_SUPPORT
}

bool QEventLoop::hasPendingEvents() const
{
    QMSG msg;
    return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
}

bool QEventLoop::processEvents( ProcessEventsFlags flags )
{
    QMSG msg;

#if defined(QT_THREAD_SUPPORT)
    QMutexLocker locker( QApplication::qt_mutex );
#endif
    emit awake();
    emit qApp->guiThreadAwake();

    QApplication::sendPostedEvents();

    if ( flags & ExcludeUserInput ) {
	while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
	    if ( msg.msg == WM_CHAR ||
		 (msg.msg >= WM_MOUSEFIRST &&
                     msg.msg <= WM_MOUSELAST) ||
		 (msg.msg >= WM_EXTMOUSEFIRST &&
                     msg.msg <= WM_EXTMOUSELAST) ||
                 msg.msg == WM_HSCROLL ||
                 msg.msg == WM_VSCROLL
            ) {
		WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
		continue;
	    }
	    break;
	}
    }

    bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);

    if ( canWait ) {				// can wait if necessary
	if ( numZeroTimers ) {			// activate full-speed timers
	    int ok = FALSE;
            while ( numZeroTimers && !ok ) {
		activateZeroTimers();
		ok = WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
	    }
	    if ( !ok )	{			// no event
		return FALSE;
	    }
	} else {
	    if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) )
		emit aboutToBlock();
#ifdef QT_THREAD_SUPPORT
	    locker.mutex()->unlock();
#endif // QT_THREAD_SUPPORT
	    if ( !WinGetMsg( 0, &msg, 0, 0, 0 ) ) {
#ifdef QT_THREAD_SUPPORT
		locker.mutex()->lock();
#endif // QT_THREAD_SUPPORT
		exit( 0 );				// WM_QUIT received
		return FALSE;
	    }
#ifdef QT_THREAD_SUPPORT
	    locker.mutex()->lock();
#endif // QT_THREAD_SUPPORT
	}
    } else {					// no-wait mode
	if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) { // no pending events
	    if ( numZeroTimers > 0 ) {		// there are 0-timers
		activateZeroTimers();
	    }
	    return FALSE;
	}
    }

    bool handled = FALSE;
    if ( msg.msg == WM_TIMER ) {		// timer message received
	if ( dispatchTimer( (uint)SHORT1FROMMP(msg.mp1), &msg ) )
	    return TRUE;
    } else if ( msg.msg && (!msg.hwnd || !QWidget::find(msg.hwnd)) ) {
	MRESULT res = 0;
	handled = qt_pmEventFilter( &msg, res );
    }

    if ( !handled ) {
	WinDispatchMsg( 0, &msg );              // send to QtWndProc
    }

    if ( !(flags & ExcludeSocketNotifiers) )
	activateSocketNotifiers();

    if ( configRequests )			// any pending configs?
	qPMProcessConfigRequests();
    QApplication::sendPostedEvents();

    return TRUE;
}

void QEventLoop::wakeUp()
{
    PTIB ptib;
    DosGetInfoBlocks( &ptib, NULL );
    MQINFO mqinfo;
    WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
    if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
        WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
}

int QEventLoop::timeToWait() const
{
    return -1;
}

int QEventLoop::activateTimers()
{
    return 0;
}

int QEventLoop::activateSocketNotifiers()
{
#if defined(QT_THREAD_SUPPORT)
    if ( d->sn_pending_list.isEmpty() )
	return 0;

    // postpone activation if ss_thread is working with the list
    if ( !ss_mutex.tryLock() )
        return 0;

    // activate entries
    int n_act = 0;
    QEvent event( QEvent::SockAct );
    QPtrListIterator<QSockNot> it( d->sn_pending_list );
    QSockNot *sn;
    while ( (sn = it.current()) ) {
	++it;
	d->sn_pending_list.removeRef( sn );
	if ( FD_ISSET(sn->fd, sn->queue) ) {
	    FD_CLR( sn->fd, sn->queue );
	    QApplication::sendEvent( sn->obj, &event );
	    n_act++;
	}
    }

    ss_mutex.unlock();

    return n_act;
#else
    return 0;
#endif // QT_THREAD_SUPPORT
}

