.\" Copyright (c) 2002-2023 Julien Nadeau Carriere .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, .\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES .\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR .\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, .\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 21, 2022 .Dt AG_EVENT 3 .Os Agar 1.7 .Sh NAME .Nm AG_Event .Nd agar event handlers / virtual functions .Sh SYNOPSIS .Bd -literal #include .Ed .Sh DESCRIPTION The .Nm (or .Ft AG_Function ) type represents an event handler (or more generally a virtual function) under an .Xr AG_Object 3 . It provides a general-purpose, thread-safe message passing system. It is used to implement virtual functions in objects. The .Nm structure contains an optional .Va name identifier (a string of up to .Dv AG_EVENT_NAME_MAX characters long). Event Handlers are declared as follows: .Pp .nr nS 1 .\" NOMANLINK .Ft void .Fn MyEventHandler "AG_Event *event" .Pp .nr nS 0 The final stack of arguments passed to .Fn MyEventHandler contains both the arguments from the initial .Fn AG_SetEvent call followed by any extra arguments appended by subsequent .Fn AG_PostEvent calls. See the .Sx EVENT ARGUMENTS section for details and examples. .Pp An .Ft AG_Event may store up to .Dv AG_EVENT_ARGS_MAX arguments. The arguments contained in an .Ft AG_Event can be accessed by the event handler either by index or by name. .Sh EVENT PROCESSING .nr nS 1 .Ft "AG_Event *" .Fn AG_SetEvent "AG_Object *obj" "const char *name" "void (*fn)(AG_Event *event)" "const char *fmt" "..." .Pp .Ft "AG_Event *" .Fn AG_AddEvent "AG_Object *obj" "const char *name" "void (*fn)(AG_Event *event)" "const char *fmt" "..." .Pp .Ft "AG_Event *" .Fn AG_FindEventHandler "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_UnsetEvent "AG_Object *obj" "const char *name" .Pp .Ft "void" .Fn AG_UnsetEventByPtr "AG_Object *obj" "AG_Event *event" .Pp .Ft "void" .Fn AG_PostEvent "AG_Object *obj" "const char *name" "const char *fmt" "..." .Pp .Ft "void" .Fn AG_PostEventByPtr "AG_Object *obj" "AG_Event *event" "const char *fmt" "..." .Pp .Ft "int" .Fn AG_SchedEvent "AG_Object *obj" "Uint32 ticks" "const char *name" "const char *fmt" "..." .Pp .nr nS 0 The .Fn AG_SetEvent function sets an event handler to service events of type .Fa name . If a handler of the given name already exists, it is replaced. The .Fn AG_AddEvent variant preserves any pre-existing handler (such that multiple handlers will be invoked by the event). .Fa fn is a pointer to the event handler function. .Fa fmt is an optional format string which specifies following arguments (see .Sx EVENT ARGUMENTS ) . .Pp .Fn AG_FindEventHandler searches for an event handler by name and returns a pointer to its .Nm structure on success or NULL if not found. .Pp .Fn AG_UnsetEvent and .Fn AG_UnsetEventByPtr delete the given event handler. .Pp .Fn AG_PostEvent raises the event identified by .Fa name under object .Fa obj . .Fa 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 .Fn AG_SetEvent followed by those added by .Fn AG_PostEvent . .Pp The .Fn AG_PostEventByPtr variant accepts a pointer to an .Nm element as opposed to looking up the event handler by name. .Pp .Fn AG_SchedEvent provides an interface similar to .Fn AG_PostEvent except that the event is scheduled to occur in the given number of ticks. .Fn AG_SchedEvent returns 0 on success or -1 if the timer could not be created. When an .Xr AG_Object 3 is detached or destroyed, any event scheduled for execution is automatically cancelled. A more flexible interface for timers is described in .Xr AG_Timer 3 (which .Fn AG_SchedEvent uses internally). .Sh EVENT ARGUMENTS The .Fn AG_SetEvent , .Fn AG_AddEvent and .Fn AG_PostEvent routines accept a special .Fa 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 .Fa event structure: .Bd -literal -offset indent .\" SYNTAX(c) void MyEventHandler(AG_Event *event) { char *s = AG_STRING(1); void *p = AG_PTR(2); int i = AG_INT(3); /* ... */ } .Ed .Pp It is customary for Agar object classes to define accessor macros .Fn AG__PTR (for accessing arguments by index), .Fn AG__NAMED (for accessing arguments by name), and .Fn AG__SELF (for accessing argument 0, equivalent to "AG__PTR(0)"). Arguments marked .Em constant must be accessed with .Fn AG_c_PTR and .Fn AG_c_SELF . .Pp Here is an example using Agar-GUI widget classes: .Bd -literal -offset indent .\" SYNTAX(c) 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); .Ed .Pp Arguments are optionally .Em named (tagged with a string) and retrieved with .Fn AG__NAMED : .Bd -literal -offset indent .\" SYNTAX(c) 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); .Ed .Pp The following argument specifiers are accepted: .Pp .Bl -tag -compact -width "%li " .It "%p" A pointer to data: .Ft "void *" . .It "%Cp" A pointer to const data: .Ft "const void *" . .It "%i" Signed integer: .Ft int . .It "%u" Unsigned integer: .Ft Uint . .It "%li" Signed long integer: .Ft long . Not in .Dv AG_SMALL . .It "%lu" Unsigned long integer: .Ft Ulong . Not in .Dv AG_SMALL . .It "%f" Real number: .Ft float . Requires .Dv AG_HAVE_FLOAT . .It "%d" Real number: .Ft double . Requires .Dv AG_HAVE_FLOAT . .It "%s" C string (NUL-terminated): .Ft "char *" . .El .Pp The following macros extract the arguments contained in an .Nm structure. If Agar is compiled with either --enable-debug or --enable-type-safety, they also check for potential accesses to incorrect types. .Pp .nr nS 1 .Ft "AG_Object *" .Fn AG_SELF "void" .Pp .Ft "const AG_Object *" .Fn AG_cSELF "void" .Pp .Ft "void *" .Fn AG_PTR "int index" .Pp .Ft "const void *" .Fn AG_cPTR "int index" .Pp .\" NOMANLINK .Ft "AG_Object *" .Fn AG_OBJECT "int index" "const char *hierarchy" .Pp .Ft "const AG_Object *" .Fn AG_cOBJECT "int index" "const char *hierarchy" .Pp .Ft "char *" .Fn AG_STRING "int index" .Pp .Ft "int" .Fn AG_INT "int index" .Pp .Ft "Uint" .Fn AG_UINT "int index" .Pp .Ft "long" .Fn AG_LONG "int index" .Pp .Ft "Ulong" .Fn AG_ULONG "int index" .Pp .Ft "float" .Fn AG_FLOAT "int index" .Pp .Ft "double" .Fn AG_DOUBLE "int index" .Pp .Ft "void *" .Fn AG_PTR_NAMED "const char *key" .Pp .Ft "const void *" .Fn AG_cPTR_NAMED "const char *key" .Pp .Ft "AG_Object *" .Fn AG_OBJECT_NAMED "const char *key" "const char *hierarchy" .Pp .Ft "const AG_Object *" .Fn AG_cOBJECT_NAMED "const char *key" "const char *hierarchy" .Pp .Ft "char *" .Fn AG_STRING_NAMED "const char *key" .Pp .Ft "int" .Fn AG_INT_NAMED "const char *key" .Pp .Ft "Uint" .Fn AG_UINT_NAMED "const char *key" .Pp .Ft "long" .Fn AG_LONG_NAMED "const char *key" .Pp .Ft "Ulong" .Fn AG_ULONG_NAMED "const char *key" .Pp .Ft "float" .Fn AG_FLOAT_NAMED "const char *key" .Pp .Ft "double" .Fn AG_DOUBLE_NAMED "const char *key" .Pp .nr nS 0 The .Fn AG_SELF and .Fn AG_cSELF macros expand to a pointer to the .Xr AG_Object 3 receiving the event (the .Fa obj argument passed to .Fn AG_PostEvent ) . They are equivalent to AG_PTR(0) and AG_cPTR(0), respectively. .Pp The following macros return a specific item in the list of arguments. When retrieving arguments by index, note that the arguments to .Fn AG_PostEvent follow the arguments to .Fn AG_SetEvent (i.e., the arguments to .Fn AG_SetEvent are pushed first onto the argument stack, followed by the arguments to .Fn AG_PostEvent , if any). These macros ensure type safety if Agar is compiled with --enable-debug or --enable-type-safety. .Pp .Fn AG_PTR returns a pointer (a .Sq %p argument). .Fn AG_cPTR returns a .Ft const pointer (a .Sq %Cp argument). .Pp .Fn AG_OBJECT returns a pointer to an .Xr AG_Object 3 (a .Sq %p argument). In debug mode, assert that the argument points to a valid .Xr AG_Object 3 by performing a validity test, and a class membership test. The .Fn AG_cOBJECT variant asserts that the pointer is a .Ft const pointer (a .Sq %Cp argument). .Pp .Fn AG_STRING returns a pointer to a string (a .Sq %s argument). .Pp .Fn AG_INT , .Fn AG_UINT , .Fn AG_LONG and .Fn AG_ULONG return a natural or long integer (a .Sq %i , .Sq %u , .Sq %li or .Sq %lu argument, respectively). .Pp .Fn AG_FLOAT and .Fn AG_DOUBLE return the given floating-point number (a .Sq %f for a .Ft float or .Sq %d for a .Ft double argument). .Pp The .Fn 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. .Sh ARGUMENT MANIPULATION In some cases it is desirable for functions to accept a list of event handler arguments like .Fn AG_SetEvent , and possibly manipulate its entries directly. For example, the .Xr AG_MenuAction 3 function of the GUI widget .Xr AG_Menu 3 accepts a pointer to an event handler function, followed by an .Fn AG_SetEvent style format string and a variable list of arguments. The following functions allow such manipulations. .Pp .nr nS 1 .Ft void .Fn AG_EventInit "AG_Event *ev" .Pp .Ft void .Fn AG_EventArgs "AG_Event *ev" "const char *fmt" "..." .Pp .Ft void .Fn AG_EventCopy "AG_Event *dst" "const AG_Event *src" .Pp .Ft "AG_Event *" .Fn AG_EventDup "const AG_Event *src" .Pp .Ft void .Fn AG_EVENT_DUMP "const AG_Event *ev" .Pp .Ft void .Fn AG_EventPushPointer "AG_Event *ev" "const char *key" "void *val" .Pp .Ft void .Fn AG_EventPushConstPointer "AG_Event *ev" "const char *key" "const void *val" .Pp .Ft void .Fn AG_EventPushString "AG_Event *ev" "const char *key" "char *val" .Pp .Ft void .Fn AG_EventPushInt "AG_Event *ev" "const char *key" "int val" .Pp .Ft void .Fn AG_EventPushUint "AG_Event *ev" "const char *key" "Uint val" .Pp .Ft void .Fn AG_EventPushLong "AG_Event *ev" "const char *key" "long val" .Pp .Ft void .Fn AG_EventPushUlong "AG_Event *ev" "const char *key" "Ulong val" .Pp .Ft void .Fn AG_EventPushFloat "AG_Event *ev" "const char *key" "float val" .Pp .Ft void .Fn AG_EventPushDouble "AG_Event *ev" "const char *key" "double val" .Pp .Ft void .Fn AG_EVENT_PUSH_ARG "va_list ap" "char formatChar" "AG_Event *ev" .Pp .Ft "void *" .Fn AG_EventPopPointer "AG_Event *ev" .Pp .Ft "const void *" .Fn AG_EventPopConstPointer "AG_Event *ev" .Pp .Ft "char *" .Fn AG_EventPopString "AG_Event *ev" .Pp .Ft "int" .Fn AG_EventPopInt "AG_Event *ev" .Pp .Ft "Uint" .Fn AG_EventPopUint "AG_Event *ev" .Pp .Ft "long" .Fn AG_EventPopLong "AG_Event *ev" .Pp .Ft "Ulong" .Fn AG_EventPopUlong "AG_Event *ev" .Pp .Ft "float" .Fn AG_EventPopFloat "AG_Event *ev" .Pp .Ft "double" .Fn AG_EventPopDouble "AG_Event *ev" .Pp .nr nS 0 .Fn AG_EventInit initializes an .Ft AG_Event structure with no arguments. .Pp .Fn AG_EventArgs initializes .Fa ev and also specifies a list of arguments (in the same format as .Fn AG_SetEvent ) . .Pp .Fn AG_EventCopy copies the function pointer and arguments from one .Nm to another. .Fn AG_EventDup returns a newly-allocated duplicate. .Pp The .Fn AG_EVENT_DUMP macro produces a listing of the arguments of .Fa ev on the console via .Xr AG_Debug 3 . .Pp The .Fn AG_EventPush* routines put a new argument on top of the argument stack, incrementing the argument count. .Fn AG_EventPop* decrement the argument count, returning a copy of the data of the last element. .Pp The .Fn AG_EVENT_PUSH_ARG macro insert an argument on the argument stack, determining the type from .Fa formatChar and the data from the following .Xr va_arg 3 arguments. The supported .Fa formatChar characters are documented in the .Sx EVENT ARGUMENTS section. .Sh EVENT QUEUES Under some circumstances, it is useful to gather .Ft AG_Event objects into a simple queue. For example, a custom event loop routine (see .Xr AG_EventLoop 3 ) or a low-level Agar driver (see .Xr AG_Driver 3 ) may gather events from input devices and later process them. .Sh STRUCTURE DATA For the .Ft AG_Event structure: .Pp .Bl -tag -compact -width "AG_Variable *argv " .It Ft char * name String identifier for the event. .It Ft int argc Argument count. .It Ft AG_Variable *argv Argument data (see .Xr AG_Variable 3 ) . .El .Sh EXAMPLES The following code fragment demonstrates a typical .Nm usage in the Agar-GUI library. We bind an action to the button press event, which is called .Sq button-pushed . This event is documented in the .Xr 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 .Ft int ) . .Bd -literal -offset indent .\" SYNTAX(c) 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"); .Ed .Pp The .Ft AG_Button API provides a shorthand constructor routine, .Fn AG_ButtonNewFn , which accepts the .Sq button-pushed event handler as argument: .Bd -literal -offset indent .\" SYNTAX(c) AG_ButtonNewFn(NULL, 0, "Say hello", SayHello, "%s", "World"); .Ed .Pp The following code fragment is equivalent: .Bd -literal -offset indent .\" SYNTAX(c) 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"); .Ed .Pp The following code fragment invokes a handler routine artificially: .Bd -literal -offset indent .\" SYNTAX(c) 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); .Ed .Sh SEE ALSO .Xr AG_EventLoop 3 , .Xr AG_Intro 3 , .Xr AG_Object 3 , .Xr AG_Timer 3 , .Xr AG_Variable 3 .Sh HISTORY The .Nm mechanism first appeared in Agar 1.0. The .Xr 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. .Fn AG_EventCopy , .Fn AG_EventDup and .Fn AG_UnsetEventByPtr appeared in Agar 1.6.0.