/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 * 
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 * 
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#if defined(_WIN32) || defined(WIN16)
#include <windows.h>
#endif

#if defined(XP_OS2)
#define INCL_WIN
#include <os2.h>
#define DefWindowProc WinDefWindowProc
typedef MPARAM WPARAM,LPARAM;
#endif /* XP_OS2 */

#include "nspr.h"
#include "plevent.h"

#if !defined(WIN32)
#include <errno.h>
#include <stddef.h>
#if !defined(XP_OS2)
#include <unistd.h>
#endif /* !XP_OS2 */
#endif /* !Win32 */

#if defined(XP_UNIX)
/* for fcntl */
#include <sys/types.h>
#include <fcntl.h>
#endif

#if defined(XP_BEOS)
#include <kernel/OS.h>
#endif

#if defined(RHAPSODY)
#undef XP_UNIX
#define XP_MAC 1
#endif

#if defined(XP_MAC)
#include <AppleEvents.h>
#if !defined(RHAPSODY)
#include "pprthred.h"
#endif
#else
#include "private/pprthred.h"
#endif /* XP_MAC */

#if defined(VMS)
/*
** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it 
** wants an event flag. So, we don't create and use a pipe for 
** notification of when an event queue has something ready, instead
** we use an event flag. Shouldn't be a problem if we only have
** a few event queues.
*/
#include <lib$routines.h>
#include <starlet.h>
#include <stsdef.h>
#endif /* VMS */


static PRLogModuleInfo *event_lm = NULL;

/*******************************************************************************
 * Private Stuff
 ******************************************************************************/

/*
** EventQueueType -- Defines notification type for an event queue
**
*/
typedef enum 
{
    EventQueueIsNative = 1,
    EventQueueIsMonitored = 2
} EventQueueType;


struct PLEventQueue {
    char*        name;
    PRCList      queue;
    PRMonitor*   monitor;
    PRThread*    handlerThread;
    EventQueueType type;
    PRBool       processingEvents;
#if defined(VMS)
    int		 efn;
    int		 notifyCount;
#elif defined(XP_UNIX)
    PRInt32      eventPipe[2];
	int          notifyCount;
#elif defined(_WIN32) || defined(WIN16)
    HWND         eventReceiverWindow;
    PRBool       removeMsg;
#elif defined(XP_OS2)
    HWND         eventReceiverWindow;
#elif defined(XP_BEOS)
    port_id      eventport;
#endif
};

#define PR_EVENT_PTR(_qp) \
    ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link)))

static PRStatus    _pl_SetupNativeNotifier(PLEventQueue* self);
static void        _pl_CleanupNativeNotifier(PLEventQueue* self);
static PRStatus    _pl_NativeNotify(PLEventQueue* self);
static PRStatus    _pl_AcknowledgeNativeNotify(PLEventQueue* self);
static void        _md_CreateEventQueue( PLEventQueue *eventQueue );
static PRInt32     _pl_GetEventCount(PLEventQueue* self);


#if defined(_WIN32) || defined(WIN16) || defined(XP_OS2)
#if defined(XP_OS2)
ULONG _pr_PostEventMsgId;
static char *_pr_eventWindowClass = "NSPR:EventWindow";
#else
UINT _pr_PostEventMsgId;
static char *_pr_eventWindowClass = "NSPR:EventWindow";
#endif /* OS2 */
#endif /* Win32, Win16, OS2 */

/*******************************************************************************
 * Event Queue Operations
 ******************************************************************************/

/*
** _pl_CreateEventQueue() -- Create the event queue
**
**
*/
static PLEventQueue *
    _pl_CreateEventQueue(
        char *name,
        PRThread *handlerThread,
        EventQueueType  qtype
)
{
    PRStatus err;
    PLEventQueue* self = NULL;
    PRMonitor* mon = NULL;

    if (event_lm == NULL)
        event_lm = PR_NewLogModule("event");

    self = PR_NEWZAP(PLEventQueue);
    if (self == NULL) return NULL;

    mon = PR_NewNamedMonitor(name);
    if (mon == NULL) goto error;

    self->name = name;
    self->monitor = mon;
    self->handlerThread = handlerThread;
    self->processingEvents = PR_FALSE;
    self->type = qtype;
#if defined(_WIN32) || defined(WIN16)
    self->removeMsg = PR_TRUE;
#endif
    PR_INIT_CLIST(&self->queue);
    if ( qtype == EventQueueIsNative ) {
        err = _pl_SetupNativeNotifier(self);
        if (err) goto error;
        _md_CreateEventQueue( self );
    }
    return self;

  error:
    if (mon != NULL)
        PR_DestroyMonitor(mon);
    PR_DELETE(self);
    return NULL;
} /* end _pl_CreateEventQueue() */


