source: trunk/src/kernel/qeventloop_pm.cpp@ 177

Last change on this file since 177 was 177, checked in by dmik, 18 years ago

kernel: Don't touch zero timers on the timer thread.

  • Property svn:keywords set to Id
File size: 49.4 KB
Line 
1/****************************************************************************
2** $Id: qeventloop_pm.cpp 177 2008-02-19 22:33:41Z dmik $
3**
4** Implementation of OS/2 startup routines and event handling
5**
6** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qeventloop_p.h"
39#include "qeventloop.h"
40#include "qapplication.h"
41#include "qptrlist.h"
42#include "qvaluelist.h"
43
44#include <sys/socket.h>
45
46#if defined(QT_THREAD_SUPPORT)
47# include "qmutex.h"
48# include "qthread.h"
49# include "qsemaphore.h"
50#endif // QT_THREAD_SUPPORT
51
52// entry point: PMWIN.305
53extern "C" HMQ APIENTRY WinQueueFromID( HAB hab, PID pid, PID tid );
54
55extern uint qGlobalPostedEventsCount();
56extern bool qt_pmEventFilter( QMSG* msg, MRESULT &result );
57
58#if !defined (QT_NO_SESSIONMANAGER)
59extern bool qt_app_canQuit(); // defined in qapplication_pm.cpp
60#endif
61
62static HAB qt_gui_hab = 0;
63static HMQ qt_gui_queue = 0;
64
65#ifdef QT_PM_NO_DOSTIMERS
66static bool dispatchTimer( uint, QMSG * );
67#endif
68static void activateZeroTimers();
69
70static void initTimers();
71static void cleanupTimers();
72
73static int numZeroTimers = 0; // number of full-speed timers
74
75// a flag to disable the warning when the console process dynamically
76// switches istelf to PM. Currently used by the UIC tool.
77bool qt_suppress_morph_warning = false;
78
79// Check that the current process is in PM mode. This is called by QEventLoop
80// and QApplication initialization routines and by initTimers() to ensure
81// that we are in PM mode and therefore it is possible to create the message
82// queue. "Morphing" to PM leaves the console attahed which can be used for
83// debugging.
84void qt_ensure_pm()
85{
86 PPIB ppib;
87 DosGetInfoBlocks( NULL, &ppib );
88 if( ppib->pib_ultype != 3 ) {
89#if defined(QT_CHECK_STATE)
90 if( !qt_suppress_morph_warning )
91 qWarning( "Qt: the program has not been linked as the Presentation "
92 "Manager application\n"
93 "but it uses GUI capabilities. Switching to PM mode "
94 "dynamically." );
95#endif
96 ppib->pib_ultype = 3;
97 }
98}
99
100enum
101{
102 // socket select notification (highest priority)
103 WM_U_SEM_SELECT = WM_SEM1,
104 // zero timer notification (lowest priority)
105 WM_U_SEM_ZEROTIMER = WM_SEM4,
106};
107
108/*****************************************************************************
109 Auxiliary object window class for dedicated message processing.
110 Declared in qwindowdefs_pm.h
111 *****************************************************************************/
112
113/*!
114 \class QPMObjectWindow qwindowdefs.h
115
116 The QPMObjectWindow class is an auxiliary class for dedicated message
117 processing. Its functionality is based on PM object windows. Once an instance
118 of this class is created, PM window messages can be sent or posted to it using
119 send() or post() methods. Subclasses should override the message() method to
120 process sent or posted messages. The hwnd() method is used whenever a PM
121 window handle of this object window is necessary to be passed as a HWND
122 argument to other calls and/or messages.
123
124 Instances of this class must be created on the main GUI thread only or on a
125 thread that has created a PM message queue and runs the PM message loop
126 \b itself. If you create an instance on a thread other than main, make sure
127 you destroy it before destroying the thread's message queue.
128
129 \note WM_CREATE and WM_DESTROY messages are processed internally and not
130 delivered do the message() method. Instead, you can use the constructor and
131 the destructor of the subclasses, respectively.
132
133 \note This class is non-portable!
134*/
135
136// returns HMQ of the current thread or NULL if no event queue has been created
137static HMQ qt_WinQueryQueue( HAB hab )
138{
139 PTIB ptib;
140 PPIB ppib;
141 DosGetInfoBlocks( &ptib, &ppib );
142 return WinQueueFromID( hab, ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid );
143}
144
145static QPtrList<HWND> qt_objWindows;
146
147/*!
148 Constructs a new object window for the current thread.
149 If \a deferred is FALSE, this method calls create() to create a PM object
150 window. Otherwise, you must call create() yourself before this object
151 window is able to process messages.
152*/
153QPMObjectWindow::QPMObjectWindow( bool deferred /* = FALSE */ ) :
154 w( NULLHANDLE )
155{
156 if ( !deferred )
157 create();
158}
159
160/*!
161 Destroys this object window.
162 This method calls destroy() to free the PM object window.
163*/
164QPMObjectWindow::~QPMObjectWindow()
165{
166 destroy();
167}
168
169/*!
170 Creates a PM object window.
171 Returns \c TRUE on success or \c FALSE otherwise.
172 The method does nothing but returns \c FALSE if the window has been
173 already created. The handle of the successfully created object window can
174 be obtained using the hwnd() method.
175
176 \note Must be called on the same thread that cosnstructed this instance.
177*/
178bool QPMObjectWindow::create()
179{
180 if ( w != NULLHANDLE )
181 return FALSE;
182
183 static const char *ClassName = "QPMObjectWindow";
184 static bool classRegistered = FALSE;
185
186 if ( !classRegistered ) {
187 WinRegisterClass( 0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID) );
188 classRegistered = TRUE;
189 }
190
191 w = WinCreateWindow( HWND_OBJECT, ClassName,
192 NULL, 0, 0, 0, 0, 0, NULL,
193 HWND_BOTTOM, 0, this, NULL );
194#ifndef QT_NO_DEBUG
195 if ( w == NULLHANDLE )
196 qSystemWarning( "QPMObjectWindow: Failed to create object window" );
197#endif
198
199 if ( w != NULLHANDLE && qt_gui_queue &&
200 qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
201 // store the handle in the list for clean destruction
202 qt_objWindows.append( &w );
203 }
204
205 return w != NULLHANDLE;
206}
207
208/*!
209 Destroys the PM object window.
210 Returns \c TRUE on success or \c FALSE otherwise.
211 The method does nothing but returns \c FALSE if the window has been
212 already destroyed (or never created).
213
214 \note Must be called on the same thread that cosnstructed this instance.
215*/
216bool QPMObjectWindow::destroy()
217{
218 if ( w == NULLHANDLE )
219 return FALSE;
220
221 if ( qt_gui_queue && qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
222 // remove the handle from the 'clean destruction' list
223 qt_objWindows.removeRef( &w );
224 }
225
226 HWND h = w;
227 w = 0; // tell windowProc() we're unsafe
228 WinDestroyWindow( h );
229
230 return TRUE;
231}
232
233//static
234MRESULT EXPENTRY QPMObjectWindow::windowProc( HWND hwnd, ULONG msg,
235 MPARAM mp1, MPARAM mp2 )
236{
237 if ( msg == WM_CREATE ) {
238 QPMObjectWindow *that = static_cast< QPMObjectWindow *>( mp1 );
239 if ( !that )
240 return (MRESULT) TRUE;
241 WinSetWindowPtr( hwnd, QWL_USER, that );
242 return (MRESULT) FALSE;
243 }
244
245 QPMObjectWindow *that =
246 static_cast< QPMObjectWindow * >( WinQueryWindowPtr( hwnd, QWL_USER ) );
247 Q_ASSERT( that );
248
249 // Note: before WinCreateWindow() returns to the constructor or after the
250 // destructor has been called, w is 0. We use this to determine that the
251 // object is in the unsafe state (VTBL is not yet initialized or has been
252 // already uninitialized), so message() points to never-never land.
253 if ( !that || !that->w )
254 return (MRESULT) FALSE;
255
256 return that->message( msg, mp1, mp2 );
257}
258
259/*!
260 \fn QPMObjectWindow::send( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
261
262 Synchronously sends a message \a msg with the given parameters \a mp1 and
263 \a mp2 to this window handle and returns a reply from the message() function.
264
265 \note Must be called on the same thread that cosnstructed this instance.
266*/
267
268/*!
269 \fn QPMObjectWindow::post( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
270
271 Asynchronously posts a message \a msg with the given parameters \a mp1 and
272 \a mp2 to this window handle. Returns \c TRUE on success and \c FALSE
273 otherwise.
274
275 \note Can be called on any thread.
276*/
277
278// Auxiliary object window to process WM_U_SEM_SELECT and WM_TIMER messages.
279// We need a dedicated window along with processing these messages directly in
280// QEventLoop::processEvents() to make sure they are processed even if the
281// current message loop is not run by Qt. This happens when a native modal
282// dialog is shown or when a top-level Qt widget is being moved or resized
283// using the mouse, or when a Qt-based DLL plugin is used by a non-Qt
284// application.
285
286class QPMAuxEventQueueWin : public QPMObjectWindow
287{
288public:
289 QPMAuxEventQueueWin() : QPMObjectWindow( TRUE ), eventLoop( NULL ) {}
290 void setEventLoop( QEventLoop *el ) { eventLoop = el; }
291 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
292private:
293 QEventLoop *eventLoop;
294};
295
296MRESULT QPMAuxEventQueueWin::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
297{
298 switch ( msg ) {
299 case WM_U_SEM_SELECT: {
300 if ( eventLoop )
301 eventLoop->activateSocketNotifiers();
302 break;
303 }
304 case WM_U_SEM_ZEROTIMER: {
305 if ( numZeroTimers ) {
306 activateZeroTimers();
307 // repost the message if there are still zero timers left
308 if ( numZeroTimers )
309 WinPostMsg( hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
310 }
311 break;
312 }
313 case WM_TIMER: {
314#ifndef QT_PM_NO_DOSTIMERS
315 QApplication::sendPostedEvents( NULL, QEvent::Timer );
316 break;
317#else // ifndef QT_PM_NO_DOSTIMERS
318 USHORT id = SHORT1FROMMP( mp1 );
319 QMSG qmsg = { hwnd(), msg, mp1, mp2 };
320 dispatchTimer( (uint) id, &qmsg );
321 break;
322#endif // ifndef QT_PM_NO_DOSTIMERS
323 }
324 }
325
326 return FALSE;
327}
328
329static QPMAuxEventQueueWin qt_aux_win;
330
331
332/*****************************************************************************
333 Safe configuration (move,resize,setGeometry) mechanism to avoid
334 recursion when processing messages.
335 *****************************************************************************/
336
337#include "qptrqueue.h"
338
339struct QPMConfigRequest {
340 WId id; // widget to be configured
341 int req; // 0=move, 1=resize, 2=setGeo
342 int x, y, w, h; // request parameters
343};
344
345static QPtrQueue<QPMConfigRequest> *configRequests = 0;
346
347void qPMRequestConfig( WId id, int req, int x, int y, int w, int h )
348{
349 if ( !configRequests ) // create queue
350 configRequests = new QPtrQueue<QPMConfigRequest>;
351 QPMConfigRequest *r = new QPMConfigRequest;
352 r->id = id; // create new request
353 r->req = req;
354 r->x = x;
355 r->y = y;
356 r->w = w;
357 r->h = h;
358 configRequests->enqueue( r ); // store request in queue
359}
360
361Q_EXPORT void qPMProcessConfigRequests() // perform requests in queue
362{
363 if ( !configRequests )
364 return;
365 QPMConfigRequest *r;
366 for ( ;; ) {
367 if ( configRequests->isEmpty() )
368 break;
369 r = configRequests->dequeue();
370 QWidget *w = QWidget::find( r->id );
371 if ( w ) { // widget exists
372 if ( w->testWState(Qt::WState_ConfigPending) )
373 return; // biting our tail
374 if ( r->req == 0 )
375 w->move( r->x, r->y );
376 else if ( r->req == 1 )
377 w->resize( r->w, r->h );
378 else
379 w->setGeometry( r->x, r->y, r->w, r->h );
380 }
381 delete r;
382 }
383 delete configRequests;
384 configRequests = 0;
385}
386
387/*****************************************************************************
388 Timer handling; Our routines depend on OS/2 PM timer functions, but we
389 need some extra handling to activate objects at timeout.
390
391 There are two implemetations of Qt timers. When QT_PM_NO_DOSTIMERS is defined
392 (NOT by default), Qt uses PM timers. In this case, there are two types of
393 timer identifiers. PM timer ids (internal use) are stored in TimerInfo. Qt
394 timer ids are indexes (+1) into the timerVec vector. Note that this PM timer
395 based implementation is error-prone due to stupid PM limitations: there are
396 only several dozens of PM timers per the whole system and this number is
397 shared across all processes (e.g. if you create too many timers, other
398 applications that also need them will suck).
399
400 When QT_PM_NO_DOSTIMERS is not defined (by default), Qt uses its own timer
401 implementation based on a signle DosAsyncTimer() and a dedicated timer thread.
402 This implementation allows virtually unlimited number of Qt timers. In this
403 case, there is only one type of type identifiers: Qt timer identifiers are
404 indexes (+1) into the timeDict dictionary.
405
406 NOTE: These functions are for internal use. QObject::startTimer() and
407 QObject::killTimer() are for public use.
408 The QTimer class provides a high-level interface which translates
409 timer events into signals.
410
411 qStartTimer( interval, obj )
412 Starts a timer which will run until it is killed with qKillTimer()
413 Arguments:
414 int interval timer interval in milliseconds
415 QObject *obj where to send the timer event
416 Returns:
417 int timer identifier, or zero if not successful
418
419 qKillTimer( timerId )
420 Stops a timer specified by a timer identifier.
421 Arguments:
422 int timerId timer identifier
423 Returns:
424 bool TRUE if successful
425
426 qKillTimer( obj )
427 Stops all timers that are sent to the specified object.
428 Arguments:
429 QObject *obj object receiving timer events
430 Returns:
431 bool TRUE if successful
432 *****************************************************************************/
433
434//
435// Internal data structure for timers
436//
437
438//
439// Template to track availability of free (unused) integer values within some
440// interval. Note that take() and give() maintain spans sorted in ascending
441// order.
442//
443template <typename T>
444class QFreeValueList
445{
446public:
447 QFreeValueList( T min, T max ) : intMin( min ), intMax( max ) {
448 freeValues.push_front( Span( min, max ) );
449 }
450 T take() {
451 Q_ASSERT( !isEmpty() );
452 Span &free = freeValues.first();
453 T freeValue = free.min;
454 if ( free.min == free.max ) freeValues.pop_front();
455 else free.min ++;
456 return freeValue;
457 }
458 void give( T val ) {
459 Q_ASSERT( val >= intMin && val <= intMax );
460 // look for the span to join
461 typename FreeList::iterator it = freeValues.begin();
462 for ( ; it != freeValues.end(); ++ it ) {
463 Span &span = (*it);
464 if ( val == span.min - 1 ) {
465 // join the less end of span
466 span.min --;
467 typename FreeList::iterator it2 = it;
468 if ( it2 != freeValues.begin() && (*--it2).max + 1 == span.min ) {
469 span.min = (*it2).min;
470 freeValues.erase( it2 );
471 }
472 return;
473 } else if ( val == span.max + 1 ) {
474 // join the greater end of span
475 span.max ++;
476 typename FreeList::iterator it2 = it;
477 if ( ++it2 != freeValues.end() && (*it2).min - 1 == span.max ) {
478 span.max = (*it2).max;
479 freeValues.erase( it2 );
480 }
481 return;
482 } else if ( val < span.min ) {
483 // all the following spans are too "far" to join
484 break;
485 }
486 // span must not include val (contradicts take())
487 Q_ASSERT( val > span.max );
488 }
489 if ( it == freeValues.end() ) freeValues.push_back( Span( val, val ) );
490 else freeValues.insert( it, Span( val, val ) );
491 }
492 bool isEmpty() { return freeValues.isEmpty(); }
493 T min() const { return intMin; }
494 T max() const { return intMax; }
495private:
496 struct Span {
497 Span() : min( 0 ), max( 0 ) {}
498 Span( T mn, T mx ) : min( mn ), max( mx ) {}
499 T min;
500 T max;
501 };
502 typedef QValueList<Span> FreeList;
503 FreeList freeValues;
504 T intMin;
505 T intMax;
506};
507
508#ifndef QT_PM_NO_DOSTIMERS
509
510#include "qintdict.h"
511
512struct TimerInfo { // internal timer info
513 uint ind; // - Qt timer identifier - 1 (= index in vector)
514 ULONG interval; // - timeout interval, milliseconds
515 ULONG last; // - last shot timestamp
516 QObject *obj; // - object to receive events
517};
518
519typedef QIntDict<TimerInfo> TimerDict; // dict of TimerInfo structs
520static TimerDict *timerDict = NULL;
521
522static QMutex timerMutex; // mutex protecting timerDict
523
524typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
525static FreeQtTIDList *freeQtTIDs = NULL;
526
527//
528// Auxiliary timer thread to wait for Dos timer events and post Qt timer events.
529//
530static class QTimerThread : public QThread
531{
532public:
533 QTimerThread();
534 virtual ~QTimerThread();
535 void run();
536 void signalQuit();
537 void ensureShot( int ival );
538private:
539 HEV hev; // OS/2 timer event semaphore
540 HTIMER htimer; // OS/2 timer
541 ULONG interval; // OS/2 timer interval
542 bool quit : 1; // quit flag
543} *timerThread = NULL;
544
545QTimerThread::QTimerThread()
546 : hev( NULLHANDLE ), htimer( NULLHANDLE )
547 , interval( 0 ), quit( false )
548{
549 APIRET rc;
550 rc = DosCreateEventSem( NULL, &hev, DC_SEM_SHARED, 0 /* reset */);
551 Q_ASSERT( rc == NO_ERROR );
552}
553
554QTimerThread::~QTimerThread()
555{
556 if ( hev != NULLHANDLE ) {
557 DosCloseEventSem( hev );
558 hev = NULLHANDLE;
559 }
560}
561
562void QTimerThread::run()
563{
564 APIRET rc;
565 ULONG now = 0;
566
567 do {
568 rc = DosWaitEventSem( hev, SEM_INDEFINITE_WAIT );
569 Q_ASSERT( rc == NO_ERROR );
570 if ( quit )
571 break;
572
573 // get current time again and calculate the interval
574 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &now, sizeof(now) );
575
576 // go through all timers and see which were shot
577
578 QMutexLocker lock( &timerMutex );
579
580 ULONG minLeft = ULONG_MAX;
581
582 QIntDictIterator<TimerInfo> it( *timerDict );
583 for ( register TimerInfo *t; (t = it.current()); ++it ) {
584 // skip zero timers
585 if ( t->interval == 0 )
586 continue;
587 ULONG spent = now - t->last;
588 if ( spent >= t->interval ) {
589 // the timer iterval has expired, post the timer event
590 QTimerEvent *e = new QTimerEvent( t->ind + 1 );
591 QApplication::postEvent( t->obj, e );
592 // set the new last stamp
593 t->last += t->interval * (spent / t->interval);
594 }
595 // calculate minimal time to the next shot
596 minLeft = QMIN( minLeft, t->interval - (spent % t->interval) );
597 }
598
599 if ( timerDict->count() > 0 ) {
600 // post a spare WM_TIMER message to make sure timers run even when
601 // the message loop is not controlled by QEventLoop::processEvents()
602 // (e.g. when moving or resizing a window using the mouse)
603 WinPostMsg( qt_aux_win.hwnd(), WM_TIMER, 0, 0 );
604 // restart the OS/2 timer
605 interval = minLeft;
606 ULONG postCnt;
607 DosResetEventSem( hev, &postCnt );
608 // check for quit (cancelQuit() could post the semaphore just before
609 // we reset it above)
610 if ( quit )
611 break;
612 rc = DosAsyncTimer( minLeft, (HSEM) hev, &htimer );
613 Q_ASSERT( rc == NO_ERROR );
614 } else {
615 htimer = NULLHANDLE;
616 interval = 0;
617 }
618
619 } while (1);
620}
621
622void QTimerThread::signalQuit()
623{
624 quit = true;
625 DosPostEventSem( hev );
626 wait();
627}
628
629// Note: must be called from under timerMutex!
630void QTimerThread::ensureShot( int ival )
631{
632 Q_ASSERT( timerMutex.locked() );
633 Q_ASSERT( ival > 0 );
634 if ( interval == 0 || interval > (ULONG) ival ) {
635 // start another timer to make sure the new Qt timer is fired off in time
636 interval = (ULONG) ival;
637 APIRET rc = DosAsyncTimer( interval, (HSEM) hev, &htimer );
638 Q_ASSERT( rc == NO_ERROR );
639 }
640}
641
642#else // ifndef QT_PM_NO_DOSTIMERS
643
644#include "qptrvector.h"
645#include "qintdict.h"
646
647struct TimerInfo { // internal timer info
648 uint ind; // - Qt timer identifier - 1
649 ULONG id; // - PM timer identifier
650 bool zero; // - zero timing
651 QObject *obj; // - object to receive events
652};
653typedef QPtrVector<TimerInfo> TimerVec; // vector of TimerInfo structs
654typedef QIntDict<TimerInfo> TimerDict; // fast dict of timers
655
656static TimerVec *timerVec = 0; // timer vector
657static TimerDict *timerDict = 0; // timer dict
658
659typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
660static FreeQtTIDList *freeQtTIDs = 0;
661typedef QFreeValueList<ULONG> FreePMTIDList; // list of free PM timer IDs
662static FreePMTIDList *freePMTIDs = 0;
663
664//
665// Timer activation (called from the event loop when WM_TIMER arrives)
666//
667static bool dispatchTimer( uint timerId, QMSG *msg )
668{
669 MRESULT res = NULL;
670 if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
671 {
672 if ( !timerVec ) // should never happen
673 return FALSE;
674 register TimerInfo *t = timerDict->find( timerId );
675 if ( !t ) // no such timer id
676 return FALSE;
677 QTimerEvent e( t->ind + 1 );
678 QApplication::sendEvent( t->obj, &e ); // send event
679 return TRUE; // timer event was processed
680 }
681 return TRUE;
682}
683
684#endif // ifndef QT_PM_NO_DOSTIMERS
685
686//
687// activate full-speed timers
688//
689static void activateZeroTimers()
690{
691#ifndef QT_PM_NO_DOSTIMERS
692 if ( !timerDict )
693 return;
694 QIntDictIterator<TimerInfo> it( *timerDict );
695 register TimerInfo *t = 0;
696 int n = numZeroTimers;
697 while ( n-- ) {
698 for ( ;; ) {
699 t = it();
700 Q_ASSERT( t );
701 if ( !t )
702 return;
703 if ( t->interval == 0 )
704 break;
705 }
706 QTimerEvent e( t->ind + 1 );
707 QApplication::sendEvent( t->obj, &e );
708 }
709#else
710 if ( !timerVec )
711 return;
712 uint i=0;
713 register TimerInfo *t = 0;
714 int n = numZeroTimers;
715 while ( n-- ) {
716 for ( ;; ) {
717 t = timerVec->at(i++);
718 if ( t && t->zero )
719 break;
720 else if ( i == timerVec->size() ) // should not happen
721 return;
722 }
723 QTimerEvent e( t->ind + 1 );
724 QApplication::sendEvent( t->obj, &e );
725 }
726#endif
727}
728
729
730//
731// Timer initialization and cleanup routines
732//
733
734static void initTimers() // initialize timers
735{
736#ifndef QT_PM_NO_DOSTIMERS
737 timerDict = new TimerDict( 29 );
738 Q_CHECK_PTR( timerDict );
739 timerDict->setAutoDelete( TRUE );
740 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
741 Q_CHECK_PTR( freeQtTIDs );
742 timerThread = new QTimerThread();
743 Q_CHECK_PTR( timerThread );
744 timerThread->start();
745#else // ifndef QT_PM_NO_DOSTIMERS
746 timerVec = new TimerVec( 128 );
747 Q_CHECK_PTR( timerVec );
748 timerVec->setAutoDelete( TRUE );
749 timerDict = new TimerDict( 29 );
750 Q_CHECK_PTR( timerDict );
751 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
752 Q_CHECK_PTR( freeQtTIDs );
753 freePMTIDs = new FreePMTIDList( 1, TID_USERMAX - 1 );
754 Q_CHECK_PTR( freePMTIDs );
755#endif // ifndef QT_PM_NO_DOSTIMERS
756}
757
758/// @todo (dmik) cleanupTimers() is only called by QEventLoop::cleanup() so it
759// won't be called if there is no QEventLoop instance created (for example,
760// when a Qt-based plugin DLL is used by a non-Qt application). Use atexit()?
761// Btw, the same relates to the QThread::cleanup() vs QApplication pair.
762// And to qt_aux_win (the DLL may be unloaded after the application has
763// destroyed the main event queue, so the static destructor will not be able
764// to properly destroy the window).
765
766static void cleanupTimers() // remove pending timers
767{
768#ifndef QT_PM_NO_DOSTIMERS
769 if ( !timerDict ) // no timers were used
770 return;
771 timerThread->signalQuit();
772 delete timerThread;
773 timerThread = NULL;
774 delete freeQtTIDs;
775 freeQtTIDs = NULL;
776 delete timerDict;
777 timerDict = NULL;
778#else // ifndef QT_PM_NO_DOSTIMERS
779 register TimerInfo *t;
780 if ( !timerVec ) // no timers were used
781 return;
782 for ( uint i=0; i<timerVec->size(); i++ ) { // kill all pending timers
783 t = timerVec->at( i );
784 if ( t && !t->zero )
785 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
786 }
787 delete freePMTIDs;
788 freePMTIDs = 0;
789 delete freeQtTIDs;
790 freeQtTIDs = 0;
791 delete timerDict;
792 timerDict = 0;
793 delete timerVec;
794 timerVec = 0;
795#endif // ifndef QT_PM_NO_DOSTIMERS
796}
797
798
799//
800// Main timer functions for starting and killing timers
801//
802
803
804int qStartTimer( int interval, QObject *obj )
805{
806 Q_ASSERT( obj );
807 if ( !obj || interval < 0 )
808 return 0;
809
810 // lazily create the auxiliary window to process WM_TIMER and
811 // WM_U_SEM_ZEROTIMER messages
812 if ( !qt_aux_win.hwnd() )
813 if ( !qt_aux_win.create() )
814 return 0;
815
816#ifndef QT_PM_NO_DOSTIMERS
817 if ( !timerDict ) // initialize timer data
818 initTimers();
819#else // ifndef QT_PM_NO_DOSTIMERS
820 if ( !timerVec ) // initialize timer data
821 initTimers();
822#endif // ifndef QT_PM_NO_DOSTIMERS
823
824 if ( freeQtTIDs->isEmpty() ) {
825#if defined(QT_CHECK_STATE)
826 qWarning( "qStartTimer: Maximum number of timers (%d) is reached.",
827 freeQtTIDs->max() - freeQtTIDs->min() + 1 );
828#endif
829 return 0;
830 }
831
832#ifdef QT_PM_NO_DOSTIMERS
833 if ( freePMTIDs->isEmpty() ) {
834#if defined(QT_CHECK_STATE)
835 qWarning( "qStartTimer: Maximum number of non-zero timers (%ld) is reached.",
836 freePMTIDs->max() - freePMTIDs->min() + 1 );
837#endif
838 return 0;
839 }
840#endif
841
842 int ind = freeQtTIDs->take(); // get free timer
843
844#ifndef QT_PM_NO_DOSTIMERS
845 register TimerInfo *t = new TimerInfo; // create timer entry
846 Q_CHECK_PTR( t );
847 t->ind = ind;
848 t->obj = obj;
849
850 t->interval = interval;
851 if ( t->interval == 0 ) { // add zero timer
852 numZeroTimers++;
853 // indicate there is a new zero timer
854 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
855 } else {
856 // set the initial last shot timestamp value to now
857 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &t->last, sizeof(t->last) );
858 }
859
860 QMutexLocker lock( &timerMutex );
861 timerDict->insert( ind, t ); // store in timer dict
862 if ( t->interval != 0 )
863 timerThread->ensureShot( t->interval );
864#else // ifndef QT_PM_NO_DOSTIMERS
865 if ( (uint) ind >= timerVec->size() ) {
866 uint newSize = timerVec->size() * 4; // increase the size
867 if ( newSize <= (uint) ind )
868 newSize = (uint) ind + 1;
869 timerVec->resize( newSize );
870 }
871
872 register TimerInfo *t = new TimerInfo; // create timer entry
873 Q_CHECK_PTR( t );
874 t->ind = ind;
875 t->obj = obj;
876
877 t->zero = interval == 0;
878 if ( t->zero ) { // add zero timer
879 t->id = 0;
880 numZeroTimers++;
881 // indicate there is a new zero timer
882 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
883 } else {
884 ULONG freeId = freePMTIDs->take(); // get free timer ID
885 t->id = WinStartTimer( 0, qt_aux_win.hwnd(), freeId, (ULONG) interval );
886 if ( t->id == 0 ) {
887#if defined(QT_CHECK_STATE)
888 qSystemWarning( "qStartTimer: Failed to create a timer." );
889#endif
890 freePMTIDs->give( freeId ); // could not start timer
891 freeQtTIDs->give( ind );
892 delete t;
893 return 0;
894 }
895 }
896 timerVec->insert( ind, t ); // store in timer vector
897 if ( !t->zero )
898 timerDict->insert( t->id, t ); // store in dict
899#endif // ifndef QT_PM_NO_DOSTIMERS
900
901 return ind + 1; // return index in vector
902}
903
904bool qKillTimer( int ind )
905{
906#ifndef QT_PM_NO_DOSTIMERS
907 if ( !timerDict )
908 return FALSE;
909 register TimerInfo *t = timerDict->find( ind-1 );
910 if ( !t )
911 return FALSE;
912 if ( t->interval == 0 ) {
913 numZeroTimers--;
914 }
915 freeQtTIDs->give( t->ind );
916 QMutexLocker lock( &timerMutex );
917 timerDict->remove( ind-1 );
918 return TRUE;
919#else // ifndef QT_PM_NO_DOSTIMERS
920 if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
921 return FALSE;
922 register TimerInfo *t = timerVec->at(ind-1);
923 if ( !t )
924 return FALSE;
925 if ( t->zero ) {
926 numZeroTimers--;
927 } else {
928 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
929 freePMTIDs->give( t->id );
930 timerDict->remove( t->id );
931 }
932 freeQtTIDs->give( t->ind );
933 timerVec->remove( ind-1 );
934 return TRUE;
935#endif // ifndef QT_PM_NO_DOSTIMERS
936}
937
938bool qKillTimer( QObject *obj )
939{
940#ifndef QT_PM_NO_DOSTIMERS
941 if ( !timerDict )
942 return FALSE;
943 QIntDictIterator<TimerInfo> it ( *timerDict );
944 for ( register TimerInfo *t; (t = it.current()); ) {
945 if ( t->obj == obj ) { // object found
946 if ( t->interval == 0 ) {
947 numZeroTimers--;
948 }
949 freeQtTIDs->give( t->ind );
950 QMutexLocker lock( &timerMutex );
951 timerDict->remove( it.currentKey() );
952 } else {
953 ++it;
954 }
955 }
956 return TRUE;
957#else // ifndef QT_PM_NO_DOSTIMERS
958 if ( !timerVec )
959 return FALSE;
960 register TimerInfo *t;
961 for ( uint i=0; i<timerVec->size(); i++ ) {
962 t = timerVec->at( i );
963 if ( t && t->obj == obj ) { // object found
964 if ( t->zero ) {
965 numZeroTimers--;
966 } else {
967 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
968 freePMTIDs->give( t->id );
969 timerDict->remove( t->id );
970 }
971 freeQtTIDs->give( t->ind );
972 timerVec->remove( i );
973 }
974 }
975 return TRUE;
976#endif // ifndef QT_PM_NO_DOSTIMERS
977}
978
979/*****************************************************************************
980 Socket notifier type
981 *****************************************************************************/
982
983QSockNotType::QSockNotType()
984 : list( 0 )
985{
986 FD_ZERO( &select_fds );
987 FD_ZERO( &enabled_fds );
988 FD_ZERO( &pending_fds );
989}
990
991QSockNotType::~QSockNotType()
992{
993 if ( list )
994 delete list;
995 list = 0;
996}
997
998/*****************************************************************************
999 socket select() thread
1000 *****************************************************************************/
1001
1002#if defined(QT_THREAD_SUPPORT)
1003
1004static class QSockSelectThread : public QThread
1005{
1006public:
1007 QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
1008 void run();
1009 void cancelSelectOrIdle( bool terminate = FALSE );
1010private:
1011 QEventLoopPrivate *d;
1012 bool exit;
1013} *ss_thread = 0;
1014
1015// recursive mutex to serialize access to socket notifier data
1016static QMutex ss_mutex( TRUE );
1017// flag to indicate the presence of sockets to do select() (we use QSemaphore
1018// instead of QWaitCondition because we need the "level-triggered" semantics,
1019// the "edge-triggered" semantics can produce deadlocks)
1020static QSemaphore ss_flag( 1 );
1021
1022// Note: must be called from under ss_mutex lock!
1023static inline void find_highest_fd( QEventLoopPrivate *d )
1024{
1025 d->sn_highest = -1;
1026 for ( int i = 0; i < 3; i ++ ) {
1027 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1028 if ( list && !list->isEmpty() ) {
1029 QSockNot *sn = list->first();
1030 while ( sn && !FD_ISSET( sn->fd, sn->active ) )
1031 sn = list->next();
1032 if ( sn )
1033 d->sn_highest = QMAX( d->sn_highest, // list is fd-sorted
1034 sn->fd );
1035 }
1036 }
1037}
1038
1039void QSockSelectThread::run()
1040{
1041 while ( !exit ) {
1042 ss_mutex.lock();
1043 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
1044 // read
1045 if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
1046 d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
1047 else
1048 FD_ZERO( &d->sn_vec[0].select_fds );
1049 // write
1050 if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
1051 d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
1052 else
1053 FD_ZERO( &d->sn_vec[1].select_fds );
1054 // except
1055 if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
1056 d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
1057 else
1058 FD_ZERO( &d->sn_vec[2].select_fds );
1059 // do select
1060 int nfds = d->sn_highest + 1;
1061 d->sn_cancel = d->sn_highest;
1062 ss_mutex.unlock();
1063 int nsel = ::select( nfds,
1064 &d->sn_vec[0].select_fds,
1065 &d->sn_vec[1].select_fds,
1066 &d->sn_vec[2].select_fds,
1067 NULL );
1068 if ( nsel > 0 ) {
1069 ss_mutex.lock();
1070 // if select says data is ready on any socket, then set
1071 // the socket notifier to pending
1072 bool activate = FALSE;
1073 for ( int i = 0; i < 3; i++ ) {
1074 if ( ! d->sn_vec[i].list )
1075 continue;
1076 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1077 QSockNot *sn = list->first();
1078 while ( sn ) {
1079 if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
1080 // see comments in QEventLoop::setSocketNotifierPending()
1081 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1082 // queue the socket activation
1083 d->sn_pending_list.insert(
1084 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1085 sn );
1086 FD_SET( sn->fd, sn->queue );
1087 // remove from enabled sockets to prevent deadlocks
1088 // on blocking sockets (and to reduce CPU load caused
1089 // by frequent select() calls) when the event queue
1090 // is not processed fast enough to handle activation
1091 FD_CLR( sn->fd, sn->active );
1092 if ( sn->fd == d->sn_highest )
1093 find_highest_fd( d );
1094 activate = TRUE;
1095 }
1096 }
1097 sn = list->next();
1098 }
1099 }
1100 ss_mutex.unlock();
1101 // post a message to activate socket notifiers
1102 if ( activate )
1103 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1104 }
1105 } else {
1106 d->sn_cancel = -1;
1107 // no sockets to select(), go to the idle state
1108 ss_mutex.unlock();
1109 // wait for the ability to capture the flag
1110 ss_flag ++;
1111 // release the flag before termination
1112 if ( exit )
1113 ss_flag --;
1114 }
1115 }
1116}
1117
1118void QSockSelectThread::cancelSelectOrIdle( bool terminate /* = FALSE */ )
1119{
1120 ss_mutex.lock();
1121 exit = terminate;
1122 if ( d->sn_cancel >= 0 ) {
1123 // terminate select() execution
1124 ::so_cancel( d->sn_cancel );
1125 } else {
1126 // terminate the idle state by releasing the flag
1127 if ( !ss_flag.available() )
1128 ss_flag --;
1129 }
1130 ss_mutex.unlock();
1131 // wait for this thread to end if the termination is requested
1132 if ( exit )
1133 wait();
1134}
1135
1136static void ss_cleanup()
1137{
1138 ss_thread->cancelSelectOrIdle( TRUE );
1139 delete ss_thread;
1140 ss_thread = 0;
1141}
1142
1143static void ss_init( QEventLoopPrivate *d )
1144{
1145 // capture the flag initially
1146 ss_flag ++;
1147 ss_thread = new QSockSelectThread( d );
1148 ss_thread->start();
1149}
1150
1151#endif // QT_THREAD_SUPPORT
1152
1153/*****************************************************************************
1154 QEventLoop Implementation for OS/2 PM
1155 *****************************************************************************/
1156
1157void QEventLoop::init()
1158{
1159 d->sn_highest = -1;
1160 d->sn_cancel = -1;
1161
1162 qt_ensure_pm();
1163 d->hab = WinInitialize( 0 );
1164 d->hmq = WinCreateMsgQueue( d->hab, 0 );
1165 qt_gui_hab = d->hab;
1166 qt_gui_queue = d->hmq;
1167
1168 qt_aux_win.setEventLoop( this );
1169
1170#if defined(QT_THREAD_SUPPORT)
1171 // there may only be one global QEventLoop instance that manages sockets
1172 Q_ASSERT( ss_thread == 0 );
1173#else
1174#if defined(QT_CHECK_STATE)
1175 qWarning( "QEventLoop::init: socket notifiers are not supported "
1176 "for a single-threaded version of Qt." );
1177#endif
1178#endif // QT_THREAD_SUPPORT
1179}
1180
1181void QEventLoop::cleanup()
1182{
1183 // timers should have been already uninitialized in appClosingDown()
1184#ifndef QT_PM_NO_DOSTIMERS
1185 Q_ASSERT( timerDict == 0 );
1186#else
1187 Q_ASSERT( timerVec == 0 );
1188#endif
1189
1190 // ss_thread should have been already stopped in appClosingDown()
1191 Q_ASSERT( ss_thread == 0 );
1192
1193 qt_aux_win.setEventLoop( 0 );
1194
1195 // destroy all windows created by QPMObjectWindow instances
1196 for ( HWND *w = qt_objWindows.first(); w; w = qt_objWindows.next() ) {
1197 Q_ASSERT( *w );
1198 WinDestroyWindow( *w );
1199 // tell QPMObjectWindow the window has been destroyed
1200 *w = 0;
1201 }
1202
1203 WinDestroyMsgQueue( d->hmq );
1204 WinTerminate( d->hab );
1205 qt_gui_queue = 0;
1206 qt_gui_hab = 0;
1207}
1208
1209void QEventLoop::appStartingUp()
1210{
1211}
1212
1213void QEventLoop::appClosingDown()
1214{
1215 // ensure the socket thread is terminated before QApplication calls
1216 // QThread::cleanup()
1217 if ( ss_thread )
1218 ss_cleanup();
1219 // the same applies to the timer thread
1220 cleanupTimers();
1221}
1222
1223void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
1224{
1225#if defined(QT_THREAD_SUPPORT)
1226 // there may only be one global QEventLoop instance that manages sockets
1227 Q_ASSERT( !qApp || qApp->eventloop == this );
1228 if ( qApp && qApp->eventloop != this )
1229 return;
1230
1231 // lazily create the auxiliary window to process WM_U_SEM_SELECT messages
1232 if ( !qt_aux_win.hwnd() )
1233 if ( !qt_aux_win.create() )
1234 return;
1235
1236 // lazily start the socket select thread
1237 if ( !ss_thread )
1238 ss_init( d );
1239
1240 int sockfd = -1;
1241 int type = -1;
1242 if ( notifier ) {
1243 sockfd = notifier->socket();
1244 type = notifier->type();
1245 }
1246 if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
1247#if defined(QT_CHECK_RANGE)
1248 qWarning( "QSocketNotifier: Internal error" );
1249#endif
1250 return;
1251 }
1252
1253 QMutexLocker locker( &ss_mutex );
1254 ss_thread->cancelSelectOrIdle();
1255
1256 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1257 QSockNot *sn;
1258
1259 if ( ! list ) {
1260 // create new list, the QSockNotType destructor will delete it for us
1261 list = new QPtrList<QSockNot>;
1262 Q_CHECK_PTR( list );
1263 list->setAutoDelete( TRUE );
1264 d->sn_vec[type].list = list;
1265 }
1266
1267 sn = new QSockNot;
1268 Q_CHECK_PTR( sn );
1269 sn->obj = notifier;
1270 sn->fd = sockfd;
1271 sn->queue = &d->sn_vec[type].pending_fds;
1272 sn->active = &d->sn_vec[type].enabled_fds;
1273
1274 if ( list->isEmpty() ) {
1275 list->insert( 0, sn );
1276 } else { // sort list by fd, decreasing
1277 QSockNot *p = list->first();
1278 while ( p && p->fd > sockfd )
1279 p = list->next();
1280#if defined(QT_CHECK_STATE)
1281 if ( p && p->fd == sockfd ) {
1282 static const char *t[] = { "read", "write", "exception" };
1283 qWarning( "QSocketNotifier: Multiple socket notifiers for "
1284 "same socket %d and type %s", sockfd, t[type] );
1285 }
1286#endif
1287 if ( p )
1288 list->insert( list->at(), sn );
1289 else
1290 list->append( sn );
1291 }
1292
1293 // enable the socket only if it's not already pending
1294 if ( !FD_ISSET( sockfd, sn->queue ) ) {
1295 FD_SET( sockfd, sn->active );
1296 d->sn_highest = QMAX( d->sn_highest, sockfd );
1297 }
1298#endif // QT_THREAD_SUPPORT
1299}
1300
1301void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
1302{
1303#if defined(QT_THREAD_SUPPORT)
1304 int sockfd = -1;
1305 int type = -1;
1306 if ( notifier ) {
1307 sockfd = notifier->socket();
1308 type = notifier->type();
1309 }
1310 if ( sockfd < 0 || type < 0 || type > 2 ) {
1311#if defined(QT_CHECK_RANGE)
1312 qWarning( "QSocketNotifier: Internal error" );
1313#endif
1314 return;
1315 }
1316
1317 if ( !ss_thread )
1318 return; // definitely not found
1319
1320 QMutexLocker locker( &ss_mutex );
1321 ss_thread->cancelSelectOrIdle();
1322
1323 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1324 QSockNot *sn;
1325 if ( ! list )
1326 return;
1327 sn = list->first();
1328 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1329 sn = list->next();
1330 if ( !sn ) // not found
1331 return;
1332
1333 // touch fd bitmaps only if there are no other notifiers for the same socket
1334 // (QPtrList curiously lacks getNext()/getPrev(), so play tambourine)
1335 QSockNot *next = list->next();
1336 if ( next ) list->prev();
1337 else list->last();
1338 QSockNot *prev = list->prev();
1339 if ( prev ) list->next();
1340 else list->first();
1341 bool unique = (!next || next->fd != sockfd) && (!prev || prev->fd != sockfd);
1342 if ( unique) {
1343 FD_CLR( sockfd, sn->active ); // clear fd bit
1344 FD_CLR( sockfd, sn->queue );
1345 }
1346 d->sn_pending_list.removeRef( sn ); // remove from activation list
1347 list->remove(); // remove notifier found above
1348
1349 if ( unique) {
1350 if ( d->sn_highest == sockfd )
1351 find_highest_fd( d );
1352 }
1353#endif // QT_THREAD_SUPPORT
1354}
1355
1356void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
1357{
1358#if defined(QT_THREAD_SUPPORT)
1359 int sockfd = -1;
1360 int type = -1;
1361 if ( notifier ) {
1362 sockfd = notifier->socket();
1363 type = notifier->type();
1364 }
1365 if ( sockfd < 0 || type < 0 || type > 2 ) {
1366#if defined(QT_CHECK_RANGE)
1367 qWarning( "QSocketNotifier: Internal error" );
1368#endif
1369 return;
1370 }
1371
1372 if ( !ss_thread )
1373 return; // definitely not found
1374
1375 QMutexLocker locker( &ss_mutex );
1376 ss_thread->cancelSelectOrIdle();
1377
1378 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1379 QSockNot *sn;
1380 if ( ! list )
1381 return;
1382 sn = list->first();
1383 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1384 sn = list->next();
1385 if ( ! sn ) { // not found
1386 return;
1387 }
1388
1389 // We choose a random activation order to be more fair under high load.
1390 // If a constant order is used and a peer early in the list can
1391 // saturate the IO, it might grab our attention completely.
1392 // Also, if we're using a straight list, the callback routines may
1393 // delete other entries from the list before those other entries are
1394 // processed.
1395 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1396 d->sn_pending_list.insert(
1397 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1398 sn
1399 );
1400 FD_SET( sockfd, sn->queue );
1401 // remove from enabled sockets, see QSockSelectThread::run()
1402 FD_CLR( sockfd, sn->active );
1403 if ( d->sn_highest == sockfd )
1404 find_highest_fd( d );
1405 }
1406#endif // QT_THREAD_SUPPORT
1407}
1408
1409bool QEventLoop::hasPendingEvents() const
1410{
1411 QMSG msg;
1412 return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
1413}
1414
1415bool QEventLoop::processEvents( ProcessEventsFlags flags )
1416{
1417 QMSG msg;
1418
1419#if defined(QT_THREAD_SUPPORT)
1420 QMutexLocker locker( QApplication::qt_mutex );
1421#endif
1422
1423 QApplication::sendPostedEvents();
1424
1425#ifndef QT_PM_NO_DOSTIMERS
1426 // we've just processed all pending events above, including QEvent::Timer
1427 // events, so remove spare WM_TIMER messages posted to qt_aux_win for the
1428 // cases when the message loop is run bypassing this method
1429 if ( qt_aux_win.hwnd() )
1430 WinPeekMsg( 0, &msg, qt_aux_win.hwnd(), WM_TIMER, WM_TIMER, PM_REMOVE );
1431#endif // ifndef QT_PM_NO_DOSTIMERS
1432
1433 if ( flags & ExcludeUserInput ) {
1434 while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
1435 if ( msg.msg == WM_CHAR ||
1436 (msg.msg >= WM_MOUSEFIRST &&
1437 msg.msg <= WM_MOUSELAST) ||
1438 (msg.msg >= WM_EXTMOUSEFIRST &&
1439 msg.msg <= WM_EXTMOUSELAST) ||
1440 msg.msg == WM_HSCROLL ||
1441 msg.msg == WM_VSCROLL
1442 ) {
1443 WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
1444 continue;
1445 }
1446 break;
1447 }
1448 }
1449
1450 bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
1451
1452 if ( canWait ) {
1453 // can wait if necessary
1454 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1455 emit aboutToBlock();
1456#ifdef QT_THREAD_SUPPORT
1457 locker.mutex()->unlock();
1458#endif
1459 WinGetMsg( 0, &msg, 0, 0, 0 );
1460#ifdef QT_THREAD_SUPPORT
1461 locker.mutex()->lock();
1462#endif
1463 emit awake();
1464 emit qApp->guiThreadAwake();
1465 }
1466 } else {
1467 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1468 // no pending events
1469 return FALSE;
1470 }
1471 }
1472
1473 if ( msg.msg == WM_QUIT ) {
1474 // process the quit request
1475#if !defined (QT_NO_SESSIONMANAGER)
1476 if ( qt_app_canQuit() ) {
1477#endif
1478 exit( 0 );
1479 return TRUE;
1480#if !defined (QT_NO_SESSIONMANAGER)
1481 } else {
1482 WinCancelShutdown( d->hmq, FALSE );
1483 return TRUE;
1484 }
1485#endif
1486 }
1487
1488 bool handled = FALSE;
1489 MRESULT res = 0;
1490
1491 if ( msg.msg == WM_U_SEM_SELECT && msg.hwnd == qt_aux_win.hwnd() ) {
1492 // socket select event received: prevent it from being handled by
1493 // qt_aux_win (to obey the ExcludeSocketNotifiers flag)
1494 handled = TRUE;
1495 // ignore socket notifier activation if processd by the Qt event hook
1496 if ( qt_pmEventFilter( &msg, res ) )
1497 flags |= ExcludeSocketNotifiers;
1498 }
1499
1500 if ( !handled && msg.msg && (!msg.hwnd || !QWidget::find( msg.hwnd )) ) {
1501 handled = qt_pmEventFilter( &msg, res );
1502 }
1503
1504 if ( !handled ) {
1505 // send to QtWndProc or to a native window procedure
1506 WinDispatchMsg( 0, &msg );
1507 }
1508
1509 if ( !(flags & ExcludeSocketNotifiers) )
1510 activateSocketNotifiers();
1511
1512 // any pending configs?
1513 if ( configRequests )
1514 qPMProcessConfigRequests();
1515 QApplication::sendPostedEvents();
1516
1517 return TRUE;
1518}
1519
1520void QEventLoop::wakeUp()
1521{
1522 PTIB ptib;
1523 DosGetInfoBlocks( &ptib, NULL );
1524 MQINFO mqinfo;
1525 WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
1526 if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
1527 WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
1528}
1529
1530int QEventLoop::timeToWait() const
1531{
1532 return -1;
1533}
1534
1535int QEventLoop::activateTimers()
1536{
1537 return 0;
1538}
1539
1540int QEventLoop::activateSocketNotifiers()
1541{
1542#if defined(QT_THREAD_SUPPORT)
1543 if ( d->sn_pending_list.isEmpty() )
1544 return 0;
1545
1546 if ( !ss_thread )
1547 return 0;
1548
1549 // postpone activation if ss_thread is working with the list
1550 if ( !ss_mutex.tryLock() ) {
1551 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1552 return 0;
1553 }
1554
1555 ss_thread->cancelSelectOrIdle();
1556
1557 // activate entries
1558 int n_act = 0;
1559 QEvent event( QEvent::SockAct );
1560 QPtrListIterator<QSockNot> it( d->sn_pending_list );
1561 QSockNot *sn;
1562 while ( (sn = it.current()) ) {
1563 ++it;
1564 d->sn_pending_list.removeRef( sn );
1565 if ( FD_ISSET(sn->fd, sn->queue) ) {
1566 FD_CLR( sn->fd, sn->queue );
1567 // reenable the socket to let it participate in the next select()
1568 FD_SET( sn->fd, sn->active );
1569 d->sn_highest = QMAX( d->sn_highest, sn->fd );
1570 QApplication::sendEvent( sn->obj, &event );
1571 n_act++;
1572 }
1573 }
1574
1575 ss_mutex.unlock();
1576
1577 return n_act;
1578#else
1579 return 0;
1580#endif // QT_THREAD_SUPPORT
1581}
1582
Note: See TracBrowser for help on using the repository browser.