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
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/
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
15
* The Original Code is mozilla.org Code.
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.
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.
36
* ***** END LICENSE BLOCK ***** */
44
#define INCL_DOSERRORS
47
#define DefWindowProc WinDefWindowProc
63
#include <sys/types.h>
68
#include <kernel/OS.h>
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
76
#include <Processes.h>
77
#define MAC_USE_WAKEUPPROCESS
84
#include "private/pprthred.h"
85
#endif /* defined(XP_MAC) */
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.
95
#include <lib$routines.h>
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.
109
/* Threshold defined in milliseconds for determining when the input
110
* and paint events have been held in the WIN32 msg queue too long
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
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.
121
#define WIN9X_PAINT_STARVATION_LIMIT 3000
124
/* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */
125
static PRInt32 _md_PerformanceSetting = 0;
126
static PRUint32 _md_StarvationDelay = 0;
127
static PRUint32 _md_SwitchTime = 0;
130
static PRLogModuleInfo *event_lm = NULL;
132
/*******************************************************************************
134
******************************************************************************/
137
** EventQueueType -- Defines notification type for an event queue
141
EventQueueIsNative = 1,
142
EventQueueIsMonitored = 2
146
struct PLEventQueue {
150
PRThread* handlerThread;
152
PRPackedBool processingEvents;
153
PRPackedBool notified;
155
PRPackedBool timerSet;
158
#if defined(XP_UNIX) && !defined(XP_MACOSX)
162
PRInt32 eventPipe[2];
164
PLGetEventIDFunc idFunc;
166
#elif defined(_WIN32) || defined(XP_OS2)
167
HWND eventReceiverWindow;
169
#elif defined(XP_BEOS)
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;
181
#define PR_EVENT_PTR(_qp) \
182
((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))
184
static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self);
185
static void _pl_CleanupNativeNotifier(PLEventQueue* self);
186
static PRStatus _pl_NativeNotify(PLEventQueue* self);
187
static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self);
188
static void _md_CreateEventQueue( PLEventQueue *eventQueue );
189
static PRInt32 _pl_GetEventCount(PLEventQueue* self);
192
#if defined(_WIN32) || defined(XP_OS2)
194
ULONG _pr_PostEventMsgId;
196
UINT _pr_PostEventMsgId;
198
static char *_pr_eventWindowClass = "XPCOM:EventWindow";
199
#endif /* Win32, OS2 */
203
static LPCTSTR _md_GetEventQueuePropName() {
204
static ATOM atom = 0;
206
atom = GlobalAddAtom("XPCOM_EventQueue");
208
return MAKEINTATOM(atom);
212
#if defined(MAC_USE_CARBON_EVENT)
214
kEventClassPL = FOUR_CHAR_CODE('PLEC'),
216
kEventProcessPLEvents = 1,
218
kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ')
221
static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData);
224
/*******************************************************************************
225
* Event Queue Operations
226
******************************************************************************/
229
** _pl_CreateEventQueue() -- Create the event queue
233
static PLEventQueue * _pl_CreateEventQueue(const char *name,
234
PRThread *handlerThread,
235
EventQueueType qtype)
238
PLEventQueue* self = NULL;
239
PRMonitor* mon = NULL;
241
if (event_lm == NULL)
242
event_lm = PR_NewLogModule("event");
244
self = PR_NEWZAP(PLEventQueue);
245
if (self == NULL) return NULL;
247
mon = PR_NewNamedMonitor(name);
248
if (mon == NULL) goto error;
252
self->handlerThread = handlerThread;
253
self->processingEvents = PR_FALSE;
256
self->timerSet = PR_FALSE;
258
#if defined(_WIN32) || defined(XP_OS2)
259
self->removeMsg = PR_TRUE;
261
#if defined(MAC_USE_WAKEUPPROCESS)
262
self->psn.lowLongOfPSN = kNoProcess;
265
self->notified = PR_FALSE;
267
PR_INIT_CLIST(&self->queue);
268
if ( qtype == EventQueueIsNative ) {
269
err = _pl_SetupNativeNotifier(self);
271
_md_CreateEventQueue( self );
277
PR_DestroyMonitor(mon);
282
PR_IMPLEMENT(PLEventQueue*)
283
PL_CreateEventQueue(const char* name, PRThread* handlerThread)
285
return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
288
PR_EXTERN(PLEventQueue *)
289
PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread)
291
return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
294
PR_EXTERN(PLEventQueue *)
295
PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread)
297
return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
300
PR_IMPLEMENT(PRMonitor*)
301
PL_GetEventQueueMonitor(PLEventQueue* self)
303
return self->monitor;
306
static void PR_CALLBACK
307
_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
310
#pragma unused (data, queue)
312
PL_DequeueEvent(event, queue);
313
PL_DestroyEvent(event);
317
PL_DestroyEventQueue(PLEventQueue* self)
319
PR_EnterMonitor(self->monitor);
321
/* destroy undelivered events */
322
PL_MapEvents(self, _pl_destroyEvent, NULL);
324
if ( self->type == EventQueueIsNative )
325
_pl_CleanupNativeNotifier(self);
327
/* destroying the monitor also destroys the name */
328
PR_ExitMonitor(self->monitor);
329
PR_DestroyMonitor(self->monitor);
334
PR_IMPLEMENT(PRStatus)
335
PL_PostEvent(PLEventQueue* self, PLEvent* event)
337
PRStatus err = PR_SUCCESS;
344
PR_EnterMonitor(mon);
346
#if defined(XP_UNIX) && !defined(XP_MACOSX)
347
if (self->idFunc && event)
348
event->id = self->idFunc(self->idFuncClosure);
351
/* insert event into thread's event queue: */
353
PR_APPEND_LINK(&event->link, &self->queue);
356
if (self->type == EventQueueIsNative && !self->notified) {
357
err = _pl_NativeNotify(self);
359
if (err != PR_SUCCESS)
362
self->notified = PR_TRUE;
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):
369
err = PR_Notify(mon);
377
PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
384
PR_ASSERT(event != NULL);
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);
394
event->lock = PR_NewLock();
398
event->condVar = PR_NewCondVar(event->lock);
399
if(!event->condVar) {
400
PR_DestroyLock(event->lock);
405
PR_Lock(event->lock);
407
entryCount = PR_GetMonitorEntryCount(self->monitor);
409
event->synchronousResult = (void*)PR_TRUE;
411
PL_PostEvent(self, event);
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
418
for (i = 0; i < entryCount; i++)
419
PR_ExitMonitor(self->monitor);
422
event->handled = PR_FALSE;
424
while (!event->handled) {
425
/* wait for event to be handled or destroyed */
426
PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT);
430
for (i = 0; i < entryCount; i++)
431
PR_EnterMonitor(self->monitor);
434
result = event->synchronousResult;
435
event->synchronousResult = NULL;
436
PR_Unlock(event->lock);
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);
446
PR_IMPLEMENT(PLEvent*)
447
PL_GetEvent(PLEventQueue* self)
449
PLEvent* event = NULL;
450
PRStatus err = PR_SUCCESS;
455
PR_EnterMonitor(self->monitor);
457
if (!PR_CLIST_IS_EMPTY(&self->queue)) {
458
if ( self->type == EventQueueIsNative &&
460
!self->processingEvents &&
461
0 == _pl_GetEventCount(self) )
463
err = _pl_AcknowledgeNativeNotify(self);
464
self->notified = PR_FALSE;
469
/* then grab the event and return it: */
470
event = PR_EVENT_PTR(self->queue.next);
471
PR_REMOVE_AND_INIT_LINK(&event->link);
475
PR_ExitMonitor(self->monitor);
480
PL_EventAvailable(PLEventQueue* self)
482
PRBool result = PR_FALSE;
487
PR_EnterMonitor(self->monitor);
489
if (!PR_CLIST_IS_EMPTY(&self->queue))
492
PR_ExitMonitor(self->monitor);
497
PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
504
PR_EnterMonitor(self->monitor);
505
qp = self->queue.next;
506
while (qp != &self->queue) {
507
PLEvent* event = PR_EVENT_PTR(qp);
509
(*fun)(event, data, self);
511
PR_ExitMonitor(self->monitor);
514
static void PR_CALLBACK
515
_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
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);
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);
531
PL_DestroyEvent(event);
535
PR_LOG(event_lm, PR_LOG_DEBUG,
536
("$$$ \tskipping event %0x for owner %0x", event, owner));
541
PL_RevokeEvents(PLEventQueue* self, void* owner)
546
PR_LOG(event_lm, PR_LOG_DEBUG,
547
("$$$ revoking events for owner %0x", owner));
550
** First we enter the monitor so that no one else can post any events
553
PR_EnterMonitor(self->monitor);
554
PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));
557
** Discard any pending events for this owner:
559
PL_MapEvents(self, _pl_DestroyEventForOwner, owner);
563
PRCList* qp = self->queue.next;
564
while (qp != &self->queue) {
565
PLEvent* event = PR_EVENT_PTR(qp);
567
PR_ASSERT(event->owner != owner);
572
PR_ExitMonitor(self->monitor);
574
PR_LOG(event_lm, PR_LOG_DEBUG,
575
("$$$ revoking events for owner %0x", owner));
579
_pl_GetEventCount(PLEventQueue* self)
584
PR_EnterMonitor(self->monitor);
585
node = PR_LIST_HEAD(&self->queue);
586
while (node != &self->queue) {
588
node = PR_NEXT_LINK(node);
590
PR_ExitMonitor(self->monitor);
596
PL_ProcessPendingEvents(PLEventQueue* self)
604
PR_EnterMonitor(self->monitor);
606
if (self->processingEvents) {
607
_pl_AcknowledgeNativeNotify(self);
608
self->notified = PR_FALSE;
609
PR_ExitMonitor(self->monitor);
612
self->processingEvents = PR_TRUE;
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
618
count = _pl_GetEventCount(self);
619
PR_ExitMonitor(self->monitor);
621
while (count-- > 0) {
622
PLEvent* event = PL_GetEvent(self);
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"));
631
PR_EnterMonitor(self->monitor);
633
if (self->type == EventQueueIsNative) {
634
count = _pl_GetEventCount(self);
637
_pl_AcknowledgeNativeNotify(self);
638
self->notified = PR_FALSE;
641
_pl_NativeNotify(self);
642
self->notified = PR_TRUE;
646
self->processingEvents = PR_FALSE;
648
PR_ExitMonitor(self->monitor);
651
/*******************************************************************************
653
******************************************************************************/
656
PL_InitEvent(PLEvent* self, void* owner,
657
PLHandleEventProc handler,
658
PLDestroyEventProc destructor)
660
#ifdef PL_POST_TIMINGS
661
self->postTime = PR_IntervalNow();
663
PR_INIT_CLIST(&self->link);
664
self->handler = handler;
665
self->destructor = destructor;
667
self->synchronousResult = NULL;
668
self->handled = PR_FALSE;
670
self->condVar = NULL;
671
#if defined(XP_UNIX) && !defined(XP_MACOSX)
677
PL_GetEventOwner(PLEvent* self)
683
PL_HandleEvent(PLEvent* self)
689
/* This event better not be on an event queue anymore. */
690
PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
692
result = self->handler(self);
693
if (NULL != self->synchronousResult) {
695
self->synchronousResult = result;
696
self->handled = PR_TRUE;
697
PR_NotifyCondVar(self->condVar);
698
PR_Unlock(self->lock);
701
/* For asynchronous events, they're destroyed by the event-handler
702
thread. See PR_PostSynchronousEvent. */
703
PL_DestroyEvent(self);
706
#ifdef PL_POST_TIMINGS
707
static long s_eventCount = 0;
708
static long s_totalTime = 0;
712
PL_DestroyEvent(PLEvent* self)
717
/* This event better not be on an event queue anymore. */
718
PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));
721
PR_DestroyCondVar(self->condVar);
723
PR_DestroyLock(self->lock);
725
#ifdef PL_POST_TIMINGS
726
s_totalTime += PR_IntervalNow() - self->postTime;
728
printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount));
731
self->destructor(self);
735
PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
738
#pragma unused (queue)
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. */
747
PR_ASSERT(queue->handlerThread == PR_GetCurrentThread());
749
PR_EnterMonitor(queue->monitor);
751
PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
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.
759
if (queue->type == EventQueueIsNative)
760
_pl_AcknowledgeNativeNotify(queue);
763
PR_REMOVE_AND_INIT_LINK(&self->link);
765
PR_ExitMonitor(queue->monitor);
769
PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation,
770
PRUint32 starvationDelay)
774
_md_StarvationDelay = starvationDelay;
776
if (favorPerformanceOverEventStarvation) {
777
_md_PerformanceSetting++;
781
_md_PerformanceSetting--;
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());
793
/*******************************************************************************
796
* For when you're only processing PLEvents and there is no native
797
* select, thread messages, or AppleEvents.
798
******************************************************************************/
800
PR_IMPLEMENT(PLEvent*)
801
PL_WaitForEvent(PLEventQueue* self)
810
PR_EnterMonitor(mon);
812
while ((event = PL_GetEvent(self)) == NULL) {
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;
825
PL_EventLoop(PLEventQueue* self)
831
PLEvent* event = PL_WaitForEvent(self);
833
/* This can only happen if the current thread is interrupted */
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"));
843
/*******************************************************************************
844
* Native Event Queues
846
* For when you need to call select, or WaitNextEvent, and yet also want
847
* to handle PLEvents.
848
******************************************************************************/
851
_pl_SetupNativeNotifier(PLEventQueue* self)
854
#pragma unused (self)
860
self->idFuncClosure = 0;
861
status = LIB$GET_EF(&self->efn);
862
if (!$VMS_STATUS_SUCCESS(status))
864
PR_LOG(event_lm, PR_LOG_DEBUG,
865
("$$$ Allocated event flag %d", self->efn));
867
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
872
self->idFuncClosure = 0;
874
err = pipe(self->eventPipe);
879
/* make the pipe nonblocking */
880
flags = fcntl(self->eventPipe[0], F_GETFL, 0);
884
err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
888
flags = fcntl(self->eventPipe[1], F_GETFL, 0);
892
err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
899
close(self->eventPipe[0]);
900
close(self->eventPipe[1]);
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
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);
914
if((self->eventport = find_port(portname)) < 0)
918
self->eventport = create_port(500, portname);
920
/* We don't use the sem, but it has to be there
922
create_sem(0, semname);
932
_pl_CleanupNativeNotifier(PLEventQueue* self)
935
#pragma unused (self)
941
PR_LOG(event_lm, PR_LOG_DEBUG,
942
("$$$ Freeing event flag %d", self->efn));
943
status = LIB$FREE_EF(&self->efn);
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;
953
RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName());
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
959
SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0);
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);
967
FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self);
968
DisposeEventComparatorUPP(comparator);
970
DisposeEventHandlerUPP(self->eventHandlerUPP);
971
RemoveEventHandler(self->eventHandlerRef);
977
static PRBool _md_WasInputPending = PR_FALSE;
978
static PRUint32 _md_InputTime = 0;
979
static PRBool _md_WasPaintPending = PR_FALSE;
980
static PRUint32 _md_PaintTime = 0;
981
/* last mouse location */
982
static POINT _md_LastMousePos;
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
******************************************************************************/
990
static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
992
PREventQueue* queue = (PREventQueue *) GetProp(hwnd, _md_GetEventQueuePropName());
993
PR_ASSERT(queue != NULL);
995
KillTimer(hwnd, TIMER_ID);
996
queue->timerSet = PR_FALSE;
997
queue->removeMsg = PR_FALSE;
998
PL_ProcessPendingEvents( queue );
999
queue->removeMsg = PR_TRUE;
1002
static PRBool _md_IsWIN9X = PR_FALSE;
1003
static PRBool _md_IsOSSet = PR_FALSE;
1005
static void _md_DetermineOSType()
1008
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1010
if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) {
1011
_md_IsWIN9X = PR_TRUE;
1015
static PRUint32 _md_GetPaintStarvationLimit()
1017
if (! _md_IsOSSet) {
1018
_md_DetermineOSType();
1019
_md_IsOSSet = PR_TRUE;
1023
return WIN9X_PAINT_STARVATION_LIMIT;
1026
return PAINT_STARVATION_LIMIT;
1031
* Determine if an event is being starved (i.e the starvation limit has
1033
* Note: this function uses the current setting and updates the contents
1034
* of the wasPending and lastTime arguments
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
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
1047
static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit,
1048
PRBool *wasPending, PRUint32 *lastTime,
1049
PRUint32 currentTime)
1051
if (*wasPending && isPending) {
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
1057
if ((currentTime - *lastTime) > starvationLimit) {
1058
return PR_TRUE; /* pending and over the limit */
1061
return PR_FALSE; /* pending but within the limit */
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
1070
*lastTime = currentTime;
1071
*wasPending = PR_TRUE;
1075
/* Event is no longer pending */
1076
*wasPending = PR_FALSE;
1080
/* Determines if the there is a pending Mouse or input event */
1082
static PRBool _md_IsInputPending(WORD qstatus)
1084
/* Return immediately there aren't any pending input or paints. */
1089
/* Is there anything other than a QS_MOUSEMOVE pending? */
1090
if ((qstatus & QS_MOUSEBUTTON) ||
1091
(qstatus & QS_KEY) ||
1092
(qstatus & QS_HOTKEY)) {
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.
1102
if (qstatus & QS_MOUSEMOVE) {
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 */
1110
/* Real mouse move */
1111
_md_LastMousePos.x = cursorPos.x;
1112
_md_LastMousePos.y = cursorPos.y;
1120
_pl_NativeNotify(PLEventQueue* self)
1125
PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
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.
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;
1140
qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT));
1142
/* Check for starved input */
1143
if (_md_EventIsStarved( _md_IsInputPending(qstatus),
1144
INPUT_STARVATION_LIMIT,
1145
&_md_WasInputPending,
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.
1153
SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1154
self->timerSet = PR_TRUE;
1157
* Clear any pending paint. _md_WasInputPending was cleared in
1158
* _md_EventIsStarved.
1160
_md_WasPaintPending = PR_FALSE;
1164
if (_md_EventIsStarved( (qstatus & QS_PAINT),
1165
_md_GetPaintStarvationLimit(),
1166
&_md_WasPaintPending,
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
1174
SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc);
1175
self->timerSet = PR_TRUE;
1178
* Clear any pending input. _md_WasPaintPending was cleared in
1179
* _md_EventIsStarved.
1181
_md_WasInputPending = PR_FALSE;
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
1191
PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
1192
(WPARAM)0, (LPARAM)self );
1195
}/* --- end _pl_NativeNotify() --- */
1201
_pl_NativeNotify(PLEventQueue* self)
1203
BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
1205
return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
1206
}/* --- end _pl_NativeNotify() --- */
1210
/* Just set the event flag */
1212
_pl_NativeNotify(PLEventQueue* self)
1214
unsigned int status;
1215
PR_LOG(event_lm, PR_LOG_DEBUG,
1216
("_pl_NativeNotify: self=%p efn=%d",
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)
1224
_pl_NativeNotify(PLEventQueue* self)
1226
#define NOTIFY_TOKEN 0xFA
1228
unsigned char buf[] = { NOTIFY_TOKEN };
1230
PR_LOG(event_lm, PR_LOG_DEBUG,
1231
("_pl_NativeNotify: self=%p",
1233
count = write(self->eventPipe[1], buf, 1);
1236
if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
1239
}/* --- end _pl_NativeNotify() --- */
1240
#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1242
#if defined(XP_BEOS)
1243
struct ThreadInterfaceData
1250
_pl_NativeNotify(PLEventQueue* self)
1252
struct ThreadInterfaceData id;
1255
write_port(self->eventport, 'natv', &id, sizeof(id));
1257
return PR_SUCCESS; /* Is this correct? */
1259
#endif /* XP_BEOS */
1261
#if defined(XP_MAC) || defined(XP_MACOSX)
1263
_pl_NativeNotify(PLEventQueue* self)
1265
#if defined(MAC_USE_CARBON_EVENT)
1268
if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents,
1269
0, kEventAttributeNone, &newEvent) != noErr)
1271
err = SetEventParameter(newEvent, kEventParamPLEventQueue,
1272
typeUInt32, sizeof(PREventQueue*), &self);
1274
err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow);
1275
ReleaseEvent(newEvent);
1279
#elif defined(MAC_USE_WAKEUPPROCESS)
1280
WakeUpProcess(&self->psn);
1284
#endif /* defined(XP_MAC) || defined(XP_MACOSX) */
1287
_pl_AcknowledgeNativeNotify(PLEventQueue* self)
1289
#if defined(_WIN32) || defined(XP_OS2)
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
1301
if (self->removeMsg) {
1302
PR_LOG(event_lm, PR_LOG_DEBUG,
1303
("_pl_AcknowledgeNativeNotify: self=%p", self));
1305
WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow,
1306
_pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
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;
1318
PR_LOG(event_lm, PR_LOG_DEBUG,
1319
("_pl_AcknowledgeNativeNotify: self=%p efn=%d",
1322
** If this is the last entry, then clear the event flag. Also make sure
1323
** the flag is cleared on any spurious wakeups.
1325
sys$clref(self->efn);
1327
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1331
PR_LOG(event_lm, PR_LOG_DEBUG,
1332
("_pl_AcknowledgeNativeNotify: self=%p",
1334
/* consume the byte NativeNotify put in our pipe: */
1335
count = read(self->eventPipe[0], &c, 1);
1336
if ((count == 1) && (c == NOTIFY_TOKEN))
1338
if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
1344
#pragma unused (self)
1347
/* nothing to do on the other platforms */
1352
PR_IMPLEMENT(PRInt32)
1353
PL_GetEventQueueSelectFD(PLEventQueue* self)
1359
return -(self->efn);
1360
#elif defined(XP_UNIX) && !defined(XP_MACOSX)
1361
return self->eventPipe[0];
1363
return -1; /* other platforms don't handle this (yet) */
1367
PR_IMPLEMENT(PRBool)
1368
PL_IsQueueOnCurrentThread( PLEventQueue *queue )
1370
PRThread *me = PR_GetCurrentThread();
1371
return me == queue->handlerThread;
1375
PL_IsQueueNative(PLEventQueue *queue)
1377
return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
1382
** Global Instance handle...
1383
** In Win32 this is the module handle of the DLL.
1386
static HINSTANCE _pr_hInstance;
1393
** Initialization routine for the DLL...
1396
BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
1400
case DLL_PROCESS_ATTACH:
1401
_pr_hInstance = hDLL;
1404
case DLL_THREAD_ATTACH:
1407
case DLL_THREAD_DETACH:
1410
case DLL_PROCESS_DETACH:
1411
_pr_hInstance = NULL;
1420
#if defined(_WIN32) || defined(XP_OS2)
1423
_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
1426
_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1429
if (_pr_PostEventMsgId == uMsg )
1431
PREventQueue *queue = (PREventQueue *)lParam;
1432
queue->removeMsg = PR_FALSE;
1433
PL_ProcessPendingEvents(queue);
1434
queue->removeMsg = PR_TRUE;
1436
return MRFROMLONG(TRUE);
1441
return DefWindowProc(hwnd, uMsg, wParam, lParam);
1444
static PRBool isInitialized;
1445
static PRCallOnceType once;
1446
static PRLock *initLock;
1449
** InitWinEventLib() -- Create the Windows initialization lock
1452
static PRStatus InitEventLib( void )
1454
PR_ASSERT( initLock == NULL );
1456
initLock = PR_NewLock();
1457
return initLock ? PR_SUCCESS : PR_FAILURE;
1460
#endif /* Win32, OS2 */
1465
** _md_CreateEventQueue() -- ModelDependent initializer
1467
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1472
** If this is the first call to PL_InitializeEventsLib(),
1473
** make the call to InitWinEventLib() to create the initLock.
1475
** Then lock the initializer lock to insure that
1476
** we have exclusive control over the initialization sequence.
1481
/* Register the windows message for XPCOM Event notification */
1482
_pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent");
1484
/* Register the class for the event receiver window */
1485
if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
1487
wc.lpfnWndProc = _md_EventReceiverProc;
1490
wc.hInstance = _pr_hInstance;
1493
wc.hbrBackground = (HBRUSH) NULL;
1494
wc.lpszMenuName = (LPCSTR) NULL;
1495
wc.lpszClassName = _pr_eventWindowClass;
1499
/* Create the event receiver window */
1500
eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
1501
"XPCOM:EventReceiver",
1503
NULL, NULL, _pr_hInstance,
1505
PR_ASSERT(eventQueue->eventReceiverWindow);
1506
/* Set a property which can be used to retrieve the event queue
1507
* within the _md_TimerProc callback
1509
SetProp(eventQueue->eventReceiverWindow,
1510
_md_GetEventQueuePropName(), (HANDLE)eventQueue);
1513
} /* end _md_CreateEventQueue() */
1518
** _md_CreateEventQueue() -- ModelDependent initializer
1520
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1522
/* Must have HMQ for this & can't assume we already have appshell */
1523
if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
1530
/* Set our app to be a PM app before attempting Win calls */
1531
DosGetInfoBlocks(&ptib, &ppib);
1532
ppib->pib_ultype = 3;
1534
hab = WinInitialize(0);
1535
hmq = WinCreateMsgQueue(hab, 0);
1539
if( !_pr_PostEventMsgId)
1541
WinRegisterClass( 0 /* hab_current */,
1542
_pr_eventWindowClass,
1543
_md_EventReceiverProc,
1546
_pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
1550
eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
1551
_pr_eventWindowClass,
1559
PR_ASSERT(eventQueue->eventReceiverWindow);
1562
} /* end _md_CreateEventQueue() */
1565
#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS)
1567
** _md_CreateEventQueue() -- ModelDependent initializer
1569
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1571
/* there's really nothing special to do here,
1572
** the guts of the unix stuff is in the setupnativenotify
1573
** and related functions.
1576
} /* end _md_CreateEventQueue() */
1577
#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */
1579
#if defined(MAC_USE_CARBON_EVENT)
1581
** _md_CreateEventQueue() -- ModelDependent initializer
1584
static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler,
1588
if (GetEventClass(inEvent) == kEventClassPL &&
1589
GetEventKind(inEvent) == kEventProcessPLEvents)
1591
PREventQueue *queue;
1592
if (GetEventParameter(inEvent, kEventParamPLEventQueue,
1593
typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1596
PL_ProcessPendingEvents(queue);
1600
return eventNotHandledErr;
1603
static pascal Boolean _md_CarbonEventComparator(EventRef inEvent,
1604
void *inCompareData)
1606
Boolean match = false;
1608
if (GetEventClass(inEvent) == kEventClassPL &&
1609
GetEventKind(inEvent) == kEventProcessPLEvents)
1611
PREventQueue *queue;
1612
match = ((GetEventParameter(inEvent, kEventParamPLEventQueue,
1613
typeUInt32, NULL, sizeof(PREventQueue*), NULL,
1614
&queue) == noErr) && (queue == inCompareData));
1619
#endif /* defined(MAC_USE_CARBON_EVENT) */
1621
#if defined(XP_MAC) || defined(XP_MACOSX)
1622
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
1624
#if defined(MAC_USE_CARBON_EVENT)
1625
eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc);
1626
PR_ASSERT(eventQueue->eventHandlerUPP);
1627
if (eventQueue->eventHandlerUPP)
1629
EventTypeSpec eventType;
1631
eventType.eventClass = kEventClassPL;
1632
eventType.eventKind = kEventProcessPLEvents;
1634
InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType,
1635
eventQueue, &eventQueue->eventHandlerRef);
1636
PR_ASSERT(eventQueue->eventHandlerRef);
1638
#elif defined(MAC_USE_WAKEUPPROCESS)
1639
OSErr err = GetCurrentProcess(&eventQueue->psn);
1640
PR_ASSERT(err == noErr);
1642
} /* end _md_CreateEventQueue() */
1643
#endif /* defined(XP_MAC) || defined(XP_MACOSX) */
1645
/* extra functions for unix */
1647
#if defined(XP_UNIX) && !defined(XP_MACOSX)
1649
PR_IMPLEMENT(PRInt32)
1650
PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID)
1658
PR_EnterMonitor(aSelf->monitor);
1660
if (aSelf->processingEvents) {
1661
PR_ExitMonitor(aSelf->monitor);
1665
aSelf->processingEvents = PR_TRUE;
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
1671
fullCount = _pl_GetEventCount(aSelf);
1672
PR_LOG(event_lm, PR_LOG_DEBUG,
1673
("$$$ fullCount is %d id is %ld\n", fullCount, aID));
1675
if (fullCount == 0) {
1676
aSelf->processingEvents = PR_FALSE;
1677
PR_ExitMonitor(aSelf->monitor);
1681
PR_ExitMonitor(aSelf->monitor);
1683
while (fullCount-- > 0) {
1684
/* peek at the next event */
1686
event = PR_EVENT_PTR(aSelf->queue.next);
1689
PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n",
1691
if (event->id >= aID) {
1692
PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking"));
1696
event = PL_GetEvent(aSelf);
1697
PL_HandleEvent(event);
1698
PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
1702
PR_EnterMonitor(aSelf->monitor);
1704
/* if full count still had items left then there's still items left
1705
in the queue. Let the native notify token stay. */
1707
if (aSelf->type == EventQueueIsNative) {
1708
fullCount = _pl_GetEventCount(aSelf);
1710
if (fullCount <= 0) {
1711
_pl_AcknowledgeNativeNotify(aSelf);
1712
aSelf->notified = PR_FALSE;
1716
aSelf->processingEvents = PR_FALSE;
1718
PR_ExitMonitor(aSelf->monitor);
1724
PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
1727
aSelf->idFunc = aFunc;
1728
aSelf->idFuncClosure = aClosure;
1732
PL_UnregisterEventIDFunc(PLEventQueue *aSelf)
1735
aSelf->idFuncClosure = 0;
1738
#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */
1740
/* --- end plevent.c --- */