VirtualBox

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

Last change on this file since 6542 was 1, checked in by vboxsync, 55 years ago

import

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