VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/threads/plevent.c@ 30135

Last change on this file since 30135 was 30135, checked in by vboxsync, 15 years ago

plevent.c: Acknowledge the native queue on darwin so that we don't end up spinning when using EventQueue::processEventQueue in a state checking loop (like in testdriver.base.waitForTasks).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.7 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org Code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#if defined(_WIN32)
39#include <windows.h>
40#endif
41
42#if defined(XP_OS2)
43#define INCL_DOS
44#define INCL_DOSERRORS
45#define INCL_WIN
46#include <os2.h>
47#define DefWindowProc WinDefWindowProc
48#endif /* XP_OS2 */
49
50#include "nspr.h"
51#include "plevent.h"
52
53#if !defined(WIN32)
54#include <errno.h>
55#include <stddef.h>
56#if !defined(XP_OS2)
57#include <unistd.h>
58#endif /* !XP_OS2 */
59#endif /* !Win32 */
60
61#if defined(XP_UNIX)
62/* for fcntl */
63#include <sys/types.h>
64#include <fcntl.h>
65#endif
66
67#if defined(XP_BEOS)
68#include <kernel/OS.h>
69#endif
70
71#if defined(XP_MACOSX)
72#if defined(MOZ_WIDGET_COCOA)
73#include <CoreFoundation/CoreFoundation.h>
74#define MAC_USE_CFRUNLOOPSOURCE
75#elif defined(TARGET_CARBON)
76/* #include <CarbonEvents.h> */
77/* #define MAC_USE_CARBON_EVENT */
78#include <CoreFoundation/CoreFoundation.h>
79#define MAC_USE_CFRUNLOOPSOURCE
80#endif
81#endif
82
83#include "private/pprthred.h"
84
85#if defined(VMS)
86/*
87** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it
88** wants an event flag. So, we don't create and use a pipe for
89** notification of when an event queue has something ready, instead
90** we use an event flag. Shouldn't be a problem if we only have
91** a few event queues.
92*/
93#include <lib$routines.h>
94#include <starlet.h>
95#include <stsdef.h>
96#endif /* VMS */
97
98#if defined(_WIN32)
99/* Comment out the following USE_TIMER define to prevent
100 * WIN32 from using a WIN32 native timer for PLEvent notification.
101 * With USE_TIMER defined we will use a timer when pending input
102 * or paint events are starved, otherwise it will use a posted
103 * WM_APP msg for PLEvent notification.
104 */
105#define USE_TIMER
106
107/* Threshold defined in milliseconds for determining when the input
108 * and paint events have been held in the WIN32 msg queue too long
109 */
110#define INPUT_STARVATION_LIMIT 50
111/* The paint starvation limit is set to the smallest value which
112 * does not cause performance degradation while running page load tests
113 */
114#define PAINT_STARVATION_LIMIT 750
115/* The WIN9X paint starvation limit is larger because it was
116 * determined that the following value was required to prevent performance
117 * degradation on page load tests for WIN98/95 only.
118 */
119#define WIN9X_PAINT_STARVATION_LIMIT 3000
120
121#define TIMER_ID 0
122/* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */
123static PRInt32 _md_PerformanceSetting = 0;
124static PRUint32 _md_StarvationDelay = 0;
125static PRUint32 _md_SwitchTime = 0;
126#endif
127
128static PRLogModuleInfo *event_lm = NULL;
129
130/*******************************************************************************
131 * Private Stuff
132 ******************************************************************************/
133
134/*
135** EventQueueType -- Defines notification type for an event queue
136**
137*/
138typedef enum {
139 EventQueueIsNative = 1,
140 EventQueueIsMonitored = 2
141} EventQueueType;
142
143
144struct PLEventQueue {
145 const char* name;
146 PRCList queue;
147 PRMonitor* monitor;
148 PRThread* handlerThread;
149 EventQueueType type;
150 PRPackedBool processingEvents;
151 PRPackedBool notified;
152#if defined(_WIN32)
153 PRPackedBool timerSet;
154#endif
155
156#if defined(XP_UNIX) && !defined(XP_MACOSX)
157#if defined(VMS)
158 int efn;
159#else
160 PRInt32 eventPipe[2];
161#endif
162 PLGetEventIDFunc idFunc;
163 void* idFuncClosure;
164#elif defined(_WIN32) || defined(XP_OS2)
165 HWND eventReceiverWindow;
166 PRBool removeMsg;
167#elif defined(XP_BEOS)
168 port_id eventport;
169#elif defined(XP_MACOSX)
170#if defined(MAC_USE_CFRUNLOOPSOURCE)
171 CFRunLoopSourceRef mRunLoopSource;
172 CFRunLoopRef mMainRunLoop;
173 CFStringRef mRunLoopModeStr; /* vbox */
174#elif defined(MAC_USE_CARBON_EVENT)
175 EventHandlerUPP eventHandlerUPP;
176 EventHandlerRef eventHandlerRef;
177#endif
178#endif
179};
180
181#define PR_EVENT_PTR(_qp) \
182 ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
183
184static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self);
185static void _pl_CleanupNativeNotifier(PLEventQueue* self);
186static PRStatus _pl_NativeNotify(PLEventQueue* self);
187static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self);
188static void _md_CreateEventQueue( PLEventQueue *eventQueue );
189static PRInt32 _pl_GetEventCount(PLEventQueue* self);
190
191
192#if defined(_WIN32) || defined(XP_OS2)
193#if defined(XP_OS2)
194ULONG _pr_PostEventMsgId;
195#else
196UINT _pr_PostEventMsgId;
197#endif /* OS2 */
198static char *_pr_eventWindowClass = "XPCOM:EventWindow";
199#endif /* Win32, OS2 */
200
201#if defined(_WIN32)
202
203static LPCTSTR _md_GetEventQueuePropName() {
204 static ATOM atom = 0;
205 if (!atom) {
206 atom = GlobalAddAtom("XPCOM_EventQueue");
207 }
208 return MAKEINTATOM(atom);
209}
210#endif
211
212#if defined(MAC_USE_CARBON_EVENT)
213enum {
214 kEventClassPL = FOUR_CHAR_CODE('PLEC'),
215
216 kEventProcessPLEvents = 1,
217
218 kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ')
219};
220
221static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData);
222#endif
223
224/*******************************************************************************
225 * Event Queue Operations
226 ******************************************************************************/
227
228/*
229** _pl_CreateEventQueue() -- Create the event queue
230**
231**
232*/
233static PLEventQueue * _pl_CreateEventQueue(const char *name,
234 PRThread *handlerThread,
235 EventQueueType qtype)
236{
237 PRStatus err;
238 PLEventQueue* self = NULL;
239 PRMonitor* mon = NULL;
240
241 if (event_lm == NULL)
242 event_lm = PR_NewLogModule("event");
243
244 self = PR_NEWZAP(PLEventQueue);
245 if (self == NULL) return NULL;
246
247 mon = PR_NewNamedMonitor(name);
248 if (mon == NULL) goto error;
249
250 self->name = name;
251 self->monitor = mon;
252 self->handlerThread = handlerThread;
253 self->processingEvents = PR_FALSE;
254 self->type = qtype;
255#if defined(_WIN32)
256 self->timerSet = PR_FALSE;
257#endif
258#if defined(_WIN32) || defined(XP_OS2)
259 self->removeMsg = PR_TRUE;
260#endif
261
262 self->notified = PR_FALSE;
263
264 PR_INIT_CLIST(&self->queue);
265 if ( qtype == EventQueueIsNative ) {
266 err = _pl_SetupNativeNotifier(self);
267 if (err) goto error;
268 _md_CreateEventQueue( self );
269 }
270 return self;
271
272 error:
273 if (mon != NULL)
274 PR_DestroyMonitor(mon);
275 PR_DELETE(self);
276 return NULL;
277}
278
279PR_IMPLEMENT(PLEventQueue*)
280PL_CreateEventQueue(const char* name, PRThread* handlerThread)
281{
282 return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
283}
284
285PR_EXTERN(PLEventQueue *)
286PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread)
287{
288 return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
289}
290
291PR_EXTERN(PLEventQueue *)
292PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread)
293{
294 return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
295}
296
297PR_IMPLEMENT(PRMonitor*)
298PL_GetEventQueueMonitor(PLEventQueue* self)
299{
300 return self->monitor;
301}
302
303static void PR_CALLBACK
304_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
305{
306 PL_DequeueEvent(event, queue);
307 PL_DestroyEvent(event);
308}
309
310PR_IMPLEMENT(void)
311PL_DestroyEventQueue(PLEventQueue* self)
312{
313 PR_EnterMonitor(self->monitor);
314
315 /* destroy undelivered events */
316 PL_MapEvents(self, _pl_destroyEvent, NULL);
317
318 if ( self->type == EventQueueIsNative )
319 _pl_CleanupNativeNotifier(self);
320
321 /* destroying the monitor also destroys the name */
322 PR_ExitMonitor(self->monitor);
323 PR_DestroyMonitor(self->monitor);
324 PR_DELETE(self);
325
326}
327
328PR_IMPLEMENT(PRStatus)
329PL_PostEvent(PLEventQueue* self, PLEvent* event)
330{
331 PRStatus err = PR_SUCCESS;
332 PRMonitor* mon;
333
334 if (self == NULL)
335 return PR_FAILURE;
336
337 mon = self->monitor;
338 PR_EnterMonitor(mon);
339
340#if defined(XP_UNIX) && !defined(XP_MACOSX)
341 if (self->idFunc && event)
342 event->id = self->idFunc(self->idFuncClosure);
343#endif
344
345 /* insert event into thread's event queue: */
346 if (event != NULL) {
347 PR_APPEND_LINK(&event->link, &self->queue);
348 }
349
350 if (self->type == EventQueueIsNative && !self->notified) {
351 err = _pl_NativeNotify(self);
352
353 if (err != PR_SUCCESS)
354 goto error;
355
356 self->notified = PR_TRUE;
357 }
358
359 /*
360 * This may fall on deaf ears if we're really notifying the native
361 * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
362 */
363 err = PR_Notify(mon);
364
365error:
366 PR_ExitMonitor(mon);
367 return err;
368}
369
370PR_IMPLEMENT(void*)
371PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
372{
373 void* result;
374
375 if (self == NULL)
376 return NULL;
377
378 PR_ASSERT(event != NULL);
379
380 if (PR_GetCurrentThread() == self->handlerThread) {
381 /* Handle the case where the thread requesting the event handling
382 * is also the thread that's supposed to do the handling. */
383 result = event->handler(event);
384 }
385 else {
386 int i, entryCount;
387
388 event->lock = PR_NewLock();
389 if (!event->lock) {
390 return NULL;
391 }
392 event->condVar = PR_NewCondVar(event->lock);
393 if(!event->condVar) {
394 PR_DestroyLock(event->lock);
395 event->lock = NULL;
396 return NULL;
397 }
398
399 PR_Lock(event->lock);
400
401 entryCount = PR_GetMonitorEntryCount(self->monitor);
402
403 event->synchronousResult = (void*)PR_TRUE;
404
405 PL_PostEvent(self, event);
406
407 /* We need temporarily to give up our event queue monitor if
408 we're holding it, otherwise, the thread we're going to wait
409 for notification from won't be able to enter it to process
410 the event. */
411 if (entryCount) {
412 for (i = 0; i < entryCount; i++)
413 PR_ExitMonitor(self->monitor);
414 }
415
416 event->handled = PR_FALSE;
417
418 while (!event->handled) {
419 /* wait for event to be handled or destroyed */
420 PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT);
421 }
422
423 if (entryCount) {
424 for (i = 0; i < entryCount; i++)
425 PR_EnterMonitor(self->monitor);
426 }
427
428 result = event->synchronousResult;
429 event->synchronousResult = NULL;
430 PR_Unlock(event->lock);
431 }
432
433 /* For synchronous events, they're destroyed here on the caller's
434 thread before the result is returned. See PL_HandleEvent. */
435 PL_DestroyEvent(event);
436
437 return result;
438}
439
440PR_IMPLEMENT(PLEvent*)
441PL_GetEvent(PLEventQueue* self)
442{
443 PLEvent* event = NULL;
444 PRStatus err = PR_SUCCESS;
445
446 if (self == NULL)
447 return NULL;
448
449 PR_EnterMonitor(self->monitor);
450
451 if (!PR_CLIST_IS_EMPTY(&self->queue)) {
452 if ( self->type == EventQueueIsNative &&
453 self->notified &&
454 !self->processingEvents &&
455 0 == _pl_GetEventCount(self) )
456 {
457 err = _pl_AcknowledgeNativeNotify(self);
458 self->notified = PR_FALSE;
459 }
460 if (err)
461 goto done;
462
463 /* then grab the event and return it: */
464 event = PR_EVENT_PTR(self->queue.next);
465 PR_REMOVE_AND_INIT_LINK(&event->link);
466 }
467
468 done:
469 PR_ExitMonitor(self->monitor);
470 return event;
471}
472
473PR_IMPLEMENT(PRBool)
474PL_EventAvailable(PLEventQueue* self)
475{
476 PRBool result = PR_FALSE;
477
478 if (self == NULL)
479 return PR_FALSE;
480
481 PR_EnterMonitor(self->monitor);
482
483 if (!PR_CLIST_IS_EMPTY(&self->queue))
484 result = PR_TRUE;
485
486 PR_ExitMonitor(self->monitor);
487 return result;
488}
489
490PR_IMPLEMENT(void)
491PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
492{
493 PRCList* qp;
494
495 if (self == NULL)
496 return;
497
498 PR_EnterMonitor(self->monitor);
499 qp = self->queue.next;
500 while (qp != &self->queue) {
501 PLEvent* event = PR_EVENT_PTR(qp);
502 qp = qp->next;
503 (*fun)(event, data, self);
504 }
505 PR_ExitMonitor(self->monitor);
506}
507
508static void PR_CALLBACK
509_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
510{
511 PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
512 if (event->owner == owner) {
513 PR_LOG(event_lm, PR_LOG_DEBUG,
514 ("$$$ \tdestroying event %0x for owner %0x", event, owner));
515 PL_DequeueEvent(event, queue);
516
517 if (event->synchronousResult == (void*)PR_TRUE) {
518 PR_Lock(event->lock);
519 event->synchronousResult = NULL;
520 event->handled = PR_TRUE;
521 PR_NotifyCondVar(event->condVar);
522 PR_Unlock(event->lock);
523 }
524 else {
525 PL_DestroyEvent(event);
526 }
527 }
528 else {
529 PR_LOG(event_lm, PR_LOG_DEBUG,
530 ("$$$ \tskipping event %0x for owner %0x", event, owner));
531 }
532}
533
534PR_IMPLEMENT(void)
535PL_RevokeEvents(PLEventQueue* self, void* owner)
536{
537 if (self == NULL)
538 return;
539
540 PR_LOG(event_lm, PR_LOG_DEBUG,
541 ("$$$ revoking events for owner %0x", owner));
542
543 /*
544 ** First we enter the monitor so that no one else can post any events
545 ** to the queue:
546 */
547 PR_EnterMonitor(self->monitor);
548 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
549
550 /*
551 ** Discard any pending events for this owner:
552 */
553 PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
554
555#ifdef DEBUG
556 {
557 PRCList* qp = self->queue.next;
558 while (qp != &self->queue) {
559 PLEvent* event = PR_EVENT_PTR(qp);
560 qp = qp->next;
561 PR_ASSERT(event->owner != owner);
562 }
563 }
564#endif /* DEBUG */
565
566 PR_ExitMonitor(self->monitor);
567
568 PR_LOG(event_lm, PR_LOG_DEBUG,
569 ("$$$ revoking events for owner %0x", owner));
570}
571
572static PRInt32
573_pl_GetEventCount(PLEventQueue* self)
574{
575 PRCList* node;
576 PRInt32 count = 0;
577
578 PR_EnterMonitor(self->monitor);
579 node = PR_LIST_HEAD(&self->queue);
580 while (node != &self->queue) {
581 count++;
582 node = PR_NEXT_LINK(node);
583 }
584 PR_ExitMonitor(self->monitor);
585
586 return count;
587}
588
589PR_IMPLEMENT(void)
590PL_ProcessPendingEvents(PLEventQueue* self)
591{
592 PRInt32 count;
593
594 if (self == NULL)
595 return;
596
597
598 PR_EnterMonitor(self->monitor);
599
600 if (self->processingEvents) {
601 _pl_AcknowledgeNativeNotify(self);
602 self->notified = PR_FALSE;
603 PR_ExitMonitor(self->monitor);
604 return;
605 }
606 self->processingEvents = PR_TRUE;
607
608 /* Only process the events that are already in the queue, and
609 * not any new events that get added. Do this by counting the
610 * number of events currently in the queue
611 */
612 count = _pl_GetEventCount(self);
613 PR_ExitMonitor(self->monitor);
614
615 while (count-- > 0) {
616 PLEvent* event = PL_GetEvent(self);
617 if (event == NULL)
618 break;
619
620 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
621 PL_HandleEvent(event);
622 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
623 }
624
625 PR_EnterMonitor(self->monitor);
626
627 if (self->type == EventQueueIsNative) {
628 count = _pl_GetEventCount(self);
629
630 if (count <= 0) {
631 _pl_AcknowledgeNativeNotify(self);
632 self->notified = PR_FALSE;
633 }
634 else {
635 _pl_NativeNotify(self);
636 self->notified = PR_TRUE;
637 }
638
639 }
640 self->processingEvents = PR_FALSE;
641
642 PR_ExitMonitor(self->monitor);
643}
644
645/*******************************************************************************
646 * Event Operations
647 ******************************************************************************/
648
649PR_IMPLEMENT(void)
650PL_InitEvent(PLEvent* self, void* owner,
651 PLHandleEventProc handler,
652 PLDestroyEventProc destructor)
653{
654#ifdef PL_POST_TIMINGS
655 self->postTime = PR_IntervalNow();
656#endif
657 PR_INIT_CLIST(&self->link);
658 self->handler = handler;
659 self->destructor = destructor;
660 self->owner = owner;
661 self->synchronousResult = NULL;
662 self->handled = PR_FALSE;
663 self->lock = NULL;
664 self->condVar = NULL;
665#if defined(XP_UNIX) && !defined(XP_MACOSX)
666 self->id = 0;
667#endif
668}
669
670PR_IMPLEMENT(void*)
671PL_GetEventOwner(PLEvent* self)
672{
673 return self->owner;
674}
675
676PR_IMPLEMENT(void)
677PL_HandleEvent(PLEvent* self)
678{
679 void* result;
680 if (self == NULL)
681 return;
682
683 /* This event better not be on an event queue anymore. */
684 PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
685
686 result = self->handler(self);
687 if (NULL != self->synchronousResult) {
688 PR_Lock(self->lock);
689 self->synchronousResult = result;
690 self->handled = PR_TRUE;
691 PR_NotifyCondVar(self->condVar);
692 PR_Unlock(self->lock);
693 }
694 else {
695 /* For asynchronous events, they're destroyed by the event-handler
696 thread. See PR_PostSynchronousEvent. */
697 PL_DestroyEvent(self);
698 }
699}
700#ifdef PL_POST_TIMINGS
701static long s_eventCount = 0;
702static long s_totalTime = 0;
703#endif
704
705PR_IMPLEMENT(void)
706PL_DestroyEvent(PLEvent* self)
707{
708 if (self == NULL)
709 return;
710
711 /* This event better not be on an event queue anymore. */
712 PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
713
714 if(self->condVar)
715 PR_DestroyCondVar(self->condVar);
716 if(self->lock)
717 PR_DestroyLock(self->lock);
718
719#ifdef PL_POST_TIMINGS
720 s_totalTime += PR_IntervalNow() - self->postTime;
721 s_eventCount++;
722 printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount));
723#endif
724
725 self->destructor(self);
726}
727
728PR_IMPLEMENT(void)
729PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
730{
731 if (self == NULL)
732 return;
733
734 /* Only the owner is allowed to dequeue events because once the
735 client has put it in the queue, they have no idea whether it's
736 been processed and destroyed or not. */
737
738 PR_ASSERT(queue->handlerThread == PR_GetCurrentThread());
739
740 PR_EnterMonitor(queue->monitor);
741
742 PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
743
744#if 0
745 /* I do not think that we need to do this anymore.
746 if we do not acknowledge and this is the only
747 only event in the queue, any calls to process
748 the eventQ will be effective noop.
749 */
750 if (queue->type == EventQueueIsNative)
751 _pl_AcknowledgeNativeNotify(queue);
752#endif
753
754 PR_REMOVE_AND_INIT_LINK(&self->link);
755
756 PR_ExitMonitor(queue->monitor);
757}
758
759PR_IMPLEMENT(void)
760PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation,
761 PRUint32 starvationDelay)
762{
763#if defined(_WIN32)
764
765 _md_StarvationDelay = starvationDelay;
766
767 if (favorPerformanceOverEventStarvation) {
768 _md_PerformanceSetting++;
769 return;
770 }
771
772 _md_PerformanceSetting--;
773
774 if (_md_PerformanceSetting == 0) {
775 /* Switched from allowing event starvation to no event starvation so grab
776 the current time to determine when to actually switch to using timers
777 instead of posted WM_APP messages. */
778 _md_SwitchTime = PR_IntervalToMilliseconds(PR_IntervalNow());
779 }
780
781#endif
782}
783
784/*******************************************************************************
785 * Pure Event Queues
786 *
787 * For when you're only processing PLEvents and there is no native
788 * select, thread messages, or AppleEvents.
789 ******************************************************************************/
790
791PR_IMPLEMENT(PLEvent*)
792PL_WaitForEvent(PLEventQueue* self)
793{
794 PLEvent* event;
795 PRMonitor* mon;
796
797 if (self == NULL)
798 return NULL;
799
800 mon = self->monitor;
801 PR_EnterMonitor(mon);
802
803 while ((event = PL_GetEvent(self)) == NULL) {
804 PRStatus err;
805 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
806 err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
807 if ((err == PR_FAILURE)
808 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
809 }
810
811 PR_ExitMonitor(mon);
812 return event;
813}
814
815PR_IMPLEMENT(void)
816PL_EventLoop(PLEventQueue* self)
817{
818 if (self == NULL)
819 return;
820
821 while (PR_TRUE) {
822 PLEvent* event = PL_WaitForEvent(self);
823 if (event == NULL) {
824 /* This can only happen if the current thread is interrupted */
825 return;
826 }
827
828 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
829 PL_HandleEvent(event);
830 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
831 }
832}
833
834/*******************************************************************************
835 * Native Event Queues
836 *
837 * For when you need to call select, or WaitNextEvent, and yet also want
838 * to handle PLEvents.
839 ******************************************************************************/
840
841static PRStatus
842_pl_SetupNativeNotifier(PLEventQueue* self)
843{
844#if defined(VMS)
845 unsigned int status;
846 self->idFunc = 0;
847 self->idFuncClosure = 0;
848 status = LIB$GET_EF(&self->efn);
849 if (!$VMS_STATUS_SUCCESS(status))
850 return PR_FAILURE;
851 PR_LOG(event_lm, PR_LOG_DEBUG,
852 ("$$$ Allocated event flag %d", self->efn));
853 return PR_SUCCESS;
854#elif defined(XP_UNIX) && !defined(XP_MACOSX)
855 int err;
856 int flags;
857
858 self->idFunc = 0;
859 self->idFuncClosure = 0;
860
861 err = pipe(self->eventPipe);
862 if (err != 0) {
863 return PR_FAILURE;
864 }
865
866 /* make the pipe nonblocking */
867 flags = fcntl(self->eventPipe[0], F_GETFL, 0);
868 if (flags == -1) {
869 goto failed;
870 }
871 err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
872 if (err == -1) {
873 goto failed;
874 }
875 flags = fcntl(self->eventPipe[1], F_GETFL, 0);
876 if (flags == -1) {
877 goto failed;
878 }
879 err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
880 if (err == -1) {
881 goto failed;
882 }
883 return PR_SUCCESS;
884
885failed:
886 close(self->eventPipe[0]);
887 close(self->eventPipe[1]);
888 return PR_FAILURE;
889#elif defined(XP_BEOS)
890 /* hook up to the nsToolkit queue, however the appshell
891 * isn't necessairly started, so we might have to create
892 * the queue ourselves
893 */
894 char portname[64];
895 char semname[64];
896 PR_snprintf(portname, sizeof(portname), "event%lx",
897 (long unsigned) self->handlerThread);
898 PR_snprintf(semname, sizeof(semname), "sync%lx",
899 (long unsigned) self->handlerThread);
900
901 if((self->eventport = find_port(portname)) < 0)
902 {
903 /* create port
904 */
905 self->eventport = create_port(500, portname);
906
907 /* We don't use the sem, but it has to be there
908 */
909 create_sem(0, semname);
910 }
911
912 return PR_SUCCESS;
913#else
914 return PR_SUCCESS;
915#endif
916}
917
918static void
919_pl_CleanupNativeNotifier(PLEventQueue* self)
920{
921#if defined(VMS)
922 {
923 unsigned int status;
924 PR_LOG(event_lm, PR_LOG_DEBUG,
925 ("$$$ Freeing event flag %d", self->efn));
926 status = LIB$FREE_EF(&self->efn);
927 }
928#elif defined(XP_UNIX) && !defined(XP_MACOSX)
929 close(self->eventPipe[0]);
930 close(self->eventPipe[1]);
931#elif defined(_WIN32)
932 if (self->timerSet) {
933 KillTimer(self->eventReceiverWindow, TIMER_ID);
934 self->timerSet = PR_FALSE;
935 }
936 RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
937
938 /* DestroyWindow doesn't do anything when called from a non ui thread. Since
939 * self->eventReceiverWindow was created on the ui thread, it must be destroyed
940 * on the ui thread.
941 */
942 SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
943
944#elif defined(XP_OS2)
945 WinDestroyWindow(self->eventReceiverWindow);
946#elif defined(MAC_USE_CFRUNLOOPSOURCE)
947
948 CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, kCFRunLoopCommonModes);
949 CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, self->mRunLoopModeStr); /* vbox */
950 CFRelease(self->mRunLoopSource);
951 CFRelease(self->mMainRunLoop);
952 CFRelease(self->mRunLoopModeStr); /* vbox */
953
954#elif defined(MAC_USE_CARBON_EVENT)
955 EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator);
956 PR_ASSERT(comparator != NULL);
957 if (comparator) {
958 FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
959 DisposeEventComparatorUPP(comparator);
960 }
961 DisposeEventHandlerUPP(self->eventHandlerUPP);
962 RemoveEventHandler(self->eventHandlerRef);
963#endif
964}
965
966#if defined(_WIN32)
967
968static PRBool _md_WasInputPending = PR_FALSE;
969static PRUint32 _md_InputTime = 0;
970static PRBool _md_WasPaintPending = PR_FALSE;
971static PRUint32 _md_PaintTime = 0;
972/* last mouse location */
973static POINT _md_LastMousePos;
974
975/*******************************************************************************
976 * Timer callback function. Timers are used on WIN32 instead of APP events
977 * when there are pending UI events because APP events can cause the GUI to lockup
978 * because posted messages are processed before other messages.
979 ******************************************************************************/
980
981static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
982{
983 PREventQueue* queue = (PREventQueue *) GetProp(hwnd, _md_GetEventQueuePropName());
984 PR_ASSERT(queue != NULL);
985
986 KillTimer(hwnd, TIMER_ID);
987 queue->timerSet = PR_FALSE;
988 queue->removeMsg = PR_FALSE;
989 PL_ProcessPendingEvents( queue );
990 queue->removeMsg = PR_TRUE;
991}
992
993static PRBool _md_IsWIN9X = PR_FALSE;
994static PRBool _md_IsOSSet = PR_FALSE;
995
996static void _md_DetermineOSType()
997{
998 OSVERSIONINFO os;
999 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1000 GetVersionEx(&os);
1001 if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
1002 _md_IsWIN9X = PR_TRUE;
1003 }
1004}
1005
1006static PRUint32 _md_GetPaintStarvationLimit()
1007{
1008 if (! _md_IsOSSet) {
1009 _md_DetermineOSType();
1010 _md_IsOSSet = PR_TRUE;
1011 }
1012
1013 if (_md_IsWIN9X) {
1014 return WIN9X_PAINT_STARVATION_LIMIT;
1015 }
1016
1017 return PAINT_STARVATION_LIMIT;
1018}
1019
1020
1021/*
1022 * Determine if an event is being starved (i.e the starvation limit has
1023 * been exceeded.
1024 * Note: this function uses the current setting and updates the contents
1025 * of the wasPending and lastTime arguments
1026 *
1027 * ispending: PR_TRUE if the event is currently pending
1028 * starvationLimit: Threshold defined in milliseconds for determining when
1029 * the event has been held in the queue too long
1030 * wasPending: PR_TRUE if the last time _md_EventIsStarved was called
1031 * the event was pending. This value is updated within
1032 * this function.
1033 * lastTime: Holds the last time the event was in the queue.
1034 * This value is updated within this function
1035 * returns: PR_TRUE if the event is starved, PR_FALSE otherwise
1036 */
1037
1038static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
1039 PRBool *wasPending, PRUint32 *lastTime,
1040 PRUint32 currentTime)
1041{
1042 if (*wasPending && isPending) {
1043 /*
1044 * It was pending previously and the event is still
1045 * pending so check to see if the elapsed time is
1046 * over the limit which indicates the event was starved
1047 */
1048 if ((currentTime - *lastTime) > starvationLimit) {
1049 return PR_TRUE; /* pending and over the limit */
1050 }
1051
1052 return PR_FALSE; /* pending but within the limit */
1053 }
1054
1055 if (isPending) {
1056 /*
1057 * was_pending must be false so record the current time
1058 * so the elapsed time can be computed the next time this
1059 * function is called
1060 */
1061 *lastTime = currentTime;
1062 *wasPending = PR_TRUE;
1063 return PR_FALSE;
1064 }
1065
1066 /* Event is no longer pending */
1067 *wasPending = PR_FALSE;
1068 return PR_FALSE;
1069}
1070
1071/* Determines if the there is a pending Mouse or input event */
1072
1073static PRBool _md_IsInputPending(WORD qstatus)
1074{
1075 /* Return immediately there aren't any pending input or paints. */
1076 if (qstatus == 0) {
1077 return PR_FALSE;
1078 }
1079
1080 /* Is there anything other than a QS_MOUSEMOVE pending? */
1081 if ((qstatus & QS_MOUSEBUTTON) ||
1082 (qstatus & QS_KEY) ||
1083 (qstatus & QS_HOTKEY)) {
1084 return PR_TRUE;
1085 }
1086
1087 /*
1088 * Mouse moves need extra processing to determine if the mouse
1089 * pointer actually changed location because Windows automatically
1090 * generates WM_MOVEMOVE events when a new window is created which
1091 * we need to filter out.
1092 */
1093 if (qstatus & QS_MOUSEMOVE) {
1094 POINT cursorPos;
1095 GetCursorPos(&cursorPos);
1096 if ((_md_LastMousePos.x == cursorPos.x) &&
1097 (_md_LastMousePos.y == cursorPos.y)) {
1098 return PR_FALSE; /* This is a fake mouse move */
1099 }
1100
1101 /* Real mouse move */
1102 _md_LastMousePos.x = cursorPos.x;
1103 _md_LastMousePos.y = cursorPos.y;
1104 return PR_TRUE;
1105 }
1106
1107 return PR_FALSE;
1108}
1109
1110static PRStatus
1111_pl_NativeNotify(PLEventQueue* self)
1112{
1113#ifdef USE_TIMER
1114 WORD qstatus;
1115
1116 PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
1117
1118 /* Since calls to set the _md_PerformanceSetting can be nested
1119 * only performance setting values <= 0 will potentially trigger
1120 * the use of a timer.
1121 */
1122 if ((_md_PerformanceSetting <= 0) &&
1123 ((now - _md_SwitchTime) > _md_StarvationDelay)) {
1124 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1125 self->timerSet = PR_TRUE;
1126 _md_WasInputPending = PR_FALSE;
1127 _md_WasPaintPending = PR_FALSE;
1128 return PR_SUCCESS;
1129 }
1130
1131 qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
1132
1133 /* Check for starved input */
1134 if (_md_EventIsStarved( _md_IsInputPending(qstatus),
1135 INPUT_STARVATION_LIMIT,
1136 &_md_WasInputPending,
1137 &_md_InputTime,
1138 now )) {
1139 /*
1140 * Use a timer for notification. Timers have the lowest priority.
1141 * They are not processed until all other events have been processed.
1142 * This allows any starved paints and input to be processed.
1143 */
1144 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1145 self->timerSet = PR_TRUE;
1146
1147 /*
1148 * Clear any pending paint. _md_WasInputPending was cleared in
1149 * _md_EventIsStarved.
1150 */
1151 _md_WasPaintPending = PR_FALSE;
1152 return PR_SUCCESS;
1153 }
1154
1155 if (_md_EventIsStarved( (qstatus & QS_PAINT),
1156 _md_GetPaintStarvationLimit(),
1157 &_md_WasPaintPending,
1158 &_md_PaintTime,
1159 now) ) {
1160 /*
1161 * Use a timer for notification. Timers have the lowest priority.
1162 * They are not processed until all other events have been processed.
1163 * This allows any starved paints and input to be processed
1164 */
1165 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1166 self->timerSet = PR_TRUE;
1167
1168 /*
1169 * Clear any pending input. _md_WasPaintPending was cleared in
1170 * _md_EventIsStarved.
1171 */
1172 _md_WasInputPending = PR_FALSE;
1173 return PR_SUCCESS;
1174 }
1175
1176 /*
1177 * Nothing is being starved so post a message instead of using a timer.
1178 * Posted messages are processed before other messages so they have the
1179 * highest priority.
1180 */
1181#endif
1182 PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
1183 (WPARAM)0, (LPARAM)self );
1184
1185 return PR_SUCCESS;
1186}/* --- end _pl_NativeNotify() --- */
1187#endif
1188
1189
1190#if defined(XP_OS2)
1191static PRStatus
1192_pl_NativeNotify(PLEventQueue* self)
1193{
1194 BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
1195 0, MPFROMP(self));
1196 return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
1197}/* --- end _pl_NativeNotify() --- */
1198#endif /* XP_OS2 */
1199
1200#if defined(VMS)
1201/* Just set the event flag */
1202static PRStatus
1203_pl_NativeNotify(PLEventQueue* self)
1204{
1205 unsigned int status;
1206 PR_LOG(event_lm, PR_LOG_DEBUG,
1207 ("_pl_NativeNotify: self=%p efn=%d",
1208 self, self->efn));
1209 status = SYS$SETEF(self->efn);
1210 return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE;
1211}/* --- end _pl_NativeNotify() --- */
1212#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1213
1214static PRStatus
1215_pl_NativeNotify(PLEventQueue* self)
1216{
1217#define NOTIFY_TOKEN 0xFA
1218 PRInt32 count;
1219 unsigned char buf[] = { NOTIFY_TOKEN };
1220
1221# ifdef VBOX
1222 /* Don't write two chars, because we'll only acknowledge one and that'll
1223 cause trouble for anyone selecting/polling on the read descriptor. */
1224 if (self->notified)
1225 return PR_SUCCESS;
1226# endif
1227
1228 PR_LOG(event_lm, PR_LOG_DEBUG,
1229 ("_pl_NativeNotify: self=%p",
1230 self));
1231 count = write(self->eventPipe[1], buf, 1);
1232 if (count == 1)
1233 return PR_SUCCESS;
1234 if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
1235 return PR_SUCCESS;
1236 return PR_FAILURE;
1237}/* --- end _pl_NativeNotify() --- */
1238#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1239
1240#if defined(XP_BEOS)
1241struct ThreadInterfaceData
1242{
1243 void *data;
1244 int32 sync;
1245};
1246
1247static PRStatus
1248_pl_NativeNotify(PLEventQueue* self)
1249{
1250 struct ThreadInterfaceData id;
1251 id.data = self;
1252 id.sync = false;
1253 write_port(self->eventport, 'natv', &id, sizeof(id));
1254
1255 return PR_SUCCESS; /* Is this correct? */
1256}
1257#endif /* XP_BEOS */
1258
1259#if defined(XP_MACOSX)
1260static PRStatus
1261_pl_NativeNotify(PLEventQueue* self)
1262{
1263#if defined(MAC_USE_CFRUNLOOPSOURCE)
1264 CFRunLoopSourceSignal(self->mRunLoopSource);
1265 CFRunLoopWakeUp(self->mMainRunLoop);
1266#elif defined(MAC_USE_CARBON_EVENT)
1267 OSErr err;
1268 EventRef newEvent;
1269 if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
1270 0, kEventAttributeNone, &newEvent) != noErr)
1271 return PR_FAILURE;
1272 err = SetEventParameter(newEvent, kEventParamPLEventQueue,
1273 typeUInt32, sizeof(PREventQueue*), &self);
1274 if (err == noErr) {
1275 err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
1276 ReleaseEvent(newEvent);
1277 }
1278 if (err != noErr)
1279 return PR_FAILURE;
1280#endif
1281 return PR_SUCCESS;
1282}
1283#endif /* defined(XP_MACOSX) */
1284
1285static PRStatus
1286_pl_AcknowledgeNativeNotify(PLEventQueue* self)
1287{
1288#if defined(_WIN32) || defined(XP_OS2)
1289#ifdef XP_OS2
1290 QMSG aMsg;
1291#else
1292 MSG aMsg;
1293#endif
1294 /*
1295 * only remove msg when we've been called directly by
1296 * PL_ProcessPendingEvents, not when we've been called by
1297 * the window proc because the window proc will remove the
1298 * msg for us.
1299 */
1300 if (self->removeMsg) {
1301 PR_LOG(event_lm, PR_LOG_DEBUG,
1302 ("_pl_AcknowledgeNativeNotify: self=%p", self));
1303#ifdef XP_OS2
1304 WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
1305 _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
1306#else
1307 PeekMessage(&aMsg, self->eventReceiverWindow,
1308 _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
1309 if (self->timerSet) {
1310 KillTimer(self->eventReceiverWindow, TIMER_ID);
1311 self->timerSet = PR_FALSE;
1312 }
1313#endif
1314 }
1315 return PR_SUCCESS;
1316#elif defined(VMS)
1317 PR_LOG(event_lm, PR_LOG_DEBUG,
1318 ("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
1319 self, self->efn));
1320 /*
1321 ** If this is the last entry, then clear the event flag. Also make sure
1322 ** the flag is cleared on any spurious wakeups.
1323 */
1324 sys$clref(self->efn);
1325 return PR_SUCCESS;
1326#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1327
1328 PRInt32 count;
1329 unsigned char c;
1330 PR_LOG(event_lm, PR_LOG_DEBUG,
1331 ("_pl_AcknowledgeNativeNotify: self=%p",
1332 self));
1333 /* consume the byte NativeNotify put in our pipe: */
1334 count = read(self->eventPipe[0], &c, 1);
1335 if ((count == 1) && (c == NOTIFY_TOKEN))
1336 return PR_SUCCESS;
1337 if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
1338 return PR_SUCCESS;
1339 return PR_FAILURE;
1340#elif defined(MAC_USE_CFRUNLOOPSOURCE) /* vbox */
1341 /* vbox */
1342 CFRunLoopRunInMode(self->mRunLoopModeStr, 0.0, 1); /* vbox */
1343 return PR_SUCCESS; /* vbox */
1344#else
1345
1346 /* nothing to do on the other platforms */
1347 return PR_SUCCESS;
1348#endif
1349}
1350
1351PR_IMPLEMENT(PRInt32)
1352PL_GetEventQueueSelectFD(PLEventQueue* self)
1353{
1354 if (self == NULL)
1355 return -1;
1356
1357#if defined(VMS)
1358 return -(self->efn);
1359#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1360 return self->eventPipe[0];
1361#else
1362 return -1; /* other platforms don't handle this (yet) */
1363#endif
1364}
1365
1366PR_IMPLEMENT(PRBool)
1367PL_IsQueueOnCurrentThread( PLEventQueue *queue )
1368{
1369 PRThread *me = PR_GetCurrentThread();
1370 return me == queue->handlerThread;
1371}
1372
1373PR_EXTERN(PRBool)
1374PL_IsQueueNative(PLEventQueue *queue)
1375{
1376 return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
1377}
1378
1379#if defined(_WIN32)
1380/*
1381** Global Instance handle...
1382** In Win32 this is the module handle of the DLL.
1383**
1384*/
1385static HINSTANCE _pr_hInstance;
1386#endif
1387
1388
1389#if defined(_WIN32)
1390
1391/*
1392** Initialization routine for the DLL...
1393*/
1394
1395BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
1396{
1397 switch (dwReason)
1398 {
1399 case DLL_PROCESS_ATTACH:
1400 _pr_hInstance = hDLL;
1401 break;
1402
1403 case DLL_THREAD_ATTACH:
1404 break;
1405
1406 case DLL_THREAD_DETACH:
1407 break;
1408
1409 case DLL_PROCESS_DETACH:
1410 _pr_hInstance = NULL;
1411 break;
1412 }
1413
1414 return TRUE;
1415}
1416#endif
1417
1418
1419#if defined(_WIN32) || defined(XP_OS2)
1420#ifdef XP_OS2
1421MRESULT EXPENTRY
1422_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
1423#else
1424LRESULT CALLBACK
1425_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1426#endif
1427{
1428 if (_pr_PostEventMsgId == uMsg )
1429 {
1430 PREventQueue *queue = (PREventQueue *)lParam;
1431 queue->removeMsg = PR_FALSE;
1432 PL_ProcessPendingEvents(queue);
1433 queue->removeMsg = PR_TRUE;
1434#ifdef XP_OS2
1435 return MRFROMLONG(TRUE);
1436#else
1437 return TRUE;
1438#endif
1439 }
1440 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1441}
1442
1443static PRBool isInitialized;
1444static PRCallOnceType once;
1445static PRLock *initLock;
1446
1447/*
1448** InitWinEventLib() -- Create the Windows initialization lock
1449**
1450*/
1451static PRStatus InitEventLib( void )
1452{
1453 PR_ASSERT( initLock == NULL );
1454
1455 initLock = PR_NewLock();
1456 return initLock ? PR_SUCCESS : PR_FAILURE;
1457}
1458
1459#endif /* Win32, OS2 */
1460
1461#if defined(_WIN32)
1462
1463/*
1464** _md_CreateEventQueue() -- ModelDependent initializer
1465*/
1466static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1467{
1468 WNDCLASS wc;
1469
1470 /*
1471 ** If this is the first call to PL_InitializeEventsLib(),
1472 ** make the call to InitWinEventLib() to create the initLock.
1473 **
1474 ** Then lock the initializer lock to insure that
1475 ** we have exclusive control over the initialization sequence.
1476 **
1477 */
1478
1479
1480 /* Register the windows message for XPCOM Event notification */
1481 _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
1482
1483 /* Register the class for the event receiver window */
1484 if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
1485 wc.style = 0;
1486 wc.lpfnWndProc = _md_EventReceiverProc;
1487 wc.cbClsExtra = 0;
1488 wc.cbWndExtra = 0;
1489 wc.hInstance = _pr_hInstance;
1490 wc.hIcon = NULL;
1491 wc.hCursor = NULL;
1492 wc.hbrBackground = (HBRUSH) NULL;
1493 wc.lpszMenuName = (LPCSTR) NULL;
1494 wc.lpszClassName = _pr_eventWindowClass;
1495 RegisterClass(&wc);
1496 }
1497
1498 /* Create the event receiver window */
1499 eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
1500 "XPCOM:EventReceiver",
1501 0, 0, 0, 10, 10,
1502 NULL, NULL, _pr_hInstance,
1503 NULL);
1504 PR_ASSERT(eventQueue->eventReceiverWindow);
1505 /* Set a property which can be used to retrieve the event queue
1506 * within the _md_TimerProc callback
1507 */
1508 SetProp(eventQueue->eventReceiverWindow,
1509 _md_GetEventQueuePropName(), (HANDLE)eventQueue);
1510
1511 return;
1512} /* end _md_CreateEventQueue() */
1513#endif /* Winxx */
1514
1515#if defined(XP_OS2)
1516/*
1517** _md_CreateEventQueue() -- ModelDependent initializer
1518*/
1519static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1520{
1521 /* Must have HMQ for this & can't assume we already have appshell */
1522 if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
1523 {
1524 PPIB ppib;
1525 PTIB ptib;
1526 HAB hab;
1527 HMQ hmq;
1528
1529 /* Set our app to be a PM app before attempting Win calls */
1530 DosGetInfoBlocks(&ptib, &ppib);
1531 ppib->pib_ultype = 3;
1532
1533 hab = WinInitialize(0);
1534 hmq = WinCreateMsgQueue(hab, 0);
1535 PR_ASSERT(hmq);
1536 }
1537
1538 if( !_pr_PostEventMsgId)
1539 {
1540 WinRegisterClass( 0 /* hab_current */,
1541 _pr_eventWindowClass,
1542 _md_EventReceiverProc,
1543 0, 0);
1544
1545 _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
1546 "XPCOM_PostEvent");
1547 }
1548
1549 eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
1550 _pr_eventWindowClass,
1551 "", 0,
1552 0, 0, 0, 0,
1553 HWND_DESKTOP,
1554 HWND_TOP,
1555 0,
1556 NULL,
1557 NULL);
1558 PR_ASSERT(eventQueue->eventReceiverWindow);
1559
1560 return;
1561} /* end _md_CreateEventQueue() */
1562#endif /* XP_OS2 */
1563
1564#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
1565/*
1566** _md_CreateEventQueue() -- ModelDependent initializer
1567*/
1568static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1569{
1570 /* there's really nothing special to do here,
1571 ** the guts of the unix stuff is in the setupnativenotify
1572 ** and related functions.
1573 */
1574 return;
1575} /* end _md_CreateEventQueue() */
1576#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
1577
1578#if defined(MAC_USE_CFRUNLOOPSOURCE)
1579static void _md_EventReceiverProc(void *info)
1580{
1581 PLEventQueue *queue = (PLEventQueue*)info;
1582 PL_ProcessPendingEvents(queue);
1583}
1584
1585#elif defined(MAC_USE_CARBON_EVENT)
1586/*
1587** _md_CreateEventQueue() -- ModelDependent initializer
1588*/
1589
1590static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
1591 EventRef inEvent,
1592 void* userData)
1593{
1594 if (GetEventClass(inEvent) == kEventClassPL &&
1595 GetEventKind(inEvent) == kEventProcessPLEvents)
1596 {
1597 PREventQueue *queue;
1598 if (GetEventParameter(inEvent, kEventParamPLEventQueue,
1599 typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1600 &queue) == noErr)
1601 {
1602 PL_ProcessPendingEvents(queue);
1603 return noErr;
1604 }
1605 }
1606 return eventNotHandledErr;
1607}
1608
1609static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
1610 void *inCompareData)
1611{
1612 Boolean match = false;
1613
1614 if (GetEventClass(inEvent) == kEventClassPL &&
1615 GetEventKind(inEvent) == kEventProcessPLEvents)
1616 {
1617 PREventQueue *queue;
1618 match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
1619 typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1620 &queue) == noErr) && (queue == inCompareData));
1621 }
1622 return match;
1623}
1624
1625#endif /* defined(MAC_USE_CARBON_EVENT) */
1626
1627#if defined(XP_MACOSX)
1628static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1629{
1630#if defined(MAC_USE_CFRUNLOOPSOURCE)
1631 CFRunLoopSourceContext sourceContext = { 0 };
1632 sourceContext.version = 0;
1633 sourceContext.info = (void*)eventQueue;
1634 sourceContext.perform = _md_EventReceiverProc;
1635
1636 /* make a run loop source */
1637 eventQueue->mRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /* order */, &sourceContext);
1638 PR_ASSERT(eventQueue->mRunLoopSource);
1639
1640 eventQueue->mMainRunLoop = CFRunLoopGetCurrent();
1641 CFRetain(eventQueue->mMainRunLoop);
1642
1643 /* and add it to the run loop */
1644 CFRunLoopAddSource(eventQueue->mMainRunLoop, eventQueue->mRunLoopSource, kCFRunLoopCommonModes);
1645
1646 /* Add it again but with a unique mode name so we can acknowledge it
1647 without processing any other message sources. */
1648 { /* vbox */
1649 char szModeName[80]; /* vbox */
1650 snprintf(szModeName, sizeof(szModeName), "VBoxXPCOMQueueMode-%p", eventQueue); /* vbox */
1651 eventQueue->mRunLoopModeStr = CFStringCreateWithCString(kCFAllocatorDefault, /* vbox */
1652 szModeName, kCFStringEncodingASCII); /* vbox */
1653 CFRunLoopAddSource(eventQueue->mMainRunLoop, /* vbox */
1654 eventQueue->mRunLoopSource, eventQueue->mRunLoopModeStr); /* vbox */
1655 } /* vbox */
1656
1657#elif defined(MAC_USE_CARBON_EVENT)
1658 eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
1659 PR_ASSERT(eventQueue->eventHandlerUPP);
1660 if (eventQueue->eventHandlerUPP)
1661 {
1662 EventTypeSpec eventType;
1663
1664 eventType.eventClass = kEventClassPL;
1665 eventType.eventKind = kEventProcessPLEvents;
1666
1667 InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
1668 eventQueue, &eventQueue->eventHandlerRef);
1669 PR_ASSERT(eventQueue->eventHandlerRef);
1670 }
1671#endif
1672} /* end _md_CreateEventQueue() */
1673#endif /* defined(XP_MACOSX) */
1674
1675/* extra functions for unix */
1676
1677#if defined(XP_UNIX) && !defined(XP_MACOSX)
1678
1679PR_IMPLEMENT(PRInt32)
1680PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
1681{
1682 PRInt32 count = 0;
1683 PRInt32 fullCount;
1684
1685 if (aSelf == NULL)
1686 return -1;
1687
1688 PR_EnterMonitor(aSelf->monitor);
1689
1690 if (aSelf->processingEvents) {
1691 PR_ExitMonitor(aSelf->monitor);
1692 return 0;
1693 }
1694
1695 aSelf->processingEvents = PR_TRUE;
1696
1697 /* Only process the events that are already in the queue, and
1698 * not any new events that get added. Do this by counting the
1699 * number of events currently in the queue
1700 */
1701 fullCount = _pl_GetEventCount(aSelf);
1702 PR_LOG(event_lm, PR_LOG_DEBUG,
1703 ("$$$ fullCount is %d id is %ld\n", fullCount, aID));
1704
1705 if (fullCount == 0) {
1706 aSelf->processingEvents = PR_FALSE;
1707 PR_ExitMonitor(aSelf->monitor);
1708 return 0;
1709 }
1710
1711 PR_ExitMonitor(aSelf->monitor);
1712
1713 while (fullCount-- > 0) {
1714 /* peek at the next event */
1715 PLEvent *event;
1716 event = PR_EVENT_PTR(aSelf->queue.next);
1717 if (event == NULL)
1718 break;
1719 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
1720 event->id));
1721 if (event->id >= aID) {
1722 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
1723 break;
1724 }
1725
1726 event = PL_GetEvent(aSelf);
1727 PL_HandleEvent(event);
1728 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
1729 count++;
1730 }
1731
1732 PR_EnterMonitor(aSelf->monitor);
1733
1734 /* if full count still had items left then there's still items left
1735 in the queue. Let the native notify token stay. */
1736
1737 if (aSelf->type == EventQueueIsNative) {
1738 fullCount = _pl_GetEventCount(aSelf);
1739
1740 if (fullCount <= 0) {
1741 _pl_AcknowledgeNativeNotify(aSelf);
1742 aSelf->notified = PR_FALSE;
1743 }
1744 }
1745
1746 aSelf->processingEvents = PR_FALSE;
1747
1748 PR_ExitMonitor(aSelf->monitor);
1749
1750 return count;
1751}
1752
1753PR_IMPLEMENT(void)
1754PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
1755 void *aClosure)
1756{
1757 aSelf->idFunc = aFunc;
1758 aSelf->idFuncClosure = aClosure;
1759}
1760
1761PR_IMPLEMENT(void)
1762PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
1763{
1764 aSelf->idFunc = 0;
1765 aSelf->idFuncClosure = 0;
1766}
1767
1768#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1769
1770/* --- end plevent.c --- */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette