Agar

<-- Back to AG_Intro.3

SYNOPSIS

#include <agar/core.h>

DESCRIPTION

The AG_Event (or AG_Function) type represents an event handler (or more generally a virtual function) under an AG_Object(3). It is the basis of Agar message passing. Event handlers are tagged with a case-insensitive string up to AG_EVENT_NAME_MAX bytes in size. Anonymous functions are denoted by an empty tag. Functions are declared as follows:


void MyEventHandler (AG_Event *event)




An AG_Event may store up to AG_EVENT_ARGS_MAX arguments. The arguments contained in an AG_Event can be accessed both by index and by name (names are case-insensitive strings up to AG_VARIABLE_NAME_MAX bytes). The argument stack contains the arguments from the initial AG_SetEvent() call first, followed by additional arguments tacked on by any subsequent AG_PostEvent() calls (see EVENT ARGUMENTS for more details and examples).

Agar objects can act as event senders or event receivers. Execution of event handlers can be delayed for a set amount of time, or marked for execution in a separate thread. AG_Event provides a thread-safe message passing system for multithreaded applications.

EVENT PROCESSING


AG_Event * AG_SetEvent (AG_Object *obj, const char *name, void (*fn)(AG_Event *event), const char *fnArgs, ...)

AG_Event * AG_AddEvent (AG_Object *obj, const char *name, void (*fn)(AG_Event *event), const char *fnArgs, ...)

AG_Event * AG_FindEventHandler (AG_Object *obj, const char *name)

void AG_UnsetEvent (AG_Object *obj, const char *name)

void AG_UnsetEventByPtr (AG_Object *obj, AG_Event *event)

int AG_PostEvent (AG_Object *obj, const char *name, const char *fmt, ...)

int AG_PostEventByPtr (AG_Object *obj, AG_Event *event, const char *fmt, ...)

int AG_SchedEvent (AG_Object *obj, Uint32 ticks, const char *name, const char *fmt, ...)


The AG_SetEvent() function registers a new event handler to service events of type name. If an event handler is already registered for the given event type, it is replaced. The AG_AddEvent() variant preserves any existing event handler, such that multiple handlers can be invoked when the event is raised. The fn argument is a pointer to the event handler function, and fnArgs is a special kind of format string specifying a list of arguments (see EVENT ARGUMENTS section).

The AG_FindEventHandler() function searches for an event handler by name, returning a pointer to the AG_Event element on success or NULL if there is no match.

AG_UnsetEvent() and AG_UnsetEventByPtr() delete the given event handler.

The AG_PostEvent() function immediately executes the event handler function associated with the given event type, if there is any. The fn and fnArgs arguments to AG_PostEvent() are interpreted in the same way as AG_SetEvent() and AG_AddEvent(), but the arguments are appended at the end of the argument list. When the event handler function retrieves arguments by index (as opposed to using argument names), it is important to remember that the arguments to AG_PostEvent() follow the arguments given to AG_SetEvent() or AG_AddEvent().

The AG_PostEvent() function returns 1 if an event handler was invoked, or 0 if there is no registered event handler for the specified event.

The AG_PostEventByPtr() variant accepts a pointer to an AG_Event element, as opposed to looking up the event handler by name.

The AG_SchedEvent() function provides an interface similar to AG_PostEvent(), except that the event is scheduled to occur in the given number of ticks AG_SchedEvent() returns 0 on success or -1 if the timer could not be created. If the object is detached or destroyed, all events scheduled for execution are automatically cancelled. A more flexible interface for implementing timers is described in AG_Timer(3) (which AG_SchedEvent() uses internally).

EVENT ARGUMENTS

The AG_SetEvent(), AG_AddEvent() and AG_PostEvent() routines accept a special fnArgs format string specifying a list of arguments to be passed to the event handler function. For example, "%s,%p,%i" (or "%s%p%i") says that the following arguments are a string, a pointer and an integer. An event handler routine will typically use accessor macros to extract argument values out of the event structure:
void
MyEventHandler(AG_Event *event)
{
	char *s = AG_STRING(1);
	void *p = AG_PTR(2);
	int i   = AG_INT(3);
	
	/* ... */
}

It is customary for Agar object classes to define accessor macros AG_<T>_PTR() (for accessing arguments by index), AG_<T>_NAMED() (for accessing arguments by name), and AG_<T>_SELF() (for accessing argument 0, equivalent to "AG_<T>_PTR(0)"). Arguments marked constant must be accessed with AG_CONST_<T>_PTR() and AG_CONST_<T>_SELF().

Here is an example using Agar-GUI widget classes:
static void
PushedOK(AG_Event *event)
{
	AG_Button *button = AG_BUTTON_SELF();
	AG_Radio *radio   = AG_RADIO_PTR(1);
	AG_Button *button = AG_BUTTON_PTR(2);
	const AG_Checkbox *cb = AG_CONST_CHECKBOX_PTR(3);

/* 	cb->invert = 1;              <- Incorrect (cb is const) */
/*	AG_WidgetDisable(cb);        <- Incorrect (cb is const) */
	AG_WidgetDisable(radio);  /* <- OK */
	AG_WidgetDisable(button); /* <- OK */
}

