Agar

<-- Back to AG_Intro.3

SYNOPSIS

#include <agar/core.h>

DESCRIPTION

The Agar object system provides object-oriented programming capabilities including inheritance and virtual functions, as well as high-level features such as serialization, timers, VFS and abstracted data types. It is implemented in C and provides bindings to other languages.

Agar objects can be organized into a tree or virtual filesystem (VFS). Any AG_Object can become the root of a VFS. A VFS can be made persistent to the degree required by the application. Object data is serialized to a machine-independent binary format (using AG_DataSource(3) calls in their load() and save() operations). While an object's metadata (including the name field) must always remain in memory, class-specific data can be serialized to storage and deserialized on demand.

INITIALIZATION


AG_Object * AG_ObjectNew (AG_Object *parent, const char *name, AG_ObjectClass *classInfo)

void AG_ObjectInit (AG_Object *obj, AG_ObjectClass *classInfo)

void AG_ObjectAttach (AG_Object *newParent, AG_Object *child)

void AG_ObjectDetach (AG_Object *child)

void AG_ObjectMoveToHead (AG_Object *obj)

void AG_ObjectMoveToTail (AG_Object *obj)

void AG_ObjectMoveUp (AG_Object *obj)

void AG_ObjectMoveDown (AG_Object *obj)

void AG_ObjectDelete (AG_Object *obj)

AG_Object * AG_ObjectRoot (AG_Object *obj)

AG_Object * AG_ObjectParent (AG_Object *obj)

AG_Object * AG_ObjectFind (AG_Object *vfsRoot, const char *format, ...)

AG_Object * AG_ObjectFindS (AG_Object *vfsRoot, const char *name)

AG_Object * AG_ObjectFindParent (AG_Object *obj, const char *name, const char *type)

AG_Object * AG_ObjectFindChild (AG_Object *obj, const char *name)

char * AG_ObjectGetName (AG_Object *obj)

int AG_ObjectCopyName (AG_Object *obj, char *buf, AG_Size bufSize)

void AG_ObjectLock (AG_Object *obj)

void AG_ObjectUnlock (AG_Object *obj)

void AG_LockVFS (AG_Object *obj)

void AG_UnlockVFS (AG_Object *obj)

void AG_ObjectSetName (AG_Object *obj, const char *fmt, ...)

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

void AG_ObjectGenName (AG_Object *obj, AG_ObjectClass *classInfo, char *name, AG_Size nameSize)

void AG_ObjectGenNamePfx (AG_Object *obj, const char *prefix, char *name, AG_Size nameSize)

AGOBJECT_FOREACH_CHILD (AG_Object *child, AG_Object *parent, TYPE type)


The AG_ObjectNew() function allocates and initializes a new object instance of the given class. The object is attached to parent, unless the argument is NULL. If name is NULL then a unique name (e.g., "object0") will be generated. If both parent and name are specified and the parent object already has a child of the given name, AG_ObjectNew() fails and returns NULL.

The AG_ObjectInit() function initializes an object of a given class. It invokes init() for every class in the inheritance hierarchy. name is an optional name for the object instance relative to its parent (maximum AG_OBJECT_NAME_MAX characters and no /). classInfo should point to an initialized AG_ObjectClass structure (see CLASSES ) . The flags argument specifies a default set of flags (see FLAGS ) .

AG_ObjectAttach() attaches an object child to another object newParent, raising an attached event. It is a no-op if newParent is NULL.

AG_ObjectDetach() removes an object child from its parent (if any), cancelling scheduled AG_Timer(3) expirations before raising detached.

AG_ObjectMoveUp(), AG_ObjectMoveDown(), AG_ObjectMoveToHead() and AG_ObjectMoveToTail() move the object in the parent object's list of child objects. This is useful when the ordering of objects is important.

AG_ObjectDelete() is a shorthand for AG_ObjectDetach() followed by AG_ObjectDestroy().

AG_ObjectRoot() returns a pointer to the root of the VFS which the given object is attached to. AG_ObjectParent() returns the object's parent.

The AG_ObjectFind() function returns the object corresponding to the specified path name. If there is no such object it returns NULL.

AG_ObjectFindParent() returns the first ancestor of the object matching either the name of the object (if name is non-NULL), or the inheritance hierarchy string of the parent (if type is non-NULL).

