VirtualBox

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

Last change on this file since 95259 was 33044, checked in by vboxsync, 14 years ago

iprt/process: eliminate RTPROC_FLAGS_DAEMONIZE_DEPRECATED, rework starting a detached process on posix platforms and eliminate one useless fork(), avoid running

atexit functions in the temporary process

XPCOM: more detached process rework, block anything not going through IPRT, clos
e a few file descriptor inheritance leaks, and clean up the file descriptor pass
ing to VBoxXPCOMIPCD

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.8 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#ifdef VBOX
866 fcntl(self->eventPipe[0], F_SETFD, FD_CLOEXEC);
867 fcntl(self->eventPipe[1], F_SETFD, FD_CLOEXEC);
868#endif
869
870 /* make the pipe nonblocking */
871 flags = fcntl(self->eventPipe[0], F_GETFL, 0);
872 if (flags == -1) {
873 goto failed;
874 }
875 err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
876 if (err == -1) {
877 goto failed;
878 }
879 flags = fcntl(self->eventPipe[1], F_GETFL, 0);
880 if (flags == -1) {
881 goto failed;
882 }
883 err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
884 if (err == -1) {
885 goto failed;
886 }
887 return PR_SUCCESS;
888
889failed:
890 close(self->eventPipe[0]);
891 close(self->eventPipe[1]);
892 return PR_FAILURE;
893#elif defined(XP_BEOS)
894 /* hook up to the nsToolkit queue, however the appshell
895 * isn't necessairly started, so we might have to create
896 * the queue ourselves
897 */
898 char portname[64];
899 char semname[64];
900 PR_snprintf(portname, sizeof(portname), "event%lx",
901 (long unsigned) self->handlerThread);
902 PR_snprintf(semname, sizeof(semname), "sync%lx",
903 (long unsigned) self->handlerThread);
904
905 if((self->eventport = find_port(portname)) < 0)
906 {
907 /* create port
908 */
909 self->eventport = create_port(500, portname);
910
911 /* We don't use the sem, but it has to be there
912 */
913 create_sem(0, semname);
914 }
915
916 return PR_SUCCESS;
917#else
918 return PR_SUCCESS;
919#endif
920}
921
922static void
923_pl_CleanupNativeNotifier(PLEventQueue* self)
924{
925#if defined(VMS)
926 {
927 unsigned int status;
928 PR_LOG(event_lm, PR_LOG_DEBUG,
929 ("$$$ Freeing event flag %d", self->efn));
930 status = LIB$FREE_EF(&self->efn);
931 }
932#elif defined(XP_UNIX) && !defined(XP_MACOSX)
933 close(self->eventPipe[0]);
934 close(self->eventPipe[1]);
935#elif defined(_WIN32)
936 if (self->timerSet) {
937 KillTimer(self->eventReceiverWindow, TIMER_ID);
938 self->timerSet = PR_FALSE;
939 }
940 RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
941
942 /* DestroyWindow doesn't do anything when called from a non ui thread. Since
943 * self->eventReceiverWindow was created on the ui thread, it must be destroyed
944 * on the ui thread.
945 */
946 SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
947
948#elif defined(XP_OS2)
949 WinDestroyWindow(self->eventReceiverWindow);
950#elif defined(MAC_USE_CFRUNLOOPSOURCE)
951
952 CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, kCFRunLoopCommonModes);
953 CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, self->mRunLoopModeStr); /* vbox */
954 CFRelease(self->mRunLoopSource);
955 CFRelease(self->mMainRunLoop);
956 CFRelease(self->mRunLoopModeStr); /* vbox */
957
958#elif defined(MAC_USE_CARBON_EVENT)
959 EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator);
960 PR_ASSERT(comparator != NULL);
961 if (comparator) {
962 FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
963 DisposeEventComparatorUPP(comparator);
964 }
965 DisposeEventHandlerUPP(self->eventHandlerUPP);
966 RemoveEventHandler(self->eventHandlerRef);
967#endif
968}
969
970#if defined(_WIN32)
971
972static PRBool _md_WasInputPending = PR_FALSE;
973static PRUint32 _md_InputTime = 0;
974static PRBool _md_WasPaintPending = PR_FALSE;
975static PRUint32 _md_PaintTime = 0;
976/* last mouse location */
977static POINT _md_LastMousePos;
978
979/*******************************************************************************
980 * Timer callback function. Timers are used on WIN32 instead of APP events
981 * when there are pending UI events because APP events can cause the GUI to lockup
982 * because posted messages are processed before other messages.
983 ******************************************************************************/
984
985static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
986{
987 PREventQueue* queue = (PREventQueue *) GetProp(hwnd, _md_GetEventQueuePropName());
988 PR_ASSERT(queue != NULL);
989
990 KillTimer(hwnd, TIMER_ID);
991 queue->timerSet = PR_FALSE;
992 queue->removeMsg = PR_FALSE;
993 PL_ProcessPendingEvents( queue );
994 queue->removeMsg = PR_TRUE;
995}
996
997static PRBool _md_IsWIN9X = PR_FALSE;
998static PRBool _md_IsOSSet = PR_FALSE;
999
1000static void _md_DetermineOSType()
1001{
1002 OSVERSIONINFO os;
1003 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1004 GetVersionEx(&os);
1005 if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
1006 _md_IsWIN9X = PR_TRUE;
1007 }
1008}
1009
1010static PRUint32 _md_GetPaintStarvationLimit()
1011{
1012 if (! _md_IsOSSet) {
1013 _md_DetermineOSType();
1014 _md_IsOSSet = PR_TRUE;
1015 }
1016
1017 if (_md_IsWIN9X) {
1018 return WIN9X_PAINT_STARVATION_LIMIT;
1019 }
1020
1021 return PAINT_STARVATION_LIMIT;
1022}
1023
1024
1025/*
1026 * Determine if an event is being starved (i.e the starvation limit has
1027 * been exceeded.
1028 * Note: this function uses the current setting and updates the contents
1029 * of the wasPending and lastTime arguments
1030 *
1031 * ispending: PR_TRUE if the event is currently pending
1032 * starvationLimit: Threshold defined in milliseconds for determining when
1033 * the event has been held in the queue too long
1034 * wasPending: PR_TRUE if the last time _md_EventIsStarved was called
1035 * the event was pending. This value is updated within
1036 * this function.
1037 * lastTime: Holds the last time the event was in the queue.
1038 * This value is updated within this function
1039 * returns: PR_TRUE if the event is starved, PR_FALSE otherwise
1040 */
1041
1042static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
1043 PRBool *wasPending, PRUint32 *lastTime,
1044 PRUint32 currentTime)
1045{
1046 if (*wasPending && isPending) {
1047 /*
1048 * It was pending previously and the event is still
1049 * pending so check to see if the elapsed time is
1050 * over the limit which indicates the event was starved
1051 */
1052 if ((currentTime - *lastTime) > starvationLimit) {
1053 return PR_TRUE; /* pending and over the limit */
1054 }
1055
1056 return PR_FALSE; /* pending but within the limit */
1057 }
1058
1059 if (isPending) {
1060 /*
1061 * was_pending must be false so record the current time
1062 * so the elapsed time can be computed the next time this
1063 * function is called
1064 */
1065 *lastTime = currentTime;
1066 *wasPending = PR_TRUE;
1067 return PR_FALSE;
1068 }
1069
1070 /* Event is no longer pending */
1071 *wasPending = PR_FALSE;
1072 return PR_FALSE;
1073}
1074
1075/* Determines if the there is a pending Mouse or input event */
1076
1077static PRBool _md_IsInputPending(WORD qstatus)
1078{
1079 /* Return immediately there aren't any pending input or paints. */
1080 if (qstatus == 0) {
1081 return PR_FALSE;
1082 }
1083
1084 /* Is there anything other than a QS_MOUSEMOVE pending? */
1085 if ((qstatus & QS_MOUSEBUTTON) ||
1086 (qstatus & QS_KEY) ||
1087 (qstatus & QS_HOTKEY)) {
1088 return PR_TRUE;
1089 }
1090
1091 /*
1092 * Mouse moves need extra processing to determine if the mouse
1093 * pointer actually changed location because Windows automatically
1094 * generates WM_MOVEMOVE events when a new window is created which
1095 * we need to filter out.
1096 */
1097 if (qstatus & QS_MOUSEMOVE) {
1098 POINT cursorPos;
1099 GetCursorPos(&cursorPos);
1100 if ((_md_LastMousePos.x == cursorPos.x) &&
1101 (_md_LastMousePos.y == cursorPos.y)) {
1102 return PR_FALSE; /* This is a fake mouse move */
1103 }
1104
1105 /* Real mouse move */
1106 _md_LastMousePos.x = cursorPos.x;
1107 _md_LastMousePos.y = cursorPos.y;
1108 return PR_TRUE;
1109 }
1110
1111 return PR_FALSE;
1112}
1113
1114static PRStatus
1115_pl_NativeNotify(PLEventQueue* self)
1116{
1117#ifdef USE_TIMER
1118 WORD qstatus;
1119
1120 PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
1121
1122 /* Since calls to set the _md_PerformanceSetting can be nested
1123 * only performance setting values <= 0 will potentially trigger
1124 * the use of a timer.
1125 */
1126 if ((_md_PerformanceSetting <= 0) &&
1127 ((now - _md_SwitchTime) > _md_StarvationDelay)) {
1128 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1129 self->timerSet = PR_TRUE;
1130 _md_WasInputPending = PR_FALSE;
1131 _md_WasPaintPending = PR_FALSE;
1132 return PR_SUCCESS;
1133 }
1134
1135 qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
1136
1137 /* Check for starved input */
1138 if (_md_EventIsStarved( _md_IsInputPending(qstatus),
1139 INPUT_STARVATION_LIMIT,
1140 &_md_WasInputPending,
1141 &_md_InputTime,
1142 now )) {
1143 /*
1144 * Use a timer for notification. Timers have the lowest priority.
1145 * They are not processed until all other events have been processed.
1146 * This allows any starved paints and input to be processed.
1147 */
1148 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1149 self->timerSet = PR_TRUE;
1150
1151 /*
1152 * Clear any pending paint. _md_WasInputPending was cleared in
1153 * _md_EventIsStarved.
1154 */
1155 _md_WasPaintPending = PR_FALSE;
1156 return PR_SUCCESS;
1157 }
1158
1159 if (_md_EventIsStarved( (qstatus & QS_PAINT),
1160 _md_GetPaintStarvationLimit(),
1161 &_md_WasPaintPending,
1162 &_md_PaintTime,
1163 now) ) {
1164 /*
1165 * Use a timer for notification. Timers have the lowest priority.
1166 * They are not processed until all other events have been processed.
1167 * This allows any starved paints and input to be processed
1168 */
1169 SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1170 self->timerSet = PR_TRUE;
1171
1172 /*
1173 * Clear any pending input. _md_WasPaintPending was cleared in
1174 * _md_EventIsStarved.
1175 */
1176 _md_WasInputPending = PR_FALSE;
1177 return PR_SUCCESS;
1178 }
1179
1180 /*
1181 * Nothing is being starved so post a message instead of using a timer.
1182 * Posted messages are processed before other messages so they have the
1183 * highest priority.
1184 */
1185#endif
1186 PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
1187 (WPARAM)0, (LPARAM)self );
1188
1189 return PR_SUCCESS;
1190}/* --- end _pl_NativeNotify() --- */
1191#endif
1192
1193
1194#if defined(XP_OS2)
1195static PRStatus
1196_pl_NativeNotify(PLEventQueue* self)
1197{
1198 BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
1199 0, MPFROMP(self));
1200 return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
1201}/* --- end _pl_NativeNotify() --- */
1202#endif /* XP_OS2 */
1203
1204#if defined(VMS)
1205/* Just set the event flag */
1206static PRStatus
1207_pl_NativeNotify(PLEventQueue* self)
1208{
1209 unsigned int status;
1210 PR_LOG(event_lm, PR_LOG_DEBUG,
1211 ("_pl_NativeNotify: self=%p efn=%d",
1212 self, self->efn));
1213 status = SYS$SETEF(self->efn);
1214 return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE;
1215}/* --- end _pl_NativeNotify() --- */
1216#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1217
1218static PRStatus
1219_pl_NativeNotify(PLEventQueue* self)
1220{
1221#define NOTIFY_TOKEN 0xFA
1222 PRInt32 count;
1223 unsigned char buf[] = { NOTIFY_TOKEN };
1224
1225# ifdef VBOX
1226 /* Don't write two chars, because we'll only acknowledge one and that'll
1227 cause trouble for anyone selecting/polling on the read descriptor. */
1228 if (self->notified)
1229 return PR_SUCCESS;
1230# endif
1231
1232 PR_LOG(event_lm, PR_LOG_DEBUG,
1233 ("_pl_NativeNotify: self=%p",
1234 self));
1235 count = write(self->eventPipe[1], buf, 1);
1236 if (count == 1)
1237 return PR_SUCCESS;
1238 if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
1239 return PR_SUCCESS;
1240 return PR_FAILURE;
1241}/* --- end _pl_NativeNotify() --- */
1242#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1243
1244#if defined(XP_BEOS)
1245struct ThreadInterfaceData
1246{
1247 void *data;
1248 int32 sync;
1249};
1250
1251static PRStatus
1252_pl_NativeNotify(PLEventQueue* self)
1253{
1254 struct ThreadInterfaceData id;
1255 id.data = self;
1256 id.sync = false;
1257 write_port(self->eventport, 'natv', &id, sizeof(id));
1258
1259 return PR_SUCCESS; /* Is this correct? */
1260}
1261#endif /* XP_BEOS */
1262
1263#if defined(XP_MACOSX)
1264static PRStatus
1265_pl_NativeNotify(PLEventQueue* self)
1266{
1267#if defined(MAC_USE_CFRUNLOOPSOURCE)
1268 CFRunLoopSourceSignal(self->mRunLoopSource);
1269 CFRunLoopWakeUp(self->mMainRunLoop);
1270#elif defined(MAC_USE_CARBON_EVENT)
1271 OSErr err;
1272 EventRef newEvent;
1273 if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
1274 0, kEventAttributeNone, &newEvent) != noErr)
1275 return PR_FAILURE;
1276 err = SetEventParameter(newEvent, kEventParamPLEventQueue,
1277 typeUInt32, sizeof(PREventQueue*), &self);
1278 if (err == noErr) {
1279 err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
1280 ReleaseEvent(newEvent);
1281 }
1282 if (err != noErr)
1283 return PR_FAILURE;
1284#endif
1285 return PR_SUCCESS;
1286}
1287#endif /* defined(XP_MACOSX) */
1288
1289static PRStatus
1290_pl_AcknowledgeNativeNotify(PLEventQueue* self)
1291{
1292#if defined(_WIN32) || defined(XP_OS2)
1293#ifdef XP_OS2
1294 QMSG aMsg;
1295#else
1296 MSG aMsg;
1297#endif
1298 /*
1299 * only remove msg when we've been called directly by
1300 * PL_ProcessPendingEvents, not when we've been called by
1301 * the window proc because the window proc will remove the
1302 * msg for us.
1303 */
1304 if (self->removeMsg) {
1305 PR_LOG(event_lm, PR_LOG_DEBUG,
1306 ("_pl_AcknowledgeNativeNotify: self=%p", self));
1307#ifdef XP_OS2
1308 WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
1309 _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
1310#else
1311 PeekMessage(&aMsg, self->eventReceiverWindow,
1312 _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
1313 if (self->timerSet) {
1314 KillTimer(self->eventReceiverWindow, TIMER_ID);
1315 self->timerSet = PR_FALSE;
1316 }
1317#endif
1318 }
1319 return PR_SUCCESS;
1320#elif defined(VMS)
1321 PR_LOG(event_lm, PR_LOG_DEBUG,
1322 ("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
1323 self, self->efn));
1324 /*
1325 ** If this is the last entry, then clear the event flag. Also make sure
1326 ** the flag is cleared on any spurious wakeups.
1327 */
1328 sys$clref(self->efn);
1329 return PR_SUCCESS;
1330#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1331
1332 PRInt32 count;
1333 unsigned char c;
1334 PR_LOG(event_lm, PR_LOG_DEBUG,
1335 ("_pl_AcknowledgeNativeNotify: self=%p",
1336 self));
1337 /* consume the byte NativeNotify put in our pipe: */
1338 count = read(self->eventPipe[0], &c, 1);
1339 if ((count == 1) && (c == NOTIFY_TOKEN))
1340 return PR_SUCCESS;
1341 if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
1342 return PR_SUCCESS;
1343 return PR_FAILURE;
1344#elif defined(MAC_USE_CFRUNLOOPSOURCE) /* vbox */
1345 /* vbox */
1346 CFRunLoopRunInMode(self->mRunLoopModeStr, 0.0, 1); /* vbox */
1347 return PR_SUCCESS; /* vbox */
1348#else
1349
1350 /* nothing to do on the other platforms */
1351 return PR_SUCCESS;
1352#endif
1353}
1354
1355PR_IMPLEMENT(PRInt32)
1356PL_GetEventQueueSelectFD(PLEventQueue* self)
1357{
1358 if (self == NULL)
1359 return -1;
1360
1361#if defined(VMS)
1362 return -(self->efn);
1363#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1364 return self->eventPipe[0];
1365#else
1366 return -1; /* other platforms don't handle this (yet) */
1367#endif
1368}
1369
1370PR_IMPLEMENT(PRBool)
1371PL_IsQueueOnCurrentThread( PLEventQueue *queue )
1372{
1373 PRThread *me = PR_GetCurrentThread();
1374 return me == queue->handlerThread;
1375}
1376
1377PR_EXTERN(PRBool)
1378PL_IsQueueNative(PLEventQueue *queue)
1379{
1380 return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
1381}
1382
1383#if defined(_WIN32)
1384/*
1385** Global Instance handle...
1386** In Win32 this is the module handle of the DLL.
1387**
1388*/
1389static HINSTANCE _pr_hInstance;
1390#endif
1391
1392
1393#if defined(_WIN32)
1394
1395/*
1396** Initialization routine for the DLL...
1397*/
1398
1399BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
1400{
1401 switch (dwReason)
1402 {
1403 case DLL_PROCESS_ATTACH:
1404 _pr_hInstance = hDLL;
1405 break;
1406
1407 case DLL_THREAD_ATTACH:
1408 break;
1409
1410 case DLL_THREAD_DETACH:
1411 break;
1412
1413 case DLL_PROCESS_DETACH:
1414 _pr_hInstance = NULL;
1415 break;
1416 }
1417
1418 return TRUE;
1419}
1420#endif
1421
1422
1423#if defined(_WIN32) || defined(XP_OS2)
1424#ifdef XP_OS2
1425MRESULT EXPENTRY
1426_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
1427#else
1428LRESULT CALLBACK
1429_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1430#endif
1431{
1432 if (_pr_PostEventMsgId == uMsg )
1433 {
1434 PREventQueue *queue = (PREventQueue *)lParam;
1435 queue->removeMsg = PR_FALSE;
1436 PL_ProcessPendingEvents(queue);
1437 queue->removeMsg = PR_TRUE;
1438#ifdef XP_OS2
1439 return MRFROMLONG(TRUE);
1440#else
1441 return TRUE;
1442#endif
1443 }
1444 return DefWindowProc(hwnd, uMsg, wParam, lParam);
1445}
1446
1447static PRBool isInitialized;
1448static PRCallOnceType once;
1449static PRLock *initLock;
1450
1451/*
1452** InitWinEventLib() -- Create the Windows initialization lock
1453**
1454*/
1455static PRStatus InitEventLib( void )
1456{
1457 PR_ASSERT( initLock == NULL );
1458
1459 initLock = PR_NewLock();
1460 return initLock ? PR_SUCCESS : PR_FAILURE;
1461}
1462
1463#endif /* Win32, OS2 */
1464
1465#if defined(_WIN32)
1466
1467/*
1468** _md_CreateEventQueue() -- ModelDependent initializer
1469*/
1470static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1471{
1472 WNDCLASS wc;
1473
1474 /*
1475 ** If this is the first call to PL_InitializeEventsLib(),
1476 ** make the call to InitWinEventLib() to create the initLock.
1477 **
1478 ** Then lock the initializer lock to insure that
1479 ** we have exclusive control over the initialization sequence.
1480 **
1481 */
1482
1483
1484 /* Register the windows message for XPCOM Event notification */
1485 _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
1486
1487 /* Register the class for the event receiver window */
1488 if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
1489 wc.style = 0;
1490 wc.lpfnWndProc = _md_EventReceiverProc;
1491 wc.cbClsExtra = 0;
1492 wc.cbWndExtra = 0;
1493 wc.hInstance = _pr_hInstance;
1494 wc.hIcon = NULL;
1495 wc.hCursor = NULL;
1496 wc.hbrBackground = (HBRUSH) NULL;
1497 wc.lpszMenuName = (LPCSTR) NULL;
1498 wc.lpszClassName = _pr_eventWindowClass;
1499 RegisterClass(&wc);
1500 }
1501
1502 /* Create the event receiver window */
1503 eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
1504 "XPCOM:EventReceiver",
1505 0, 0, 0, 10, 10,
1506 NULL, NULL, _pr_hInstance,
1507 NULL);
1508 PR_ASSERT(eventQueue->eventReceiverWindow);
1509 /* Set a property which can be used to retrieve the event queue
1510 * within the _md_TimerProc callback
1511 */
1512 SetProp(eventQueue->eventReceiverWindow,
1513 _md_GetEventQueuePropName(), (HANDLE)eventQueue);
1514
1515 return;
1516} /* end _md_CreateEventQueue() */
1517#endif /* Winxx */
1518
1519#if defined(XP_OS2)
1520/*
1521** _md_CreateEventQueue() -- ModelDependent initializer
1522*/
1523static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1524{
1525 /* Must have HMQ for this & can't assume we already have appshell */
1526 if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
1527 {
1528 PPIB ppib;
1529 PTIB ptib;
1530 HAB hab;
1531 HMQ hmq;
1532
1533 /* Set our app to be a PM app before attempting Win calls */
1534 DosGetInfoBlocks(&ptib, &ppib);
1535 ppib->pib_ultype = 3;
1536
1537 hab = WinInitialize(0);
1538 hmq = WinCreateMsgQueue(hab, 0);
1539 PR_ASSERT(hmq);
1540 }
1541
1542 if( !_pr_PostEventMsgId)
1543 {
1544 WinRegisterClass( 0 /* hab_current */,
1545 _pr_eventWindowClass,
1546 _md_EventReceiverProc,
1547 0, 0);
1548
1549 _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
1550 "XPCOM_PostEvent");
1551 }
1552
1553 eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
1554 _pr_eventWindowClass,
1555 "", 0,
1556 0, 0, 0, 0,
1557 HWND_DESKTOP,
1558 HWND_TOP,
1559 0,
1560 NULL,
1561 NULL);
1562 PR_ASSERT(eventQueue->eventReceiverWindow);
1563
1564 return;
1565} /* end _md_CreateEventQueue() */
1566#endif /* XP_OS2 */
1567
1568#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
1569/*
1570** _md_CreateEventQueue() -- ModelDependent initializer
1571*/
1572static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1573{
1574 /* there's really nothing special to do here,
1575 ** the guts of the unix stuff is in the setupnativenotify
1576 ** and related functions.
1577 */
1578 return;
1579} /* end _md_CreateEventQueue() */
1580#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
1581
1582#if defined(MAC_USE_CFRUNLOOPSOURCE)
1583static void _md_EventReceiverProc(void *info)
1584{
1585 PLEventQueue *queue = (PLEventQueue*)info;
1586 PL_ProcessPendingEvents(queue);
1587}
1588
1589#elif defined(MAC_USE_CARBON_EVENT)
1590/*
1591** _md_CreateEventQueue() -- ModelDependent initializer
1592*/
1593
1594static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
1595 EventRef inEvent,
1596 void* userData)
1597{
1598 if (GetEventClass(inEvent) == kEventClassPL &&
1599 GetEventKind(inEvent) == kEventProcessPLEvents)
1600 {
1601 PREventQueue *queue;
1602 if (GetEventParameter(inEvent, kEventParamPLEventQueue,
1603 typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1604 &queue) == noErr)
1605 {
1606 PL_ProcessPendingEvents(queue);
1607 return noErr;
1608 }
1609 }
1610 return eventNotHandledErr;
1611}
1612
1613static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
1614 void *inCompareData)
1615{
1616 Boolean match = false;
1617
1618 if (GetEventClass(inEvent) == kEventClassPL &&
1619 GetEventKind(inEvent) == kEventProcessPLEvents)
1620 {
1621 PREventQueue *queue;
1622 match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
1623 typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1624 &queue) == noErr) && (queue == inCompareData));
1625 }
1626 return match;
1627}
1628
1629#endif /* defined(MAC_USE_CARBON_EVENT) */
1630
1631#if defined(XP_MACOSX)
1632static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1633{
1634#if defined(MAC_USE_CFRUNLOOPSOURCE)
1635 CFRunLoopSourceContext sourceContext = { 0 };
1636 sourceContext.version = 0;
1637 sourceContext.info = (void*)eventQueue;
1638 sourceContext.perform = _md_EventReceiverProc;
1639
1640 /* make a run loop source */
1641 eventQueue->mRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /* order */, &sourceContext);
1642 PR_ASSERT(eventQueue->mRunLoopSource);
1643
1644 eventQueue->mMainRunLoop = CFRunLoopGetCurrent();
1645 CFRetain(eventQueue->mMainRunLoop);
1646
1647 /* and add it to the run loop */
1648 CFRunLoopAddSource(eventQueue->mMainRunLoop, eventQueue->mRunLoopSource, kCFRunLoopCommonModes);
1649
1650 /* Add it again but with a unique mode name so we can acknowledge it
1651 without processing any other message sources. */
1652 { /* vbox */
1653 char szModeName[80]; /* vbox */
1654 snprintf(szModeName, sizeof(szModeName), "VBoxXPCOMQueueMode-%p", eventQueue); /* vbox */
1655 eventQueue->mRunLoopModeStr = CFStringCreateWithCString(kCFAllocatorDefault, /* vbox */
1656 szModeName, kCFStringEncodingASCII); /* vbox */
1657 CFRunLoopAddSource(eventQueue->mMainRunLoop, /* vbox */
1658 eventQueue->mRunLoopSource, eventQueue->mRunLoopModeStr); /* vbox */
1659 } /* vbox */
1660
1661#elif defined(MAC_USE_CARBON_EVENT)
1662 eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
1663 PR_ASSERT(eventQueue->eventHandlerUPP);
1664 if (eventQueue->eventHandlerUPP)
1665 {
1666 EventTypeSpec eventType;
1667
1668 eventType.eventClass = kEventClassPL;
1669 eventType.eventKind = kEventProcessPLEvents;
1670
1671 InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
1672 eventQueue, &eventQueue->eventHandlerRef);
1673 PR_ASSERT(eventQueue->eventHandlerRef);
1674 }
1675#endif
1676} /* end _md_CreateEventQueue() */
1677#endif /* defined(XP_MACOSX) */
1678
1679/* extra functions for unix */
1680
1681#if defined(XP_UNIX) && !defined(XP_MACOSX)
1682
1683PR_IMPLEMENT(PRInt32)
1684PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
1685{
1686 PRInt32 count = 0;
1687 PRInt32 fullCount;
1688
1689 if (aSelf == NULL)
1690 return -1;
1691
1692 PR_EnterMonitor(aSelf->monitor);
1693
1694 if (aSelf->processingEvents) {
1695 PR_ExitMonitor(aSelf->monitor);
1696 return 0;
1697 }
1698
1699 aSelf->processingEvents = PR_TRUE;
1700
1701 /* Only process the events that are already in the queue, and
1702 * not any new events that get added. Do this by counting the
1703 * number of events currently in the queue
1704 */
1705 fullCount = _pl_GetEventCount(aSelf);
1706 PR_LOG(event_lm, PR_LOG_DEBUG,
1707 ("$$$ fullCount is %d id is %ld\n", fullCount, aID));
1708
1709 if (fullCount == 0) {
1710 aSelf->processingEvents = PR_FALSE;
1711 PR_ExitMonitor(aSelf->monitor);
1712 return 0;
1713 }
1714
1715 PR_ExitMonitor(aSelf->monitor);
1716
1717 while (fullCount-- > 0) {
1718 /* peek at the next event */
1719 PLEvent *event;
1720 event = PR_EVENT_PTR(aSelf->queue.next);
1721 if (event == NULL)
1722 break;
1723 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
1724 event->id));
1725 if (event->id >= aID) {
1726 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
1727 break;
1728 }
1729
1730 event = PL_GetEvent(aSelf);
1731 PL_HandleEvent(event);
1732 PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
1733 count++;
1734 }
1735
1736 PR_EnterMonitor(aSelf->monitor);
1737
1738 /* if full count still had items left then there's still items left
1739 in the queue. Let the native notify token stay. */
1740
1741 if (aSelf->type == EventQueueIsNative) {
1742 fullCount = _pl_GetEventCount(aSelf);
1743
1744 if (fullCount <= 0) {
1745 _pl_AcknowledgeNativeNotify(aSelf);
1746 aSelf->notified = PR_FALSE;
1747 }
1748 }
1749
1750 aSelf->processingEvents = PR_FALSE;
1751
1752 PR_ExitMonitor(aSelf->monitor);
1753
1754 return count;
1755}
1756
1757PR_IMPLEMENT(void)
1758PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
1759 void *aClosure)
1760{
1761 aSelf->idFunc = aFunc;
1762 aSelf->idFuncClosure = aClosure;
1763}
1764
1765PR_IMPLEMENT(void)
1766PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
1767{
1768 aSelf->idFunc = 0;
1769 aSelf->idFuncClosure = 0;
1770}
1771
1772#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1773
1774/* --- 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