PR_IMPLEMENT(PLEventQueue*)
PL_CreateEventQueue(char* name, PRThread* handlerThread)
{
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
}

PR_EXTERN(PLEventQueue *) 
    PL_CreateNativeEventQueue(
        char *name, 
        PRThread *handlerThread
    )
{
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative ));
} /* --- end PL_CreateNativeEventQueue() --- */

PR_EXTERN(PLEventQueue *) 
    PL_CreateMonitoredEventQueue(
        char *name,
        PRThread *handlerThread
    )
{
    return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored ));
} /* --- end PL_CreateMonitoredEventQueue() --- */



PR_IMPLEMENT(PRMonitor*)
PL_GetEventQueueMonitor(PLEventQueue* self)
{
    return self->monitor;
}

static void PR_CALLBACK
_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue)
{
#ifdef XP_MAC
#pragma unused (data, queue)
#endif
    PL_DequeueEvent(event, queue);
    PL_DestroyEvent(event);
}

PR_IMPLEMENT(void)
PL_DestroyEventQueue(PLEventQueue* self)
{
    PR_EnterMonitor(self->monitor);

    /* destroy undelivered events */
    PL_MapEvents(self, _pl_destroyEvent, NULL);

    if ( self->type == EventQueueIsNative )
        _pl_CleanupNativeNotifier(self);

    /* destroying the monitor also destroys the name */
    PR_ExitMonitor(self->monitor);
    PR_DestroyMonitor(self->monitor);
    PR_DELETE(self);

}

PR_IMPLEMENT(PRStatus)
PL_PostEvent(PLEventQueue* self, PLEvent* event)
{
    PRStatus err = PR_SUCCESS;
    PRMonitor* mon;

    if (self == NULL)
    return PR_FAILURE;

    mon = self->monitor;
    PR_EnterMonitor(mon);

    /* insert event into thread's event queue: */
    if (event != NULL) {
    PR_APPEND_LINK(&event->link, &self->queue);
    }

    /* notify even if event is NULL */
    if ( self->type == EventQueueIsNative )
        err = _pl_NativeNotify(self);

    /*
     * This may fall on deaf ears if we're really notifying the native 
     * thread, and no one has called PL_WaitForEvent (or PL_EventLoop):
     */
    err = PR_Notify(mon);
    PR_ExitMonitor(mon);
    return err;
}

PR_IMPLEMENT(void*)
PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event)
{
    void* result;

    if (self == NULL)
    return NULL;

    PR_ASSERT(event != NULL);
    PR_CEnterMonitor(event);

    if (PR_CurrentThread() == self->handlerThread) {
	/* Handle the case where the thread requesting the event handling
	   is also the thread that's supposed to do the handling. */
	result = event->handler(event);
    }
    else {
	int i, entryCount = PR_GetMonitorEntryCount(self->monitor);

	event->synchronousResult = (void*)PR_TRUE;
	PL_PostEvent(self, event);
	/* We need to temporarily give up our event queue monitor if
	   we're holding it, otherwise, the thread we're going to wait
	   for notification from won't be able to enter it to process
	   the event. */
	if (entryCount) {
	    for (i = 0; i < entryCount; i++)
		PR_ExitMonitor(self->monitor);
	}
	PR_CWait(event, PR_INTERVAL_NO_TIMEOUT);	/* wait for event to be handled or destroyed */
	if (entryCount) {
	    for (i = 0; i < entryCount; i++)
		PR_EnterMonitor(self->monitor);
	}
    	result = event->synchronousResult;
	event->synchronousResult = NULL;
    }

    PR_CExitMonitor(event);

    /* For synchronous events, they're destroyed here on the caller's
       thread before the result is returned. See PL_HandleEvent. */
    PL_DestroyEvent(event);

    return result;
}

