Back to index

lightning-sunbird  0.9+nobinonly
plevent.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org Code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #if defined(_WIN32)
00039 #include <windows.h>
00040 #endif
00041 
00042 #if defined(XP_OS2)
00043 #define INCL_DOS
00044 #define INCL_DOSERRORS
00045 #define INCL_WIN
00046 #include <os2.h>
00047 #define DefWindowProc WinDefWindowProc
00048 #endif /* XP_OS2 */
00049 
00050 #include "nspr.h"
00051 #include "plevent.h"
00052 
00053 #if !defined(WIN32)
00054 #include <errno.h>
00055 #include <stddef.h>
00056 #if !defined(XP_OS2)
00057 #include <unistd.h>
00058 #endif /* !XP_OS2 */
00059 #endif /* !Win32 */
00060 
00061 #if defined(XP_UNIX)
00062 /* for fcntl */
00063 #include <sys/types.h>
00064 #include <fcntl.h>
00065 #endif
00066 
00067 #if defined(XP_BEOS)
00068 #include <kernel/OS.h>
00069 #endif
00070 
00071 #if defined(XP_MACOSX)
00072 #if defined(MOZ_WIDGET_COCOA)
00073 #include <CoreFoundation/CoreFoundation.h>
00074 #define MAC_USE_CFRUNLOOPSOURCE
00075 #elif defined(TARGET_CARBON)
00076 /* #include <CarbonEvents.h> */
00077 /* #define MAC_USE_CARBON_EVENT */
00078 #include <CoreFoundation/CoreFoundation.h>
00079 #define MAC_USE_CFRUNLOOPSOURCE
00080 #endif
00081 #endif
00082 
00083 #include "private/pprthred.h"
00084 
00085 #if defined(VMS)
00086 /*
00087 ** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it
00088 ** wants an event flag. So, we don't create and use a pipe for
00089 ** notification of when an event queue has something ready, instead
00090 ** we use an event flag. Shouldn't be a problem if we only have
00091 ** a few event queues.
00092 */
00093 #include <lib$routines.h>
00094 #include <starlet.h>
00095 #include <stsdef.h>
00096 #endif /* VMS */
00097 
00098 #if defined(_WIN32)
00099 /* Comment out the following USE_TIMER define to prevent
00100  * WIN32 from using a WIN32 native timer for PLEvent notification.
00101  * With USE_TIMER defined we will use a timer when pending input
00102  * or paint events are starved, otherwise it will use a posted
00103  * WM_APP msg for PLEvent notification.
00104  */
00105 
00106 #ifndef WINCE
00107 #define USE_TIMER
00108 #endif
00109 
00110 /* Threshold defined in milliseconds for determining when the input
00111  * and paint events have been held in the WIN32 msg queue too long
00112  */
00113 #define INPUT_STARVATION_LIMIT    50
00114 /* The paint starvation limit is set to the smallest value which
00115  * does not cause performance degradation while running page load tests
00116  */
00117 #define PAINT_STARVATION_LIMIT   750
00118 /* The WIN9X paint starvation limit is larger because it was
00119  * determined that the following value was required to prevent performance
00120  * degradation on page load tests for WIN98/95 only.
00121  */
00122 #define WIN9X_PAINT_STARVATION_LIMIT 3000
00123 
00124 #define TIMER_ID 0
00125 /* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */
00126 static PRInt32  _md_PerformanceSetting = 0;
00127 static PRUint32 _md_StarvationDelay    = 0;
00128 static PRUint32 _md_SwitchTime         = 0;
00129 #endif
00130 
00131 static PRLogModuleInfo *event_lm = NULL;
00132 
00133 /*******************************************************************************
00134  * Private Stuff
00135  ******************************************************************************/
00136 
00137 /*
00138 ** EventQueueType -- Defines notification type for an event queue
00139 **
00140 */
00141 typedef enum {
00142     EventQueueIsNative = 1,
00143     EventQueueIsMonitored = 2
00144 } EventQueueType;
00145 
00146 
00147 struct PLEventQueue {
00148     const char*         name;
00149     PRCList             queue;
00150     PRMonitor*          monitor;
00151     PRThread*           handlerThread;
00152     EventQueueType      type;
00153     PRPackedBool        processingEvents;
00154     PRPackedBool        notified;
00155 #if defined(_WIN32) 
00156     PRPackedBool        timerSet;
00157 #endif
00158 
00159 #if defined(XP_UNIX) && !defined(XP_MACOSX)
00160 #if defined(VMS)
00161     int                 efn;
00162 #else
00163     PRInt32             eventPipe[2];
00164 #endif
00165     PLGetEventIDFunc    idFunc;
00166     void*               idFuncClosure;
00167 #elif defined(_WIN32) || defined(XP_OS2)
00168     HWND                eventReceiverWindow;
00169     PRBool              removeMsg;
00170 #elif defined(XP_BEOS)
00171     port_id             eventport;
00172 #elif defined(XP_MACOSX)
00173 #if defined(MAC_USE_CFRUNLOOPSOURCE)
00174     CFRunLoopSourceRef  mRunLoopSource;
00175     CFRunLoopRef        mMainRunLoop;
00176 #elif defined(MAC_USE_CARBON_EVENT)
00177     EventHandlerUPP     eventHandlerUPP;
00178     EventHandlerRef     eventHandlerRef;
00179 #endif
00180 #endif
00181 };
00182 
00183 #define PR_EVENT_PTR(_qp) \
00184     ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
00185 
00186 static PRStatus    _pl_SetupNativeNotifier(PLEventQueue* self);
00187 static void        _pl_CleanupNativeNotifier(PLEventQueue* self);
00188 static PRStatus    _pl_NativeNotify(PLEventQueue* self);
00189 static PRStatus    _pl_AcknowledgeNativeNotify(PLEventQueue* self);
00190 static void        _md_CreateEventQueue( PLEventQueue *eventQueue );
00191 static PRInt32     _pl_GetEventCount(PLEventQueue* self);
00192 
00193 
00194 #if defined(_WIN32) || defined(XP_OS2)
00195 #if defined(XP_OS2)
00196 ULONG _pr_PostEventMsgId;
00197 #else
00198 UINT _pr_PostEventMsgId;
00199 #endif /* OS2 */
00200 static char *_pr_eventWindowClass = "XPCOM:EventWindow";
00201 #endif /* Win32, OS2 */
00202 
00203 #if defined(_WIN32)
00204 
00205 static LPCTSTR _md_GetEventQueuePropName() {
00206     static ATOM atom = 0;
00207     if (!atom) {
00208         atom = GlobalAddAtom("XPCOM_EventQueue");
00209     }
00210     return MAKEINTATOM(atom);
00211 }
00212 #endif
00213 
00214 #if defined(MAC_USE_CARBON_EVENT)
00215 enum {
00216   kEventClassPL         = FOUR_CHAR_CODE('PLEC'),
00217 
00218   kEventProcessPLEvents = 1,
00219 
00220   kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ')
00221 };
00222 
00223 static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData);
00224 #endif
00225 
00226 /*******************************************************************************
00227  * Event Queue Operations
00228  ******************************************************************************/
00229 
00230 /*
00231 ** _pl_CreateEventQueue() -- Create the event queue
00232 **
00233 **
00234 */
00235 static PLEventQueue * _pl_CreateEventQueue(const char *name,
00236                                            PRThread *handlerThread,
00237                                            EventQueueType  qtype)
00238 {
00239     PRStatus err;
00240     PLEventQueue* self = NULL;
00241     PRMonitor* mon = NULL;
00242 
00243     if (event_lm == NULL)
00244         event_lm = PR_NewLogModule("event");
00245 
00246     self = PR_NEWZAP(PLEventQueue);
00247     if (self == NULL) return NULL;
00248 
00249     mon = PR_NewNamedMonitor(name);
00250     if (mon == NULL) goto error;
00251 
00252     self->name = name;
00253     self->monitor = mon;
00254     self->handlerThread = handlerThread;
00255     self->processingEvents = PR_FALSE;
00256     self->type = qtype;
00257 #if defined(_WIN32)
00258     self->timerSet = PR_FALSE;
00259 #endif
00260 #if defined(_WIN32) || defined(XP_OS2)
00261     self->removeMsg = PR_TRUE;
00262 #endif
00263 
00264     self->notified = PR_FALSE;
00265 
00266     PR_INIT_CLIST(&self->queue);
00267     if ( qtype == EventQueueIsNative ) {
00268         err = _pl_SetupNativeNotifier(self);
00269         if (err) goto error;
00270         _md_CreateEventQueue( self );
00271     }
00272     return self;
00273 
00274   error:
00275     if (mon != NULL)
00276         PR_DestroyMonitor(mon);
00277     PR_DELETE(self);
00278     return NULL;
00279 }
00280 
00281 PLEventQueue*
00282 PL_CreateEventQueue(const char* name, PRThread* handlerThread)
00283 {
00284     return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
00285 }
00286 
00287 PR_EXTERN(PLEventQueue *) 
00288 PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread)
00289 {
00290     return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
00291 }
00292 
00293 PR_EXTERN(PLEventQueue *) 
00294 PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread)
00295 {
00296     return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
00297 }
00298 
00299 PRMonitor*
00300 PL_GetEventQueueMonitor(PLEventQueue* self)
00301 {
00302     return self->monitor;
00303 }
00304 
00305 static void PR_CALLBACK
00306 _pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
00307 {
00308     PL_DequeueEvent(event, queue);
00309     PL_DestroyEvent(event);
00310 }
00311 
00312 void
00313 PL_DestroyEventQueue(PLEventQueue* self)
00314 {
00315     PR_EnterMonitor(self->monitor);
00316 
00317     /* destroy undelivered events */
00318     PL_MapEvents(self, _pl_destroyEvent, NULL);
00319 
00320     if ( self->type == EventQueueIsNative )
00321         _pl_CleanupNativeNotifier(self);
00322 
00323     /* destroying the monitor also destroys the name */
00324     PR_ExitMonitor(self->monitor);
00325     PR_DestroyMonitor(self->monitor);
00326     PR_DELETE(self);
00327 
00328 }
00329 
00330 PRStatus
00331 PL_PostEvent(PLEventQueue* self, PLEvent* event)
00332 {
00333     PRStatus err = PR_SUCCESS;
00334     PRMonitor* mon;
00335 
00336     if (self == NULL)
00337         return PR_FAILURE;
00338 
00339     mon = self->monitor;
00340     PR_EnterMonitor(mon);
00341 
00342 #if defined(XP_UNIX) && !defined(XP_MACOSX)
00343     if (self->idFunc && event)
00344         event->id = self->idFunc(self->idFuncClosure);
00345 #endif
00346 
00347     /* insert event into thread's event queue: */
00348     if (event != NULL) {
00349         PR_APPEND_LINK(&event->link, &self->queue);
00350     }
00351 
00352     if (self->type == EventQueueIsNative && !self->notified) {
00353         err = _pl_NativeNotify(self);
00354 
00355         if (err != PR_SUCCESS)
00356             goto error;
00357 
00358         self->notified = PR_TRUE;
00359     }
00360 
00361     /*
00362      * This may fall on deaf ears if we're really notifying the native
00363      * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
00364      */
00365     err = PR_Notify(mon);
00366 
00367 error:
00368     PR_ExitMonitor(mon);
00369     return err;
00370 }
00371 
00372 void*
00373 PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
00374 {
00375     void* result;
00376 
00377     if (self == NULL)
00378         return NULL;
00379 
00380     PR_ASSERT(event != NULL);
00381 
00382     if (PR_GetCurrentThread() == self->handlerThread) {
00383         /* Handle the case where the thread requesting the event handling
00384          * is also the thread that's supposed to do the handling. */
00385         result = event->handler(event);
00386     }
00387     else {
00388         int i, entryCount;
00389 
00390         event->lock = PR_NewLock();
00391         if (!event->lock) {
00392           return NULL;
00393         }
00394         event->condVar = PR_NewCondVar(event->lock);
00395         if(!event->condVar) {
00396           PR_DestroyLock(event->lock);
00397           event->lock = NULL;
00398           return NULL;
00399         }
00400 
00401         PR_Lock(event->lock);
00402 
00403         entryCount = PR_GetMonitorEntryCount(self->monitor);
00404 
00405         event->synchronousResult = (void*)PR_TRUE;
00406 
00407         PL_PostEvent(self, event);
00408 
00409         /* We need temporarily to give up our event queue monitor if
00410            we're holding it, otherwise, the thread we're going to wait
00411            for notification from won't be able to enter it to process
00412            the event. */
00413         if (entryCount) {
00414             for (i = 0; i < entryCount; i++)
00415                 PR_ExitMonitor(self->monitor);
00416         }
00417 
00418         event->handled = PR_FALSE;
00419 
00420         while (!event->handled) {
00421             /* wait for event to be handled or destroyed */
00422             PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT);
00423         }
00424 
00425         if (entryCount) {
00426             for (i = 0; i < entryCount; i++)
00427                 PR_EnterMonitor(self->monitor);
00428         }
00429 
00430         result = event->synchronousResult;
00431         event->synchronousResult = NULL;
00432         PR_Unlock(event->lock);
00433     }
00434 
00435     /* For synchronous events, they're destroyed here on the caller's
00436        thread before the result is returned. See PL_HandleEvent. */
00437     PL_DestroyEvent(event);
00438 
00439     return result;
00440 }
00441 
00442 PLEvent*
00443 PL_GetEvent(PLEventQueue* self)
00444 {
00445     PLEvent* event = NULL;
00446     PRStatus err = PR_SUCCESS;
00447 
00448     if (self == NULL)
00449         return NULL;
00450 
00451     PR_EnterMonitor(self->monitor);
00452 
00453     if (!PR_CLIST_IS_EMPTY(&self->queue)) {
00454         if ( self->type == EventQueueIsNative &&
00455              self->notified                   &&
00456              !self->processingEvents          &&
00457              0 == _pl_GetEventCount(self)     )
00458         {
00459             err = _pl_AcknowledgeNativeNotify(self);
00460             self->notified = PR_FALSE;
00461         }
00462         if (err)
00463             goto done;
00464 
00465         /* then grab the event and return it: */
00466         event = PR_EVENT_PTR(self->queue.next);
00467         PR_REMOVE_AND_INIT_LINK(&event->link);
00468     }
00469 
00470   done:
00471     PR_ExitMonitor(self->monitor);
00472     return event;
00473 }
00474 
00475 PRBool
00476 PL_EventAvailable(PLEventQueue* self)
00477 {
00478     PRBool result = PR_FALSE;
00479 
00480     if (self == NULL)
00481         return PR_FALSE;
00482 
00483     PR_EnterMonitor(self->monitor);
00484 
00485     if (!PR_CLIST_IS_EMPTY(&self->queue))
00486         result = PR_TRUE;
00487 
00488     PR_ExitMonitor(self->monitor);
00489     return result;
00490 }
00491 
00492 void
00493 PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
00494 {
00495     PRCList* qp;
00496 
00497     if (self == NULL)
00498         return;
00499 
00500     PR_EnterMonitor(self->monitor);
00501     qp = self->queue.next;
00502     while (qp != &self->queue) {
00503         PLEvent* event = PR_EVENT_PTR(qp);
00504         qp = qp->next;
00505         (*fun)(event, data, self);
00506     }
00507     PR_ExitMonitor(self->monitor);
00508 }
00509 
00510 static void PR_CALLBACK
00511 _pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
00512 {
00513     PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
00514     if (event->owner == owner) {
00515         PR_LOG(event_lm, PR_LOG_DEBUG,
00516                ("$$$ \tdestroying event %0x for owner %0x", event, owner));
00517         PL_DequeueEvent(event, queue);
00518 
00519         if (event->synchronousResult == (void*)PR_TRUE) {
00520             PR_Lock(event->lock);
00521             event->synchronousResult = NULL;
00522             event->handled = PR_TRUE;
00523             PR_NotifyCondVar(event->condVar);
00524             PR_Unlock(event->lock);
00525         }
00526         else {
00527             PL_DestroyEvent(event);
00528         }
00529     }
00530     else {
00531         PR_LOG(event_lm, PR_LOG_DEBUG,
00532                ("$$$ \tskipping event %0x for owner %0x", event, owner));
00533     }
00534 }
00535 
00536 void
00537 PL_RevokeEvents(PLEventQueue* self, void* owner)
00538 {
00539     if (self == NULL)
00540         return;
00541 
00542     PR_LOG(event_lm, PR_LOG_DEBUG,
00543          ("$$$ revoking events for owner %0x", owner));
00544 
00545     /*
00546     ** First we enter the monitor so that no one else can post any events
00547     ** to the queue:
00548     */
00549     PR_EnterMonitor(self->monitor);
00550     PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
00551 
00552     /*
00553     ** Discard any pending events for this owner:
00554     */
00555     PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
00556 
00557 #ifdef DEBUG
00558     {
00559         PRCList* qp = self->queue.next;
00560         while (qp != &self->queue) {
00561             PLEvent* event = PR_EVENT_PTR(qp);
00562             qp = qp->next;
00563             PR_ASSERT(event->owner != owner);
00564         }
00565     }
00566 #endif /* DEBUG */
00567 
00568     PR_ExitMonitor(self->monitor);
00569 
00570     PR_LOG(event_lm, PR_LOG_DEBUG,
00571            ("$$$ revoking events for owner %0x", owner));
00572 }
00573 
00574 static PRInt32
00575 _pl_GetEventCount(PLEventQueue* self)
00576 {
00577     PRCList* node;
00578     PRInt32  count = 0;
00579 
00580     PR_EnterMonitor(self->monitor);
00581     node = PR_LIST_HEAD(&self->queue);
00582     while (node != &self->queue) {
00583         count++;
00584         node = PR_NEXT_LINK(node);
00585     }
00586     PR_ExitMonitor(self->monitor);
00587 
00588     return count;
00589 }
00590 
00591 void
00592 PL_ProcessPendingEvents(PLEventQueue* self)
00593 {
00594     PRInt32 count;
00595 
00596     if (self == NULL)
00597         return;
00598 
00599 
00600     PR_EnterMonitor(self->monitor);
00601 
00602     if (self->processingEvents) {
00603         _pl_AcknowledgeNativeNotify(self);
00604         self->notified = PR_FALSE;
00605         PR_ExitMonitor(self->monitor);
00606         return;
00607     }
00608     self->processingEvents = PR_TRUE;
00609 
00610     /* Only process the events that are already in the queue, and
00611      * not any new events that get added. Do this by counting the
00612      * number of events currently in the queue
00613      */
00614     count = _pl_GetEventCount(self);
00615     PR_ExitMonitor(self->monitor);
00616 
00617     while (count-- > 0) {
00618         PLEvent* event = PL_GetEvent(self);
00619         if (event == NULL)
00620             break;
00621 
00622         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
00623         PL_HandleEvent(event);
00624         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
00625     }
00626 
00627     PR_EnterMonitor(self->monitor);
00628 
00629     if (self->type == EventQueueIsNative) {
00630         count = _pl_GetEventCount(self);
00631 
00632         if (count <= 0) {
00633             _pl_AcknowledgeNativeNotify(self);
00634             self->notified = PR_FALSE;
00635         }
00636         else {
00637             _pl_NativeNotify(self);
00638             self->notified = PR_TRUE;
00639         }
00640 
00641     }
00642     self->processingEvents = PR_FALSE;
00643 
00644     PR_ExitMonitor(self->monitor);
00645 }
00646 
00647 /*******************************************************************************
00648  * Event Operations
00649  ******************************************************************************/
00650 
00651 void
00652 PL_InitEvent(PLEvent* self, void* owner,
00653              PLHandleEventProc handler,
00654              PLDestroyEventProc destructor)
00655 {
00656 #ifdef PL_POST_TIMINGS
00657     self->postTime = PR_IntervalNow();
00658 #endif
00659     PR_INIT_CLIST(&self->link);
00660     self->handler = handler;
00661     self->destructor = destructor;
00662     self->owner = owner;
00663     self->synchronousResult = NULL;
00664     self->handled = PR_FALSE;
00665     self->lock = NULL;
00666     self->condVar = NULL;
00667 #if defined(XP_UNIX) && !defined(XP_MACOSX)
00668     self->id = 0;
00669 #endif
00670 }
00671 
00672 void*
00673 PL_GetEventOwner(PLEvent* self)
00674 {
00675     return self->owner;
00676 }
00677 
00678 void
00679 PL_HandleEvent(PLEvent* self)
00680 {
00681     void* result;
00682     if (self == NULL)
00683         return;
00684 
00685     /* This event better not be on an event queue anymore. */
00686     PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
00687 
00688     result = self->handler(self);
00689     if (NULL != self->synchronousResult) {
00690         PR_Lock(self->lock);
00691         self->synchronousResult = result;
00692         self->handled = PR_TRUE;
00693         PR_NotifyCondVar(self->condVar);
00694         PR_Unlock(self->lock);
00695     }
00696     else {
00697         /* For asynchronous events, they're destroyed by the event-handler
00698            thread. See PR_PostSynchronousEvent. */
00699         PL_DestroyEvent(self);
00700     }
00701 }
00702 #ifdef PL_POST_TIMINGS
00703 static long s_eventCount = 0;
00704 static long s_totalTime  = 0;
00705 #endif
00706 
00707 void
00708 PL_DestroyEvent(PLEvent* self)
00709 {
00710     if (self == NULL)
00711         return;
00712 
00713     /* This event better not be on an event queue anymore. */
00714     PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
00715 
00716     if(self->condVar)
00717       PR_DestroyCondVar(self->condVar);
00718     if(self->lock)
00719       PR_DestroyLock(self->lock);
00720 
00721 #ifdef PL_POST_TIMINGS
00722     s_totalTime += PR_IntervalNow() - self->postTime;
00723     s_eventCount++;
00724     printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount));
00725 #endif
00726 
00727     self->destructor(self);
00728 }
00729 
00730 void
00731 PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
00732 {
00733     if (self == NULL)
00734         return;
00735 
00736     /* Only the owner is allowed to dequeue events because once the
00737        client has put it in the queue, they have no idea whether it's
00738        been processed and destroyed or not. */
00739 
00740     PR_ASSERT(queue->handlerThread == PR_GetCurrentThread());
00741 
00742     PR_EnterMonitor(queue->monitor);
00743 
00744     PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
00745 
00746 #if 0
00747     /*  I do not think that we need to do this anymore.
00748         if we do not acknowledge and this is the only
00749         only event in the queue, any calls to process
00750         the eventQ will be effective noop.
00751     */
00752     if (queue->type == EventQueueIsNative)
00753       _pl_AcknowledgeNativeNotify(queue);
00754 #endif
00755 
00756     PR_REMOVE_AND_INIT_LINK(&self->link);
00757 
00758     PR_ExitMonitor(queue->monitor);
00759 }
00760 
00761 void
00762 PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation,
00763                         PRUint32 starvationDelay)
00764 {
00765 #if defined(_WIN32)
00766 
00767     _md_StarvationDelay = starvationDelay;
00768 
00769     if (favorPerformanceOverEventStarvation) {
00770         _md_PerformanceSetting++;
00771         return;
00772     }
00773 
00774     _md_PerformanceSetting--;
00775 
00776     if (_md_PerformanceSetting == 0) {
00777       /* Switched from allowing event starvation to no event starvation so grab
00778          the current time to determine when to actually switch to using timers
00779          instead of posted WM_APP messages. */
00780       _md_SwitchTime = PR_IntervalToMilliseconds(PR_IntervalNow());
00781     }
00782 
00783 #endif
00784 }
00785 
00786 /*******************************************************************************
00787  * Pure Event Queues
00788  *
00789  * For when you're only processing PLEvents and there is no native
00790  * select, thread messages, or AppleEvents.
00791  ******************************************************************************/
00792 
00793 PLEvent*
00794 PL_WaitForEvent(PLEventQueue* self)
00795 {
00796     PLEvent* event;
00797     PRMonitor* mon;
00798 
00799     if (self == NULL)
00800         return NULL;
00801 
00802     mon = self->monitor;
00803     PR_EnterMonitor(mon);
00804 
00805     while ((event = PL_GetEvent(self)) == NULL) {
00806         PRStatus err;
00807         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
00808         err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
00809         if ((err == PR_FAILURE)
00810             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
00811     }
00812 
00813     PR_ExitMonitor(mon);
00814     return event;
00815 }
00816 
00817 void
00818 PL_EventLoop(PLEventQueue* self)
00819 {
00820     if (self == NULL)
00821         return;
00822 
00823     while (PR_TRUE) {
00824         PLEvent* event = PL_WaitForEvent(self);
00825         if (event == NULL) {
00826             /* This can only happen if the current thread is interrupted */
00827             return;
00828         }
00829 
00830         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
00831         PL_HandleEvent(event);
00832         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
00833     }
00834 }
00835 
00836 /*******************************************************************************
00837  * Native Event Queues
00838  *
00839  * For when you need to call select, or WaitNextEvent, and yet also want
00840  * to handle PLEvents.
00841  ******************************************************************************/
00842 
00843 static PRStatus
00844 _pl_SetupNativeNotifier(PLEventQueue* self)
00845 {
00846 #if defined(VMS)
00847     unsigned int status;
00848     self->idFunc = 0;
00849     self->idFuncClosure = 0;
00850     status = LIB$GET_EF(&self->efn);
00851     if (!$VMS_STATUS_SUCCESS(status))
00852         return PR_FAILURE;
00853     PR_LOG(event_lm, PR_LOG_DEBUG,
00854            ("$$$ Allocated event flag %d", self->efn));
00855     return PR_SUCCESS;
00856 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
00857     int err;
00858     int flags;
00859 
00860     self->idFunc = 0;
00861     self->idFuncClosure = 0;
00862 
00863     err = pipe(self->eventPipe);
00864     if (err != 0) {
00865         return PR_FAILURE;
00866     }
00867 
00868     /* make the pipe nonblocking */
00869     flags = fcntl(self->eventPipe[0], F_GETFL, 0);
00870     if (flags == -1) {
00871         goto failed;
00872     }
00873     err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
00874     if (err == -1) {
00875         goto failed;
00876     }
00877     flags = fcntl(self->eventPipe[1], F_GETFL, 0);
00878     if (flags == -1) {
00879         goto failed;
00880     }
00881     err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
00882     if (err == -1) {
00883         goto failed;
00884     }
00885     return PR_SUCCESS;
00886 
00887 failed:
00888     close(self->eventPipe[0]);
00889     close(self->eventPipe[1]);
00890     return PR_FAILURE;
00891 #elif defined(XP_BEOS)
00892     /* hook up to the nsToolkit queue, however the appshell
00893      * isn't necessairly started, so we might have to create
00894      * the queue ourselves
00895      *
00896      * Set up the port for communicating. As restarts thru execv may occur
00897      * and ports survive those (with faulty events as result). Combined with the fact
00898      * that nsAppShell.cpp may or may not have created the port we need to take extra
00899      * care that the port is created for this launch, otherwise we need to reopen it
00900      * so that faulty messages gets lost.
00901      *
00902      * We do this by checking if the sem has been created. If it is we can reuse the port (if it exists).
00903      * Otherwise we need to create the sem and the port, deleting any open ports before.
00904      */
00905      
00906     sem_info info;
00907     int32 cookie = 0;
00908 
00909     char portname[64];
00910     char semname[64];
00911     PR_snprintf(portname, sizeof(portname), "event%lx", 
00912                 (long unsigned) self->handlerThread);
00913     PR_snprintf(semname, sizeof(semname), "sync%lx", 
00914                 (long unsigned) self->handlerThread);
00915 
00916     self->eventport = find_port(portname);
00917     while(get_next_sem_info(0, &cookie, &info) == B_OK)
00918     {
00919       if(strcmp(semname, info.name) != 0) {
00920         continue;
00921       }
00922 
00923       /* found semaphore */
00924       if(self->eventport < 0) {
00925         self->eventport = create_port(200, portname);
00926       }
00927       return PR_SUCCESS;
00928     }
00929     /* setup the port and semaphore */
00930     if(self->eventport >= 0) 
00931     {
00932       delete_port( self->eventport );
00933     }
00934     self->eventport = create_port(200, portname);
00935         /* We don't use the sem, but it has to be there
00936          */
00937         create_sem(0, semname);
00938     return PR_SUCCESS;
00939 #else
00940     return PR_SUCCESS;
00941 #endif
00942 }
00943 
00944 static void
00945 _pl_CleanupNativeNotifier(PLEventQueue* self)
00946 {
00947 #if defined(VMS)
00948     {
00949         unsigned int status;
00950         PR_LOG(event_lm, PR_LOG_DEBUG,
00951            ("$$$ Freeing event flag %d", self->efn));
00952         status = LIB$FREE_EF(&self->efn);
00953     }
00954 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
00955     close(self->eventPipe[0]);
00956     close(self->eventPipe[1]);
00957 #elif defined(_WIN32)
00958     if (self->timerSet) {
00959         KillTimer(self->eventReceiverWindow, TIMER_ID);
00960         self->timerSet = PR_FALSE;
00961     }
00962     RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
00963 
00964     /* DestroyWindow doesn't do anything when called from a non ui thread.  Since 
00965      * self->eventReceiverWindow was created on the ui thread, it must be destroyed
00966      * on the ui thread.
00967      */
00968     SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
00969 
00970 #elif defined(XP_OS2)
00971     WinDestroyWindow(self->eventReceiverWindow);
00972 #elif defined(MAC_USE_CFRUNLOOPSOURCE)
00973 
00974     CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, kCFRunLoopCommonModes);
00975     CFRelease(self->mRunLoopSource);
00976     CFRelease(self->mMainRunLoop);
00977 
00978 #elif defined(MAC_USE_CARBON_EVENT)
00979     EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator);
00980     PR_ASSERT(comparator != NULL);
00981     if (comparator) {
00982       FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
00983       DisposeEventComparatorUPP(comparator);
00984     }
00985     DisposeEventHandlerUPP(self->eventHandlerUPP);
00986     RemoveEventHandler(self->eventHandlerRef);
00987 #endif
00988 }
00989 
00990 #if defined(_WIN32)
00991 
00992 static PRBool   _md_WasInputPending = PR_FALSE;
00993 static PRUint32 _md_InputTime = 0;
00994 static PRBool   _md_WasPaintPending = PR_FALSE;
00995 static PRUint32 _md_PaintTime = 0;
00996 /* last mouse location */
00997 static POINT    _md_LastMousePos;
00998 
00999 /*******************************************************************************
01000  * Timer callback function. Timers are used on WIN32 instead of APP events
01001  * when there are pending UI events because APP events can cause the GUI to lockup
01002  * because posted messages are processed before other messages.
01003  ******************************************************************************/
01004 
01005 static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
01006 {
01007     PREventQueue* queue =  (PREventQueue  *) GetProp(hwnd, _md_GetEventQueuePropName());
01008     PR_ASSERT(queue != NULL);
01009 
01010     KillTimer(hwnd, TIMER_ID);
01011     queue->timerSet = PR_FALSE;
01012     queue->removeMsg = PR_FALSE;
01013     PL_ProcessPendingEvents( queue );
01014     queue->removeMsg = PR_TRUE;
01015 }
01016 
01017 static PRBool _md_IsWIN9X = PR_FALSE;
01018 static PRBool _md_IsOSSet = PR_FALSE;
01019 
01020 static void _md_DetermineOSType()
01021 {
01022     OSVERSIONINFO os;
01023     os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
01024     GetVersionEx(&os);
01025     if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
01026         _md_IsWIN9X = PR_TRUE;
01027     }
01028 }
01029 
01030 static PRUint32 _md_GetPaintStarvationLimit()
01031 {
01032     if (! _md_IsOSSet) {
01033         _md_DetermineOSType();
01034         _md_IsOSSet = PR_TRUE;
01035     }
01036 
01037     if (_md_IsWIN9X) {
01038         return WIN9X_PAINT_STARVATION_LIMIT;
01039     }
01040 
01041     return PAINT_STARVATION_LIMIT;
01042 }
01043 
01044 
01045 /*
01046  * Determine if an event is being starved (i.e the starvation limit has
01047  * been exceeded.
01048  * Note: this function uses the current setting and updates the contents
01049  * of the wasPending and lastTime arguments
01050  *
01051  * ispending:       PR_TRUE if the event is currently pending
01052  * starvationLimit: Threshold defined in milliseconds for determining when
01053  *                  the event has been held in the queue too long
01054  * wasPending:      PR_TRUE if the last time _md_EventIsStarved was called
01055  *                  the event was pending.  This value is updated within
01056  *                  this function.
01057  * lastTime:        Holds the last time the event was in the queue.
01058  *                  This value is updated within this function
01059  * returns:         PR_TRUE if the event is starved, PR_FALSE otherwise
01060  */
01061 
01062 static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
01063                                  PRBool *wasPending, PRUint32 *lastTime,
01064                                  PRUint32 currentTime)
01065 {
01066     if (*wasPending && isPending) {
01067         /*
01068          * It was pending previously and the event is still
01069          * pending so check to see if the elapsed time is
01070          * over the limit which indicates the event was starved
01071          */
01072         if ((currentTime - *lastTime) > starvationLimit) {
01073             return PR_TRUE; /* pending and over the limit */
01074         }
01075 
01076         return PR_FALSE; /* pending but within the limit */
01077     }
01078 
01079     if (isPending) {
01080         /*
01081          * was_pending must be false so record the current time
01082          * so the elapsed time can be computed the next time this
01083          * function is called
01084          */
01085         *lastTime = currentTime;
01086         *wasPending = PR_TRUE;
01087         return PR_FALSE;
01088     }
01089 
01090     /* Event is no longer pending */
01091     *wasPending = PR_FALSE;
01092     return PR_FALSE;
01093 }
01094 
01095 /* Determines if the there is a pending Mouse or input event */
01096 
01097 static PRBool _md_IsInputPending(WORD qstatus)
01098 {
01099     /* Return immediately there aren't any pending input or paints. */
01100     if (qstatus == 0) {
01101         return PR_FALSE;
01102     }
01103 
01104     /* Is there anything other than a QS_MOUSEMOVE pending? */
01105     if ((qstatus & QS_MOUSEBUTTON) ||
01106         (qstatus & QS_KEY) 
01107 #ifndef WINCE
01108           || (qstatus & QS_HOTKEY)
01109 #endif 
01110         ) {
01111       return PR_TRUE;
01112     }
01113 
01114     /*
01115      * Mouse moves need extra processing to determine if the mouse
01116      * pointer actually changed location because Windows automatically
01117      * generates WM_MOVEMOVE events when a new window is created which
01118      * we need to filter out.
01119      */
01120     if (qstatus & QS_MOUSEMOVE) {
01121         POINT cursorPos;
01122         GetCursorPos(&cursorPos);
01123         if ((_md_LastMousePos.x == cursorPos.x) &&
01124             (_md_LastMousePos.y == cursorPos.y)) {
01125             return PR_FALSE; /* This is a fake mouse move */
01126         }
01127 
01128         /* Real mouse move */
01129         _md_LastMousePos.x = cursorPos.x;
01130         _md_LastMousePos.y = cursorPos.y;
01131         return PR_TRUE;
01132     }
01133 
01134     return PR_FALSE;
01135 }
01136 
01137 static PRStatus
01138 _pl_NativeNotify(PLEventQueue* self)
01139 {
01140 #ifdef USE_TIMER
01141     WORD qstatus;
01142 
01143     PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
01144 
01145     /* Since calls to set the _md_PerformanceSetting can be nested
01146      * only performance setting values <= 0 will potentially trigger
01147      * the use of a timer.
01148      */
01149     if ((_md_PerformanceSetting <= 0) &&
01150         ((now - _md_SwitchTime) > _md_StarvationDelay)) {
01151         SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
01152         self->timerSet = PR_TRUE;
01153         _md_WasInputPending = PR_FALSE;
01154         _md_WasPaintPending = PR_FALSE;
01155         return PR_SUCCESS;
01156     }
01157 
01158     qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
01159 
01160     /* Check for starved input */
01161     if (_md_EventIsStarved( _md_IsInputPending(qstatus),
01162                             INPUT_STARVATION_LIMIT,
01163                             &_md_WasInputPending,
01164                             &_md_InputTime,
01165                             now )) {
01166         /*
01167          * Use a timer for notification. Timers have the lowest priority.
01168          * They are not processed until all other events have been processed.
01169          * This allows any starved paints and input to be processed.
01170          */
01171         SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
01172         self->timerSet = PR_TRUE;
01173 
01174         /*
01175          * Clear any pending paint.  _md_WasInputPending was cleared in
01176          * _md_EventIsStarved.
01177          */
01178         _md_WasPaintPending = PR_FALSE;
01179         return PR_SUCCESS;
01180     }
01181 
01182     if (_md_EventIsStarved( (qstatus & QS_PAINT),
01183                             _md_GetPaintStarvationLimit(),
01184                             &_md_WasPaintPending,
01185                             &_md_PaintTime,
01186                             now) ) {
01187         /*
01188          * Use a timer for notification. Timers have the lowest priority.
01189          * They are not processed until all other events have been processed.
01190          * This allows any starved paints and input to be processed
01191          */
01192         SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
01193         self->timerSet = PR_TRUE;
01194 
01195         /*
01196          * Clear any pending input.  _md_WasPaintPending was cleared in
01197          * _md_EventIsStarved.
01198          */
01199         _md_WasInputPending = PR_FALSE;
01200         return PR_SUCCESS;
01201     }
01202 
01203     /*
01204      * Nothing is being starved so post a message instead of using a timer.
01205      * Posted messages are processed before other messages so they have the
01206      * highest priority.
01207      */
01208 #endif
01209     PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
01210                 (WPARAM)0, (LPARAM)self );
01211 
01212     return PR_SUCCESS;
01213 }/* --- end _pl_NativeNotify() --- */
01214 #endif
01215 
01216 
01217 #if defined(XP_OS2)
01218 static PRStatus
01219 _pl_NativeNotify(PLEventQueue* self)
01220 {
01221     BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
01222                           0, MPFROMP(self));
01223     return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
01224 }/* --- end _pl_NativeNotify() --- */
01225 #endif /* XP_OS2 */
01226 
01227 #if defined(VMS)
01228 /* Just set the event flag */
01229 static PRStatus
01230 _pl_NativeNotify(PLEventQueue* self)
01231 {
01232     unsigned int status;
01233     PR_LOG(event_lm, PR_LOG_DEBUG,
01234            ("_pl_NativeNotify: self=%p efn=%d",
01235             self, self->efn));
01236     status = SYS$SETEF(self->efn);
01237     return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE;
01238 }/* --- end _pl_NativeNotify() --- */
01239 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
01240 
01241 static PRStatus
01242 _pl_NativeNotify(PLEventQueue* self)
01243 {
01244 #define NOTIFY_TOKEN    0xFA
01245     PRInt32 count;
01246     unsigned char buf[] = { NOTIFY_TOKEN };
01247 
01248     PR_LOG(event_lm, PR_LOG_DEBUG,
01249            ("_pl_NativeNotify: self=%p",
01250             self));
01251     count = write(self->eventPipe[1], buf, 1);
01252     if (count == 1)
01253         return PR_SUCCESS;
01254     if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
01255         return PR_SUCCESS;
01256     return PR_FAILURE;
01257 }/* --- end _pl_NativeNotify() --- */
01258 #endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
01259 
01260 #if defined(XP_BEOS)
01261 struct ThreadInterfaceData
01262 {
01263     void  *data;
01264     thread_id waitingThread;
01265 };
01266 
01267 static PRStatus
01268 _pl_NativeNotify(PLEventQueue* self)
01269 {
01270     struct ThreadInterfaceData id;
01271     id.data = self;
01272     id.waitingThread = 0;
01273     write_port(self->eventport, 'natv', &id, sizeof(id));
01274 
01275     return PR_SUCCESS;    /* Is this correct? */
01276 }
01277 #endif /* XP_BEOS */
01278 
01279 #if defined(XP_MACOSX)
01280 static PRStatus
01281 _pl_NativeNotify(PLEventQueue* self)
01282 {
01283 #if defined(MAC_USE_CFRUNLOOPSOURCE)
01284        CFRunLoopSourceSignal(self->mRunLoopSource);
01285        CFRunLoopWakeUp(self->mMainRunLoop);
01286 #elif defined(MAC_USE_CARBON_EVENT)
01287     OSErr err;
01288     EventRef newEvent;
01289     if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
01290                     0, kEventAttributeNone, &newEvent) != noErr)
01291         return PR_FAILURE;
01292     err = SetEventParameter(newEvent, kEventParamPLEventQueue,
01293                             typeUInt32, sizeof(PREventQueue*), &self);
01294     if (err == noErr) {
01295         err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
01296         ReleaseEvent(newEvent);
01297     }
01298     if (err != noErr)
01299         return PR_FAILURE;
01300 #endif
01301     return PR_SUCCESS;
01302 }
01303 #endif /* defined(XP_MACOSX) */
01304 
01305 static PRStatus
01306 _pl_AcknowledgeNativeNotify(PLEventQueue* self)
01307 {
01308 #if defined(_WIN32) || defined(XP_OS2)
01309 #ifdef XP_OS2
01310     QMSG aMsg;
01311 #else
01312     MSG aMsg;
01313 #endif
01314     /*
01315      * only remove msg when we've been called directly by
01316      * PL_ProcessPendingEvents, not when we've been called by
01317      * the window proc because the window proc will remove the
01318      * msg for us.
01319      */
01320     if (self->removeMsg) {
01321         PR_LOG(event_lm, PR_LOG_DEBUG,
01322                ("_pl_AcknowledgeNativeNotify: self=%p", self));
01323 #ifdef XP_OS2
01324         WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
01325                    _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
01326 #else
01327         PeekMessage(&aMsg, self->eventReceiverWindow,
01328                     _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
01329         if (self->timerSet) {
01330             KillTimer(self->eventReceiverWindow, TIMER_ID);
01331             self->timerSet = PR_FALSE;
01332         }
01333 #endif
01334     }
01335     return PR_SUCCESS;
01336 #elif defined(VMS)
01337     PR_LOG(event_lm, PR_LOG_DEBUG,
01338             ("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
01339              self, self->efn));
01340     /*
01341     ** If this is the last entry, then clear the event flag. Also make sure
01342     ** the flag is cleared on any spurious wakeups.
01343     */
01344     sys$clref(self->efn);
01345     return PR_SUCCESS;
01346 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
01347 
01348     PRInt32 count;
01349     unsigned char c;
01350     PR_LOG(event_lm, PR_LOG_DEBUG,
01351             ("_pl_AcknowledgeNativeNotify: self=%p",
01352              self));
01353     /* consume the byte NativeNotify put in our pipe: */
01354     count = read(self->eventPipe[0], &c, 1);
01355     if ((count == 1) && (c == NOTIFY_TOKEN))
01356         return PR_SUCCESS;
01357     if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
01358         return PR_SUCCESS;
01359     return PR_FAILURE;
01360 #else
01361 
01362     /* nothing to do on the other platforms */
01363     return PR_SUCCESS;
01364 #endif
01365 }
01366 
01367 PRInt32
01368 PL_GetEventQueueSelectFD(PLEventQueue* self)
01369 {
01370     if (self == NULL)
01371     return -1;
01372 
01373 #if defined(VMS)
01374     return -(self->efn);
01375 #elif defined(XP_UNIX) && !defined(XP_MACOSX)
01376     return self->eventPipe[0];
01377 #else
01378     return -1;    /* other platforms don't handle this (yet) */
01379 #endif
01380 }
01381 
01382 PRBool
01383 PL_IsQueueOnCurrentThread( PLEventQueue *queue )
01384 {
01385     PRThread *me = PR_GetCurrentThread();
01386     return me == queue->handlerThread;
01387 }
01388 
01389 PR_EXTERN(PRBool)
01390 PL_IsQueueNative(PLEventQueue *queue)
01391 {
01392     return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
01393 }
01394 
01395 #if defined(_WIN32) || defined(XP_OS2)
01396 #ifdef XP_OS2
01397 MRESULT EXPENTRY
01398 _md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
01399 #else
01400 LRESULT CALLBACK
01401 _md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
01402 #endif
01403 {
01404     if (_pr_PostEventMsgId == uMsg )
01405     {
01406         PREventQueue *queue = (PREventQueue *)lParam;
01407         queue->removeMsg = PR_FALSE;
01408         PL_ProcessPendingEvents(queue);
01409         queue->removeMsg = PR_TRUE;
01410 #ifdef XP_OS2
01411         return MRFROMLONG(TRUE);
01412 #else
01413         return TRUE;
01414 #endif
01415     }
01416     return DefWindowProc(hwnd, uMsg, wParam, lParam);
01417 }
01418 
01419 static PRBool   isInitialized;
01420 static PRCallOnceType once;
01421 static PRLock   *initLock;
01422 
01423 /*
01424 ** InitWinEventLib() -- Create the Windows initialization lock
01425 **
01426 */
01427 static PRStatus InitEventLib( void )
01428 {
01429     PR_ASSERT( initLock == NULL );
01430 
01431     initLock = PR_NewLock();
01432     return initLock ? PR_SUCCESS : PR_FAILURE;
01433 }
01434 
01435 #endif /* Win32, OS2 */
01436 
01437 #if defined(_WIN32)
01438 
01439 /*
01440 ** _md_CreateEventQueue() -- ModelDependent initializer
01441 */
01442 static void _md_CreateEventQueue( PLEventQueue *eventQueue )
01443 {
01444     WNDCLASS wc;
01445     HANDLE h = GetModuleHandle(NULL);
01446 
01447     /*
01448     ** If this is the first call to PL_InitializeEventsLib(),
01449     ** make the call to InitWinEventLib() to create the initLock.
01450     **
01451     ** Then lock the initializer lock to insure that
01452     ** we have exclusive control over the initialization sequence.
01453     **
01454     */
01455 
01456 
01457     /* Register the windows message for XPCOM Event notification */
01458     _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
01459 
01460     /* Register the class for the event receiver window */
01461     if (!GetClassInfo(h, _pr_eventWindowClass, &wc)) {
01462         wc.style         = 0;
01463         wc.lpfnWndProc   = _md_EventReceiverProc;
01464         wc.cbClsExtra    = 0;
01465         wc.cbWndExtra    = 0;
01466         wc.hInstance     = h;
01467         wc.hIcon         = NULL;
01468         wc.hCursor       = NULL;
01469         wc.hbrBackground = (HBRUSH) NULL;
01470         wc.lpszMenuName  = (LPCSTR) NULL;
01471         wc.lpszClassName = _pr_eventWindowClass;
01472         RegisterClass(&wc);
01473     }
01474 
01475     /* Create the event receiver window */
01476     eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
01477                                         "XPCOM:EventReceiver",
01478                                             0, 0, 0, 10, 10,
01479                                             NULL, NULL, h,
01480                                             NULL);
01481     PR_ASSERT(eventQueue->eventReceiverWindow);
01482     /* Set a property which can be used to retrieve the event queue
01483      * within the _md_TimerProc callback
01484      */
01485     SetProp(eventQueue->eventReceiverWindow,
01486             _md_GetEventQueuePropName(), (HANDLE)eventQueue);
01487 
01488     return;
01489 } /* end _md_CreateEventQueue() */
01490 #endif /* Winxx */
01491 
01492 #if defined(XP_OS2)
01493 /*
01494 ** _md_CreateEventQueue() -- ModelDependent initializer
01495 */
01496 static void _md_CreateEventQueue( PLEventQueue *eventQueue )
01497 {
01498     /* Must have HMQ for this & can't assume we already have appshell */
01499     if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
01500     {
01501        PPIB ppib;
01502        PTIB ptib;
01503        HAB hab;
01504        HMQ hmq;
01505 
01506        /* Set our app to be a PM app before attempting Win calls */
01507        DosGetInfoBlocks(&ptib, &ppib);
01508        ppib->pib_ultype = 3;
01509 
01510        hab = WinInitialize(0);
01511        hmq = WinCreateMsgQueue(hab, 0);
01512        PR_ASSERT(hmq);
01513     }
01514 
01515     if( !_pr_PostEventMsgId)
01516     {
01517         WinRegisterClass( 0 /* hab_current */,
01518                          _pr_eventWindowClass,
01519                          _md_EventReceiverProc,
01520                          0, 0);
01521 
01522         _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
01523                                         "XPCOM_PostEvent");
01524     }
01525 
01526     eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
01527                                                        _pr_eventWindowClass,
01528                                                        "", 0,
01529                                                        0, 0, 0, 0,
01530                                                        HWND_DESKTOP,
01531                                                        HWND_TOP,
01532                                                        0,
01533                                                        NULL,
01534                                                        NULL);
01535     PR_ASSERT(eventQueue->eventReceiverWindow);
01536 
01537     return;
01538 } /* end _md_CreateEventQueue() */
01539 #endif /* XP_OS2 */
01540 
01541 #if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
01542 /*
01543 ** _md_CreateEventQueue() -- ModelDependent initializer
01544 */
01545 static void _md_CreateEventQueue( PLEventQueue *eventQueue )
01546 {
01547     /* there's really nothing special to do here,
01548     ** the guts of the unix stuff is in the setupnativenotify
01549     ** and related functions.
01550     */
01551     return;
01552 } /* end _md_CreateEventQueue() */
01553 #endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
01554 
01555 #if defined(MAC_USE_CFRUNLOOPSOURCE)
01556 static void _md_EventReceiverProc(void *info)
01557 {
01558   PLEventQueue *queue = (PLEventQueue*)info;
01559   PL_ProcessPendingEvents(queue);
01560 }
01561 
01562 #elif defined(MAC_USE_CARBON_EVENT)
01563 /*
01564 ** _md_CreateEventQueue() -- ModelDependent initializer
01565 */
01566 
01567 static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
01568                                              EventRef inEvent,
01569                                              void* userData)
01570 {
01571     if (GetEventClass(inEvent) == kEventClassPL &&
01572         GetEventKind(inEvent) == kEventProcessPLEvents)
01573     {
01574         PREventQueue *queue;
01575         if (GetEventParameter(inEvent, kEventParamPLEventQueue,
01576                               typeUInt32, NULL, sizeof(PREventQueue*), NULL,
01577                               &queue) == noErr)
01578         {
01579             PL_ProcessPendingEvents(queue);
01580             return noErr;
01581         }
01582     }
01583     return eventNotHandledErr;
01584 }
01585 
01586 static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
01587                                                 void *inCompareData)
01588 {
01589     Boolean match = false;
01590 
01591     if (GetEventClass(inEvent) == kEventClassPL &&
01592         GetEventKind(inEvent) == kEventProcessPLEvents)
01593     {
01594         PREventQueue *queue;
01595         match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
01596                                     typeUInt32, NULL, sizeof(PREventQueue*), NULL,
01597                                     &queue) == noErr) && (queue == inCompareData));
01598     }
01599     return match;
01600 }
01601 
01602 #endif /* defined(MAC_USE_CARBON_EVENT) */
01603 
01604 #if defined(XP_MACOSX)
01605 static void _md_CreateEventQueue( PLEventQueue *eventQueue )
01606 {
01607 #if defined(MAC_USE_CFRUNLOOPSOURCE)
01608     CFRunLoopSourceContext sourceContext = { 0 };
01609     sourceContext.version = 0;
01610     sourceContext.info = (void*)eventQueue;
01611     sourceContext.perform = _md_EventReceiverProc;
01612 
01613     /* make a run loop source */
01614     eventQueue->mRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /* order */, &sourceContext);
01615     PR_ASSERT(eventQueue->mRunLoopSource);
01616     
01617     eventQueue->mMainRunLoop = CFRunLoopGetCurrent();
01618     CFRetain(eventQueue->mMainRunLoop);
01619     
01620     /* and add it to the run loop */
01621     CFRunLoopAddSource(eventQueue->mMainRunLoop, eventQueue->mRunLoopSource, kCFRunLoopCommonModes);
01622 
01623 #elif defined(MAC_USE_CARBON_EVENT)
01624     eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
01625     PR_ASSERT(eventQueue->eventHandlerUPP);
01626     if (eventQueue->eventHandlerUPP)
01627     {
01628       EventTypeSpec     eventType;
01629 
01630       eventType.eventClass = kEventClassPL;
01631       eventType.eventKind  = kEventProcessPLEvents;
01632 
01633       InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
01634                                      eventQueue, &eventQueue->eventHandlerRef);
01635       PR_ASSERT(eventQueue->eventHandlerRef);
01636     }
01637 #endif
01638 } /* end _md_CreateEventQueue() */
01639 #endif /* defined(XP_MACOSX) */
01640 
01641 /* extra functions for unix */
01642 
01643 #if defined(XP_UNIX) && !defined(XP_MACOSX)
01644 
01645 PRInt32
01646 PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
01647 {
01648     PRInt32 count = 0;
01649     PRInt32 fullCount;
01650 
01651     if (aSelf == NULL)
01652         return -1;
01653 
01654     PR_EnterMonitor(aSelf->monitor);
01655 
01656     if (aSelf->processingEvents) {
01657         PR_ExitMonitor(aSelf->monitor);
01658         return 0;
01659     }
01660 
01661     aSelf->processingEvents = PR_TRUE;
01662 
01663     /* Only process the events that are already in the queue, and
01664      * not any new events that get added. Do this by counting the
01665      * number of events currently in the queue
01666      */
01667     fullCount = _pl_GetEventCount(aSelf);
01668     PR_LOG(event_lm, PR_LOG_DEBUG,
01669            ("$$$ fullCount is %d id is %ld\n", fullCount, aID));
01670 
01671     if (fullCount == 0) {
01672         aSelf->processingEvents = PR_FALSE;
01673         PR_ExitMonitor(aSelf->monitor);
01674         return 0;
01675     }
01676 
01677     PR_ExitMonitor(aSelf->monitor);
01678 
01679     while (fullCount-- > 0) {
01680         /* peek at the next event */
01681         PLEvent *event;
01682         event = PR_EVENT_PTR(aSelf->queue.next);
01683         if (event == NULL)
01684             break;
01685         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
01686                                         event->id));
01687         if (event->id >= aID) {
01688             PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
01689             break;
01690         }
01691 
01692         event = PL_GetEvent(aSelf);
01693         PL_HandleEvent(event);
01694         PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
01695         count++;
01696     }
01697 
01698     PR_EnterMonitor(aSelf->monitor);
01699 
01700     /* if full count still had items left then there's still items left
01701        in the queue.  Let the native notify token stay. */
01702 
01703     if (aSelf->type == EventQueueIsNative) {
01704         fullCount = _pl_GetEventCount(aSelf);
01705 
01706         if (fullCount <= 0) {
01707             _pl_AcknowledgeNativeNotify(aSelf);
01708             aSelf->notified = PR_FALSE;
01709         }
01710     }
01711 
01712     aSelf->processingEvents = PR_FALSE;
01713 
01714     PR_ExitMonitor(aSelf->monitor);
01715 
01716     return count;
01717 }
01718 
01719 void
01720 PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
01721                        void *aClosure)
01722 {
01723     aSelf->idFunc = aFunc;
01724     aSelf->idFuncClosure = aClosure;
01725 }
01726 
01727 void
01728 PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
01729 {
01730     aSelf->idFunc = 0;
01731     aSelf->idFuncClosure = 0;
01732 }
01733 
01734 #endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
01735 
01736 /* --- end plevent.c --- */