зеркало из https://github.com/mozilla/gecko-dev.git
643 строки
18 KiB
C
643 строки
18 KiB
C
/* -*- 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.
|
|
*/
|
|
|
|
/**********************************************************************
|
|
NSPL Events
|
|
|
|
Defining Events
|
|
---------------
|
|
|
|
Events are essentially structures that represent argument lists for a
|
|
function that will run on another thread. All event structures you
|
|
define must include a PLEvent struct as their first field:
|
|
|
|
typedef struct MyEventType {
|
|
PLEvent e;
|
|
// arguments follow...
|
|
int x;
|
|
char* y;
|
|
} MyEventType;
|
|
|
|
It is also essential that you establish a model of ownership for each
|
|
argument passed in an event record, i.e. whether particular arguments
|
|
will be deleted by the event destruction callback, or whether they
|
|
only loaned to the event handler callback, and guaranteed to persist
|
|
until the time at which the handler is called.
|
|
|
|
Sending Events
|
|
--------------
|
|
|
|
Events are initialized by PL_InitEvent and can be sent via
|
|
PL_PostEvent or PL_PostSynchronousEvent. Events can also have an
|
|
owner. The owner of an event can revoke all the events in a given
|
|
event-queue by calling PL_RevokeEvents. An owner might want
|
|
to do this if, for instance, it is being destroyed, and handling the
|
|
events after the owner's destruction would cause an error (e.g. an
|
|
MWContext).
|
|
|
|
Since the act of initializing and posting an event must be coordinated
|
|
with it's possible revocation, it is essential that the event-queue's
|
|
monitor be entered surrounding the code that constructs, initializes
|
|
and posts the event:
|
|
|
|
void postMyEvent(MyOwner* owner, int x, char* y)
|
|
{
|
|
MyEventType* event;
|
|
|
|
PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
|
|
|
|
// construct
|
|
event = PR_NEW(MyEventType);
|
|
if (event == NULL) goto done;
|
|
|
|
// initialize
|
|
PL_InitEvent(event, owner,
|
|
(PLHandleEventProc)handleMyEvent,
|
|
(PLDestroyEventProc)destroyMyEvent);
|
|
event->x = x;
|
|
event->y = strdup(y);
|
|
|
|
// post
|
|
PL_PostEvent(myQueue, &event->e);
|
|
|
|
done:
|
|
PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
|
|
}
|
|
|
|
If you don't call PL_InitEvent and PL_PostEvent within the
|
|
event-queue's monitor, you'll get a big red assert.
|
|
|
|
Handling Events
|
|
---------------
|
|
|
|
To handle an event you must write a callback that is passed the event
|
|
record you defined containing the event's arguments:
|
|
|
|
void* handleMyEvent(MyEventType* event)
|
|
{
|
|
doit(event->x, event->y);
|
|
return NULL; // you could return a value for a sync event
|
|
}
|
|
|
|
Similarly for the destruction callback:
|
|
|
|
void destroyMyEvent(MyEventType* event)
|
|
{
|
|
free(event->y); // created by strdup
|
|
free(event);
|
|
}
|
|
|
|
Processing Events in Your Event Loop
|
|
------------------------------------
|
|
|
|
If your main loop only processes events delivered to the event queue,
|
|
things are rather simple. You just get the next event (which may
|
|
block), and then handle it:
|
|
|
|
while (1) {
|
|
event = PL_GetEvent(myQueue);
|
|
PL_HandleEvent(event);
|
|
}
|
|
|
|
However, if other things must be waited on, you'll need to obtain a
|
|
file-descriptor that represents your event queue, and hand it to select:
|
|
|
|
fd = PL_GetEventQueueSelectFD(myQueue);
|
|
...add fd to select set...
|
|
while (select(...)) {
|
|
if (...fd...) {
|
|
PL_ProcessPendingEvents(myQueue);
|
|
}
|
|
...
|
|
}
|
|
|
|
Of course, with Motif and Windows it's more complicated than that, and
|
|
on Mac it's completely different, but you get the picture.
|
|
|
|
Revoking Events
|
|
---------------
|
|
If at any time an owner of events is about to be destroyed, you must
|
|
take steps to ensure that no one tries to use the event queue after
|
|
the owner is gone (or a crash may result). You can do this by either
|
|
processing all the events in the queue before destroying the owner:
|
|
|
|
{
|
|
...
|
|
PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
|
|
PL_ProcessPendingEvents(myQueue);
|
|
DestroyMyOwner(owner);
|
|
PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
|
|
...
|
|
}
|
|
|
|
or by revoking the events that are in the queue for that owner. This
|
|
removes them from the queue and calls their destruction callback:
|
|
|
|
{
|
|
...
|
|
PL_ENTER_EVENT_QUEUE_MONITOR(myQueue);
|
|
PL_RevokeEvents(myQueue, owner);
|
|
DestroyMyOwner(owner);
|
|
PL_EXIT_EVENT_QUEUE_MONITOR(myQueue);
|
|
...
|
|
}
|
|
|
|
In either case it is essential that you be in the event-queue's monitor
|
|
to ensure that all events are removed from the queue for that owner,
|
|
and to ensure that no more events will be delivered for that owner.
|
|
**********************************************************************/
|
|
|
|
#ifndef plevent_h___
|
|
#define plevent_h___
|
|
|
|
#include "prtypes.h"
|
|
#include "prclist.h"
|
|
#include "prthread.h"
|
|
#include "prlock.h"
|
|
#include "prcvar.h"
|
|
#include "prmon.h"
|
|
|
|
/* For HWND */
|
|
#if defined(XP_WIN32)
|
|
#include <windef.h>
|
|
#elif defined(XP_OS2)
|
|
#define INCL_DOSMISC
|
|
#define INCL_DOSPROCESS
|
|
#define INCL_DOSERRORS
|
|
#include <os2.h>
|
|
#endif
|
|
|
|
PR_BEGIN_EXTERN_C
|
|
|
|
/* Typedefs */
|
|
|
|
typedef struct PLEvent PLEvent;
|
|
typedef struct PLEventQueue PLEventQueue;
|
|
|
|
/*******************************************************************************
|
|
* Event Queue Operations
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** Creates a new event queue. Returns NULL on failure.
|
|
*/
|
|
PR_EXTERN(PLEventQueue*)
|
|
PL_CreateEventQueue(const char* name, PRThread* handlerThread);
|
|
|
|
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_CreateNativeEventQueue()
|
|
**
|
|
** DESCRIPTION:
|
|
** PL_CreateNativeEventQueue() creates an event queue that
|
|
** uses platform specific notify mechanisms.
|
|
**
|
|
** For Unix, the platform specific notify mechanism provides
|
|
** an FD that may be extracted using the function
|
|
** PL_GetEventQueueSelectFD(). The FD returned may be used in
|
|
** a select() function call.
|
|
**
|
|
** For Windows, the platform specific notify mechanism
|
|
** provides an event receiver window that is called by
|
|
** Windows to process the event using the windows message
|
|
** pump engine.
|
|
**
|
|
** INPUTS:
|
|
** name: A name, as a diagnostic aid.
|
|
**
|
|
** handlerThread: A pointer to the PRThread structure for
|
|
** the thread that will "handle" events posted to this event
|
|
** queue.
|
|
**
|
|
** RETURNS:
|
|
** A pointer to a PLEventQueue structure or NULL.
|
|
**
|
|
*/
|
|
PR_EXTERN(PLEventQueue *)
|
|
PL_CreateNativeEventQueue(
|
|
const char *name,
|
|
PRThread *handlerThread
|
|
);
|
|
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_CreateMonitoredEventQueue()
|
|
**
|
|
** DESCRIPTION:
|
|
** PL_CreateMonitoredEventQueue() creates an event queue. No
|
|
** platform specific notify mechanism is created with the
|
|
** event queue.
|
|
**
|
|
** Users of this type of event queue must explicitly poll the
|
|
** event queue to retreive and process events.
|
|
**
|
|
**
|
|
** INPUTS:
|
|
** name: A name, as a diagnostic aid.
|
|
**
|
|
** handlerThread: A pointer to the PRThread structure for
|
|
** the thread that will "handle" events posted to this event
|
|
** queue.
|
|
**
|
|
** RETURNS:
|
|
** A pointer to a PLEventQueue structure or NULL.
|
|
**
|
|
*/
|
|
PR_EXTERN(PLEventQueue *)
|
|
PL_CreateMonitoredEventQueue(
|
|
const char *name,
|
|
PRThread *handlerThread
|
|
);
|
|
|
|
/*
|
|
** Destroys an event queue.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_DestroyEventQueue(PLEventQueue* self);
|
|
|
|
/*
|
|
** Returns the monitor associated with an event queue. This monitor is
|
|
** selectable. The monitor should be entered to protect against anyone
|
|
** calling PL_RevokeEvents while the event is trying to be constructed
|
|
** and delivered.
|
|
*/
|
|
PR_EXTERN(PRMonitor*)
|
|
PL_GetEventQueueMonitor(PLEventQueue* self);
|
|
|
|
#define PL_ENTER_EVENT_QUEUE_MONITOR(queue) \
|
|
PR_EnterMonitor(PL_GetEventQueueMonitor(queue))
|
|
|
|
#define PL_EXIT_EVENT_QUEUE_MONITOR(queue) \
|
|
PR_ExitMonitor(PL_GetEventQueueMonitor(queue))
|
|
|
|
/*
|
|
** Posts an event to an event queue, waking up any threads waiting for an
|
|
** event. If event is NULL, notification still occurs, but no event will
|
|
** be available.
|
|
**
|
|
** Any events delivered by this routine will be destroyed by PL_HandleEvent
|
|
** when it is called (by the event-handling thread).
|
|
*/
|
|
PR_EXTERN(PRStatus)
|
|
PL_PostEvent(PLEventQueue* self, PLEvent* event);
|
|
|
|
/*
|
|
** Like PL_PostEvent, this routine posts an event to the event handling
|
|
** thread, but does so synchronously, waiting for the result. The result
|
|
** which is the value of the handler routine is returned.
|
|
**
|
|
** Any events delivered by this routine will be not be destroyed by
|
|
** PL_HandleEvent, but instead will be destroyed just before the result is
|
|
** returned (by the current thread).
|
|
*/
|
|
PR_EXTERN(void*)
|
|
PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event);
|
|
|
|
/*
|
|
** Gets an event from an event queue. Returns NULL if no event is
|
|
** available.
|
|
*/
|
|
PR_EXTERN(PLEvent*)
|
|
PL_GetEvent(PLEventQueue* self);
|
|
|
|
/*
|
|
** Returns true if there is an event available for PL_GetEvent.
|
|
*/
|
|
PR_EXTERN(PRBool)
|
|
PL_EventAvailable(PLEventQueue* self);
|
|
|
|
/*
|
|
** This is the type of the function that must be passed to PL_MapEvents
|
|
** (see description below).
|
|
*/
|
|
typedef void
|
|
(PR_CALLBACK *PLEventFunProc)(PLEvent* event, void* data, PLEventQueue* queue);
|
|
|
|
/*
|
|
** Applies a function to every event in the event queue. This can be used
|
|
** to selectively handle, filter, or remove events. The data pointer is
|
|
** passed to each invocation of the function fun.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data);
|
|
|
|
/*
|
|
** This routine walks an event queue and destroys any event whose owner is
|
|
** the owner specified. The == operation is used to compare owners.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_RevokeEvents(PLEventQueue* self, void* owner);
|
|
|
|
/*
|
|
** This routine processes all pending events in the event queue. It can be
|
|
** called from the thread's main event-processing loop whenever the event
|
|
** queue's selectFD is ready (returned by PL_GetEventQueueSelectFD).
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_ProcessPendingEvents(PLEventQueue* self);
|
|
|
|
/*******************************************************************************
|
|
* Pure Event Queues
|
|
*
|
|
* For when you're only processing PLEvents and there is no native
|
|
* select, thread messages, or AppleEvents.
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** Blocks until an event can be returned from the event queue. This routine
|
|
** may return NULL if the current thread is interrupted.
|
|
*/
|
|
PR_EXTERN(PLEvent*)
|
|
PL_WaitForEvent(PLEventQueue* self);
|
|
|
|
/*
|
|
** One stop shopping if all you're going to do is process PLEvents. Just
|
|
** call this and it loops forever processing events as they arrive. It will
|
|
** terminate when your thread is interrupted or dies.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_EventLoop(PLEventQueue* self);
|
|
|
|
/*******************************************************************************
|
|
* Native Event Queues
|
|
*
|
|
* For when you need to call select, or WaitNextEvent, and yet also want
|
|
* to handle PLEvents.
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** This routine allows you to grab the file descriptor associated with an
|
|
** event queue and use it in the readFD set of select. Useful for platforms
|
|
** that support select, and must wait on other things besides just PLEvents.
|
|
*/
|
|
PR_EXTERN(PRInt32)
|
|
PL_GetEventQueueSelectFD(PLEventQueue* self);
|
|
|
|
/*
|
|
** This routine will allow you to check to see if the given eventQueue in
|
|
** on the current thread. It will return PR_TRUE if so, else it will return
|
|
** PR_FALSE
|
|
*/
|
|
PR_EXTERN(PRBool)
|
|
PL_IsQueueOnCurrentThread( PLEventQueue *queue );
|
|
|
|
/*
|
|
** Returns whether the queue is native (true) or monitored (false)
|
|
*/
|
|
PR_EXTERN(PRBool)
|
|
PL_IsQueueNative(PLEventQueue *queue);
|
|
|
|
/*******************************************************************************
|
|
* Event Operations
|
|
******************************************************************************/
|
|
|
|
/*
|
|
** The type of an event handler function. This function is passed as an
|
|
** initialization argument to PL_InitEvent, and called by
|
|
** PL_HandleEvent. If the event is called synchronously, a void* result
|
|
** may be returned (otherwise any result will be ignored).
|
|
*/
|
|
typedef void*
|
|
(PR_CALLBACK *PLHandleEventProc)(PLEvent* self);
|
|
|
|
/*
|
|
** The type of an event destructor function. This function is passed as
|
|
** an initialization argument to PL_InitEvent, and called by
|
|
** PL_DestroyEvent.
|
|
*/
|
|
typedef void
|
|
(PR_CALLBACK *PLDestroyEventProc)(PLEvent* self);
|
|
|
|
/*
|
|
** Initializes an event. Usually events are embedded in a larger event
|
|
** structure which holds event-specific data, so this is an initializer
|
|
** for that embedded part of the structure.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_InitEvent(PLEvent* self, void* owner,
|
|
PLHandleEventProc handler,
|
|
PLDestroyEventProc destructor);
|
|
|
|
/*
|
|
** Returns the owner of an event.
|
|
*/
|
|
PR_EXTERN(void*)
|
|
PL_GetEventOwner(PLEvent* self);
|
|
|
|
/*
|
|
** Handles an event, calling the event's handler routine.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_HandleEvent(PLEvent* self);
|
|
|
|
/*
|
|
** Destroys an event, calling the event's destructor.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_DestroyEvent(PLEvent* self);
|
|
|
|
/*
|
|
** Removes an event from an event queue.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_DequeueEvent(PLEvent* self, PLEventQueue* queue);
|
|
|
|
|
|
/*
|
|
* Give hint to native PL_Event notification mechanism. If the native
|
|
* platform needs to tradeoff performance vs. native event starvation
|
|
* this hint tells the native dispatch code which to favor.
|
|
* The default is to prevent event starvation.
|
|
*
|
|
* Calls to this function may be nested. When the number of calls that
|
|
* pass PR_TRUE is subtracted from the number of calls that pass PR_FALSE
|
|
* is greater than 0, performance is given precedence over preventing
|
|
* event starvation.
|
|
*
|
|
* The starvationDelay arg is only used when
|
|
* favorPerformanceOverEventStarvation is PR_FALSE. It is the
|
|
* amount of time in milliseconds to wait before the PR_FALSE actually
|
|
* takes effect.
|
|
*/
|
|
PR_EXTERN(void)
|
|
PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation, PRUint32 starvationDelay);
|
|
|
|
|
|
/*******************************************************************************
|
|
* Private Stuff
|
|
******************************************************************************/
|
|
|
|
struct PLEvent {
|
|
PRCList link;
|
|
PLHandleEventProc handler;
|
|
PLDestroyEventProc destructor;
|
|
void* owner;
|
|
void* synchronousResult;
|
|
PRLock* lock;
|
|
PRCondVar* condVar;
|
|
PRBool handled;
|
|
#ifdef PL_POST_TIMINGS
|
|
PRIntervalTime postTime;
|
|
#endif
|
|
#ifdef XP_UNIX
|
|
unsigned long id;
|
|
#endif /* XP_UNIX */
|
|
/* other fields follow... */
|
|
};
|
|
|
|
/******************************************************************************/
|
|
|
|
/*
|
|
** Returns the event queue associated with the main thread.
|
|
**
|
|
*/
|
|
#if defined(XP_WIN) || defined(XP_OS2)
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_GetNativeEventReceiverWindow()
|
|
**
|
|
** DESCRIPTION:
|
|
** PL_GetNativeEventReceiverWindow() returns the windows
|
|
** handle of the event receiver window associated with the
|
|
** referenced PLEventQueue argument.
|
|
**
|
|
** INPUTS:
|
|
** PLEventQueue pointer
|
|
**
|
|
** RETURNS:
|
|
** event receiver window handle.
|
|
**
|
|
** RESTRICTIONS: MS-Windows ONLY.
|
|
**
|
|
*/
|
|
PR_EXTERN(HWND)
|
|
PL_GetNativeEventReceiverWindow(
|
|
PLEventQueue *eqp
|
|
);
|
|
#endif /* XP_WIN || XP_OS2 */
|
|
|
|
#ifdef XP_UNIX
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_ProcessEventsBeforeID()
|
|
**
|
|
** DESCRIPTION:
|
|
**
|
|
** PL_ProcessEventsBeforeID() will process events in a native event
|
|
** queue that have an id that is older than the ID passed in.
|
|
**
|
|
** INPUTS:
|
|
** PLEventQueue *aSelf
|
|
** unsigned long aID
|
|
**
|
|
** RETURNS:
|
|
** PRInt32 number of requests processed, -1 on error.
|
|
**
|
|
** RESTRICTIONS: Unix only (well, X based unix only)
|
|
*/
|
|
PR_EXTERN(PRInt32)
|
|
PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID);
|
|
|
|
/* This prototype is a function that can be called when an event is
|
|
posted to stick an ID on it. */
|
|
|
|
typedef unsigned long
|
|
(PR_CALLBACK *PLGetEventIDFunc)(void *aClosure);
|
|
|
|
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_RegisterEventIDFunc()
|
|
**
|
|
** DESCRIPTION:
|
|
**
|
|
** This function registers a function for getting the ID on unix for
|
|
** this event queue.
|
|
**
|
|
** INPUTS:
|
|
** PLEventQueue *aSelf
|
|
** PLGetEventIDFunc func
|
|
** void *aClosure
|
|
**
|
|
** RETURNS:
|
|
** void
|
|
**
|
|
** RESTRICTIONS: Unix only (well, X based unix only) */
|
|
PR_EXTERN(void)
|
|
PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc,
|
|
void *aClosure);
|
|
|
|
/* -----------------------------------------------------------------------
|
|
** FUNCTION: PL_RegisterEventIDFunc()
|
|
**
|
|
** DESCRIPTION:
|
|
**
|
|
** This function unregisters a function for getting the ID on unix for
|
|
** this event queue.
|
|
**
|
|
** INPUTS:
|
|
** PLEventQueue *aSelf
|
|
**
|
|
** RETURNS:
|
|
** void
|
|
**
|
|
** RESTRICTIONS: Unix only (well, X based unix only) */
|
|
PR_EXTERN(void)
|
|
PL_UnregisterEventIDFunc(PLEventQueue *aSelf);
|
|
|
|
#endif /* XP_UNIX */
|
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
#if defined(NO_NSPR_10_SUPPORT)
|
|
#else
|
|
/********* ???????????????? FIX ME ??????????????????????????? *****/
|
|
/********************** Some old definitions *****************************/
|
|
|
|
/* Re: prevent.h->plevent.h */
|
|
#define PREvent PLEvent
|
|
#define PREventQueue PLEventQueue
|
|
#define PR_CreateEventQueue PL_CreateEventQueue
|
|
#define PR_DestroyEventQueue PL_DestroyEventQueue
|
|
#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor
|
|
#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR
|
|
#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR
|
|
#define PR_PostEvent PL_PostEvent
|
|
#define PR_PostSynchronousEvent PL_PostSynchronousEvent
|
|
#define PR_GetEvent PL_GetEvent
|
|
#define PR_EventAvailable PL_EventAvailable
|
|
#define PREventFunProc PLEventFunProc
|
|
#define PR_MapEvents PL_MapEvents
|
|
#define PR_RevokeEvents PL_RevokeEvents
|
|
#define PR_ProcessPendingEvents PL_ProcessPendingEvents
|
|
#define PR_WaitForEvent PL_WaitForEvent
|
|
#define PR_EventLoop PL_EventLoop
|
|
#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD
|
|
#define PRHandleEventProc PLHandleEventProc
|
|
#define PRDestroyEventProc PLDestroyEventProc
|
|
#define PR_InitEvent PL_InitEvent
|
|
#define PR_GetEventOwner PL_GetEventOwner
|
|
#define PR_HandleEvent PL_HandleEvent
|
|
#define PR_DestroyEvent PL_DestroyEvent
|
|
#define PR_DequeueEvent PL_DequeueEvent
|
|
#define PR_GetMainEventQueue PL_GetMainEventQueue
|
|
|
|
/********* ????????????? End Fix me ?????????????????????????????? *****/
|
|
#endif /* NO_NSPR_10_SUPPORT */
|
|
|
|
PR_END_EXTERN_C
|
|
|
|
#endif /* plevent_h___ */
|