PR_IMPLEMENT(PLEvent*)
PL_GetEvent(PLEventQueue* self)
{
    PLEvent* event = NULL;
    PRStatus err = PR_SUCCESS;

    if (self == NULL)
    return NULL;

    PR_EnterMonitor(self->monitor);

    if (!PR_CLIST_IS_EMPTY(&self->queue)) {
      if ( self->type == EventQueueIsNative )
        err = _pl_AcknowledgeNativeNotify(self);

      if (err) goto done;

      /* then grab the event and return it: */
      event = PR_EVENT_PTR(self->queue.next);
      PR_REMOVE_AND_INIT_LINK(&event->link);
    }

  done:
    PR_ExitMonitor(self->monitor);
    return event;
}

PR_IMPLEMENT(PRBool)
PL_EventAvailable(PLEventQueue* self)
{
    PRBool result = PR_FALSE;

    if (self == NULL)
    return PR_FALSE;

    PR_EnterMonitor(self->monitor);

    if (!PR_CLIST_IS_EMPTY(&self->queue)) 
    result = PR_TRUE;

    PR_ExitMonitor(self->monitor);
    return result;
}

PR_IMPLEMENT(void)
PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data)
{
    PRCList* qp;

    if (self == NULL)
    return;

    PR_EnterMonitor(self->monitor);
    qp = self->queue.next;
    while (qp != &self->queue) {
    PLEvent* event = PR_EVENT_PTR(qp);
    qp = qp->next;
    (*fun)(event, data, self);
    }
    PR_ExitMonitor(self->monitor);
}

static void PR_CALLBACK
_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue)
{
    PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0);
    if (event->owner == owner) {
    PR_LOG(event_lm, PR_LOG_DEBUG,
           ("$$$ \tdestroying event %0x for owner %0x", event, owner));
    PL_DequeueEvent(event, queue);
    if (event->synchronousResult == (void*)PR_TRUE) {
        PR_CEnterMonitor(event);
        event->synchronousResult = NULL;
        PR_CNotify(event);
        PR_CExitMonitor(event);
    }
    else {
        PL_DestroyEvent(event);
    }
    }
    else {
    PR_LOG(event_lm, PR_LOG_DEBUG,
           ("$$$ \tskipping event %0x for owner %0x", event, owner));
    }
}

PR_IMPLEMENT(void)
PL_RevokeEvents(PLEventQueue* self, void* owner)
{
    if (self == NULL)
    return;

    PR_LOG(event_lm, PR_LOG_DEBUG,
         ("$$$ revoking events for owner %0x", owner));

    /*
    ** First we enter the monitor so that no one else can post any events
    ** to the queue:
    */
    PR_EnterMonitor(self->monitor);
    PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner));

    /*
    ** Discard any pending events for this owner:
    */
    PL_MapEvents(self, _pl_DestroyEventForOwner, owner);

#ifdef DEBUG
    {
	PRCList* qp = self->queue.next;
	while (qp != &self->queue) {
	    PLEvent* event = PR_EVENT_PTR(qp);
	    qp = qp->next;
	    PR_ASSERT(event->owner != owner);
	}
    }
#endif /* DEBUG */

    PR_ExitMonitor(self->monitor);

    PR_LOG(event_lm, PR_LOG_DEBUG,
           ("$$$ revoking events for owner %0x", owner));
}

static PRInt32
_pl_GetEventCount(PLEventQueue* self)
{
    PRCList* node;
    PRInt32  count = 0;

    PR_EnterMonitor(self->monitor);
    node = PR_LIST_HEAD(&self->queue);
    while (node != &self->queue) {
        count++;
        node = PR_NEXT_LINK(node);
    }
    PR_ExitMonitor(self->monitor);

    return count;
}