AG_Window *win = AG_WindowNew(0);
AG_Checkbox *radio = AG_RadioNew(window, 0, NULL);
AG_Button *button = AG_ButtonNew(window, 0, "OK");
AG_Checkbox *checkbox = AG_CheckboxNew(window, 0, "Some option");

AG_SetEvent(button, "button-pushed",
    PushedOK, "%p,%Cp", radio, checkbox);

Arguments are optionally named (tagged with a string) and retrieved with AG_<TYPE>_NAMED():
static void
CopySomeData(AG_Event *event)
{
	const void *src = AG_CONST_PTR_NAMED("src");
	void *dst = AG_PTR_NAMED("dst");
	long offs = AG_LONG_NAMED("offs");

	/* ... */
}

void *src, *dst;
long offs = 0;

AG_SetEvent(obj, "some-event",
    CopySomeData, "%Cp(src),%p(dst),%li(offs)", src, dst, offs);

The following argument specifiers are accepted:
"%p"A pointer to data: void *.
"%Cp"A pointer to const data: const void *.
"%i"Signed integer: int.
"%u"Unsigned integer: Uint.
"%li"Signed long integer: long. Not in AG_SMALL.
"%lu"Unsigned long integer: Ulong. Not in AG_SMALL.
"%f"Real number: float. Requires AG_HAVE_FLOAT.
"%d"Real number: double. Requires AG_HAVE_FLOAT.
"%s"C string (NUL-terminated): char *.

The following macros extract the arguments contained in an AG_Event structure. If Agar is compiled with either --enable-debug or --enable-type-safety, they also check for potential accesses to incorrect types.


AG_Object * AG_SELF (void)

const AG_Object * AG_CONST_SELF (void)

void * AG_PTR (int index)

const void * AG_CONST_PTR (int index)

AG_Object * AG_OBJECT (int index, const char *hierarchy)

const AG_Object * AG_CONST_OBJECT (int index, const char *hierarchy)

char * AG_STRING (int index)

int AG_INT (int index)

Uint AG_UINT (int index)

long AG_LONG (int index)

Ulong AG_ULONG (int index)

float AG_FLOAT (int index)

double AG_DOUBLE (int index)

void * AG_PTR_NAMED (const char *key)

const void * AG_CONST_PTR_NAMED (const char *key)

AG_Object * AG_OBJECT_NAMED (const char *key, const char *hierarchy)

const AG_Object * AG_CONST_OBJECT_NAMED (const char *key, const char *hierarchy)

char * AG_STRING_NAMED (const char *key)

int AG_INT_NAMED (const char *key)

Uint AG_UINT_NAMED (const char *key)

long AG_LONG_NAMED (const char *key)

Ulong AG_ULONG_NAMED (const char *key)

float AG_FLOAT_NAMED (const char *key)

double AG_DOUBLE_NAMED (const char *key)


The AG_SELF() and AG_CONST_SELF() macros expand to a pointer to the AG_Object(3) receiving the event (the obj argument passed to AG_PostEvent()). They are equivalent to AG_PTR(0) and AG_CONST_PTR(0), respectively.

The following macros return a specific item in the list of arguments. When retrieving arguments by index, note that the arguments to AG_PostEvent() follow the arguments to AG_SetEvent() (i.e., the arguments to AG_SetEvent() are pushed first onto the argument stack, followed by the arguments to AG_PostEvent(), if any). These macros ensure type safety if Agar is compiled with --enable-debug or --enable-type-safety.

AG_PTR() returns a pointer (previously passed as a %p argument). AG_CONST_PTR() returns a pointer (previously passed as a %Cp argument).

AG_OBJECT() returns a pointer to an AG_Object(3) (previously passed as a %p argument). In debug mode, assert that the argument points to a valid AG_Object(3) by performing a validity test, and a class membership test. The AG_CONST_OBJECT() variant asserts that the object pointer was passed as "%Cp".

AG_STRING() returns a pointer to a string passed as a %s argument.

AG_INT(), AG_UINT(), AG_LONG() and AG_ULONG() return a natural or long integer passed as %i, %u, %li or %lu argument respectively.

AG_FLOAT() and AG_DOUBLE() return the given floating-point number, previously passed as a real %f or %d argument.

The AG_*_NAMED() macros retrieve the given argument by name instead of by index. If there is no argument matching the name, a fatal error is raised.

ARGUMENT MANIPULATION

In some cases it is desirable for functions to accept a list of event handler arguments like AG_SetEvent(), and possibly manipulate its entries directly. For example, the AG_MenuAction(3) function of the GUI widget AG_Menu(3) accepts a pointer to an event handler function, followed by an AG_SetEvent() style format string and a variable list of arguments. The following functions allow such manipulations.