AG_ObjectFindChild() searches the child objects directly under obj for an object called name. It returns a pointer to the object if found or NULL.

AG_ObjectGetName() returns an autoallocated string containing the full pathname of an object (relative to the root of its VFS). If insufficient memory is available to construct the path, it fails and returns NULL.

AG_ObjectCopyName() copies the object's full pathname (relative to its VFS root) to a fixed-size buffer buf of size bufSize in bytes. It returns 0 on success or -1 if the buffer is too small for the full path.

AG_ObjectLock() and AG_ObjectUnlock() acquire or release the locking device associated with the given object. This is a mutex protecting all read/write members of the AG_Object structure, except parent, root and the list of child objects cobjs which are all considered part of the virtual filesystem and are instead protected by AG_LockVFS().

The AG_ObjectLock() mutex can be used as a general-purpose locking device which is guaranteed to be held during processing of all events posted to the object as well as during object operations such as load() and save().

AG_LockVFS() and AG_UnlockVFS() acquire or release the lock which protect the layout of the entire VFS which obj is a part of.

Note: If Agar is compiled with --disable-threads then AG_ObjectLock(), AG_ObjectUnlock(), AG_LockVFS() and AG_UnlockVFS() become no-ops.

AG_ObjectSetName() updates the name of the given object. If the object is attached to a VFS then the VFS must be locked.

AG_ObjectGenName() generates a unique name for a child object of obj. The class name in lowercase is used as prefix, followed by a number. The generatedstring is copied to the fixed-size buffer name of size nameSize in bytes. The AG_ObjectGenNamePfx() variant generates a name using the specified prefix instead of the class name.

The AGOBJECT_FOREACH_CHILD() macro iterates child over every child object of parent. The child pointer is cast to the given structure type, without type checking. Example:
struct my_class *chld;

AGOBJECT_FOREACH_CHILD(chld, parent, my_class) {
	printf("Child object: %s\\n", AGOBJECT(chld)->name);
}

CLASSES


void AG_RegisterClass (AG_ObjectClass *classInfo)

void AG_UnregisterClass (AG_ObjectClass *classInfo)

AG_ObjectClass * AG_CreateClass (const char *classSpec, AG_Size objectSize, AG_Size classSize, Uint major, Uint minor)

AG_ObjectInitFn AG_ClassSetInit (AG_ObjectClass *cl, AG_ObjectInitFn fn)

AG_ObjectResetFn AG_ClassSetReset (AG_ObjectClass *cl, AG_ObjectResetFn fn)

AG_ObjectDestroyFn AG_ClassSetDestroy (AG_ObjectClass *cl, AG_ObjectDestroyFn fn)

AG_ObjectLoadFn AG_ClassSetLoad (AG_ObjectClass *cl, AG_ObjectLoadFn fn)

AG_ObjectSaveFn AG_ClassSetSave (AG_ObjectClass *cl, AG_ObjectSaveFn fn)

AG_ObjectEditFn AG_ClassSetEdit (AG_ObjectClass *cl, AG_ObjectEditFn fn)

void AG_DestroyClass (AG_ObjectClass *cl)

void AG_RegisterNamespace (const char *name, const char *prefix, const char *url)

void AG_UnregisterNamespace (const char *name)

AG_ObjectClass * AG_LookupClass (const char *classSpec)

AG_ObjectClass * AG_LoadClass (const char *classSpec)

void AG_RegisterModuleDirectory (const char *path)

void AG_UnregisterModuleDirectory (const char *path)

int AG_OfClass (AG_Object *obj, const char *pattern)

char * AG_ObjectGetClassName (const AG_Object *obj, int full)

AG_ObjectClass * AG_ObjectSuperclass (const AG_Object *obj)

int AG_ObjectGetInheritHier (AG_Object *obj, AG_ObjectClass **pHier, int *nHier)

AGOBJECT_FOREACH_CLASS (AG_Object *child, AG_Object *parent, TYPE type, const char *pattern)


The AG_RegisterClass() function registers a new object class. classInfo should be an initialized AG_ObjectClass structure:
typedef struct ag_object_class {
	char hier[AG_OBJECT_HIER_MAX];	/* Full inheritance hierarchy */
	AG_Size size;             	/* Size of instance structure */
	AG_Version ver;          	/* Version numbers */
	void (*init)(void *obj);
	void (*reset)(void *obj);
	void (*destroy)(void *obj);
	int  (*load)(void *obj, AG_DataSource *ds, const AG_Version *ver);
	int  (*save)(void *obj, AG_DataSource *ds);
	void *(*edit)(void *obj);
	/* ... */
} AG_ObjectClass;