PR_IMPLEMENT(void)
PL_ProcessPendingEvents(PLEventQueue* self)
{
    PRInt32 count;

#ifdef XP_UNIX
    PRCList* node;
#endif

    if (self == NULL)
      return;

    if (self->processingEvents) {
#ifdef XP_UNIX /* because this appears necessary only for unix and scares me */
      /* if we're called re-entrantly many times, the notification pipe
         must be allowed to empty or the fool thing will notify us
         so anxiously that the CPU will peg and system events will be
         drowned out. */
      if (self->notifyCount > 0 && self->type == EventQueueIsNative)
        _pl_AcknowledgeNativeNotify(self);
#endif
      return;
    }

    self->processingEvents = PR_TRUE;

    /* Only process the events that are already in the queue, and
     * not any new events that get added. Do this by counting the
     * number of events currently in the queue
     */
    count = _pl_GetEventCount(self);
    while (count-- > 0) {
      PLEvent* event = PL_GetEvent(self);
      if (event == NULL)
        break;

      PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
      PL_HandleEvent(event);
      PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
    }

#ifdef XP_UNIX
    /* now restore any notifications that we destroyed re-entrantly, above */
    PR_EnterMonitor(self->monitor);
    if (self->type == EventQueueIsNative) {
      count = self->notifyCount;
      node = PR_LIST_HEAD(&self->queue);
      while (node != &self->queue) {
        if (--count < 0)
          _pl_NativeNotify(self);
        node = PR_NEXT_LINK(node);
      }
    }
    PR_ExitMonitor(self->monitor);
#endif

    self->processingEvents = PR_FALSE;
}

/*******************************************************************************
 * Event Operations
 ******************************************************************************/

PR_IMPLEMENT(void)
PL_InitEvent(PLEvent* self, void* owner,
         PLHandleEventProc handler,
         PLDestroyEventProc destructor)
{
    PR_INIT_CLIST(&self->link);
    self->handler = handler;
    self->destructor = destructor;
    self->owner = owner;
    self->synchronousResult = NULL;
}

PR_IMPLEMENT(void*)
PL_GetEventOwner(PLEvent* self)
{
    return self->owner;
}

PR_IMPLEMENT(void)
PL_HandleEvent(PLEvent* self)
{
    void* result;

    if (self == NULL)
        return;

    /* This event better not be on an event queue anymore. */
    PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));

    result = (*self->handler)(self);
    if (NULL != self->synchronousResult) {
    PR_CEnterMonitor(self);
    self->synchronousResult = result;
    PR_CNotify(self);    /* wake up the guy waiting for the result */
    PR_CExitMonitor(self);
    }
    else {
    /* For asynchronous events, they're destroyed by the event-handler
       thread. See PR_PostSynchronousEvent. */
    PL_DestroyEvent(self);
    }
}

PR_IMPLEMENT(void)
PL_DestroyEvent(PLEvent* self)
{
    if (self == NULL)
    return;

    /* This event better not be on an event queue anymore. */
    PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link));

    (*self->destructor)(self);
}

PR_IMPLEMENT(void)
PL_DequeueEvent(PLEvent* self, PLEventQueue* queue)
{
#ifdef XP_MAC
#pragma unused (queue)
#endif
    if (self == NULL)
    return;

    /* Only the owner is allowed to dequeue events because once the 
       client has put it in the queue, they have no idea whether it's 
       been processed and destroyed or not. */
/*    PR_ASSERT(queue->handlerThread == PR_CurrentThread());*/

    PR_EnterMonitor(queue->monitor);

    PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link));
    PR_REMOVE_AND_INIT_LINK(&self->link);

    PR_ExitMonitor(queue->monitor);
}

/*******************************************************************************
 * Pure Event Queues
 *
 * For when you're only processing PLEvents and there is no native
 * select, thread messages, or AppleEvents.
 ******************************************************************************/

