summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/threads/plevent.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/xpcom/threads/plevent.c1774
1 files changed, 1774 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/threads/plevent.c b/src/libs/xpcom18a4/xpcom/threads/plevent.c
new file mode 100644
index 00000000..c5e9040f
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/threads/plevent.c
@@ -0,0 +1,1774 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#if defined(XP_OS2)
+#define INCL_DOS
+#define INCL_DOSERRORS
+#define INCL_WIN
+#include <os2.h>
+#define DefWindowProc WinDefWindowProc
+#endif /* XP_OS2 */
+
+#include "nspr.h"
+#include "plevent.h"
+
+#if !defined(WIN32)
+#include <errno.h>
+#include <stddef.h>
+#if !defined(XP_OS2)
+#include <unistd.h>
+#endif /* !XP_OS2 */
+#endif /* !Win32 */
+
+#if defined(XP_UNIX)
+/* for fcntl */
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#if defined(XP_BEOS)
+#include <kernel/OS.h>
+#endif
+
+#if defined(XP_MACOSX)
+#if defined(MOZ_WIDGET_COCOA)
+#include <CoreFoundation/CoreFoundation.h>
+#define MAC_USE_CFRUNLOOPSOURCE
+#elif defined(TARGET_CARBON)
+/* #include <CarbonEvents.h> */
+/* #define MAC_USE_CARBON_EVENT */
+#include <CoreFoundation/CoreFoundation.h>
+#define MAC_USE_CFRUNLOOPSOURCE
+#endif
+#endif
+
+#include "private/pprthred.h"
+
+#if defined(VMS)
+/*
+** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it
+** wants an event flag. So, we don't create and use a pipe for
+** notification of when an event queue has something ready, instead
+** we use an event flag. Shouldn't be a problem if we only have
+** a few event queues.
+*/
+#include <lib$routines.h>
+#include <starlet.h>
+#include <stsdef.h>
+#endif /* VMS */
+
+#if defined(_WIN32)
+/* Comment out the following USE_TIMER define to prevent
+ * WIN32 from using a WIN32 native timer for PLEvent notification.
+ * With USE_TIMER defined we will use a timer when pending input
+ * or paint events are starved, otherwise it will use a posted
+ * WM_APP msg for PLEvent notification.
+ */
+#define USE_TIMER
+
+/* Threshold defined in milliseconds for determining when the input
+ * and paint events have been held in the WIN32 msg queue too long
+ */
+#define INPUT_STARVATION_LIMIT 50
+/* The paint starvation limit is set to the smallest value which
+ * does not cause performance degradation while running page load tests
+ */
+#define PAINT_STARVATION_LIMIT 750
+/* The WIN9X paint starvation limit is larger because it was
+ * determined that the following value was required to prevent performance
+ * degradation on page load tests for WIN98/95 only.
+ */
+#define WIN9X_PAINT_STARVATION_LIMIT 3000
+
+#define TIMER_ID 0
+/* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */
+static PRInt32 _md_PerformanceSetting = 0;
+static PRUint32 _md_StarvationDelay = 0;
+static PRUint32 _md_SwitchTime = 0;
+#endif
+
+static PRLogModuleInfo *event_lm = NULL;
+
+/*******************************************************************************
+ * Private Stuff
+ ******************************************************************************/
+
+/*
+** EventQueueType -- Defines notification type for an event queue
+**
+*/
+typedef enum {
+ EventQueueIsNative = 1,
+ EventQueueIsMonitored = 2
+} EventQueueType;
+
+
+struct PLEventQueue {
+ const char* name;
+ PRCList queue;
+ PRMonitor* monitor;
+ PRThread* handlerThread;
+ EventQueueType type;
+ PRPackedBool processingEvents;
+ PRPackedBool notified;
+#if defined(_WIN32)
+ PRPackedBool timerSet;
+#endif
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+#if defined(VMS)
+ int efn;
+#else
+ PRInt32 eventPipe[2];
+#endif
+ PLGetEventIDFunc idFunc;
+ void* idFuncClosure;
+#elif defined(_WIN32) || defined(XP_OS2)
+ HWND eventReceiverWindow;
+ PRBool removeMsg;
+#elif defined(XP_BEOS)
+ port_id eventport;
+#elif defined(XP_MACOSX)
+#if defined(MAC_USE_CFRUNLOOPSOURCE)
+ CFRunLoopSourceRef mRunLoopSource;
+ CFRunLoopRef mMainRunLoop;
+ CFStringRef mRunLoopModeStr; /* vbox */
+#elif defined(MAC_USE_CARBON_EVENT)
+ EventHandlerUPP eventHandlerUPP;
+ EventHandlerRef eventHandlerRef;
+#endif
+#endif
+};
+
+#define PR_EVENT_PTR(_qp) \
+ ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
+
+static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self);
+static void _pl_CleanupNativeNotifier(PLEventQueue* self);
+static PRStatus _pl_NativeNotify(PLEventQueue* self);
+static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self);
+static void _md_CreateEventQueue( PLEventQueue *eventQueue );
+static PRInt32 _pl_GetEventCount(PLEventQueue* self);
+
+
+#if defined(_WIN32) || defined(XP_OS2)
+#if defined(XP_OS2)
+ULONG _pr_PostEventMsgId;
+#else
+UINT _pr_PostEventMsgId;
+#endif /* OS2 */
+static char *_pr_eventWindowClass = "XPCOM:EventWindow";
+#endif /* Win32, OS2 */
+
+#if defined(_WIN32)
+
+static LPCTSTR _md_GetEventQueuePropName() {
+ static ATOM atom = 0;
+ if (!atom) {
+ atom = GlobalAddAtom("XPCOM_EventQueue");
+ }
+ return MAKEINTATOM(atom);
+}
+#endif
+
+#if defined(MAC_USE_CARBON_EVENT)
+enum {
+ kEventClassPL = FOUR_CHAR_CODE('PLEC'),
+
+ kEventProcessPLEvents = 1,
+
+ kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ')
+};
+
+static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData);
+#endif
+
+/*******************************************************************************
+ * Event Queue Operations
+ ******************************************************************************/
+
+/*
+** _pl_CreateEventQueue() -- Create the event queue
+**
+**
+*/
+static PLEventQueue * _pl_CreateEventQueue(const char *name,
+ PRThread *handlerThread,
+ EventQueueType qtype)
+{
+ PRStatus err;
+ PLEventQueue* self = NULL;
+ PRMonitor* mon = NULL;
+
+ if (event_lm == NULL)
+ event_lm = PR_NewLogModule("event");
+
+ self = PR_NEWZAP(PLEventQueue);
+ if (self == NULL) return NULL;
+
+ mon = PR_NewNamedMonitor(name);
+ if (mon == NULL) goto error;
+
+ self->name = name;
+ self->monitor = mon;
+ self->handlerThread = handlerThread;
+ self->processingEvents = PR_FALSE;
+ self->type = qtype;
+#if defined(_WIN32)
+ self->timerSet = PR_FALSE;
+#endif
+#if defined(_WIN32) || defined(XP_OS2)
+ self->removeMsg = PR_TRUE;
+#endif
+
+ self->notified = PR_FALSE;
+
+ PR_INIT_CLIST(&self->queue);
+ if ( qtype == EventQueueIsNative ) {
+ err = _pl_SetupNativeNotifier(self);
+ if (err) goto error;
+ _md_CreateEventQueue( self );
+ }
+ return self;
+
+ error:
+ if (mon != NULL)
+ PR_DestroyMonitor(mon);
+ PR_DELETE(self);
+ return NULL;
+}
+
+PR_IMPLEMENT(PLEventQueue*)
+PL_CreateEventQueue(const char* name, PRThread* handlerThread)
+{
+ return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
+}
+
+PR_EXTERN(PLEventQueue *)
+PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread)
+{
+ return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
+}
+
+PR_EXTERN(PLEventQueue *)
+PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread)
+{
+ return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
+}
+
+PR_IMPLEMENT(PRMonitor*)
+PL_GetEventQueueMonitor(PLEventQueue* self)
+{
+ return self->monitor;
+}
+
+static void PR_CALLBACK
+_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
+{
+ PL_DequeueEvent(event, queue);
+ PL_DestroyEvent(event);
+}
+
+PR_IMPLEMENT(void)
+PL_DestroyEventQueue(PLEventQueue* self)
+{
+ PR_EnterMonitor(self->monitor);
+
+ /* destroy undelivered events */
+ PL_MapEvents(self, _pl_destroyEvent, NULL);
+
+ if ( self->type == EventQueueIsNative )
+ _pl_CleanupNativeNotifier(self);
+
+ /* destroying the monitor also destroys the name */
+ PR_ExitMonitor(self->monitor);
+ PR_DestroyMonitor(self->monitor);
+ PR_DELETE(self);
+
+}
+
+PR_IMPLEMENT(PRStatus)
+PL_PostEvent(PLEventQueue* self, PLEvent* event)
+{
+ PRStatus err = PR_SUCCESS;
+ PRMonitor* mon;
+
+ if (self == NULL)
+ return PR_FAILURE;
+
+ mon = self->monitor;
+ PR_EnterMonitor(mon);
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ if (self->idFunc && event)
+ event->id = self->idFunc(self->idFuncClosure);
+#endif
+
+ /* insert event into thread's event queue: */
+ if (event != NULL) {
+ PR_APPEND_LINK(&event->link, &self->queue);
+ }
+
+ if (self->type == EventQueueIsNative && !self->notified) {
+ err = _pl_NativeNotify(self);
+
+ if (err != PR_SUCCESS)
+ goto error;
+
+ self->notified = PR_TRUE;
+ }
+
+ /*
+ * This may fall on deaf ears if we're really notifying the native
+ * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
+ */
+ err = PR_Notify(mon);
+
+error:
+ PR_ExitMonitor(mon);
+ return err;
+}
+
+PR_IMPLEMENT(void*)
+PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
+{
+ void* result;
+
+ if (self == NULL)
+ return NULL;
+
+ PR_ASSERT(event != NULL);
+
+ if (PR_GetCurrentThread() == self->handlerThread) {
+ /* Handle the case where the thread requesting the event handling
+ * is also the thread that's supposed to do the handling. */
+ result = event->handler(event);
+ }
+ else {
+ int i, entryCount;
+
+ event->lock = PR_NewLock();
+ if (!event->lock) {
+ return NULL;
+ }
+ event->condVar = PR_NewCondVar(event->lock);
+ if(!event->condVar) {
+ PR_DestroyLock(event->lock);
+ event->lock = NULL;
+ return NULL;
+ }
+
+ PR_Lock(event->lock);
+
+ entryCount = PR_GetMonitorEntryCount(self->monitor);
+
+ event->synchronousResult = (void*)PR_TRUE;
+
+ PL_PostEvent(self, event);
+
+ /* We need temporarily to give up our event queue monitor if
+ we're holding it, otherwise, the thread we're going to wait
+ for notification from won't be able to enter it to process
+ the event. */
+ if (entryCount) {
+ for (i = 0; i < entryCount; i++)
+ PR_ExitMonitor(self->monitor);
+ }
+
+ event->handled = PR_FALSE;
+
+ while (!event->handled) {
+ /* wait for event to be handled or destroyed */
+ PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ if (entryCount) {
+ for (i = 0; i < entryCount; i++)
+ PR_EnterMonitor(self->monitor);
+ }
+
+ result = event->synchronousResult;
+ event->synchronousResult = NULL;
+ PR_Unlock(event->lock);
+ }
+
+ /* For synchronous events, they're destroyed here on the caller's
+ thread before the result is returned. See PL_HandleEvent. */
+ PL_DestroyEvent(event);
+
+ return result;
+}
+
+PR_IMPLEMENT(PLEvent*)
+PL_GetEvent(PLEventQueue* self)
+{
+ PLEvent* event = NULL;
+ PRStatus err = PR_SUCCESS;
+
+ if (self == NULL)
+ return NULL;
+
+ PR_EnterMonitor(self->monitor);
+
+ if (!PR_CLIST_IS_EMPTY(&self->queue)) {
+ if ( self->type == EventQueueIsNative &&
+ self->notified &&
+ !self->processingEvents &&
+ 0 == _pl_GetEventCount(self) )
+ {
+ err = _pl_AcknowledgeNativeNotify(self);
+ self->notified = PR_FALSE;
+ }
+ if (err)
+ goto done;
+
+ /* then grab the event and return it: */
+ event = PR_EVENT_PTR(self->queue.next);
+ PR_REMOVE_AND_INIT_LINK(&event->link);
+ }
+
+ done:
+ PR_ExitMonitor(self->monitor);
+ return event;
+}
+
+PR_IMPLEMENT(PRBool)
+PL_EventAvailable(PLEventQueue* self)
+{
+ PRBool result = PR_FALSE;
+
+ if (self == NULL)
+ return PR_FALSE;
+
+ PR_EnterMonitor(self->monitor);
+
+ if (!PR_CLIST_IS_EMPTY(&self->queue))
+ result = PR_TRUE;
+
+ PR_ExitMonitor(self->monitor);
+ return result;
+}
+
+PR_IMPLEMENT(void)
+PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
+{
+ PRCList* qp;
+
+ if (self == NULL)
+ return;
+
+ PR_EnterMonitor(self->monitor);
+ qp = self->queue.next;
+ while (qp != &self->queue) {
+ PLEvent* event = PR_EVENT_PTR(qp);
+ qp = qp->next;
+ (*fun)(event, data, self);
+ }
+ PR_ExitMonitor(self->monitor);
+}
+
+static void PR_CALLBACK
+_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
+{
+ PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
+ if (event->owner == owner) {
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ \tdestroying event %0x for owner %0x", event, owner));
+ PL_DequeueEvent(event, queue);
+
+ if (event->synchronousResult == (void*)PR_TRUE) {
+ PR_Lock(event->lock);
+ event->synchronousResult = NULL;
+ event->handled = PR_TRUE;
+ PR_NotifyCondVar(event->condVar);
+ PR_Unlock(event->lock);
+ }
+ else {
+ PL_DestroyEvent(event);
+ }
+ }
+ else {
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ \tskipping event %0x for owner %0x", event, owner));
+ }
+}
+
+PR_IMPLEMENT(void)
+PL_RevokeEvents(PLEventQueue* self, void* owner)
+{
+ if (self == NULL)
+ return;
+
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ revoking events for owner %0x", owner));
+
+ /*
+ ** First we enter the monitor so that no one else can post any events
+ ** to the queue:
+ */
+ PR_EnterMonitor(self->monitor);
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
+
+ /*
+ ** Discard any pending events for this owner:
+ */
+ PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
+
+#ifdef DEBUG
+ {
+ PRCList* qp = self->queue.next;
+ while (qp != &self->queue) {
+ PLEvent* event = PR_EVENT_PTR(qp);
+ qp = qp->next;
+ PR_ASSERT(event->owner != owner);
+ }
+ }
+#endif /* DEBUG */
+
+ PR_ExitMonitor(self->monitor);
+
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ revoking events for owner %0x", owner));
+}
+
+static PRInt32
+_pl_GetEventCount(PLEventQueue* self)
+{
+ PRCList* node;
+ PRInt32 count = 0;
+
+ PR_EnterMonitor(self->monitor);
+ node = PR_LIST_HEAD(&self->queue);
+ while (node != &self->queue) {
+ count++;
+ node = PR_NEXT_LINK(node);
+ }
+ PR_ExitMonitor(self->monitor);
+
+ return count;
+}
+
+PR_IMPLEMENT(void)
+PL_ProcessPendingEvents(PLEventQueue* self)
+{
+ PRInt32 count;
+
+ if (self == NULL)
+ return;
+
+
+ PR_EnterMonitor(self->monitor);
+
+ if (self->processingEvents) {
+ _pl_AcknowledgeNativeNotify(self);
+ self->notified = PR_FALSE;
+ PR_ExitMonitor(self->monitor);
+ return;
+ }
+ self->processingEvents = PR_TRUE;
+
+ /* Only process the events that are already in the queue, and
+ * not any new events that get added. Do this by counting the
+ * number of events currently in the queue
+ */
+ count = _pl_GetEventCount(self);
+ PR_ExitMonitor(self->monitor);
+
+ while (count-- > 0) {
+ PLEvent* event = PL_GetEvent(self);
+ if (event == NULL)
+ break;
+
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
+ PL_HandleEvent(event);
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
+ }
+
+ PR_EnterMonitor(self->monitor);
+
+ if (self->type == EventQueueIsNative) {
+ count = _pl_GetEventCount(self);
+
+ if (count <= 0) {
+ _pl_AcknowledgeNativeNotify(self);
+ self->notified = PR_FALSE;
+ }
+ else {
+ _pl_NativeNotify(self);
+ self->notified = PR_TRUE;
+ }
+
+ }
+ self->processingEvents = PR_FALSE;
+
+ PR_ExitMonitor(self->monitor);
+}
+
+/*******************************************************************************
+ * Event Operations
+ ******************************************************************************/
+
+PR_IMPLEMENT(void)
+PL_InitEvent(PLEvent* self, void* owner,
+ PLHandleEventProc handler,
+ PLDestroyEventProc destructor)
+{
+#ifdef PL_POST_TIMINGS
+ self->postTime = PR_IntervalNow();
+#endif
+ PR_INIT_CLIST(&self->link);
+ self->handler = handler;
+ self->destructor = destructor;
+ self->owner = owner;
+ self->synchronousResult = NULL;
+ self->handled = PR_FALSE;
+ self->lock = NULL;
+ self->condVar = NULL;
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ self->id = 0;
+#endif
+}
+
+PR_IMPLEMENT(void*)
+PL_GetEventOwner(PLEvent* self)
+{
+ return self->owner;
+}
+
+PR_IMPLEMENT(void)
+PL_HandleEvent(PLEvent* self)
+{
+ void* result;
+ if (self == NULL)
+ return;
+
+ /* This event better not be on an event queue anymore. */
+ PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
+
+ result = self->handler(self);
+ if (NULL != self->synchronousResult) {
+ PR_Lock(self->lock);
+ self->synchronousResult = result;
+ self->handled = PR_TRUE;
+ PR_NotifyCondVar(self->condVar);
+ PR_Unlock(self->lock);
+ }
+ else {
+ /* For asynchronous events, they're destroyed by the event-handler
+ thread. See PR_PostSynchronousEvent. */
+ PL_DestroyEvent(self);
+ }
+}
+#ifdef PL_POST_TIMINGS
+static long s_eventCount = 0;
+static long s_totalTime = 0;
+#endif
+
+PR_IMPLEMENT(void)
+PL_DestroyEvent(PLEvent* self)
+{
+ if (self == NULL)
+ return;
+
+ /* This event better not be on an event queue anymore. */
+ PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
+
+ if(self->condVar)
+ PR_DestroyCondVar(self->condVar);
+ if(self->lock)
+ PR_DestroyLock(self->lock);
+
+#ifdef PL_POST_TIMINGS
+ s_totalTime += PR_IntervalNow() - self->postTime;
+ s_eventCount++;
+ printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount));
+#endif
+
+ self->destructor(self);
+}
+
+PR_IMPLEMENT(void)
+PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
+{
+ if (self == NULL)
+ return;
+
+ /* Only the owner is allowed to dequeue events because once the
+ client has put it in the queue, they have no idea whether it's
+ been processed and destroyed or not. */
+
+ PR_ASSERT(queue->handlerThread == PR_GetCurrentThread());
+
+ PR_EnterMonitor(queue->monitor);
+
+ PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
+
+#if 0
+ /* I do not think that we need to do this anymore.
+ if we do not acknowledge and this is the only
+ only event in the queue, any calls to process
+ the eventQ will be effective noop.
+ */
+ if (queue->type == EventQueueIsNative)
+ _pl_AcknowledgeNativeNotify(queue);
+#endif
+
+ PR_REMOVE_AND_INIT_LINK(&self->link);
+
+ PR_ExitMonitor(queue->monitor);
+}
+
+PR_IMPLEMENT(void)
+PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation,
+ PRUint32 starvationDelay)
+{
+#if defined(_WIN32)
+
+ _md_StarvationDelay = starvationDelay;
+
+ if (favorPerformanceOverEventStarvation) {
+ _md_PerformanceSetting++;
+ return;
+ }
+
+ _md_PerformanceSetting--;
+
+ if (_md_PerformanceSetting == 0) {
+ /* Switched from allowing event starvation to no event starvation so grab
+ the current time to determine when to actually switch to using timers
+ instead of posted WM_APP messages. */
+ _md_SwitchTime = PR_IntervalToMilliseconds(PR_IntervalNow());
+ }
+
+#endif
+}
+
+/*******************************************************************************
+ * Pure Event Queues
+ *
+ * For when you're only processing PLEvents and there is no native
+ * select, thread messages, or AppleEvents.
+ ******************************************************************************/
+
+PR_IMPLEMENT(PLEvent*)
+PL_WaitForEvent(PLEventQueue* self)
+{
+ PLEvent* event;
+ PRMonitor* mon;
+
+ if (self == NULL)
+ return NULL;
+
+ mon = self->monitor;
+ PR_EnterMonitor(mon);
+
+ while ((event = PL_GetEvent(self)) == NULL) {
+ PRStatus err;
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
+ err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
+ if ((err == PR_FAILURE)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ }
+
+ PR_ExitMonitor(mon);
+ return event;
+}
+
+PR_IMPLEMENT(void)
+PL_EventLoop(PLEventQueue* self)
+{
+ if (self == NULL)
+ return;
+
+ while (PR_TRUE) {
+ PLEvent* event = PL_WaitForEvent(self);
+ if (event == NULL) {
+ /* This can only happen if the current thread is interrupted */
+ return;
+ }
+
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
+ PL_HandleEvent(event);
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
+ }
+}
+
+/*******************************************************************************
+ * Native Event Queues
+ *
+ * For when you need to call select, or WaitNextEvent, and yet also want
+ * to handle PLEvents.
+ ******************************************************************************/
+
+static PRStatus
+_pl_SetupNativeNotifier(PLEventQueue* self)
+{
+#if defined(VMS)
+ unsigned int status;
+ self->idFunc = 0;
+ self->idFuncClosure = 0;
+ status = LIB$GET_EF(&self->efn);
+ if (!$VMS_STATUS_SUCCESS(status))
+ return PR_FAILURE;
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ Allocated event flag %d", self->efn));
+ return PR_SUCCESS;
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+ int err;
+ int flags;
+
+ self->idFunc = 0;
+ self->idFuncClosure = 0;
+
+ err = pipe(self->eventPipe);
+ if (err != 0) {
+ return PR_FAILURE;
+ }
+#ifdef VBOX
+ fcntl(self->eventPipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(self->eventPipe[1], F_SETFD, FD_CLOEXEC);
+#endif
+
+ /* make the pipe nonblocking */
+ flags = fcntl(self->eventPipe[0], F_GETFL, 0);
+ if (flags == -1) {
+ goto failed;
+ }
+ err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
+ if (err == -1) {
+ goto failed;
+ }
+ flags = fcntl(self->eventPipe[1], F_GETFL, 0);
+ if (flags == -1) {
+ goto failed;
+ }
+ err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
+ if (err == -1) {
+ goto failed;
+ }
+ return PR_SUCCESS;
+
+failed:
+ close(self->eventPipe[0]);
+ close(self->eventPipe[1]);
+ return PR_FAILURE;
+#elif defined(XP_BEOS)
+ /* hook up to the nsToolkit queue, however the appshell
+ * isn't necessairly started, so we might have to create
+ * the queue ourselves
+ */
+ char portname[64];
+ char semname[64];
+ PR_snprintf(portname, sizeof(portname), "event%lx",
+ (long unsigned) self->handlerThread);
+ PR_snprintf(semname, sizeof(semname), "sync%lx",
+ (long unsigned) self->handlerThread);
+
+ if((self->eventport = find_port(portname)) < 0)
+ {
+ /* create port
+ */
+ self->eventport = create_port(500, portname);
+
+ /* We don't use the sem, but it has to be there
+ */
+ create_sem(0, semname);
+ }
+
+ return PR_SUCCESS;
+#else
+ return PR_SUCCESS;
+#endif
+}
+
+static void
+_pl_CleanupNativeNotifier(PLEventQueue* self)
+{
+#if defined(VMS)
+ {
+ unsigned int status;
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ Freeing event flag %d", self->efn));
+ status = LIB$FREE_EF(&self->efn);
+ }
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+ close(self->eventPipe[0]);
+ close(self->eventPipe[1]);
+#elif defined(_WIN32)
+ if (self->timerSet) {
+ KillTimer(self->eventReceiverWindow, TIMER_ID);
+ self->timerSet = PR_FALSE;
+ }
+ RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
+
+ /* DestroyWindow doesn't do anything when called from a non ui thread. Since
+ * self->eventReceiverWindow was created on the ui thread, it must be destroyed
+ * on the ui thread.
+ */
+ SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
+
+#elif defined(XP_OS2)
+ WinDestroyWindow(self->eventReceiverWindow);
+#elif defined(MAC_USE_CFRUNLOOPSOURCE)
+
+ CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, kCFRunLoopCommonModes);
+ CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, self->mRunLoopModeStr); /* vbox */
+ CFRelease(self->mRunLoopSource);
+ CFRelease(self->mMainRunLoop);
+ CFRelease(self->mRunLoopModeStr); /* vbox */
+
+#elif defined(MAC_USE_CARBON_EVENT)
+ EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator);
+ PR_ASSERT(comparator != NULL);
+ if (comparator) {
+ FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
+ DisposeEventComparatorUPP(comparator);
+ }
+ DisposeEventHandlerUPP(self->eventHandlerUPP);
+ RemoveEventHandler(self->eventHandlerRef);
+#endif
+}
+
+#if defined(_WIN32)
+
+static PRBool _md_WasInputPending = PR_FALSE;
+static PRUint32 _md_InputTime = 0;
+static PRBool _md_WasPaintPending = PR_FALSE;
+static PRUint32 _md_PaintTime = 0;
+/* last mouse location */
+static POINT _md_LastMousePos;
+
+/*******************************************************************************
+ * Timer callback function. Timers are used on WIN32 instead of APP events
+ * when there are pending UI events because APP events can cause the GUI to lockup
+ * because posted messages are processed before other messages.
+ ******************************************************************************/
+
+static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
+{
+ PREventQueue* queue = (PREventQueue *) GetProp(hwnd, _md_GetEventQueuePropName());
+ PR_ASSERT(queue != NULL);
+
+ KillTimer(hwnd, TIMER_ID);
+ queue->timerSet = PR_FALSE;
+ queue->removeMsg = PR_FALSE;
+ PL_ProcessPendingEvents( queue );
+ queue->removeMsg = PR_TRUE;
+}
+
+static PRBool _md_IsWIN9X = PR_FALSE;
+static PRBool _md_IsOSSet = PR_FALSE;
+
+static void _md_DetermineOSType()
+{
+ OSVERSIONINFO os;
+ os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&os);
+ if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
+ _md_IsWIN9X = PR_TRUE;
+ }
+}
+
+static PRUint32 _md_GetPaintStarvationLimit()
+{
+ if (! _md_IsOSSet) {
+ _md_DetermineOSType();
+ _md_IsOSSet = PR_TRUE;
+ }
+
+ if (_md_IsWIN9X) {
+ return WIN9X_PAINT_STARVATION_LIMIT;
+ }
+
+ return PAINT_STARVATION_LIMIT;
+}
+
+
+/*
+ * Determine if an event is being starved (i.e the starvation limit has
+ * been exceeded.
+ * Note: this function uses the current setting and updates the contents
+ * of the wasPending and lastTime arguments
+ *
+ * ispending: PR_TRUE if the event is currently pending
+ * starvationLimit: Threshold defined in milliseconds for determining when
+ * the event has been held in the queue too long
+ * wasPending: PR_TRUE if the last time _md_EventIsStarved was called
+ * the event was pending. This value is updated within
+ * this function.
+ * lastTime: Holds the last time the event was in the queue.
+ * This value is updated within this function
+ * returns: PR_TRUE if the event is starved, PR_FALSE otherwise
+ */
+
+static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
+ PRBool *wasPending, PRUint32 *lastTime,
+ PRUint32 currentTime)
+{
+ if (*wasPending && isPending) {
+ /*
+ * It was pending previously and the event is still
+ * pending so check to see if the elapsed time is
+ * over the limit which indicates the event was starved
+ */
+ if ((currentTime - *lastTime) > starvationLimit) {
+ return PR_TRUE; /* pending and over the limit */
+ }
+
+ return PR_FALSE; /* pending but within the limit */
+ }
+
+ if (isPending) {
+ /*
+ * was_pending must be false so record the current time
+ * so the elapsed time can be computed the next time this
+ * function is called
+ */
+ *lastTime = currentTime;
+ *wasPending = PR_TRUE;
+ return PR_FALSE;
+ }
+
+ /* Event is no longer pending */
+ *wasPending = PR_FALSE;
+ return PR_FALSE;
+}
+
+/* Determines if the there is a pending Mouse or input event */
+
+static PRBool _md_IsInputPending(WORD qstatus)
+{
+ /* Return immediately there aren't any pending input or paints. */
+ if (qstatus == 0) {
+ return PR_FALSE;
+ }
+
+ /* Is there anything other than a QS_MOUSEMOVE pending? */
+ if ((qstatus & QS_MOUSEBUTTON) ||
+ (qstatus & QS_KEY) ||
+ (qstatus & QS_HOTKEY)) {
+ return PR_TRUE;
+ }
+
+ /*
+ * Mouse moves need extra processing to determine if the mouse
+ * pointer actually changed location because Windows automatically
+ * generates WM_MOVEMOVE events when a new window is created which
+ * we need to filter out.
+ */
+ if (qstatus & QS_MOUSEMOVE) {
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ if ((_md_LastMousePos.x == cursorPos.x) &&
+ (_md_LastMousePos.y == cursorPos.y)) {
+ return PR_FALSE; /* This is a fake mouse move */
+ }
+
+ /* Real mouse move */
+ _md_LastMousePos.x = cursorPos.x;
+ _md_LastMousePos.y = cursorPos.y;
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+#ifdef USE_TIMER
+ WORD qstatus;
+
+ PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
+
+ /* Since calls to set the _md_PerformanceSetting can be nested
+ * only performance setting values <= 0 will potentially trigger
+ * the use of a timer.
+ */
+ if ((_md_PerformanceSetting <= 0) &&
+ ((now - _md_SwitchTime) > _md_StarvationDelay)) {
+ SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
+ self->timerSet = PR_TRUE;
+ _md_WasInputPending = PR_FALSE;
+ _md_WasPaintPending = PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
+
+ /* Check for starved input */
+ if (_md_EventIsStarved( _md_IsInputPending(qstatus),
+ INPUT_STARVATION_LIMIT,
+ &_md_WasInputPending,
+ &_md_InputTime,
+ now )) {
+ /*
+ * Use a timer for notification. Timers have the lowest priority.
+ * They are not processed until all other events have been processed.
+ * This allows any starved paints and input to be processed.
+ */
+ SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
+ self->timerSet = PR_TRUE;
+
+ /*
+ * Clear any pending paint. _md_WasInputPending was cleared in
+ * _md_EventIsStarved.
+ */
+ _md_WasPaintPending = PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ if (_md_EventIsStarved( (qstatus & QS_PAINT),
+ _md_GetPaintStarvationLimit(),
+ &_md_WasPaintPending,
+ &_md_PaintTime,
+ now) ) {
+ /*
+ * Use a timer for notification. Timers have the lowest priority.
+ * They are not processed until all other events have been processed.
+ * This allows any starved paints and input to be processed
+ */
+ SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
+ self->timerSet = PR_TRUE;
+
+ /*
+ * Clear any pending input. _md_WasPaintPending was cleared in
+ * _md_EventIsStarved.
+ */
+ _md_WasInputPending = PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ /*
+ * Nothing is being starved so post a message instead of using a timer.
+ * Posted messages are processed before other messages so they have the
+ * highest priority.
+ */
+#endif
+ PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
+ (WPARAM)0, (LPARAM)self );
+
+ return PR_SUCCESS;
+}/* --- end _pl_NativeNotify() --- */
+#endif
+
+
+#if defined(XP_OS2)
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+ BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
+ 0, MPFROMP(self));
+ return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
+}/* --- end _pl_NativeNotify() --- */
+#endif /* XP_OS2 */
+
+#if defined(VMS)
+/* Just set the event flag */
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+ unsigned int status;
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("_pl_NativeNotify: self=%p efn=%d",
+ self, self->efn));
+ status = SYS$SETEF(self->efn);
+ return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE;
+}/* --- end _pl_NativeNotify() --- */
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+#define NOTIFY_TOKEN 0xFA
+ PRInt32 count;
+ unsigned char buf[] = { NOTIFY_TOKEN };
+
+# ifdef VBOX
+ /* Don't write two chars, because we'll only acknowledge one and that'll
+ cause trouble for anyone selecting/polling on the read descriptor. */
+ if (self->notified)
+ return PR_SUCCESS;
+# endif
+
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("_pl_NativeNotify: self=%p",
+ self));
+ count = write(self->eventPipe[1], buf, 1);
+ if (count == 1)
+ return PR_SUCCESS;
+ if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ return PR_SUCCESS;
+ return PR_FAILURE;
+}/* --- end _pl_NativeNotify() --- */
+#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
+
+#if defined(XP_BEOS)
+struct ThreadInterfaceData
+{
+ void *data;
+ int32 sync;
+};
+
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+ struct ThreadInterfaceData id;
+ id.data = self;
+ id.sync = false;
+ write_port(self->eventport, 'natv', &id, sizeof(id));
+
+ return PR_SUCCESS; /* Is this correct? */
+}
+#endif /* XP_BEOS */
+
+#if defined(XP_MACOSX)
+static PRStatus
+_pl_NativeNotify(PLEventQueue* self)
+{
+#if defined(MAC_USE_CFRUNLOOPSOURCE)
+ CFRunLoopSourceSignal(self->mRunLoopSource);
+ CFRunLoopWakeUp(self->mMainRunLoop);
+#elif defined(MAC_USE_CARBON_EVENT)
+ OSErr err;
+ EventRef newEvent;
+ if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
+ 0, kEventAttributeNone, &newEvent) != noErr)
+ return PR_FAILURE;
+ err = SetEventParameter(newEvent, kEventParamPLEventQueue,
+ typeUInt32, sizeof(PREventQueue*), &self);
+ if (err == noErr) {
+ err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
+ ReleaseEvent(newEvent);
+ }
+ if (err != noErr)
+ return PR_FAILURE;
+#endif
+ return PR_SUCCESS;
+}
+#endif /* defined(XP_MACOSX) */
+
+static PRStatus
+_pl_AcknowledgeNativeNotify(PLEventQueue* self)
+{
+#if defined(_WIN32) || defined(XP_OS2)
+#ifdef XP_OS2
+ QMSG aMsg;
+#else
+ MSG aMsg;
+#endif
+ /*
+ * only remove msg when we've been called directly by
+ * PL_ProcessPendingEvents, not when we've been called by
+ * the window proc because the window proc will remove the
+ * msg for us.
+ */
+ if (self->removeMsg) {
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("_pl_AcknowledgeNativeNotify: self=%p", self));
+#ifdef XP_OS2
+ WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
+ _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
+#else
+ PeekMessage(&aMsg, self->eventReceiverWindow,
+ _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
+ if (self->timerSet) {
+ KillTimer(self->eventReceiverWindow, TIMER_ID);
+ self->timerSet = PR_FALSE;
+ }
+#endif
+ }
+ return PR_SUCCESS;
+#elif defined(VMS)
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
+ self, self->efn));
+ /*
+ ** If this is the last entry, then clear the event flag. Also make sure
+ ** the flag is cleared on any spurious wakeups.
+ */
+ sys$clref(self->efn);
+ return PR_SUCCESS;
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+
+ PRInt32 count;
+ unsigned char c;
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("_pl_AcknowledgeNativeNotify: self=%p",
+ self));
+ /* consume the byte NativeNotify put in our pipe: */
+ count = read(self->eventPipe[0], &c, 1);
+ if ((count == 1) && (c == NOTIFY_TOKEN))
+ return PR_SUCCESS;
+ if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+ return PR_SUCCESS;
+ return PR_FAILURE;
+#elif defined(MAC_USE_CFRUNLOOPSOURCE) /* vbox */
+ /* vbox */
+ CFRunLoopRunInMode(self->mRunLoopModeStr, 0.0, 1); /* vbox */
+ return PR_SUCCESS; /* vbox */
+#else
+
+ /* nothing to do on the other platforms */
+ return PR_SUCCESS;
+#endif
+}
+
+PR_IMPLEMENT(PRInt32)
+PL_GetEventQueueSelectFD(PLEventQueue* self)
+{
+ if (self == NULL)
+ return -1;
+
+#if defined(VMS)
+ return -(self->efn);
+#elif defined(XP_UNIX) && !defined(XP_MACOSX)
+ return self->eventPipe[0];
+#else
+ return -1; /* other platforms don't handle this (yet) */
+#endif
+}
+
+PR_IMPLEMENT(PRBool)
+PL_IsQueueOnCurrentThread( PLEventQueue *queue )
+{
+ PRThread *me = PR_GetCurrentThread();
+ return me == queue->handlerThread;
+}
+
+PR_EXTERN(PRBool)
+PL_IsQueueNative(PLEventQueue *queue)
+{
+ return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
+}
+
+#if defined(_WIN32)
+/*
+** Global Instance handle...
+** In Win32 this is the module handle of the DLL.
+**
+*/
+static HINSTANCE _pr_hInstance;
+#endif
+
+
+#if defined(_WIN32)
+
+/*
+** Initialization routine for the DLL...
+*/
+
+BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ _pr_hInstance = hDLL;
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ _pr_hInstance = NULL;
+ break;
+ }
+
+ return TRUE;
+}
+#endif
+
+
+#if defined(_WIN32) || defined(XP_OS2)
+#ifdef XP_OS2
+MRESULT EXPENTRY
+_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
+#else
+LRESULT CALLBACK
+_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+#endif
+{
+ if (_pr_PostEventMsgId == uMsg )
+ {
+ PREventQueue *queue = (PREventQueue *)lParam;
+ queue->removeMsg = PR_FALSE;
+ PL_ProcessPendingEvents(queue);
+ queue->removeMsg = PR_TRUE;
+#ifdef XP_OS2
+ return MRFROMLONG(TRUE);
+#else
+ return TRUE;
+#endif
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+static PRBool isInitialized;
+static PRCallOnceType once;
+static PRLock *initLock;
+
+/*
+** InitWinEventLib() -- Create the Windows initialization lock
+**
+*/
+static PRStatus InitEventLib( void )
+{
+ PR_ASSERT( initLock == NULL );
+
+ initLock = PR_NewLock();
+ return initLock ? PR_SUCCESS : PR_FAILURE;
+}
+
+#endif /* Win32, OS2 */
+
+#if defined(_WIN32)
+
+/*
+** _md_CreateEventQueue() -- ModelDependent initializer
+*/
+static void _md_CreateEventQueue( PLEventQueue *eventQueue )
+{
+ WNDCLASS wc;
+
+ /*
+ ** If this is the first call to PL_InitializeEventsLib(),
+ ** make the call to InitWinEventLib() to create the initLock.
+ **
+ ** Then lock the initializer lock to insure that
+ ** we have exclusive control over the initialization sequence.
+ **
+ */
+
+
+ /* Register the windows message for XPCOM Event notification */
+ _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
+
+ /* Register the class for the event receiver window */
+ if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
+ wc.style = 0;
+ wc.lpfnWndProc = _md_EventReceiverProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = _pr_hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH) NULL;
+ wc.lpszMenuName = (LPCSTR) NULL;
+ wc.lpszClassName = _pr_eventWindowClass;
+ RegisterClass(&wc);
+ }
+
+ /* Create the event receiver window */
+ eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
+ "XPCOM:EventReceiver",
+ 0, 0, 0, 10, 10,
+ NULL, NULL, _pr_hInstance,
+ NULL);
+ PR_ASSERT(eventQueue->eventReceiverWindow);
+ /* Set a property which can be used to retrieve the event queue
+ * within the _md_TimerProc callback
+ */
+ SetProp(eventQueue->eventReceiverWindow,
+ _md_GetEventQueuePropName(), (HANDLE)eventQueue);
+
+ return;
+} /* end _md_CreateEventQueue() */
+#endif /* Winxx */
+
+#if defined(XP_OS2)
+/*
+** _md_CreateEventQueue() -- ModelDependent initializer
+*/
+static void _md_CreateEventQueue( PLEventQueue *eventQueue )
+{
+ /* Must have HMQ for this & can't assume we already have appshell */
+ if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
+ {
+ PPIB ppib;
+ PTIB ptib;
+ HAB hab;
+ HMQ hmq;
+
+ /* Set our app to be a PM app before attempting Win calls */
+ DosGetInfoBlocks(&ptib, &ppib);
+ ppib->pib_ultype = 3;
+
+ hab = WinInitialize(0);
+ hmq = WinCreateMsgQueue(hab, 0);
+ PR_ASSERT(hmq);
+ }
+
+ if( !_pr_PostEventMsgId)
+ {
+ WinRegisterClass( 0 /* hab_current */,
+ _pr_eventWindowClass,
+ _md_EventReceiverProc,
+ 0, 0);
+
+ _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
+ "XPCOM_PostEvent");
+ }
+
+ eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
+ _pr_eventWindowClass,
+ "", 0,
+ 0, 0, 0, 0,
+ HWND_DESKTOP,
+ HWND_TOP,
+ 0,
+ NULL,
+ NULL);
+ PR_ASSERT(eventQueue->eventReceiverWindow);
+
+ return;
+} /* end _md_CreateEventQueue() */
+#endif /* XP_OS2 */
+
+#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
+/*
+** _md_CreateEventQueue() -- ModelDependent initializer
+*/
+static void _md_CreateEventQueue( PLEventQueue *eventQueue )
+{
+ /* there's really nothing special to do here,
+ ** the guts of the unix stuff is in the setupnativenotify
+ ** and related functions.
+ */
+ return;
+} /* end _md_CreateEventQueue() */
+#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
+
+#if defined(MAC_USE_CFRUNLOOPSOURCE)
+static void _md_EventReceiverProc(void *info)
+{
+ PLEventQueue *queue = (PLEventQueue*)info;
+ PL_ProcessPendingEvents(queue);
+}
+
+#elif defined(MAC_USE_CARBON_EVENT)
+/*
+** _md_CreateEventQueue() -- ModelDependent initializer
+*/
+
+static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
+ EventRef inEvent,
+ void* userData)
+{
+ if (GetEventClass(inEvent) == kEventClassPL &&
+ GetEventKind(inEvent) == kEventProcessPLEvents)
+ {
+ PREventQueue *queue;
+ if (GetEventParameter(inEvent, kEventParamPLEventQueue,
+ typeUInt32, NULL, sizeof(PREventQueue*), NULL,
+ &queue) == noErr)
+ {
+ PL_ProcessPendingEvents(queue);
+ return noErr;
+ }
+ }
+ return eventNotHandledErr;
+}
+
+static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
+ void *inCompareData)
+{
+ Boolean match = false;
+
+ if (GetEventClass(inEvent) == kEventClassPL &&
+ GetEventKind(inEvent) == kEventProcessPLEvents)
+ {
+ PREventQueue *queue;
+ match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
+ typeUInt32, NULL, sizeof(PREventQueue*), NULL,
+ &queue) == noErr) && (queue == inCompareData));
+ }
+ return match;
+}
+
+#endif /* defined(MAC_USE_CARBON_EVENT) */
+
+#if defined(XP_MACOSX)
+static void _md_CreateEventQueue( PLEventQueue *eventQueue )
+{
+#if defined(MAC_USE_CFRUNLOOPSOURCE)
+ CFRunLoopSourceContext sourceContext = { 0 };
+ sourceContext.version = 0;
+ sourceContext.info = (void*)eventQueue;
+ sourceContext.perform = _md_EventReceiverProc;
+
+ /* make a run loop source */
+ eventQueue->mRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /* order */, &sourceContext);
+ PR_ASSERT(eventQueue->mRunLoopSource);
+
+ eventQueue->mMainRunLoop = CFRunLoopGetCurrent();
+ CFRetain(eventQueue->mMainRunLoop);
+
+ /* and add it to the run loop */
+ CFRunLoopAddSource(eventQueue->mMainRunLoop, eventQueue->mRunLoopSource, kCFRunLoopCommonModes);
+
+ /* Add it again but with a unique mode name so we can acknowledge it
+ without processing any other message sources. */
+ { /* vbox */
+ char szModeName[80]; /* vbox */
+ snprintf(szModeName, sizeof(szModeName), "VBoxXPCOMQueueMode-%p", eventQueue); /* vbox */
+ eventQueue->mRunLoopModeStr = CFStringCreateWithCString(kCFAllocatorDefault, /* vbox */
+ szModeName, kCFStringEncodingASCII); /* vbox */
+ CFRunLoopAddSource(eventQueue->mMainRunLoop, /* vbox */
+ eventQueue->mRunLoopSource, eventQueue->mRunLoopModeStr); /* vbox */
+ } /* vbox */
+
+#elif defined(MAC_USE_CARBON_EVENT)
+ eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
+ PR_ASSERT(eventQueue->eventHandlerUPP);
+ if (eventQueue->eventHandlerUPP)
+ {
+ EventTypeSpec eventType;
+
+ eventType.eventClass = kEventClassPL;
+ eventType.eventKind = kEventProcessPLEvents;
+
+ InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
+ eventQueue, &eventQueue->eventHandlerRef);
+ PR_ASSERT(eventQueue->eventHandlerRef);
+ }
+#endif
+} /* end _md_CreateEventQueue() */
+#endif /* defined(XP_MACOSX) */
+
+/* extra functions for unix */
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+
+PR_IMPLEMENT(PRInt32)
+PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
+{
+ PRInt32 count = 0;
+ PRInt32 fullCount;
+
+ if (aSelf == NULL)
+ return -1;
+
+ PR_EnterMonitor(aSelf->monitor);
+
+ if (aSelf->processingEvents) {
+ PR_ExitMonitor(aSelf->monitor);
+ return 0;
+ }
+
+ aSelf->processingEvents = PR_TRUE;
+
+ /* Only process the events that are already in the queue, and
+ * not any new events that get added. Do this by counting the
+ * number of events currently in the queue
+ */
+ fullCount = _pl_GetEventCount(aSelf);
+ PR_LOG(event_lm, PR_LOG_DEBUG,
+ ("$$$ fullCount is %d id is %ld\n", fullCount, aID));
+
+ if (fullCount == 0) {
+ aSelf->processingEvents = PR_FALSE;
+ PR_ExitMonitor(aSelf->monitor);
+ return 0;
+ }
+
+ PR_ExitMonitor(aSelf->monitor);
+
+ while (fullCount-- > 0) {
+ /* peek at the next event */
+ PLEvent *event;
+ event = PR_EVENT_PTR(aSelf->queue.next);
+ if (event == NULL)
+ break;
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
+ event->id));
+ if (event->id >= aID) {
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
+ break;
+ }
+
+ event = PL_GetEvent(aSelf);
+ PL_HandleEvent(event);
+ PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
+ count++;
+ }
+
+ PR_EnterMonitor(aSelf->monitor);
+
+ /* if full count still had items left then there's still items left
+ in the queue. Let the native notify token stay. */
+
+ if (aSelf->type == EventQueueIsNative) {
+ fullCount = _pl_GetEventCount(aSelf);
+
+ if (fullCount <= 0) {
+ _pl_AcknowledgeNativeNotify(aSelf);
+ aSelf->notified = PR_FALSE;
+ }
+ }
+
+ aSelf->processingEvents = PR_FALSE;
+
+ PR_ExitMonitor(aSelf->monitor);
+
+ return count;
+}
+
+PR_IMPLEMENT(void)
+PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
+ void *aClosure)
+{
+ aSelf->idFunc = aFunc;
+ aSelf->idFuncClosure = aClosure;
+}
+
+PR_IMPLEMENT(void)
+PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
+{
+ aSelf->idFunc = 0;
+ aSelf->idFuncClosure = 0;
+}
+
+#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
+
+/* --- end plevent.c --- */