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

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

kernel: Implemented DOS timer based Qt timers (#35).

  • Property svn:keywords set to Id
File size: 49.3 KB
Line 
1/****************************************************************************
2** $Id: qeventloop_pm.cpp 176 2008-02-18 23:35:23Z 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 ULONG spent = now - t->last;
585 if ( spent >= t->interval ) {
586 // the timer iterval has expired, post the timer event
587 QTimerEvent *e = new QTimerEvent( t->ind + 1 );
588 QApplication::postEvent( t->obj, e );
589 // set the new last stamp
590 t->last += t->interval * (spent / t->interval);
591 }
592 // calculate minimal time to the next shot
593 minLeft = QMIN( minLeft, t->interval - (spent % t->interval) );
594 }
595
596 if ( timerDict->count() > 0 ) {
597 // post a spare WM_TIMER message to make sure timers run even when
598 // the message loop is not controlled by QEventLoop::processEvents()
599 // (e.g. when moving or resizing a window using the mouse)
600 WinPostMsg( qt_aux_win.hwnd(), WM_TIMER, 0, 0 );
601 // restart the OS/2 timer
602 interval = minLeft;
603 ULONG postCnt;
604 DosResetEventSem( hev, &postCnt );
605 // check for quit (cancelQuit() could post the semaphore just before
606 // we reset it above)
607 if ( quit )
608 break;
609 rc = DosAsyncTimer( minLeft, (HSEM) hev, &htimer );
610 Q_ASSERT( rc == NO_ERROR );
611 } else {
612 htimer = NULLHANDLE;
613 interval = 0;
614 }
615
616 } while (1);
617}
618
619void QTimerThread::signalQuit()
620{
621 quit = true;
622 DosPostEventSem( hev );
623 wait();
624}
625
626// Note: must be called from under timerMutex!
627void QTimerThread::ensureShot( int ival )
628{
629 Q_ASSERT( timerMutex.locked() );
630 Q_ASSERT( ival > 0 );
631 if ( interval == 0 || interval > (ULONG) ival ) {
632 // start another timer to make sure the new Qt timer is fired off in time
633 interval = (ULONG) ival;
634 APIRET rc = DosAsyncTimer( interval, (HSEM) hev, &htimer );
635 Q_ASSERT( rc == NO_ERROR );
636 }
637}
638
639#else // ifndef QT_PM_NO_DOSTIMERS
640
641#include "qptrvector.h"
642#include "qintdict.h"
643
644struct TimerInfo { // internal timer info
645 uint ind; // - Qt timer identifier - 1
646 ULONG id; // - PM timer identifier
647 bool zero; // - zero timing
648 QObject *obj; // - object to receive events
649};
650typedef QPtrVector<TimerInfo> TimerVec; // vector of TimerInfo structs
651typedef QIntDict<TimerInfo> TimerDict; // fast dict of timers
652
653static TimerVec *timerVec = 0; // timer vector
654static TimerDict *timerDict = 0; // timer dict
655
656typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
657static FreeQtTIDList *freeQtTIDs = 0;
658typedef QFreeValueList<ULONG> FreePMTIDList; // list of free PM timer IDs
659static FreePMTIDList *freePMTIDs = 0;
660
661//
662// Timer activation (called from the event loop when WM_TIMER arrives)
663//
664static bool dispatchTimer( uint timerId, QMSG *msg )
665{
666 MRESULT res = NULL;
667 if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
668 {
669 if ( !timerVec ) // should never happen
670 return FALSE;
671 register TimerInfo *t = timerDict->find( timerId );
672 if ( !t ) // no such timer id
673 return FALSE;
674 QTimerEvent e( t->ind + 1 );
675 QApplication::sendEvent( t->obj, &e ); // send event
676 return TRUE; // timer event was processed
677 }
678 return TRUE;
679}
680
681#endif // ifndef QT_PM_NO_DOSTIMERS
682
683//
684// activate full-speed timers
685//
686static void activateZeroTimers()
687{
688#ifndef QT_PM_NO_DOSTIMERS
689 if ( !timerDict )
690 return;
691 QIntDictIterator<TimerInfo> it( *timerDict );
692 register TimerInfo *t = 0;
693 int n = numZeroTimers;
694 while ( n-- ) {
695 for ( ;; ) {
696 t = it();
697 Q_ASSERT( t );
698 if ( !t )
699 return;
700 if ( t->interval == 0 )
701 break;
702 }
703 QTimerEvent e( t->ind + 1 );
704 QApplication::sendEvent( t->obj, &e );
705 }
706#else
707 if ( !timerVec )
708 return;
709 uint i=0;
710 register TimerInfo *t = 0;
711 int n = numZeroTimers;
712 while ( n-- ) {
713 for ( ;; ) {
714 t = timerVec->at(i++);
715 if ( t && t->zero )
716 break;
717 else if ( i == timerVec->size() ) // should not happen
718 return;
719 }
720 QTimerEvent e( t->ind + 1 );
721 QApplication::sendEvent( t->obj, &e );
722 }
723#endif
724}
725
726
727//
728// Timer initialization and cleanup routines
729//
730
731static void initTimers() // initialize timers
732{
733#ifndef QT_PM_NO_DOSTIMERS
734 timerDict = new TimerDict( 29 );
735 Q_CHECK_PTR( timerDict );
736 timerDict->setAutoDelete( TRUE );
737 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
738 Q_CHECK_PTR( freeQtTIDs );
739 timerThread = new QTimerThread();
740 Q_CHECK_PTR( timerThread );
741 timerThread->start();
742#else // ifndef QT_PM_NO_DOSTIMERS
743 timerVec = new TimerVec( 128 );
744 Q_CHECK_PTR( timerVec );
745 timerVec->setAutoDelete( TRUE );
746 timerDict = new TimerDict( 29 );
747 Q_CHECK_PTR( timerDict );
748 freeQtTIDs = new FreeQtTIDList( 0, 1023 ); // resonable max amount of timers
749 Q_CHECK_PTR( freeQtTIDs );
750 freePMTIDs = new FreePMTIDList( 1, TID_USERMAX - 1 );
751 Q_CHECK_PTR( freePMTIDs );
752#endif // ifndef QT_PM_NO_DOSTIMERS
753}
754
755/// @todo (dmik) cleanupTimers() is only called by QEventLoop::cleanup() so it
756// won't be called if there is no QEventLoop instance created (for example,
757// when a Qt-based plugin DLL is used by a non-Qt application). Use atexit()?
758// Btw, the same relates to the QThread::cleanup() vs QApplication pair.
759// And to qt_aux_win (the DLL may be unloaded after the application has
760// destroyed the main event queue, so the static destructor will not be able
761// to properly destroy the window).
762
763static void cleanupTimers() // remove pending timers
764{
765#ifndef QT_PM_NO_DOSTIMERS
766 if ( !timerDict ) // no timers were used
767 return;
768 timerThread->signalQuit();
769 delete timerThread;
770 timerThread = NULL;
771 delete freeQtTIDs;
772 freeQtTIDs = NULL;
773 delete timerDict;
774 timerDict = NULL;
775#else // ifndef QT_PM_NO_DOSTIMERS
776 register TimerInfo *t;
777 if ( !timerVec ) // no timers were used
778 return;
779 for ( uint i=0; i<timerVec->size(); i++ ) { // kill all pending timers
780 t = timerVec->at( i );
781 if ( t && !t->zero )
782 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
783 }
784 delete freePMTIDs;
785 freePMTIDs = 0;
786 delete freeQtTIDs;
787 freeQtTIDs = 0;
788 delete timerDict;
789 timerDict = 0;
790 delete timerVec;
791 timerVec = 0;
792#endif // ifndef QT_PM_NO_DOSTIMERS
793}
794
795
796//
797// Main timer functions for starting and killing timers
798//
799
800
801int qStartTimer( int interval, QObject *obj )
802{
803 Q_ASSERT( obj );
804 if ( !obj || interval < 0 )
805 return 0;
806
807 // lazily create the auxiliary window to process WM_TIMER and
808 // WM_U_SEM_ZEROTIMER messages
809 if ( !qt_aux_win.hwnd() )
810 if ( !qt_aux_win.create() )
811 return 0;
812
813#ifndef QT_PM_NO_DOSTIMERS
814 if ( !timerDict ) // initialize timer data
815 initTimers();
816#else // ifndef QT_PM_NO_DOSTIMERS
817 if ( !timerVec ) // initialize timer data
818 initTimers();
819#endif // ifndef QT_PM_NO_DOSTIMERS
820
821 if ( freeQtTIDs->isEmpty() ) {
822#if defined(QT_CHECK_STATE)
823 qWarning( "qStartTimer: Maximum number of timers (%d) is reached.",
824 freeQtTIDs->max() - freeQtTIDs->min() + 1 );
825#endif
826 return 0;
827 }
828
829#ifdef QT_PM_NO_DOSTIMERS
830 if ( freePMTIDs->isEmpty() ) {
831#if defined(QT_CHECK_STATE)
832 qWarning( "qStartTimer: Maximum number of non-zero timers (%ld) is reached.",
833 freePMTIDs->max() - freePMTIDs->min() + 1 );
834#endif
835 return 0;
836 }
837#endif
838
839 int ind = freeQtTIDs->take(); // get free timer
840
841#ifndef QT_PM_NO_DOSTIMERS
842 register TimerInfo *t = new TimerInfo; // create timer entry
843 Q_CHECK_PTR( t );
844 t->ind = ind;
845 t->obj = obj;
846
847 t->interval = interval;
848 if ( t->interval == 0 ) { // add zero timer
849 numZeroTimers++;
850 // indicate there is a new zero timer
851 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
852 } else {
853 // set the initial last shot timestamp value to now
854 DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &t->last, sizeof(t->last) );
855 }
856
857 QMutexLocker lock( &timerMutex );
858 timerDict->insert( ind, t ); // store in timer dict
859 if ( t->interval != 0 )
860 timerThread->ensureShot( t->interval );
861#else // ifndef QT_PM_NO_DOSTIMERS
862 if ( (uint) ind >= timerVec->size() ) {
863 uint newSize = timerVec->size() * 4; // increase the size
864 if ( newSize <= (uint) ind )
865 newSize = (uint) ind + 1;
866 timerVec->resize( newSize );
867 }
868
869 register TimerInfo *t = new TimerInfo; // create timer entry
870 Q_CHECK_PTR( t );
871 t->ind = ind;
872 t->obj = obj;
873
874 t->zero = interval == 0;
875 if ( t->zero ) { // add zero timer
876 t->id = 0;
877 numZeroTimers++;
878 // indicate there is a new zero timer
879 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
880 } else {
881 ULONG freeId = freePMTIDs->take(); // get free timer ID
882 t->id = WinStartTimer( 0, qt_aux_win.hwnd(), freeId, (ULONG) interval );
883 if ( t->id == 0 ) {
884#if defined(QT_CHECK_STATE)
885 qSystemWarning( "qStartTimer: Failed to create a timer." );
886#endif
887 freePMTIDs->give( freeId ); // could not start timer
888 freeQtTIDs->give( ind );
889 delete t;
890 return 0;
891 }
892 }
893 timerVec->insert( ind, t ); // store in timer vector
894 if ( !t->zero )
895 timerDict->insert( t->id, t ); // store in dict
896#endif // ifndef QT_PM_NO_DOSTIMERS
897
898 return ind + 1; // return index in vector
899}
900
901bool qKillTimer( int ind )
902{
903#ifndef QT_PM_NO_DOSTIMERS
904 if ( !timerDict )
905 return FALSE;
906 register TimerInfo *t = timerDict->find( ind-1 );
907 if ( !t )
908 return FALSE;
909 if ( t->interval == 0 ) {
910 numZeroTimers--;
911 }
912 freeQtTIDs->give( t->ind );
913 QMutexLocker lock( &timerMutex );
914 timerDict->remove( ind-1 );
915 return TRUE;
916#else // ifndef QT_PM_NO_DOSTIMERS
917 if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
918 return FALSE;
919 register TimerInfo *t = timerVec->at(ind-1);
920 if ( !t )
921 return FALSE;
922 if ( t->zero ) {
923 numZeroTimers--;
924 } else {
925 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
926 freePMTIDs->give( t->id );
927 timerDict->remove( t->id );
928 }
929 freeQtTIDs->give( t->ind );
930 timerVec->remove( ind-1 );
931 return TRUE;
932#endif // ifndef QT_PM_NO_DOSTIMERS
933}
934
935bool qKillTimer( QObject *obj )
936{
937#ifndef QT_PM_NO_DOSTIMERS
938 if ( !timerDict )
939 return FALSE;
940 QIntDictIterator<TimerInfo> it ( *timerDict );
941 for ( register TimerInfo *t; (t = it.current()); ) {
942 if ( t->obj == obj ) { // object found
943 if ( t->interval == 0 ) {
944 numZeroTimers--;
945 }
946 freeQtTIDs->give( t->ind );
947 QMutexLocker lock( &timerMutex );
948 timerDict->remove( it.currentKey() );
949 } else {
950 ++it;
951 }
952 }
953 return TRUE;
954#else // ifndef QT_PM_NO_DOSTIMERS
955 if ( !timerVec )
956 return FALSE;
957 register TimerInfo *t;
958 for ( uint i=0; i<timerVec->size(); i++ ) {
959 t = timerVec->at( i );
960 if ( t && t->obj == obj ) { // object found
961 if ( t->zero ) {
962 numZeroTimers--;
963 } else {
964 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
965 freePMTIDs->give( t->id );
966 timerDict->remove( t->id );
967 }
968 freeQtTIDs->give( t->ind );
969 timerVec->remove( i );
970 }
971 }
972 return TRUE;
973#endif // ifndef QT_PM_NO_DOSTIMERS
974}
975
976/*****************************************************************************
977 Socket notifier type
978 *****************************************************************************/
979
980QSockNotType::QSockNotType()
981 : list( 0 )
982{
983 FD_ZERO( &select_fds );
984 FD_ZERO( &enabled_fds );
985 FD_ZERO( &pending_fds );
986}
987
988QSockNotType::~QSockNotType()
989{
990 if ( list )
991 delete list;
992 list = 0;
993}
994
995/*****************************************************************************
996 socket select() thread
997 *****************************************************************************/
998
999#if defined(QT_THREAD_SUPPORT)
1000
1001static class QSockSelectThread : public QThread
1002{
1003public:
1004 QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
1005 void run();
1006 void cancelSelectOrIdle( bool terminate = FALSE );
1007private:
1008 QEventLoopPrivate *d;
1009 bool exit;
1010} *ss_thread = 0;
1011
1012// recursive mutex to serialize access to socket notifier data
1013static QMutex ss_mutex( TRUE );
1014// flag to indicate the presence of sockets to do select() (we use QSemaphore
1015// instead of QWaitCondition because we need the "level-triggered" semantics,
1016// the "edge-triggered" semantics can produce deadlocks)
1017static QSemaphore ss_flag( 1 );
1018
1019// Note: must be called from under ss_mutex lock!
1020static inline void find_highest_fd( QEventLoopPrivate *d )
1021{
1022 d->sn_highest = -1;
1023 for ( int i = 0; i < 3; i ++ ) {
1024 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1025 if ( list && !list->isEmpty() ) {
1026 QSockNot *sn = list->first();
1027 while ( sn && !FD_ISSET( sn->fd, sn->active ) )
1028 sn = list->next();
1029 if ( sn )
1030 d->sn_highest = QMAX( d->sn_highest, // list is fd-sorted
1031 sn->fd );
1032 }
1033 }
1034}
1035
1036void QSockSelectThread::run()
1037{
1038 while ( !exit ) {
1039 ss_mutex.lock();
1040 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
1041 // read
1042 if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
1043 d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
1044 else
1045 FD_ZERO( &d->sn_vec[0].select_fds );
1046 // write
1047 if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
1048 d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
1049 else
1050 FD_ZERO( &d->sn_vec[1].select_fds );
1051 // except
1052 if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
1053 d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
1054 else
1055 FD_ZERO( &d->sn_vec[2].select_fds );
1056 // do select
1057 int nfds = d->sn_highest + 1;
1058 d->sn_cancel = d->sn_highest;
1059 ss_mutex.unlock();
1060 int nsel = ::select( nfds,
1061 &d->sn_vec[0].select_fds,
1062 &d->sn_vec[1].select_fds,
1063 &d->sn_vec[2].select_fds,
1064 NULL );
1065 if ( nsel > 0 ) {
1066 ss_mutex.lock();
1067 // if select says data is ready on any socket, then set
1068 // the socket notifier to pending
1069 bool activate = FALSE;
1070 for ( int i = 0; i < 3; i++ ) {
1071 if ( ! d->sn_vec[i].list )
1072 continue;
1073 QPtrList<QSockNot> *list = d->sn_vec[i].list;
1074 QSockNot *sn = list->first();
1075 while ( sn ) {
1076 if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
1077 // see comments in QEventLoop::setSocketNotifierPending()
1078 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1079 // queue the socket activation
1080 d->sn_pending_list.insert(
1081 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1082 sn );
1083 FD_SET( sn->fd, sn->queue );
1084 // remove from enabled sockets to prevent deadlocks
1085 // on blocking sockets (and to reduce CPU load caused
1086 // by frequent select() calls) when the event queue
1087 // is not processed fast enough to handle activation
1088 FD_CLR( sn->fd, sn->active );
1089 if ( sn->fd == d->sn_highest )
1090 find_highest_fd( d );
1091 activate = TRUE;
1092 }
1093 }
1094 sn = list->next();
1095 }
1096 }
1097 ss_mutex.unlock();
1098 // post a message to activate socket notifiers
1099 if ( activate )
1100 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1101 }
1102 } else {
1103 d->sn_cancel = -1;
1104 // no sockets to select(), go to the idle state
1105 ss_mutex.unlock();
1106 // wait for the ability to capture the flag
1107 ss_flag ++;
1108 // release the flag before termination
1109 if ( exit )
1110 ss_flag --;
1111 }
1112 }
1113}
1114
1115void QSockSelectThread::cancelSelectOrIdle( bool terminate /* = FALSE */ )
1116{
1117 ss_mutex.lock();
1118 exit = terminate;
1119 if ( d->sn_cancel >= 0 ) {
1120 // terminate select() execution
1121 ::so_cancel( d->sn_cancel );
1122 } else {
1123 // terminate the idle state by releasing the flag
1124 if ( !ss_flag.available() )
1125 ss_flag --;
1126 }
1127 ss_mutex.unlock();
1128 // wait for this thread to end if the termination is requested
1129 if ( exit )
1130 wait();
1131}
1132
1133static void ss_cleanup()
1134{
1135 ss_thread->cancelSelectOrIdle( TRUE );
1136 delete ss_thread;
1137 ss_thread = 0;
1138}
1139
1140static void ss_init( QEventLoopPrivate *d )
1141{
1142 // capture the flag initially
1143 ss_flag ++;
1144 ss_thread = new QSockSelectThread( d );
1145 ss_thread->start();
1146}
1147
1148#endif // QT_THREAD_SUPPORT
1149
1150/*****************************************************************************
1151 QEventLoop Implementation for OS/2 PM
1152 *****************************************************************************/
1153
1154void QEventLoop::init()
1155{
1156 d->sn_highest = -1;
1157 d->sn_cancel = -1;
1158
1159 qt_ensure_pm();
1160 d->hab = WinInitialize( 0 );
1161 d->hmq = WinCreateMsgQueue( d->hab, 0 );
1162 qt_gui_hab = d->hab;
1163 qt_gui_queue = d->hmq;
1164
1165 qt_aux_win.setEventLoop( this );
1166
1167#if defined(QT_THREAD_SUPPORT)
1168 // there may only be one global QEventLoop instance that manages sockets
1169 Q_ASSERT( ss_thread == 0 );
1170#else
1171#if defined(QT_CHECK_STATE)
1172 qWarning( "QEventLoop::init: socket notifiers are not supported "
1173 "for a single-threaded version of Qt." );
1174#endif
1175#endif // QT_THREAD_SUPPORT
1176}
1177
1178void QEventLoop::cleanup()
1179{
1180 // timers should have been already uninitialized in appClosingDown()
1181#ifndef QT_PM_NO_DOSTIMERS
1182 Q_ASSERT( timerDict == 0 );
1183#else
1184 Q_ASSERT( timerVec == 0 );
1185#endif
1186
1187 // ss_thread should have been already stopped in appClosingDown()
1188 Q_ASSERT( ss_thread == 0 );
1189
1190 qt_aux_win.setEventLoop( 0 );
1191
1192 // destroy all windows created by QPMObjectWindow instances
1193 for ( HWND *w = qt_objWindows.first(); w; w = qt_objWindows.next() ) {
1194 Q_ASSERT( *w );
1195 WinDestroyWindow( *w );
1196 // tell QPMObjectWindow the window has been destroyed
1197 *w = 0;
1198 }
1199
1200 WinDestroyMsgQueue( d->hmq );
1201 WinTerminate( d->hab );
1202 qt_gui_queue = 0;
1203 qt_gui_hab = 0;
1204}
1205
1206void QEventLoop::appStartingUp()
1207{
1208}
1209
1210void QEventLoop::appClosingDown()
1211{
1212 // ensure the socket thread is terminated before QApplication calls
1213 // QThread::cleanup()
1214 if ( ss_thread )
1215 ss_cleanup();
1216 // the same applies to the timer thread
1217 cleanupTimers();
1218}
1219
1220void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
1221{
1222#if defined(QT_THREAD_SUPPORT)
1223 // there may only be one global QEventLoop instance that manages sockets
1224 Q_ASSERT( !qApp || qApp->eventloop == this );
1225 if ( qApp && qApp->eventloop != this )
1226 return;
1227
1228 // lazily create the auxiliary window to process WM_U_SEM_SELECT messages
1229 if ( !qt_aux_win.hwnd() )
1230 if ( !qt_aux_win.create() )
1231 return;
1232
1233 // lazily start the socket select thread
1234 if ( !ss_thread )
1235 ss_init( d );
1236
1237 int sockfd = -1;
1238 int type = -1;
1239 if ( notifier ) {
1240 sockfd = notifier->socket();
1241 type = notifier->type();
1242 }
1243 if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
1244#if defined(QT_CHECK_RANGE)
1245 qWarning( "QSocketNotifier: Internal error" );
1246#endif
1247 return;
1248 }
1249
1250 QMutexLocker locker( &ss_mutex );
1251 ss_thread->cancelSelectOrIdle();
1252
1253 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1254 QSockNot *sn;
1255
1256 if ( ! list ) {
1257 // create new list, the QSockNotType destructor will delete it for us
1258 list = new QPtrList<QSockNot>;
1259 Q_CHECK_PTR( list );
1260 list->setAutoDelete( TRUE );
1261 d->sn_vec[type].list = list;
1262 }
1263
1264 sn = new QSockNot;
1265 Q_CHECK_PTR( sn );
1266 sn->obj = notifier;
1267 sn->fd = sockfd;
1268 sn->queue = &d->sn_vec[type].pending_fds;
1269 sn->active = &d->sn_vec[type].enabled_fds;
1270
1271 if ( list->isEmpty() ) {
1272 list->insert( 0, sn );
1273 } else { // sort list by fd, decreasing
1274 QSockNot *p = list->first();
1275 while ( p && p->fd > sockfd )
1276 p = list->next();
1277#if defined(QT_CHECK_STATE)
1278 if ( p && p->fd == sockfd ) {
1279 static const char *t[] = { "read", "write", "exception" };
1280 qWarning( "QSocketNotifier: Multiple socket notifiers for "
1281 "same socket %d and type %s", sockfd, t[type] );
1282 }
1283#endif
1284 if ( p )
1285 list->insert( list->at(), sn );
1286 else
1287 list->append( sn );
1288 }
1289
1290 // enable the socket only if it's not already pending
1291 if ( !FD_ISSET( sockfd, sn->queue ) ) {
1292 FD_SET( sockfd, sn->active );
1293 d->sn_highest = QMAX( d->sn_highest, sockfd );
1294 }
1295#endif // QT_THREAD_SUPPORT
1296}
1297
1298void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
1299{
1300#if defined(QT_THREAD_SUPPORT)
1301 int sockfd = -1;
1302 int type = -1;
1303 if ( notifier ) {
1304 sockfd = notifier->socket();
1305 type = notifier->type();
1306 }
1307 if ( sockfd < 0 || type < 0 || type > 2 ) {
1308#if defined(QT_CHECK_RANGE)
1309 qWarning( "QSocketNotifier: Internal error" );
1310#endif
1311 return;
1312 }
1313
1314 if ( !ss_thread )
1315 return; // definitely not found
1316
1317 QMutexLocker locker( &ss_mutex );
1318 ss_thread->cancelSelectOrIdle();
1319
1320 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1321 QSockNot *sn;
1322 if ( ! list )
1323 return;
1324 sn = list->first();
1325 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1326 sn = list->next();
1327 if ( !sn ) // not found
1328 return;
1329
1330 // touch fd bitmaps only if there are no other notifiers for the same socket
1331 // (QPtrList curiously lacks getNext()/getPrev(), so play tambourine)
1332 QSockNot *next = list->next();
1333 if ( next ) list->prev();
1334 else list->last();
1335 QSockNot *prev = list->prev();
1336 if ( prev ) list->next();
1337 else list->first();
1338 bool unique = (!next || next->fd != sockfd) && (!prev || prev->fd != sockfd);
1339 if ( unique) {
1340 FD_CLR( sockfd, sn->active ); // clear fd bit
1341 FD_CLR( sockfd, sn->queue );
1342 }
1343 d->sn_pending_list.removeRef( sn ); // remove from activation list
1344 list->remove(); // remove notifier found above
1345
1346 if ( unique) {
1347 if ( d->sn_highest == sockfd )
1348 find_highest_fd( d );
1349 }
1350#endif // QT_THREAD_SUPPORT
1351}
1352
1353void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
1354{
1355#if defined(QT_THREAD_SUPPORT)
1356 int sockfd = -1;
1357 int type = -1;
1358 if ( notifier ) {
1359 sockfd = notifier->socket();
1360 type = notifier->type();
1361 }
1362 if ( sockfd < 0 || type < 0 || type > 2 ) {
1363#if defined(QT_CHECK_RANGE)
1364 qWarning( "QSocketNotifier: Internal error" );
1365#endif
1366 return;
1367 }
1368
1369 if ( !ss_thread )
1370 return; // definitely not found
1371
1372 QMutexLocker locker( &ss_mutex );
1373 ss_thread->cancelSelectOrIdle();
1374
1375 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1376 QSockNot *sn;
1377 if ( ! list )
1378 return;
1379 sn = list->first();
1380 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1381 sn = list->next();
1382 if ( ! sn ) { // not found
1383 return;
1384 }
1385
1386 // We choose a random activation order to be more fair under high load.
1387 // If a constant order is used and a peer early in the list can
1388 // saturate the IO, it might grab our attention completely.
1389 // Also, if we're using a straight list, the callback routines may
1390 // delete other entries from the list before those other entries are
1391 // processed.
1392 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1393 d->sn_pending_list.insert(
1394 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1395 sn
1396 );
1397 FD_SET( sockfd, sn->queue );
1398 // remove from enabled sockets, see QSockSelectThread::run()
1399 FD_CLR( sockfd, sn->active );
1400 if ( d->sn_highest == sockfd )
1401 find_highest_fd( d );
1402 }
1403#endif // QT_THREAD_SUPPORT
1404}
1405
1406bool QEventLoop::hasPendingEvents() const
1407{
1408 QMSG msg;
1409 return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
1410}
1411
1412bool QEventLoop::processEvents( ProcessEventsFlags flags )
1413{
1414 QMSG msg;
1415
1416#if defined(QT_THREAD_SUPPORT)
1417 QMutexLocker locker( QApplication::qt_mutex );
1418#endif
1419
1420 QApplication::sendPostedEvents();
1421
1422#ifndef QT_PM_NO_DOSTIMERS
1423 // we've just processed all pending events above, including QEvent::Timer
1424 // events, so remove spare WM_TIMER messages posted to qt_aux_win for the
1425 // cases when the message loop is run bypassing this method
1426 if ( qt_aux_win.hwnd() )
1427 WinPeekMsg( 0, &msg, qt_aux_win.hwnd(), WM_TIMER, WM_TIMER, PM_REMOVE );
1428#endif // ifndef QT_PM_NO_DOSTIMERS
1429
1430 if ( flags & ExcludeUserInput ) {
1431 while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
1432 if ( msg.msg == WM_CHAR ||
1433 (msg.msg >= WM_MOUSEFIRST &&
1434 msg.msg <= WM_MOUSELAST) ||
1435 (msg.msg >= WM_EXTMOUSEFIRST &&
1436 msg.msg <= WM_EXTMOUSELAST) ||
1437 msg.msg == WM_HSCROLL ||
1438 msg.msg == WM_VSCROLL
1439 ) {
1440 WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
1441 continue;
1442 }
1443 break;
1444 }
1445 }
1446
1447 bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
1448
1449 if ( canWait ) {
1450 // can wait if necessary
1451 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1452 emit aboutToBlock();
1453#ifdef QT_THREAD_SUPPORT
1454 locker.mutex()->unlock();
1455#endif
1456 WinGetMsg( 0, &msg, 0, 0, 0 );
1457#ifdef QT_THREAD_SUPPORT
1458 locker.mutex()->lock();
1459#endif
1460 emit awake();
1461 emit qApp->guiThreadAwake();
1462 }
1463 } else {
1464 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1465 // no pending events
1466 return FALSE;
1467 }
1468 }
1469
1470 if ( msg.msg == WM_QUIT ) {
1471 // process the quit request
1472#if !defined (QT_NO_SESSIONMANAGER)
1473 if ( qt_app_canQuit() ) {
1474#endif
1475 exit( 0 );
1476 return TRUE;
1477#if !defined (QT_NO_SESSIONMANAGER)
1478 } else {
1479 WinCancelShutdown( d->hmq, FALSE );
1480 return TRUE;
1481 }
1482#endif
1483 }
1484
1485 bool handled = FALSE;
1486 MRESULT res = 0;
1487
1488 if ( msg.msg == WM_U_SEM_SELECT && msg.hwnd == qt_aux_win.hwnd() ) {
1489 // socket select event received: prevent it from being handled by
1490 // qt_aux_win (to obey the ExcludeSocketNotifiers flag)
1491 handled = TRUE;
1492 // ignore socket notifier activation if processd by the Qt event hook
1493 if ( qt_pmEventFilter( &msg, res ) )
1494 flags |= ExcludeSocketNotifiers;
1495 }
1496
1497 if ( !handled && msg.msg && (!msg.hwnd || !QWidget::find( msg.hwnd )) ) {
1498 handled = qt_pmEventFilter( &msg, res );
1499 }
1500
1501 if ( !handled ) {
1502 // send to QtWndProc or to a native window procedure
1503 WinDispatchMsg( 0, &msg );
1504 }
1505
1506 if ( !(flags & ExcludeSocketNotifiers) )
1507 activateSocketNotifiers();
1508
1509 // any pending configs?
1510 if ( configRequests )
1511 qPMProcessConfigRequests();
1512 QApplication::sendPostedEvents();
1513
1514 return TRUE;
1515}
1516
1517void QEventLoop::wakeUp()
1518{
1519 PTIB ptib;
1520 DosGetInfoBlocks( &ptib, NULL );
1521 MQINFO mqinfo;
1522 WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
1523 if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
1524 WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
1525}
1526
1527int QEventLoop::timeToWait() const
1528{
1529 return -1;
1530}
1531
1532int QEventLoop::activateTimers()
1533{
1534 return 0;
1535}
1536
1537int QEventLoop::activateSocketNotifiers()
1538{
1539#if defined(QT_THREAD_SUPPORT)
1540 if ( d->sn_pending_list.isEmpty() )
1541 return 0;
1542
1543 if ( !ss_thread )
1544 return 0;
1545
1546 // postpone activation if ss_thread is working with the list
1547 if ( !ss_mutex.tryLock() ) {
1548 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1549 return 0;
1550 }
1551
1552 ss_thread->cancelSelectOrIdle();
1553
1554 // activate entries
1555 int n_act = 0;
1556 QEvent event( QEvent::SockAct );
1557 QPtrListIterator<QSockNot> it( d->sn_pending_list );
1558 QSockNot *sn;
1559 while ( (sn = it.current()) ) {
1560 ++it;
1561 d->sn_pending_list.removeRef( sn );
1562 if ( FD_ISSET(sn->fd, sn->queue) ) {
1563 FD_CLR( sn->fd, sn->queue );
1564 // reenable the socket to let it participate in the next select()
1565 FD_SET( sn->fd, sn->active );
1566 d->sn_highest = QMAX( d->sn_highest, sn->fd );
1567 QApplication::sendEvent( sn->obj, &event );
1568 n_act++;
1569 }
1570 }
1571
1572 ss_mutex.unlock();
1573
1574 return n_act;
1575#else
1576 return 0;
1577#endif // QT_THREAD_SUPPORT
1578}
1579
Note: See TracBrowser for help on using the repository browser.