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

Last change on this file since 154 was 132, checked in by dmik, 19 years ago

Kernel: QEventLoop: Fixed: Recognize WM_U_SEM_SELECT only for the auxiliary internal window to allow other windows to receive WM_SEM messages when they need.

  • Property svn:keywords set to Id
File size: 40.0 KB
Line 
1/****************************************************************************
2** $Id: qeventloop_pm.cpp 132 2006-10-08 21:04:46Z 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
65static void initTimers();
66static void cleanupTimers();
67static bool dispatchTimer( uint, QMSG * );
68static bool activateTimer( uint );
69static void activateZeroTimers();
70
71static int numZeroTimers = 0; // number of full-speed timers
72
73// a flag to disable the warning when the console process dynamically
74// switches istelf to PM. Currently used by the UIC tool.
75bool qt_suppress_morph_warning = false;
76
77// Check that the current process is in PM mode. This is called by QEventLoop
78// and QApplication initialization routines and by initTimers() to ensure
79// that we are in PM mode and therefore it is possible to create the message
80// queue. "Morphing" to PM leaves the console attahed which can be used for
81// debugging.
82void qt_ensure_pm()
83{
84 PPIB ppib;
85 DosGetInfoBlocks( NULL, &ppib );
86 if( ppib->pib_ultype != 3 ) {
87#if defined(QT_CHECK_STATE)
88 if( !qt_suppress_morph_warning )
89 qWarning( "Qt: the program has not been linked as the Presentation "
90 "Manager application\n"
91 "but it uses GUI capabilities. Switching to PM mode "
92 "dynamically." );
93#endif
94 ppib->pib_ultype = 3;
95 }
96}
97
98enum
99{
100 // socket select notification (highest priority)
101 WM_U_SEM_SELECT = WM_SEM1,
102 // zero timer notification (lowest priority)
103 WM_U_SEM_ZEROTIMER = WM_SEM4,
104};
105
106/*****************************************************************************
107 Auxiliary object window class for dedicated message processing.
108 Declared in qwindowdefs_pm.h
109 *****************************************************************************/
110
111/*!
112 \class QPMObjectWindow qwindowdefs.h
113
114 The QPMObjectWindow class is an auxiliary class for dedicated message
115 processing. Its functionality is based on PM object windows. Once an instance
116 of this class is created, PM window messages can be sent or posted to it using
117 send() or post() methods. Subclasses should override the message() method to
118 process sent or posted messages. The hwnd() method is used whenever a PM
119 window handle of this object window is necessary to be passed as a HWND
120 argument to other calls and/or messages.
121
122 Instances of this class must be created on the main GUI thread only or on a
123 thread that has created a PM message queue and runs the PM message loop
124 \b itself. If you create an instance on a thread other than main, make sure
125 you destroy it before destroying the thread's message queue.
126
127 \note WM_CREATE and WM_DESTROY messages are processed internally and not
128 delivered do the message() method. Instead, you can use the constructor and
129 the destructor of the subclasses, respectively.
130
131 \note This class is non-portable!
132*/
133
134// returns HMQ of the current thread or NULL if no event queue has been created
135static HMQ qt_WinQueryQueue( HAB hab )
136{
137 PTIB ptib;
138 PPIB ppib;
139 DosGetInfoBlocks( &ptib, &ppib );
140 return WinQueueFromID( hab, ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid );
141}
142
143static QPtrList<HWND> qt_objWindows;
144
145/*!
146 Constructs a new object window for the current thread.
147 If \a deferred is FALSE, this method calls create() to create a PM object
148 window. Otherwise, you must call create() yourself before this object
149 window is able to process messages.
150*/
151QPMObjectWindow::QPMObjectWindow( bool deferred /* = FALSE */ ) :
152 w( NULLHANDLE )
153{
154 if ( !deferred )
155 create();
156}
157
158/*!
159 Destroys this object window.
160 This method calls destroy() to free the PM object window.
161*/
162QPMObjectWindow::~QPMObjectWindow()
163{
164 destroy();
165}
166
167/*!
168 Creates a PM object window.
169 Returns \c TRUE on success or \c FALSE otherwise.
170 The method does nothing but returns \c FALSE if the window has been
171 already created. The handle of the successfully created object window can
172 be obtained using the hwnd() method.
173
174 \note Must be called on the same thread that cosnstructed this instance.
175*/
176bool QPMObjectWindow::create()
177{
178 if ( w != NULLHANDLE )
179 return FALSE;
180
181 static const char *ClassName = "QPMObjectWindow";
182 static bool classRegistered = FALSE;
183
184 if ( !classRegistered ) {
185 WinRegisterClass( 0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID) );
186 classRegistered = TRUE;
187 }
188
189 w = WinCreateWindow( HWND_OBJECT, ClassName,
190 NULL, 0, 0, 0, 0, 0, NULL,
191 HWND_BOTTOM, 0, this, NULL );
192#ifndef QT_NO_DEBUG
193 if ( w == NULLHANDLE )
194 qSystemWarning( "QPMObjectWindow: Failed to create object window" );
195#endif
196
197 if ( w != NULLHANDLE && qt_gui_queue &&
198 qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
199 // store the handle in the list for clean destruction
200 qt_objWindows.append( &w );
201 }
202
203 return w != NULLHANDLE;
204}
205
206/*!
207 Destroys the PM object window.
208 Returns \c TRUE on success or \c FALSE otherwise.
209 The method does nothing but returns \c FALSE if the window has been
210 already destroyed (or never created).
211
212 \note Must be called on the same thread that cosnstructed this instance.
213*/
214bool QPMObjectWindow::destroy()
215{
216 if ( w == NULLHANDLE )
217 return FALSE;
218
219 if ( qt_gui_queue && qt_WinQueryQueue( 0 ) == qt_gui_queue ) {
220 // remove the handle from the 'clean destruction' list
221 qt_objWindows.removeRef( &w );
222 }
223
224 HWND h = w;
225 w = 0; // tell windowProc() we're unsafe
226 WinDestroyWindow( h );
227
228 return TRUE;
229}
230
231//static
232MRESULT EXPENTRY QPMObjectWindow::windowProc( HWND hwnd, ULONG msg,
233 MPARAM mp1, MPARAM mp2 )
234{
235 if ( msg == WM_CREATE ) {
236 QPMObjectWindow *that = static_cast< QPMObjectWindow *>( mp1 );
237 if ( !that )
238 return (MRESULT) TRUE;
239 WinSetWindowPtr( hwnd, QWL_USER, that );
240 return (MRESULT) FALSE;
241 }
242
243 QPMObjectWindow *that =
244 static_cast< QPMObjectWindow * >( WinQueryWindowPtr( hwnd, QWL_USER ) );
245 Q_ASSERT( that );
246
247 // Note: before WinCreateWindow() returns to the constructor or after the
248 // destructor has been called, w is 0. We use this to determine that the
249 // object is in the unsafe state (VTBL is not yet initialized or has been
250 // already uninitialized), so message() points to never-never land.
251 if ( !that || !that->w )
252 return (MRESULT) FALSE;
253
254 return that->message( msg, mp1, mp2 );
255}
256
257/*!
258 \fn QPMObjectWindow::send( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
259
260 Synchronously sends a message \a msg with the given parameters \a mp1 and
261 \a mp2 to this window handle and returns a reply from the message() function.
262
263 \note Must be called on the same thread that cosnstructed this instance.
264*/
265
266/*!
267 \fn QPMObjectWindow::post( ULONG msg, MPARAM mp1, MPARAM mp2 ) const
268
269 Asynchronously posts a message \a msg with the given parameters \a mp1 and
270 \a mp2 to this window handle. Returns \c TRUE on success and \c FALSE
271 otherwise.
272
273 \note Can be called on any thread.
274*/
275
276// Auxiliary object window to process WM_U_SEM_SELECT and WM_TIMER messages.
277// We need a dedicated window along with processing these messages directly in
278// QEventLoop::processEvents() to make sure they are processed even if the
279// current message loop is not run by Qt. This happens when a native modal
280// dialog is shown or when a top-level Qt widget is being moved or resized
281// using the mouse, or when a Qt-based DLL plugin is used by a non-Qt
282// application.
283
284class QPMAuxEventQueueWin : public QPMObjectWindow
285{
286public:
287 QPMAuxEventQueueWin() : QPMObjectWindow( TRUE ), eventLoop( NULL ) {}
288 void setEventLoop( QEventLoop *el ) { eventLoop = el; }
289 MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
290private:
291 QEventLoop *eventLoop;
292};
293
294MRESULT QPMAuxEventQueueWin::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
295{
296 switch ( msg ) {
297 case WM_U_SEM_SELECT: {
298 if ( eventLoop )
299 eventLoop->activateSocketNotifiers();
300 break;
301 }
302 case WM_U_SEM_ZEROTIMER: {
303 if ( numZeroTimers ) {
304 activateZeroTimers();
305 // repost the message if there are still zero timers left
306 if ( numZeroTimers )
307 WinPostMsg( hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
308 }
309 break;
310 }
311 case WM_TIMER: {
312 USHORT id = SHORT1FROMMP( mp1 );
313 QMSG qmsg = { hwnd(), msg, mp1, mp2 };
314 dispatchTimer( (uint) id, &qmsg );
315 break;
316 }
317 }
318
319 return FALSE;
320}
321
322static QPMAuxEventQueueWin qt_aux_win;
323
324
325/*****************************************************************************
326 Safe configuration (move,resize,setGeometry) mechanism to avoid
327 recursion when processing messages.
328 *****************************************************************************/
329
330#include "qptrqueue.h"
331
332struct QPMConfigRequest {
333 WId id; // widget to be configured
334 int req; // 0=move, 1=resize, 2=setGeo
335 int x, y, w, h; // request parameters
336};
337
338static QPtrQueue<QPMConfigRequest> *configRequests = 0;
339
340void qPMRequestConfig( WId id, int req, int x, int y, int w, int h )
341{
342 if ( !configRequests ) // create queue
343 configRequests = new QPtrQueue<QPMConfigRequest>;
344 QPMConfigRequest *r = new QPMConfigRequest;
345 r->id = id; // create new request
346 r->req = req;
347 r->x = x;
348 r->y = y;
349 r->w = w;
350 r->h = h;
351 configRequests->enqueue( r ); // store request in queue
352}
353
354Q_EXPORT void qPMProcessConfigRequests() // perform requests in queue
355{
356 if ( !configRequests )
357 return;
358 QPMConfigRequest *r;
359 for ( ;; ) {
360 if ( configRequests->isEmpty() )
361 break;
362 r = configRequests->dequeue();
363 QWidget *w = QWidget::find( r->id );
364 if ( w ) { // widget exists
365 if ( w->testWState(Qt::WState_ConfigPending) )
366 return; // biting our tail
367 if ( r->req == 0 )
368 w->move( r->x, r->y );
369 else if ( r->req == 1 )
370 w->resize( r->w, r->h );
371 else
372 w->setGeometry( r->x, r->y, r->w, r->h );
373 }
374 delete r;
375 }
376 delete configRequests;
377 configRequests = 0;
378}
379
380/*****************************************************************************
381 Timer handling; Our routines depend on OS/2 PM timer functions, but we
382 need some extra handling to activate objects at timeout.
383
384 Implementation note: There are two types of timer identifiers. PM
385 timer ids (internal use) are stored in TimerInfo. Qt timer ids are
386 indexes (+1) into the timerVec vector.
387
388 NOTE: These functions are for internal use. QObject::startTimer() and
389 QObject::killTimer() are for public use.
390 The QTimer class provides a high-level interface which translates
391 timer events into signals.
392
393 qStartTimer( interval, obj )
394 Starts a timer which will run until it is killed with qKillTimer()
395 Arguments:
396 int interval timer interval in milliseconds
397 QObject *obj where to send the timer event
398 Returns:
399 int timer identifier, or zero if not successful
400
401 qKillTimer( timerId )
402 Stops a timer specified by a timer identifier.
403 Arguments:
404 int timerId timer identifier
405 Returns:
406 bool TRUE if successful
407
408 qKillTimer( obj )
409 Stops all timers that are sent to the specified object.
410 Arguments:
411 QObject *obj object receiving timer events
412 Returns:
413 bool TRUE if successful
414 *****************************************************************************/
415
416//
417// Internal data structure for timers
418//
419
420#include "qptrvector.h"
421#include "qintdict.h"
422
423struct TimerInfo { // internal timer info
424 uint ind; // - Qt timer identifier - 1
425 ULONG id; // - PM timer identifier
426 bool zero; // - zero timing
427 QObject *obj; // - object to receive events
428};
429typedef QPtrVector<TimerInfo> TimerVec; // vector of TimerInfo structs
430typedef QIntDict<TimerInfo> TimerDict; // fast dict of timers
431
432static TimerVec *timerVec = 0; // timer vector
433static TimerDict *timerDict = 0; // timer dict
434
435template <typename T> // template to track availability
436class QFreeValueList // of free (unused) integer
437{ // values within some interval
438public:
439 QFreeValueList( T min, T max ) : intMin( min ), intMax( max ) {
440 freeValues.push_front( Span( min, max ) );
441 }
442 T take() {
443 Q_ASSERT( !isEmpty() );
444 Span &free = freeValues.first();
445 T freeValue = free.min;
446 if ( free.min == free.max ) freeValues.pop_front();
447 else free.min ++;
448 return freeValue;
449 }
450 void give( T val ) {
451 Q_ASSERT( val >= intMin && val <= intMax );
452 typename FreeList::iterator it = freeValues.begin();
453 for ( ; it != freeValues.end(); ++ it ) {
454 Span &span = (*it);
455 if ( val == span.min - 1 ) {
456 span.min --;
457 typename FreeList::iterator it2 = it;
458 if ( it2 != freeValues.begin() && (*--it2).max + 1 == span.min ) {
459 span.min = (*it2).min;
460 freeValues.erase( it2 );
461 }
462 return;
463 } else if ( val == span.max + 1 ) {
464 span.max ++;
465 typename FreeList::iterator it2 = it;
466 if ( ++it2 != freeValues.end() && (*it2).min - 1 == span.max ) {
467 span.max = (*it2).max;
468 freeValues.erase( it2 );
469 }
470 return;
471 }
472 else if ( val < span.min )
473 break;
474 Q_ASSERT( val > span.max );
475 }
476 if ( it == freeValues.end() ) freeValues.push_back( Span( val, val ) );
477 else freeValues.insert( it, Span( val, val ) );
478 }
479 bool isEmpty() { return freeValues.isEmpty(); }
480 T min() const { return intMin; }
481 T max() const { return intMax; }
482private:
483 struct Span {
484 Span() : min( 0 ), max( 0 ) {}
485 Span( T mn, T mx ) : min( mn ), max( mx ) {}
486 T min;
487 T max;
488 };
489 typedef QValueList<Span> FreeList;
490 FreeList freeValues;
491 T intMin;
492 T intMax;
493};
494
495typedef QFreeValueList<int> FreeQtTIDList; // list of free Qt timer IDs
496static FreeQtTIDList *freeQtTIDs = 0;
497typedef QFreeValueList<ULONG> FreePMTIDList; // list of free PM timer IDs
498static FreePMTIDList *freePMTIDs = 0;
499
500// Activate a timer, used by both event-loop based and simple timers.
501
502static bool dispatchTimer( uint timerId, QMSG *msg )
503{
504 MRESULT res = NULL;
505 if ( !msg || !qApp || !qt_pmEventFilter(msg,res) )
506 return activateTimer( timerId );
507 return TRUE;
508}
509
510
511//
512// Timer activation (called from the event loop when WM_TIMER arrives)
513//
514
515static bool activateTimer( uint id ) // activate timer
516{
517 if ( !timerVec ) // should never happen
518 return FALSE;
519 register TimerInfo *t = timerDict->find( id );
520 if ( !t ) // no such timer id
521 return FALSE;
522 QTimerEvent e( t->ind + 1 );
523 QApplication::sendEvent( t->obj, &e ); // send event
524 return TRUE; // timer event was processed
525}
526
527static void activateZeroTimers() // activate full-speed timers
528{
529 if ( !timerVec )
530 return;
531 uint i=0;
532 register TimerInfo *t = 0;
533 int n = numZeroTimers;
534 while ( n-- ) {
535 for ( ;; ) {
536 t = timerVec->at(i++);
537 if ( t && t->zero )
538 break;
539 else if ( i == timerVec->size() ) // should not happen
540 return;
541 }
542 QTimerEvent e( t->ind + 1 );
543 QApplication::sendEvent( t->obj, &e );
544 }
545}
546
547
548//
549// Timer initialization and cleanup routines
550//
551
552static void initTimers() // initialize timers
553{
554 timerVec = new TimerVec( 128 );
555 Q_CHECK_PTR( timerVec );
556 timerVec->setAutoDelete( TRUE );
557 timerDict = new TimerDict( 29 );
558 Q_CHECK_PTR( timerDict );
559 freeQtTIDs = new FreeQtTIDList( 0, INT_MAX );
560 Q_CHECK_PTR( freeQtTIDs );
561 freePMTIDs = new FreePMTIDList( 1, TID_USERMAX - 1 );
562 Q_CHECK_PTR( freePMTIDs );
563}
564
565/// @todo (dmik) cleanupTimers() is only called by QEventLoop::cleanup() so it
566// won't be called if there is no QEventLoop instance created (for example,
567// when a Qt-based plugin DLL is used by a non-Qt application). Use atexit()?
568// Btw, the same relates to the QThread::cleanup() vs QApplication pair.
569// And to qt_aux_win (the DLL may be unloaded after the application has
570// destroyed the main event queue, so the static destructor will not be able
571// to properly destroy the window).
572
573static void cleanupTimers() // remove pending timers
574{
575 register TimerInfo *t;
576 if ( !timerVec ) // no timers were used
577 return;
578 for ( uint i=0; i<timerVec->size(); i++ ) { // kill all pending timers
579 t = timerVec->at( i );
580 if ( t && !t->zero )
581 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
582 }
583 delete freePMTIDs;
584 freePMTIDs = 0;
585 delete freeQtTIDs;
586 freeQtTIDs = 0;
587 delete timerDict;
588 timerDict = 0;
589 delete timerVec;
590 timerVec = 0;
591}
592
593
594//
595// Main timer functions for starting and killing timers
596//
597
598
599int qStartTimer( int interval, QObject *obj )
600{
601 Q_ASSERT( obj );
602 if ( !obj )
603 return 0;
604
605 // lazily create the auxiliary window to process WM_TIMER messages
606 if ( !qt_aux_win.hwnd() )
607 if ( !qt_aux_win.create() )
608 return 0;
609
610 register TimerInfo *t;
611 if ( !timerVec ) // initialize timer data
612 initTimers();
613
614 if ( freeQtTIDs->isEmpty() ) {
615#if defined(QT_CHECK_STATE)
616 qWarning( "qStartTimer: Maximum number of timers (%d) is reached.",
617 freeQtTIDs->max() - freeQtTIDs->min() + 1 );
618#endif
619 return 0;
620 }
621
622 if ( freePMTIDs->isEmpty() ) {
623#if defined(QT_CHECK_STATE)
624 qWarning( "qStartTimer: Maximum number of non-zero timers (%ld) is reached.",
625 freePMTIDs->max() - freePMTIDs->min() + 1 );
626#endif
627 return 0;
628 }
629
630 int ind = freeQtTIDs->take(); // get free timer
631 if ( (uint) ind >= timerVec->size() ) {
632 uint newSize = timerVec->size() * 4; // increase the size
633 if ( newSize <= (uint) ind )
634 newSize = (uint) ind + 1;
635 timerVec->resize( newSize );
636 }
637 t = new TimerInfo; // create timer entry
638 Q_CHECK_PTR( t );
639 t->ind = ind;
640 t->obj = obj;
641
642 t->zero = interval == 0;
643 if ( t->zero ) { // add zero timer
644 t->id = 0;
645 numZeroTimers++;
646 // indicate there is a new zero timer
647 WinPostMsg( qt_aux_win.hwnd(), WM_U_SEM_ZEROTIMER, 0, 0 );
648 } else {
649 ULONG freeId = freePMTIDs->take(); // get free timer ID
650 t->id = WinStartTimer( 0, qt_aux_win.hwnd(), freeId, (ULONG) interval );
651 if ( t->id == 0 ) {
652#if defined(QT_CHECK_STATE)
653 qSystemWarning( "qStartTimer: Failed to create a timer." );
654#endif
655 freePMTIDs->give( freeId ); // could not start timer
656 freeQtTIDs->give( ind );
657 delete t;
658 return 0;
659 }
660 }
661 timerVec->insert( ind, t ); // store in timer vector
662 if ( !t->zero )
663 timerDict->insert( t->id, t ); // store in dict
664 return ind + 1; // return index in vector
665}
666
667bool qKillTimer( int ind )
668{
669 if ( !timerVec || ind <= 0 || (uint)ind > timerVec->size() )
670 return FALSE;
671 register TimerInfo *t = timerVec->at(ind-1);
672 if ( !t )
673 return FALSE;
674 if ( t->zero ) {
675 numZeroTimers--;
676 } else {
677 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
678 freePMTIDs->give( t->id );
679 timerDict->remove( t->id );
680 }
681 freeQtTIDs->give( t->ind );
682 timerVec->remove( ind-1 );
683 return TRUE;
684}
685
686bool qKillTimer( QObject *obj )
687{
688 if ( !timerVec )
689 return FALSE;
690 register TimerInfo *t;
691 for ( uint i=0; i<timerVec->size(); i++ ) {
692 t = timerVec->at( i );
693 if ( t && t->obj == obj ) { // object found
694 if ( t->zero ) {
695 numZeroTimers--;
696 } else {
697 WinStopTimer( 0, qt_aux_win.hwnd(), t->id );
698 freePMTIDs->give( t->id );
699 timerDict->remove( t->id );
700 }
701 freeQtTIDs->give( t->ind );
702 timerVec->remove( i );
703 }
704 }
705 return TRUE;
706}
707
708/*****************************************************************************
709 Socket notifier type
710 *****************************************************************************/
711
712QSockNotType::QSockNotType()
713 : list( 0 )
714{
715 FD_ZERO( &select_fds );
716 FD_ZERO( &enabled_fds );
717 FD_ZERO( &pending_fds );
718}
719
720QSockNotType::~QSockNotType()
721{
722 if ( list )
723 delete list;
724 list = 0;
725}
726
727/*****************************************************************************
728 socket select() thread
729 *****************************************************************************/
730
731#if defined(QT_THREAD_SUPPORT)
732
733static class QSockSelectThread : public QThread
734{
735public:
736 QSockSelectThread( QEventLoopPrivate *_d ) : d( _d ), exit( FALSE ) {};
737 void run();
738 void cancelSelectOrIdle( bool terminate = FALSE );
739private:
740 QEventLoopPrivate *d;
741 bool exit;
742} *ss_thread = 0;
743
744// recursive mutex to serialize access to socket notifier data
745static QMutex ss_mutex( TRUE );
746// flag to indicate the presence of sockets to do select() (we use QSemaphore
747// instead of QWaitCondition because we need the "level-triggered" semantics,
748// the "edge-triggered" semantics can produce deadlocks)
749static QSemaphore ss_flag( 1 );
750
751// Note: must be called from under ss_mutex lock!
752static inline void find_highest_fd( QEventLoopPrivate *d )
753{
754 d->sn_highest = -1;
755 for ( int i = 0; i < 3; i ++ ) {
756 QPtrList<QSockNot> *list = d->sn_vec[i].list;
757 if ( list && !list->isEmpty() ) {
758 QSockNot *sn = list->first();
759 while ( sn && !FD_ISSET( sn->fd, sn->active ) )
760 sn = list->next();
761 if ( sn )
762 d->sn_highest = QMAX( d->sn_highest, // list is fd-sorted
763 sn->fd );
764 }
765 }
766}
767
768void QSockSelectThread::run()
769{
770 while ( !exit ) {
771 ss_mutex.lock();
772 if ( d->sn_highest >= 0 ) { // has socket notifier(s)
773 // read
774 if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
775 d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
776 else
777 FD_ZERO( &d->sn_vec[0].select_fds );
778 // write
779 if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
780 d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
781 else
782 FD_ZERO( &d->sn_vec[1].select_fds );
783 // except
784 if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
785 d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
786 else
787 FD_ZERO( &d->sn_vec[2].select_fds );
788 // do select
789 int nfds = d->sn_highest + 1;
790 d->sn_cancel = d->sn_highest;
791 ss_mutex.unlock();
792 int nsel = ::select( nfds,
793 &d->sn_vec[0].select_fds,
794 &d->sn_vec[1].select_fds,
795 &d->sn_vec[2].select_fds,
796 NULL );
797 if ( nsel > 0 ) {
798 ss_mutex.lock();
799 // if select says data is ready on any socket, then set
800 // the socket notifier to pending
801 bool activate = FALSE;
802 for ( int i = 0; i < 3; i++ ) {
803 if ( ! d->sn_vec[i].list )
804 continue;
805 QPtrList<QSockNot> *list = d->sn_vec[i].list;
806 QSockNot *sn = list->first();
807 while ( sn ) {
808 if ( FD_ISSET( sn->fd, &d->sn_vec[i].select_fds ) ) {
809 // see comments in QEventLoop::setSocketNotifierPending()
810 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
811 // queue the socket activation
812 d->sn_pending_list.insert(
813 (rand() & 0xff) % (d->sn_pending_list.count()+1),
814 sn );
815 FD_SET( sn->fd, sn->queue );
816 // remove from enabled sockets to prevent deadlocks
817 // on blocking sockets (and to reduce CPU load caused
818 // by frequent select() calls) when the event queue
819 // is not processed fast enough to handle activation
820 FD_CLR( sn->fd, sn->active );
821 if ( sn->fd == d->sn_highest )
822 find_highest_fd( d );
823 activate = TRUE;
824 }
825 }
826 sn = list->next();
827 }
828 }
829 ss_mutex.unlock();
830 // post a message to activate socket notifiers
831 if ( activate )
832 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
833 }
834 } else {
835 d->sn_cancel = -1;
836 // no sockets to select(), go to the idle state
837 ss_mutex.unlock();
838 // wait for the ability to capture the flag
839 ss_flag ++;
840 // release the flag before termination
841 if ( exit )
842 ss_flag --;
843 }
844 }
845}
846
847void QSockSelectThread::cancelSelectOrIdle( bool terminate /* = FALSE */ )
848{
849 ss_mutex.lock();
850 exit = terminate;
851 if ( d->sn_cancel >= 0 ) {
852 // terminate select() execution
853 ::so_cancel( d->sn_cancel );
854 } else {
855 // terminate the idle state by releasing the flag
856 if ( !ss_flag.available() )
857 ss_flag --;
858 }
859 ss_mutex.unlock();
860 // wait for this thread to end if the termination is requested
861 if ( exit )
862 wait();
863}
864
865static void ss_cleanup()
866{
867 ss_thread->cancelSelectOrIdle( TRUE );
868 delete ss_thread;
869 ss_thread = 0;
870}
871
872static void ss_init( QEventLoopPrivate *d )
873{
874 // capture the flag initially
875 ss_flag ++;
876 ss_thread = new QSockSelectThread( d );
877 ss_thread->start();
878}
879
880#endif // QT_THREAD_SUPPORT
881
882/*****************************************************************************
883 QEventLoop Implementation for OS/2 PM
884 *****************************************************************************/
885
886void QEventLoop::init()
887{
888 d->sn_highest = -1;
889 d->sn_cancel = -1;
890
891 qt_ensure_pm();
892 d->hab = WinInitialize( 0 );
893 d->hmq = WinCreateMsgQueue( d->hab, 0 );
894 qt_gui_hab = d->hab;
895 qt_gui_queue = d->hmq;
896
897 qt_aux_win.setEventLoop( this );
898
899#if defined(QT_THREAD_SUPPORT)
900 // there may only be one global QEventLoop instance that manages sockets
901 Q_ASSERT( ss_thread == 0 );
902#else
903#if defined(QT_CHECK_STATE)
904 qWarning( "QEventLoop::init: socket notifiers are not supported "
905 "for a single-threaded version of Qt." );
906#endif
907#endif // QT_THREAD_SUPPORT
908}
909
910void QEventLoop::cleanup()
911{
912 cleanupTimers();
913
914 // ss_thread should have been stopped already in appClosingDown()
915 Q_ASSERT( ss_thread == 0 );
916
917 qt_aux_win.setEventLoop( 0 );
918
919 // destroy all windows created by QPMObjectWindow instances
920 for ( HWND *w = qt_objWindows.first(); w; w = qt_objWindows.next() ) {
921 Q_ASSERT( *w );
922 WinDestroyWindow( *w );
923 // tell QPMObjectWindow the window has been destroyed
924 *w = 0;
925 }
926
927 WinDestroyMsgQueue( d->hmq );
928 WinTerminate( d->hab );
929 qt_gui_queue = 0;
930 qt_gui_hab = 0;
931}
932
933void QEventLoop::appStartingUp()
934{
935}
936
937void QEventLoop::appClosingDown()
938{
939 // ensure the thread is terminated before QApplication calls
940 // QThread::cleanup()
941 if ( ss_thread )
942 ss_cleanup();
943}
944
945void QEventLoop::registerSocketNotifier( QSocketNotifier *notifier )
946{
947#if defined(QT_THREAD_SUPPORT)
948 // there may only be one global QEventLoop instance that manages sockets
949 Q_ASSERT( !qApp || qApp->eventloop == this );
950 if ( qApp && qApp->eventloop != this )
951 return;
952
953 // lazily create the auxiliary window to process WM_U_SEM_SELECT messages
954 if ( !qt_aux_win.hwnd() )
955 if ( !qt_aux_win.create() )
956 return;
957
958 // lazily start the socket select thread
959 if ( !ss_thread )
960 ss_init( d );
961
962 int sockfd = -1;
963 int type = -1;
964 if ( notifier ) {
965 sockfd = notifier->socket();
966 type = notifier->type();
967 }
968 if ( sockfd < 0 || sockfd >= FD_SETSIZE || type < 0 || type > 2 ) {
969#if defined(QT_CHECK_RANGE)
970 qWarning( "QSocketNotifier: Internal error" );
971#endif
972 return;
973 }
974
975 QMutexLocker locker( &ss_mutex );
976 ss_thread->cancelSelectOrIdle();
977
978 QPtrList<QSockNot> *list = d->sn_vec[type].list;
979 QSockNot *sn;
980
981 if ( ! list ) {
982 // create new list, the QSockNotType destructor will delete it for us
983 list = new QPtrList<QSockNot>;
984 Q_CHECK_PTR( list );
985 list->setAutoDelete( TRUE );
986 d->sn_vec[type].list = list;
987 }
988
989 sn = new QSockNot;
990 Q_CHECK_PTR( sn );
991 sn->obj = notifier;
992 sn->fd = sockfd;
993 sn->queue = &d->sn_vec[type].pending_fds;
994 sn->active = &d->sn_vec[type].enabled_fds;
995
996 if ( list->isEmpty() ) {
997 list->insert( 0, sn );
998 } else { // sort list by fd, decreasing
999 QSockNot *p = list->first();
1000 while ( p && p->fd > sockfd )
1001 p = list->next();
1002#if defined(QT_CHECK_STATE)
1003 if ( p && p->fd == sockfd ) {
1004 static const char *t[] = { "read", "write", "exception" };
1005 qWarning( "QSocketNotifier: Multiple socket notifiers for "
1006 "same socket %d and type %s", sockfd, t[type] );
1007 }
1008#endif
1009 if ( p )
1010 list->insert( list->at(), sn );
1011 else
1012 list->append( sn );
1013 }
1014
1015 // enable the socket only if it's not already pending
1016 if ( !FD_ISSET( sockfd, sn->queue ) ) {
1017 FD_SET( sockfd, sn->active );
1018 d->sn_highest = QMAX( d->sn_highest, sockfd );
1019 }
1020#endif // QT_THREAD_SUPPORT
1021}
1022
1023void QEventLoop::unregisterSocketNotifier( QSocketNotifier *notifier )
1024{
1025#if defined(QT_THREAD_SUPPORT)
1026 int sockfd = -1;
1027 int type = -1;
1028 if ( notifier ) {
1029 sockfd = notifier->socket();
1030 type = notifier->type();
1031 }
1032 if ( sockfd < 0 || type < 0 || type > 2 ) {
1033#if defined(QT_CHECK_RANGE)
1034 qWarning( "QSocketNotifier: Internal error" );
1035#endif
1036 return;
1037 }
1038
1039 if ( !ss_thread )
1040 return; // definitely not found
1041
1042 QMutexLocker locker( &ss_mutex );
1043 ss_thread->cancelSelectOrIdle();
1044
1045 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1046 QSockNot *sn;
1047 if ( ! list )
1048 return;
1049 sn = list->first();
1050 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1051 sn = list->next();
1052 if ( !sn ) // not found
1053 return;
1054
1055 // touch fd bitmaps only if there are no other notifiers for the same socket
1056 // (QPtrList curiously lacks getNext()/getPrev(), so play tambourine)
1057 QSockNot *next = list->next();
1058 if ( next ) list->prev();
1059 else list->last();
1060 QSockNot *prev = list->prev();
1061 if ( prev ) list->next();
1062 else list->first();
1063 bool unique = (!next || next->fd != sockfd) && (!prev || prev->fd != sockfd);
1064 if ( unique) {
1065 FD_CLR( sockfd, sn->active ); // clear fd bit
1066 FD_CLR( sockfd, sn->queue );
1067 }
1068 d->sn_pending_list.removeRef( sn ); // remove from activation list
1069 list->remove(); // remove notifier found above
1070
1071 if ( unique) {
1072 if ( d->sn_highest == sockfd )
1073 find_highest_fd( d );
1074 }
1075#endif // QT_THREAD_SUPPORT
1076}
1077
1078void QEventLoop::setSocketNotifierPending( QSocketNotifier *notifier )
1079{
1080#if defined(QT_THREAD_SUPPORT)
1081 int sockfd = -1;
1082 int type = -1;
1083 if ( notifier ) {
1084 sockfd = notifier->socket();
1085 type = notifier->type();
1086 }
1087 if ( sockfd < 0 || type < 0 || type > 2 ) {
1088#if defined(QT_CHECK_RANGE)
1089 qWarning( "QSocketNotifier: Internal error" );
1090#endif
1091 return;
1092 }
1093
1094 if ( !ss_thread )
1095 return; // definitely not found
1096
1097 QMutexLocker locker( &ss_mutex );
1098 ss_thread->cancelSelectOrIdle();
1099
1100 QPtrList<QSockNot> *list = d->sn_vec[type].list;
1101 QSockNot *sn;
1102 if ( ! list )
1103 return;
1104 sn = list->first();
1105 while ( sn && !(sn->obj == notifier && sn->fd == sockfd) )
1106 sn = list->next();
1107 if ( ! sn ) { // not found
1108 return;
1109 }
1110
1111 // We choose a random activation order to be more fair under high load.
1112 // If a constant order is used and a peer early in the list can
1113 // saturate the IO, it might grab our attention completely.
1114 // Also, if we're using a straight list, the callback routines may
1115 // delete other entries from the list before those other entries are
1116 // processed.
1117 if ( !FD_ISSET( sn->fd, sn->queue ) ) {
1118 d->sn_pending_list.insert(
1119 (rand() & 0xff) % (d->sn_pending_list.count()+1),
1120 sn
1121 );
1122 FD_SET( sockfd, sn->queue );
1123 // remove from enabled sockets, see QSockSelectThread::run()
1124 FD_CLR( sockfd, sn->active );
1125 if ( d->sn_highest == sockfd )
1126 find_highest_fd( d );
1127 }
1128#endif // QT_THREAD_SUPPORT
1129}
1130
1131bool QEventLoop::hasPendingEvents() const
1132{
1133 QMSG msg;
1134 return qGlobalPostedEventsCount() || WinPeekMsg( 0, &msg, NULL, 0, 0, PM_NOREMOVE );
1135}
1136
1137bool QEventLoop::processEvents( ProcessEventsFlags flags )
1138{
1139 QMSG msg;
1140
1141#if defined(QT_THREAD_SUPPORT)
1142 QMutexLocker locker( QApplication::qt_mutex );
1143#endif
1144
1145 QApplication::sendPostedEvents();
1146
1147 if ( flags & ExcludeUserInput ) {
1148 while ( WinPeekMsg( 0, &msg, 0, 0, 0, PM_NOREMOVE ) ) {
1149 if ( msg.msg == WM_CHAR ||
1150 (msg.msg >= WM_MOUSEFIRST &&
1151 msg.msg <= WM_MOUSELAST) ||
1152 (msg.msg >= WM_EXTMOUSEFIRST &&
1153 msg.msg <= WM_EXTMOUSELAST) ||
1154 msg.msg == WM_HSCROLL ||
1155 msg.msg == WM_VSCROLL
1156 ) {
1157 WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE );
1158 continue;
1159 }
1160 break;
1161 }
1162 }
1163
1164 bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
1165
1166 if ( canWait ) {
1167 // can wait if necessary
1168 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1169 emit aboutToBlock();
1170#ifdef QT_THREAD_SUPPORT
1171 locker.mutex()->unlock();
1172#endif
1173 WinGetMsg( 0, &msg, 0, 0, 0 );
1174#ifdef QT_THREAD_SUPPORT
1175 locker.mutex()->lock();
1176#endif
1177 emit awake();
1178 emit qApp->guiThreadAwake();
1179 }
1180 } else {
1181 if ( !WinPeekMsg( 0, &msg, 0, 0, 0, PM_REMOVE ) ) {
1182 // no pending events
1183 return FALSE;
1184 }
1185 }
1186
1187 if ( msg.msg == WM_QUIT ) {
1188 // process the quit request
1189#if !defined (QT_NO_SESSIONMANAGER)
1190 if ( qt_app_canQuit() ) {
1191#endif
1192 exit( 0 );
1193 return TRUE;
1194#if !defined (QT_NO_SESSIONMANAGER)
1195 } else {
1196 WinCancelShutdown( d->hmq, FALSE );
1197 return TRUE;
1198 }
1199#endif
1200 }
1201
1202 bool handled = FALSE;
1203 MRESULT res = 0;
1204
1205 if ( msg.msg == WM_U_SEM_SELECT && msg.hwnd == qt_aux_win.hwnd() ) {
1206 // socket select event received: prevent it from being handled by
1207 // qt_aux_win (to obey the ExcludeSocketNotifiers flag)
1208 handled = TRUE;
1209 // ignore socket notifier activation if processd by the Qt event hook
1210 if ( qt_pmEventFilter( &msg, res ) )
1211 flags |= ExcludeSocketNotifiers;
1212 }
1213
1214 if ( !handled && msg.msg && (!msg.hwnd || !QWidget::find( msg.hwnd )) ) {
1215 handled = qt_pmEventFilter( &msg, res );
1216 }
1217
1218 if ( !handled ) {
1219 // send to QtWndProc or to a native window procedure
1220 WinDispatchMsg( 0, &msg );
1221 }
1222
1223 if ( !(flags & ExcludeSocketNotifiers) )
1224 activateSocketNotifiers();
1225
1226 // any pending configs?
1227 if ( configRequests )
1228 qPMProcessConfigRequests();
1229 QApplication::sendPostedEvents();
1230
1231 return TRUE;
1232}
1233
1234void QEventLoop::wakeUp()
1235{
1236 PTIB ptib;
1237 DosGetInfoBlocks( &ptib, NULL );
1238 MQINFO mqinfo;
1239 WinQueryQueueInfo( qt_gui_queue, &mqinfo, sizeof(MQINFO) );
1240 if ( ptib->tib_ptib2->tib2_ultid != mqinfo.tid )
1241 WinPostQueueMsg( qt_gui_queue, WM_NULL, 0, 0 );
1242}
1243
1244int QEventLoop::timeToWait() const
1245{
1246 return -1;
1247}
1248
1249int QEventLoop::activateTimers()
1250{
1251 return 0;
1252}
1253
1254int QEventLoop::activateSocketNotifiers()
1255{
1256#if defined(QT_THREAD_SUPPORT)
1257 if ( d->sn_pending_list.isEmpty() )
1258 return 0;
1259
1260 if ( !ss_thread )
1261 return 0;
1262
1263 // postpone activation if ss_thread is working with the list
1264 if ( !ss_mutex.tryLock() ) {
1265 qt_aux_win.post( WM_U_SEM_SELECT, 0, 0 );
1266 return 0;
1267 }
1268
1269 ss_thread->cancelSelectOrIdle();
1270
1271 // activate entries
1272 int n_act = 0;
1273 QEvent event( QEvent::SockAct );
1274 QPtrListIterator<QSockNot> it( d->sn_pending_list );
1275 QSockNot *sn;
1276 while ( (sn = it.current()) ) {
1277 ++it;
1278 d->sn_pending_list.removeRef( sn );
1279 if ( FD_ISSET(sn->fd, sn->queue) ) {
1280 FD_CLR( sn->fd, sn->queue );
1281 // reenable the socket to let it participate in the next select()
1282 FD_SET( sn->fd, sn->active );
1283 d->sn_highest = QMAX( d->sn_highest, sn->fd );
1284 QApplication::sendEvent( sn->obj, &event );
1285 n_act++;
1286 }
1287 }
1288
1289 ss_mutex.unlock();
1290
1291 return n_act;
1292#else
1293 return 0;
1294#endif // QT_THREAD_SUPPORT
1295}
1296
Note: See TracBrowser for help on using the repository browser.