PR_IMPLEMENT(PLEvent*)
PL_WaitForEvent(PLEventQueue* self)
{
    PLEvent* event;
    PRMonitor* mon;

    if (self == NULL)
    return NULL;

    mon = self->monitor;
    PR_EnterMonitor(mon);

    while ((event = PL_GetEvent(self)) == NULL) {
        PRStatus err;
        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event"));
        err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
        if ((err == PR_FAILURE)
            && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
    }

    PR_ExitMonitor(mon);
    return event;
}

PR_IMPLEMENT(void)
PL_EventLoop(PLEventQueue* self)
{
    if (self == NULL)
    return;

    while (PR_TRUE) {
    PLEvent* event = PL_WaitForEvent(self);
    if (event == NULL) {
        /* This can only happen if the current thread is interrupted */
        return;
    }

        PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event"));
    PL_HandleEvent(event);
    PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event"));
    }
}

/*******************************************************************************
 * Native Event Queues
 *
 * For when you need to call select, or WaitNextEvent, and yet also want
 * to handle PLEvents.
 ******************************************************************************/

static PRStatus
_pl_SetupNativeNotifier(PLEventQueue* self)
{
#if defined(XP_MAC)
#pragma unused (self)
#endif

#if defined(VMS)
    {
        unsigned int status;
        status = LIB$GET_EF(&self->efn);
        if (!$VMS_STATUS_SUCCESS(status))
            return PR_FAILURE;
        PR_LOG(event_lm, PR_LOG_DEBUG,
           ("$$$ Allocated event flag %d", self->efn));
        return PR_SUCCESS;
    }
#elif defined(XP_UNIX)
    int err;
    int flags;

    err = pipe(self->eventPipe);
    if (err != 0) {
        return PR_FAILURE;
    }

    /* make the pipe nonblocking */
    flags = fcntl(self->eventPipe[0], F_GETFL, 0);
    if (flags == -1) {
        goto failed;
    }
    err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK);
    if (err == -1) {
        goto failed;
    }
    flags = fcntl(self->eventPipe[1], F_GETFL, 0);
    if (flags == -1) {
        goto failed;
    }
    err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK);
    if (err == -1) {
        goto failed;
    }
    return PR_SUCCESS;

failed:
    close(self->eventPipe[0]);
    close(self->eventPipe[1]);
    return PR_FAILURE;
#elif defined(XP_BEOS)
	/* hook up to the nsToolkit queue, however the appshell
	 * isn't necessairly started, so we might have to create
	 * the queue ourselves
	 */
	char		portname[64];
	char		semname[64];
	sprintf(portname, "event%lx", self->handlerThread);
	sprintf(semname, "sync%lx", self->handlerThread);

	if((self->eventport = find_port(portname)) < 0)
	{
		/* create port
		 */
		self->eventport = create_port(100, portname);

		/* We don't use the sem, but it has to be there
		 */
		create_sem(0, semname);
	}

    return PR_SUCCESS;
#else
    return PR_SUCCESS;
#endif
}

static void
_pl_CleanupNativeNotifier(PLEventQueue* self)
{
#if defined(XP_MAC)
#pragma unused (self)
#endif

#if defined(VMS)
    {
        unsigned int status;
        PR_LOG(event_lm, PR_LOG_DEBUG,
           ("$$$ Freeing event flag %d", self->efn));
        status = LIB$FREE_EF(&self->efn);
    }
#elif defined(XP_UNIX)
    close(self->eventPipe[0]);
    close(self->eventPipe[1]);
#elif defined(_WIN32) || defined(WIN16)
    DestroyWindow(self->eventReceiverWindow);
#endif
}

#if defined(_WIN32) || defined(WIN16)
static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
	PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId,
	    (WPARAM)0, (LPARAM)self);
    return PR_SUCCESS;
}/* --- end _pl_NativeNotify() --- */
#endif

#if defined(XP_OS2)
static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
    BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId,
                       0, MPFROMP(self));
    return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE;
}/* --- end _pl_NativeNotify() --- */
#endif /* XP_OS2 */

#if defined(VMS)
/* Just set the event flag */
static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
        unsigned int status;
        PR_LOG(event_lm, PR_LOG_DEBUG,
           ("_pl_NativeNotify: self=%p notifyCount=%d efn=%d",
            self, self->notifyCount, self->efn));
        self->notifyCount++;
        status = SYS$SETEF(self->efn);
        if ($VMS_STATUS_SUCCESS(status))
            return PR_SUCCESS;
        else
            return PR_FAILURE;
}/* --- end _pl_NativeNotify() --- */
#elif defined(XP_UNIX)
static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
#define NOTIFY_TOKEN    0xFA
    PRInt32 count;
    unsigned char buf[] = { NOTIFY_TOKEN };

    PR_LOG(event_lm, PR_LOG_DEBUG,
           ("_pl_NativeNotify: self=%p notifyCount=%d",
            self, self->notifyCount));
    count = write(self->eventPipe[1], buf, 1);
	if (count == 1) {
		self->notifyCount++;
		return PR_SUCCESS;
	} else if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
		return PR_SUCCESS;
	} else
		return PR_FAILURE;
}/* --- end _pl_NativeNotify() --- */
#endif /* XP_UNIX */

#if defined(XP_BEOS)
struct ThreadInterfaceData
{
	void	*data;
	int32	sync;
};

