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

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

Kernel: Improved QEventLoop:

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