For example:
AG_ObjectClass MyClass = {
	"MyClass",
	sizeof(MyClass),
	{ 0,0 },
	Init,
	NULL,	/* reset */
	NULL,	/* destroy */
	Load,
	Save,
	NULL	/* edit */
};

We can define new operations (or other class-specific data) by overloading AG_ObjectClass. The AG_WidgetClass class in Agar-GUI, for instance, overloads AG_ObjectClass and adds 3 new methods:
typedef struct ag_widget_class {
	struct ag_object_class _inherit;
	void (*draw)(void *);
	void (*size_request)(void *, AG_SizeReq *);
	int  (*size_allocate)(void *, const AG_SizeAlloc *);
} AG_WidgetClass;

For example:
AG_WidgetClass agButtonClass = {
	{
		"AG_Widget:AG_Button",  /* or "Agar(Widget:Button)" */
		sizeof(AG_Button),
		{ 0,0 },
		Init,
		NULL,	/* reset */
		NULL,	/* destroy */
		NULL,	/* load */
		NULL,	/* save */
		NULL	/* edit */
	},
	Draw,
	SizeRequest,
	SizeAllocate
};

The first field hier is the inheritance hierarchy string. For example, "AG_Widget:AG_Button" says that AG_Button is a direct subclass of AG_Widget (and AG_Widget is implicitely a subclass of AG_Object).

Alternatively, if a namespace called "Agar" exists and is mapped to the "AG_" prefix then the inheritance hierarchy can be also written as "Agar(Widget:Button)". If implementing the class requires specific libraries available as dynamically loaded modules via AG_DSO(3), this can be indicated in the hier string by a terminating "@" followed by one or more library names, separated by commas. For example:
    "AG_Widget:MY_Widget@myLib,myOtherLib"

The size member specifies the size in bytes of the object instance structure. ver is an optional datafile version number (see AG_Version(3)).

init() initializes a new object instance. It is called after successful allocation of a new object by AG_ObjectNew() or AG_ObjectInit().

reset() restores the state of the object to an initial state. It is invoked by AG_ObjectLoad() prior to load(), and also by AG_ObjectDestroy() prior to destroy().

destroy() frees all resources allocated by init() (excluding any which were freed previously by reset()).

load() reads the serialized state of object obj from data source ds. save() saves the state of obj to data source ds. load() and save() must both return 0 on success or -1 on failure. See AG_DataSource(3) and the SERIALIZATION section.

edit() is an application-specific method. In a typical Agar GUI application edit() may generate and return an AG_Window(3) or an AG_Box(3).

AG_UnregisterClass() removes the specified object class.

AG_CreateClass() offers an alternative to passing a statically-initialized AG_ObjectClass to AG_RegisterClass(). The AG_CreateClass() function allocates and initializes an AG_ObjectClass structure (or derivative thereof). AG_ClassSetInit(), AG_ClassSetReset(), AG_ClassSetDestroy(), AG_ClassSetLoad(), AG_ClassSetSave() and AG_ClassSetEdit() can be used to subsequently set the function pointers for the individual operations. They return a pointer to the previous operation. AG_DestroyClass() unregisters and frees an auto-allocated AG_ObjectClass (or derivative thereof).

AG_RegisterNamespace() registers a new namespace with the specified name, prefix and informational URL. For example, Agar registers its own using:
AG_RegisterNamespace("Agar", "AG_", "http://libagar.org/");

Once the namespace is registered, it is possible to specify inheritance hierarchies using the partitioned namespace format:
Agar(Widget:Button):MyLib(MyButton)

which is equivalent to the conventional format:
AG_Widget:AG_Button:MY_Button

The AG_UnregisterNamespace() function removes all information about the specified namespace.

The AG_LookupClass() function looks up the AG_ObjectClass structure describing the specified class (in namespace or expanded format). If there is no currently registered class matching the specification, AG_LookupClass() returns NULL.