static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
	struct ThreadInterfaceData	 id;
	id.data = self;
	id.sync = false;
	write_port(self->eventport, 'natv', &id, sizeof(id));

    return PR_SUCCESS;    /* Is this correct? */
}
#endif /* XP_BEOS */

#if defined(XP_MAC)
static PRStatus
_pl_NativeNotify(PLEventQueue* self)
{
#pragma unused (self)
    return PR_SUCCESS;    /* XXX can fail? */
}
#endif /* XP_MAC */

static PRStatus
_pl_AcknowledgeNativeNotify(PLEventQueue* self)
{
#if defined(_WIN32) || defined(WIN16)
    MSG aMsg;
    /*
     * only remove msg when we've been called directly by
     * PL_ProcessPendingEvents, not when we've been called by
     * the window proc because the window proc will remove the
     * msg for us.
     */
    if (self->removeMsg) {
        PR_LOG(event_lm, PR_LOG_DEBUG,
                ("_pl_AcknowledgeNativeNotify: self=%p", self));
        PeekMessage(&aMsg, self->eventReceiverWindow,
                _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE);
    }
    return PR_SUCCESS;
#elif defined(VMS)
    PR_LOG(event_lm, PR_LOG_DEBUG,
            ("_pl_AcknowledgeNativeNotify: self=%p notifyCount=%d efn=%d",
             self, self->notifyCount, self->efn));
    /*
    ** If this is the last entry, then clear the event flag. Also make sure
    ** the flag is cleared on any spurious wakeups.
    */
    if (self->notifyCount <= 1)
        sys$clref(self->efn);

    if (self->notifyCount <= 0)
        return PR_SUCCESS;

    self->notifyCount--;

    return PR_SUCCESS;
#elif defined(XP_UNIX)

    PRInt32 count;
    unsigned char c;

    PR_LOG(event_lm, PR_LOG_DEBUG,
            ("_pl_AcknowledgeNativeNotify: self=%p notifyCount=%d",
             self, self->notifyCount));
	if (self->notifyCount <= 0) return PR_SUCCESS;
    /* consume the byte NativeNotify put in our pipe: */
    count = read(self->eventPipe[0], &c, 1);
	if ((count == 1) && (c == NOTIFY_TOKEN)) {
		self->notifyCount--;
		return PR_SUCCESS;
	} else if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
		return PR_SUCCESS;
	} else
		return PR_FAILURE;
#else

#if defined(XP_MAC)
#pragma unused (self)
#endif

    /* nothing to do on the other platforms */
    return PR_SUCCESS;
#endif
}

PR_IMPLEMENT(PRInt32)
PL_GetEventQueueSelectFD(PLEventQueue* self)
{
    if (self == NULL)
    return -1;

#if defined(VMS)
    return -(self->efn);
#elif defined(XP_UNIX)
    return self->eventPipe[0];
#else
    return -1;    /* other platforms don't handle this (yet) */
#endif
}

PR_IMPLEMENT(PRBool)
PL_IsQueueOnCurrentThread( PLEventQueue *queue )
{
    PRThread *me = PR_GetCurrentThread();
    if ( me == queue->handlerThread )
        return PR_TRUE;
    else
        return PR_FALSE;
} /* end PL_IsQueueOnCurrentThread() */

PR_EXTERN(PRBool)
PL_IsQueueNative(PLEventQueue *queue)
{
    return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE;
}

#if defined(WIN16) || defined(_WIN32)
/*
** Global Instance handle...
** In Win32 this is the module handle of the DLL.
**
*/
static HINSTANCE _pr_hInstance;
#endif

#if defined(WIN16)
/*
** Function LibMain() is required by Win16
**
*/
int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, 
                      WORD cbHeapSize, LPSTR lpszCmdLine )
{
    _pr_hInstance = hInst;
    return TRUE;
}
#endif /* WIN16 */

#if defined(_WIN32)

/*
** Initialization routine for the NSPR DLL...
*/

BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
{
  switch (dwReason)
  {
    case DLL_PROCESS_ATTACH:
      _pr_hInstance = hDLL;
      break;

    case DLL_THREAD_ATTACH:
      break;

    case DLL_THREAD_DETACH:
      break;

    case DLL_PROCESS_DETACH:
      _pr_hInstance = NULL;
      break;
  }

    return TRUE;
}
#endif


