AG_Event
— agar
event handlers / virtual functions
The AG_Event
(or
AG_Function) type represents an event handler (or more
generally a virtual function) under an
AG_Object(3).
It provides a general-purpose, thread-safe message passing system. It is
used to implement virtual functions in objects. The
AG_Event
structure contains an optional
name identifier (a string of up to
AG_EVENT_NAME_MAX
characters long). Event Handlers
are declared as follows:
void
MyEventHandler
(AG_Event
*event);
The final stack of arguments passed to
MyEventHandler
()
contains both the arguments from the initial
AG_SetEvent
() call followed by any extra arguments
appended by subsequent AG_PostEvent
() calls. See the
EVENT ARGUMENTS section for
details and examples.
An AG_Event may store up to
AG_EVENT_ARGS_MAX
arguments. The arguments contained
in an AG_Event can be accessed by the event handler
either by index or by name.
AG_Event *
AG_SetEvent
(AG_Object
*obj, const char
*name, void
(*fn)(AG_Event *event),
const char *fmt,
...);
AG_Event *
AG_AddEvent
(AG_Object
*obj, const char
*name, void
(*fn)(AG_Event *event),
const char *fmt,
...);
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);
void
AG_PostEvent
(AG_Object
*obj, const char
*name, const char
*fmt, ...);
void
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 sets an event handler to service events of type
name. If a handler of the given name already exists,
it is replaced. The AG_AddEvent
() variant preserves
any pre-existing handler (such that multiple handlers will be invoked by the
event). fn is a pointer to the event handler function.
fmt is an optional format string which specifies
following arguments (see EVENT
ARGUMENTS).
AG_FindEventHandler
()
searches for an event handler by name and returns a pointer to its
AG_Event
structure on success or NULL if not
found.
AG_UnsetEvent
()
and AG_UnsetEventByPtr
() delete the given event
handler.
AG_PostEvent
()
raises the event identified by name under object
obj. fmt is an optional format
string specifying following arguments that will be passed to the event
handler function. The event handler will see the arguments added by
AG_SetEvent
() followed by those added by
AG_PostEvent
().
The
AG_PostEventByPtr
()
variant accepts a pointer to an AG_Event
element as
opposed to looking up the event handler by name.
AG_SchedEvent
()
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. When an
AG_Object(3)
is detached or destroyed, any event scheduled for execution is automatically
cancelled. A more flexible interface for timers is described in
AG_Timer(3)
(which AG_SchedEvent
() uses internally).
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_c<T>_PTR
()
and
AG_c<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_cCHECKBOX_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_cPTR_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_cSELF
(void);
void *
AG_PTR
(int
index);
const void *
AG_cPTR
(int
index);
AG_Object *
AG_OBJECT
(int
index, const char
*hierarchy);
const AG_Object *
AG_cOBJECT
(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_cPTR_NAMED
(const
char *key);
AG_Object *
AG_OBJECT_NAMED
(const
char *key, const char
*hierarchy);
const AG_Object *
AG_cOBJECT_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_cSELF
() 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_cPTR(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 (a ‘%p’ argument).
AG_cPTR
() returns a const
pointer (a ‘%Cp’ argument).
AG_OBJECT
()
returns a pointer to an
AG_Object(3)
(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_cOBJECT
() variant asserts that the pointer is a
const pointer (a ‘%Cp’ argument).
AG_STRING
()
returns a pointer to a string (a ‘%s’ argument).
AG_INT
(),
AG_UINT
(), AG_LONG
() and
AG_ULONG
() return a natural or long integer (a
‘%i’, ‘%u’, ‘%li’ or
‘%lu’ argument, respectively).
AG_FLOAT
()
and AG_DOUBLE
() return the given floating-point
number (a ‘%f’ for a float or
‘%d’ for a double 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.
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.
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.
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)).
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);
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.