AG_LoadClass() ensures that the object class specified in classSpec (see AG_RegisterClass() for details on the format) is registered, possibly loading one or more dynamic library files if they are specified in the string. Dynamic library dependencies are given in the form of a terminating @lib1,lib2,... string. AG_LoadClass() scans the registered module directories (see AG_RegisterModuleDirectory()) for the libraries specified in the string. Bare library names are given (the actual filenames are platform-dependent). Libraries that are found (and not already in memory) are loaded via AG_DSO(3). The first library must define a myFooClass symbol (where myFoo is the name of the class transformed from MY_Foo), for an AG_ObjectClass structure describing the class (i.e., the same structure that is passed to AG_RegisterClass()).

AG_UnloadClass() unregisters the specified class and also decrements the reference count of any dynamically-located module associated with it. If this reference count reaches zero, the module is removed from the current process's address space.

The AG_RegisterModuleDirectory() function adds the specified directory to the module search path. AG_UnregisterModuleDirectory() removes the specified directory from the search path.

Given an inheritance hierarchy string (with wildcards), AG_OfClass() evaluates whether obj is an instance of the specified class and returns a boolean (0 = False, 1 = True) indicating whether the object is an instance of a matching class. For example:
AG_Button *btn = AG_ButtonNew( ... );

if (AG_OfClass(btn, "AG_Widget:AG_Button")) {
	/*
	 * btn is an instance of AG_Button, and *not* a subclass of it.
	 */
}
if (AG_OfClass(btn, "AG_Widget:AG_Button:*")) {
	/*
	 * btn is an instance of AG_Button, or a subclass of AG_Button.
	 */
}

Fast paths are provided for patterns such as "Super:Sub:*" and "Super:Sub", but patterns such as "Super:*:Sub:*" are also supported.

AG_ObjectGetClassName() returns a newly-allocated string containing the name of the class of an object obj. If full is 1, return the complete inheritance hierarchy (e.g., "AG_Widget:AG_Button"). Otherwise, return only the subclass (e.g., "AG_Button").

AG_ObjectSuperclass() returns a pointer to the AG_ObjectClass structure describing the superclass of obj. If obj is an instance of the base class (AG_Object), then a pointer to the AG_Object class is returned.

The AG_ObjectGetInheritHier() function returns into pHier an array of AG_ObjectClass pointers describing the inheritance hierarchy of an object. The size of the array is returned into nHier. If the returned item count is > 0, the returned array should be freed when no longer in use. AG_ObjectGetInheritHier() returns 0 on success or -1 if there is insufficient memory.

The AGOBJECT_FOREACH_CLASS() macro iterates child over every child object of parent which is an instance of the class specified by pattern. child is cast to the given structure type. Example:
struct my_class *chld;

AGOBJECT_FOREACH_CLASS(chld, parent, my_class, "MyClass") {
	printf("Object %s is an instance of MyClass\\n",
	    AGOBJECT(chld)->name);
}

RELEASING RESOURCES


void AG_ObjectDestroy (AG_Object *obj)

void AG_ObjectReset (AG_Object *obj)

void AG_ObjectFreeEvents (AG_Object *obj)

void AG_ObjectFreeVariables (AG_Object *obj)

void AG_ObjectFreeChildren (AG_Object *obj)


AG_ObjectReset() restores the state of an object to some initial state. It invokes the object's reset(), which is expected to bring the object to a consistent state prior to deserialization (before load()).

AG_ObjectDestroy() frees all resources allocated by an object. It invokes the reset() and destroy() methods over each class in the inheritance hierarchy. AG_ObjectDestroy() also cancels any scheduled AG_Timer(3) expiration. AG_ObjectDestroy() implies AG_ObjectFreeEvents(), AG_ObjectFreeVariables() and AG_ObjectFreeChildren(). Unless AG_OBJECT_STATIC is set, AG_ObjectDestroy() also implies free(3).

AG_ObjectFreeEvents() clears all configured event handlers (also cancelling any scheduled timer expirations).

AG_ObjectFreeVariables() clears the property table (i.e., the table of AG_Variable(3)) associated with the object.

AG_ObjectFreeChildren() invokes AG_ObjectDetach() and AG_ObjectDestroy() on all child objects of parent.

SERIALIZATION


int AG_ObjectLoad (AG_Object *obj)

int AG_ObjectLoadFromFile (AG_Object *obj, const char *file)