#if defined(WIN16) || defined(_WIN32) || defined(XP_OS2)
#ifdef XP_OS2
MRESULT EXPENTRY
_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam)
#else
LRESULT CALLBACK 
#if defined(WIN16)
__loadds
#endif
_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
#endif
{
    if (_pr_PostEventMsgId == uMsg )
    {
		PREventQueue *queue = (PREventQueue *)lParam;
    
#if defined(_WIN32) || defined(WIN16)
        queue->removeMsg = PR_FALSE;
#endif
    	PL_ProcessPendingEvents(queue);
#if defined(_WIN32) || defined(WIN16)
        queue->removeMsg = PR_TRUE;
#endif
#ifdef XP_OS2
            return MRFROMLONG(TRUE);
#else
            return TRUE;
#endif
        }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


static PRBool   isInitialized;
static PRCallOnceType once;
static PRLock   *initLock;

/*
** InitWinEventLib() -- Create the Windows initialization lock
**
*/
static PRStatus InitEventLib( void )
{
    PR_ASSERT( initLock == NULL );
    
    initLock = PR_NewLock();
    if ( NULL == initLock )
        return PR_FAILURE;
    else
        return PR_SUCCESS;
} /* end InitWinEventLib() */

#endif /* Win16, Win32, OS2 */

#if defined(_WIN32) || defined(WIN16)
/*
** _md_CreateEventQueue() -- ModelDependent initializer
*/
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
{
    WNDCLASS wc;
    
    /*
    ** If this is the first call to PL_InitializeEventsLib(),
    ** make the call to InitWinEventLib() to create the initLock.
    **
    ** Then lock the initializer lock to insure that
    ** we have exclusive control over the initialization sequence.
    **
    */


    /* Register the windows message for NSPR Event notification */
    _pr_PostEventMsgId = RegisterWindowMessage("NSPR_PostEvent");

    /* Register the class for the event receiver window */
    if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) {
        wc.style         = 0;
        wc.lpfnWndProc   = _md_EventReceiverProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = _pr_hInstance;
        wc.hIcon         = NULL;
        wc.hCursor       = NULL;
        wc.hbrBackground = (HBRUSH) NULL;
        wc.lpszMenuName  = (LPCSTR) NULL;
        wc.lpszClassName = _pr_eventWindowClass;
        RegisterClass(&wc);
        }
        
    /* Create the event receiver window */
    eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass,
                                        "NSPR:EventReceiver",
                                            0, 0, 0, 10, 10,
                                            NULL, NULL, _pr_hInstance,
                                            NULL);
    PR_ASSERT(eventQueue->eventReceiverWindow);

    return;    
} /* end _md_CreateEventQueue() */
#endif /* Winxx */

#if defined(XP_OS2)
/*
** _md_CreateEventQueue() -- ModelDependent initializer
*/
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
{
    /* Must have HMQ for this & can't assume we already have appshell */
    if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0))
    {
       HAB hab = WinInitialize( 0);
       WinCreateMsgQueue( hab, 0);
    }

    if( !_pr_PostEventMsgId)
    {
       WinRegisterClass( 0 /* hab_current */,
                         _pr_eventWindowClass,
                         _md_EventReceiverProc,
                         0, 0);

       _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(),
                                        "NSPR_PostEvent");
    }

    eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP,
                                                       _pr_eventWindowClass,
                                                       "", 0,
                                                       0, 0, 0, 0,
                                                       HWND_DESKTOP,
                                                       HWND_TOP,
                                                       0,
                                                       NULL,
                                                       NULL);
    PR_ASSERT(eventQueue->eventReceiverWindow);

    return;    
} /* end _md_CreateEventQueue() */
#endif /* XP_OS2 */

#if defined(XP_UNIX) || defined(XP_MAC) || defined(XP_BEOS)
/*
** _md_CreateEventQueue() -- ModelDependent initializer
*/
static void _md_CreateEventQueue( PLEventQueue *eventQueue )
{
#ifdef XP_MAC 
#pragma unused(eventQueue) 
#endif 

    /* there's really nothing special to do here,
    ** the guts of the unix stuff is in the setupnativenotify
    ** and related functions.
    */
    return;    
} /* end _md_CreateEventQueue() */
#endif /* XP_UNIX */
/* --- end plevent.c --- */
