VirtualBox

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

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

xpcom18a4/xpcom/threads/pleven.c: Backported the CFRunLoop changes from the mozilla1.8.0 branch. These changes are related to mozilla bugs 271050 and 282940. This should fix the VBoxHeadless issue on Snow Leopard.

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

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