int AG_ObjectLoadFromDB (AG_Object *obj, AG_Db *db, const AG_Dbt *key)

int AG_ObjectLoadData (AG_Object *obj)

int AG_ObjectLoadDataFromFile (AG_Object *obj, const char *file)

int AG_ObjectLoadGeneric (AG_Object *obj)

int AG_ObjectLoadGenericFromFile (AG_Object *obj, const char *file)

int AG_ObjectSave (AG_Object *obj)

int AG_ObjectSaveAll (AG_Object *obj)

int AG_ObjectSaveToFile (AG_Object *obj, const char *path)

int AG_ObjectSaveToDB (AG_Object *obj, AG_Db *db, const AG_Dbt *key)

int AG_ObjectSerialize (AG_Object *obj, AG_DataSource *ds)

int AG_ObjectUnserialize (AG_Object *obj, AG_DataSource *ds)

int AG_ObjectReadHeader (AG_DataSource *ds, AG_ObjectHeader *header)

int AG_ObjectPageIn (AG_Object *obj)

int AG_ObjectPageOut (AG_Object *obj)


These functions implement serialization, or archiving of the state of an AG_Object to a flat, machine-independent binary format.

The AG_ObjectLoad*() family of functions load the state of an Agar object from some binary data source. The generic AG_Object state is loaded first, followed by the object's serialized data (which is read by invoking the load() function of every class in the inheritance hierarchy). The AG_ObjectLoad(), AG_ObjectLoadGeneric() and AG_ObjectLoadData() functions look for an archive file in the default search path (using the load-path setting of AG_Config(3)). The AG_ObjectLoadFromFile(), AG_ObjectLoadGenericFromFile() and AG_ObjectLoadDataFromFile() variants attempt to load the object state from a specific file. The AG_ObjectLoadFromDB() variant loads the object state from the given AG_Db(3) database entry.

The AG_ObjectSave*() family of functions serialize and save the state of the given object. The generic AG_Object state is written first, followed by the object's serialized data (which is written by invoking the save() function of every class in the inheritance hierarchy). The AG_ObjectSave() function creates an archive of the given object in the default location (i.e., the save-path setting of AG_Config(3)). The AG_ObjectSaveAll() variant saves the object's children as well as the object itself. AG_ObjectSaveToFile() archives the object to the specified file. AG_ObjectSaveToDB() archives the object to the given AG_Db(3) entry.

The AG_ObjectSerialize() function writes an archive of the given object to the specified AG_DataSource(3), and AG_ObjectUnserialize() reads an archive of the given object.

The AG_ObjectReadHeader() routine attempts to read the header of a serialized Agar object from a AG_DataSource(3) and returns 0 on success or -1 if no valid header could be read. On success, header information is returned into the header structure:
typedef struct ag_object_header {
	char hier[AG_OBJECT_HIER_MAX];	    /* Inheritance hierarchy */
	char libs[AG_OBJECT_LIBS_MAX];	    /* Library list */
	char classSpec[AG_OBJECT_HIER_MAX]; /* Full class specification */
	Uint32 dataOffs;                    /* Dataset offset */
	AG_Version ver;                     /* AG_Object version */
	Uint flags;                         /* Object flags */
} AG_ObjectHeader;

The AG_ObjectPageIn() function loads an object's data into memory and sets the AG_OBJECT_RESIDENT flag. AG_ObjectPageOut() checks whether an object is referenced by another object and if that is not the case, the data is serialized to permanent storage, freed from memory and AG_OBJECT_RESIDENT is cleared. Both functions return 0 on success or -1 if an error has occurred.

FLAGS