void AG_EventInit (AG_Event *ev)

void AG_EventArgs (AG_Event *ev, const char *fmt, ...)

void AG_EventCopy (AG_Event *dst, const AG_Event *src)

AG_Event * AG_EventDup (const AG_Event *src)

void AG_EVENT_DUMP (const AG_Event *ev)

void AG_EventPushPointer (AG_Event *ev, const char *key, void *val)

void AG_EventPushConstPointer (AG_Event *ev, const char *key, const void *val)

void AG_EventPushString (AG_Event *ev, const char *key, char *val)

void AG_EventPushInt (AG_Event *ev, const char *key, int val)

void AG_EventPushUint (AG_Event *ev, const char *key, Uint val)

void AG_EventPushLong (AG_Event *ev, const char *key, long val)

void AG_EventPushUlong (AG_Event *ev, const char *key, Ulong val)

void AG_EventPushFloat (AG_Event *ev, const char *key, float val)

void AG_EventPushDouble (AG_Event *ev, const char *key, double val)

void AG_EVENT_PUSH_ARG (va_list ap, char formatChar, AG_Event *ev)

void * AG_EventPopPointer (AG_Event *ev)

const void * AG_EventPopConstPointer (AG_Event *ev)

char * AG_EventPopString (AG_Event *ev)

int AG_EventPopInt (AG_Event *ev)

Uint AG_EventPopUint (AG_Event *ev)

long AG_EventPopLong (AG_Event *ev)

Ulong AG_EventPopUlong (AG_Event *ev)

float AG_EventPopFloat (AG_Event *ev)

double AG_EventPopDouble (AG_Event *ev)


AG_EventInit() initializes an AG_Event structure with no arguments.

AG_EventArgs() initializes ev and also specifies a list of arguments (in the same format as AG_SetEvent()).

AG_EventCopy() copies the function pointer and arguments from one AG_Event to another. AG_EventDup() returns a newly-allocated duplicate.

The AG_EVENT_DUMP() macro produces a listing of the arguments of ev on the console via AG_Debug(3).

The AG_EventPush*() routines put a new argument on top of the argument stack, incrementing the argument count. AG_EventPop*() decrement the argument count, returning a copy of the data of the last element.

The AG_EVENT_PUSH_ARG() macro insert an argument on the argument stack, determining the type from formatChar and the data from the following va_arg(3) arguments. The supported formatChar characters are documented in the EVENT ARGUMENTS section.

EVENT QUEUES

Under some circumstances, it is useful to gather AG_Event objects into a simple queue. For example, a custom event loop routine (see AG_EventLoop(3)) or a low-level Agar driver (see AG_Driver(3)) may gather events from input devices and later process them.

STRUCTURE DATA

For the AG_Event structure:
char name* String identifier for the event.
int argc Argument count.
AG_Variable *argv Argument data (see AG_Variable(3)).

EXAMPLES

The following code fragment demonstrates a typical AG_Event usage in the Agar-GUI library. We bind an action to the button press event, which is called button-pushed. This event is documented in the AG_Button(3) manual, and so are the arguments it appends to the list of arguments passed to the event handler (in this case, a single int).
void
SayHello(AG_Event *event)
{
	char *s   = AG_STRING(1);  /* From AG_SetEvent() */
	int state = AG_INT(2);     /* From later AG_PostEvent() */

	AG_TextMsg(AG_MSG_INFO, "Hello, %s! (state=%d)", s, state);
}

AG_Button *btn;

btn = AG_ButtonNew(NULL, 0, "Say hello");
AG_SetEvent(btn, "button-pushed", SayHello, "%s", "World");

The AG_Button API provides a shorthand constructor routine, AG_ButtonNewFn(), which accepts the button-pushed event handler as argument:
AG_ButtonNewFn(NULL, 0, "Say hello", SayHello, "%s", "World");

The following code fragment is equivalent:
AG_Button *btn;
AG_Event *ev;

btn = AG_ButtonNew(NULL, 0, "Say hello");
ev = AG_SetEvent(btn, "button-pushed", SayHello, NULL);
AG_EventPushString(ev, NULL, "World");

The following code fragment invokes a handler routine artificially:
void
SayHello(AG_Event *event)
{
	char *s = AG_STRING(1);
	int   i = AG_INT(2);
}

AG_Event ev;

AG_EventArgs(&ev, "%s,%d", "Foo string", 1234);
SayHello(&ev);

SEE ALSO

AG_EventLoop(3), AG_Intro(3), AG_Object(3), AG_Timer(3), AG_Variable(3)

HISTORY

The AG_Event mechanism first appeared in Agar 1.0. The AG_Variable(3) structure was first used to represent event handler arguments in Agar 1.3.4. Agar 1.6.0 added the CONST argument accessor macros and introduced validity and class membership tests for object pointers in event handler arguments. AG_EventCopy(), AG_EventDup() and AG_UnsetEventByPtr() appeared in Agar 1.6.0.
Csoft.net ElectronTubeStore