The following public AG_Object flags are defined:
AG_OBJECT_FLOATING_VARSRemove all entries of the AG_Variable(3) table in AG_ObjectLoad(). By default, the existing table is preserved and entries are created or replaced by items found in the archive.
AG_OBJECT_NON_PERSISTENTDisables archiving of the object and its children. If set, AG_ObjectSave() becomes a no-op and AG_ObjectLoad() calls will fail.
AG_OBJECT_INDESTRUCTIBLEApplication-specific advisory flag.
AG_OBJECT_RESIDENTThe object's data exists in memory. Set by AG_ObjectPageIn() and AG_ObjectPageOut().
AG_OBJECT_STATICObject is statically allocated (or allocated via a facility other than malloc(3)). Disable use of free(3) by AG_ObjectDestroy().
AG_OBJECT_READONLYApplication-specific advisory flag.
AG_OBJECT_REOPEN_ONLOADIf an edit() operation is defined, indicate that elements associated with its return value (such as GUI windows or elements in the case of a GUI application) should be recreated whenever AG_ObjectLoad() is used.
AG_OBJECT_REMAIN_DATAPrevent the object's data from being freed by AG_ObjectReset() when a AG_ObjectPageOut() call is made and the reference count reaches zero.
AG_OBJECT_DEBUGApplication-specific debugging flag.
AG_OBJECT_NAME_ONATTACHAutomatically generate a unique name for the object as soon as AG_ObjectAttach() occurs.
AG_OBJECT_CHLD_AUTOSAVESerialize the object's children in AG_ObjectSerialize().

EVENTS

The AG_Object mechanism generates the following events:
attached (AG_Object *parent)
The object has been attached to a new parent.
detached (AG_Object *parent)
The object has been detached from its parent.
renamed (void)
The object's name has changed.
object-post-load (const char *path)
Invoked by AG_ObjectLoadData(), on success. If the object was loaded from file, path is the pathname of the file.
bound (AG_Variable *V)
A new variable binding has been created, or the value of an existing binding has been updated; see AG_Variable(3) for details.

STRUCTURE DATA

For the AG_ObjectClass structure (see CLASSES section):
char *hier Full inheritance hierarchy.
AG_Size size Size of instance structure (in bytes).
AG_Version ver Versioning information (see AG_Version(3)).
void (*init) Initialization routine.
void (*reset) Cleanup routine (for AG_ObjectReset()).
void (*destroy) Final cleanup routine.
int (*load) Deserialization routine.
int (*save) Serialization routine.
void *(*edit) Application-specific entry point.

The following read-only members are initialized internally:
char *name The name for this class only.
char *libs Comma-separated list of DSO modules.
AG_ObjectClass *super Pointer to the superclass.
TAILQ(AG_ObjectClass) sub Direct subclasses of this class.

For the AG_Object structure:
char name[AG_OBJECT_NAME_MAX] Unique (in parent) identifier for this object instance. May not contain " / ".
AG_ObjectClass *cls A pointer to the AG_ObjectClass for this object's class (see CLASSES section).
Uint flags Option flags for this object instance (see FLAGS section).
TAILQ(AG_Event) events Table of registered event handlers (set by AG_SetEvent(3)) and virtual functions (set by AG_Set<Type>Fn()).
TAILQ(AG_Timer) timers List of active timers (see AG_Timer(3)).
TAILQ(AG_Variable) vars Named variables (see AG_Variable(3)).
TAILQ(AG_Object) children List of child objects. The AGOBJECT_FOREACH_CHILD(), AGOBJECT_FOREACH_CHILD_REVERSE(), AGOBJECT_NEXT_CHILD(), AGOBJECT_LAST_CHILD() and AGOBJECT_FOREACH_CLASS() macros can be used to iterate over this list.

EXAMPLES

The Agar GUI system represents user interfaces using a tree of AG_Widget(3) objects attached to a parent AG_Window(3) which is itself attached to a parent AG_Driver(3).

The SG(3) scene-graph structure of Agar-SG is a VFS of SG_Node(3) objects. Non-visible nodes can be paged out to storage, saving memory.

Edacious (https://edacious.org/) represents circuits, components and simulation data using an in-memory VFS. Circuits are saved to a flat binary file which embeds the circuit's serialized data with that of its sub-components (which may include third-party components, in which case AG_Object will autoload any required DSOs).

See core/dummy_object.[ch] and tests/objsystem*.c in Agar sources.

SEE ALSO

AG_Event(3), AG_Intro(3), AG_Timer(3), AG_Variable(3)

HISTORY

The AG_Object interface appeared in Agar 1.0. AG_ObjectFreeDataset() was renamed AG_ObjectReset() in Agar 1.6.0. The functions AG_CreateClass(), AG_ClassSetInit(), AG_ClassSetReset(), AG_ClassSetDestroy(), AG_ClassSetLoad(), AG_ClassSetSave(), AG_ClassSetEdit(), AG_DestroyClass() and AG_ObjectGetClassName() appeared in Agar 1.6.0.
Csoft.net ElectronTubeStore