quakeforge/libs/gib/gib_object.c
Brian Koropoff 687a0845b6 Introducing the GIB Object System (TM). This breaks scriptable HUD for now
and probably has enough bugs to leave the Orkin man scratching his head,
but it works and allows you to do neat things like write classes in GIB
(amazing!) and subclass builtin classes (which are Object and Thread at the
moment, Hash should be coming soon as a replacement for stem and leaf
variables).
2003-09-11 06:03:13 +00:00

374 lines
8.1 KiB
C

/*
gib_object.c
GIB object functions
Copyright (C) 2003 Brian Koropoff
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
#include "QF/hash.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/gib.h"
#include "gib_object.h"
#include "gib_handle.h"
#include "gib_classes.h"
hashtab_t *gib_classes;
/*
Hashtable callbacks
*/
static const char *
GIB_Class_Get_Key (void *ele, void *ptr)
{
return ((gib_class_t *) ele)->name;
}
static void
GIB_Class_Free (void *ele, void *ptr)
{
gib_class_t *b;
b = (gib_class_t *) ele;
free ((void *)b->name);
free (b);
}
static const char *
GIB_Method_Get_Key (void *ele, void *ptr)
{
return ((gib_method_t *) ele)->name;
}
static void
GIB_Method_Free (void *ele, void *ptr)
{
// FIXME: Do something here
}
static const char *
GIB_Signal_Get_Key (void *ele, void *ptr)
{
return ((gib_signal_t *) ele)->name;
}
static void
GIB_Signal_Free (void *ele, void *ptr)
{
gib_signal_t *sig;
gib_slot_t *slot;
sig = (gib_signal_t *) ele;
slot = llist_remove (llist_getnode (sig->receiver->slots,
sig->slot));
free ((void *)sig->name);
free ((void *)slot->mesg);
free (sig);
free (slot);
}
// Linked list callbacks
static void
GIB_Slot_Free (void *ele, void *ptr)
{
gib_signal_t *sig;
gib_slot_t *slot;
slot = (gib_slot_t *) ele;
sig = Hash_DelElement (slot->sender->signals, slot->signal);
free ((void *)sig->name);
free ((void *)slot->mesg);
free (sig);
free (slot);
}
static hashtab_t *
GIB_Method_Build_Hash (gib_class_t *class, hashtab_t *inherited,
gib_methodtab_t *methods)
{
gib_methodtab_t *m;
gib_method_t *method;
hashtab_t *new = Hash_NewTable (1024, GIB_Method_Get_Key, GIB_Method_Free, 0);
for (m = methods; m->name; m++) {
method = malloc (sizeof (gib_method_t));
method->parent = inherited ? Hash_Find (inherited, m->name) : NULL;
method->name = strdup (m->name);
method->func = m->func;
method->data = m->data;
method->class = class;
Hash_Add (new, method);
}
if (inherited) {
void **list;
for (list = Hash_GetList (inherited); *list; list++)
if (!Hash_Find (new, GIB_Method_Get_Key (*list,
NULL)))
Hash_Add (new, *list);
}
return new;
}
void
GIB_Class_Create (gib_classdesc_t *desc)
{
static const char *init = "init";
gib_class_t *parent = NULL, *class = calloc (1, sizeof (gib_class_t));
if (desc->parentname && (parent = Hash_Find (gib_classes, desc->parentname))) {
class->parent = parent;
class->depth = parent->depth + 1;
} else
class->depth = 0;
class->name = strdup (desc->name);
class->construct = desc->construct;
class->class_construct = desc->class_construct;
class->destruct = desc->destruct;
class->methods = GIB_Method_Build_Hash (class, parent ?
parent->methods : NULL, desc->methods);
class->class_methods = GIB_Method_Build_Hash (class, parent ?
parent->class_methods : NULL, desc->class_methods);
Hash_Add (gib_classes, class);
// Create a class object
class->classobj = GIB_Object_Create (desc->name, true);
GIB_Send (class->classobj, 1, &init, NULL, NULL);
}
/*
GIB_Object_Create
Creates a new object from a named class.
Does not send object an initialization
message!
*/
gib_object_t *
GIB_Object_Create (const char *classname, qboolean classobj)
{
gib_class_t *temp, *class = Hash_Find (gib_classes, classname);
gib_object_t *obj;
int i;
if (!class)
return NULL;
obj = calloc (1, sizeof (gib_object_t));
obj->class = class;
obj->data = malloc (sizeof (void *) * (class->depth+1));
obj->methods = classobj ? class->class_methods : class->methods;
obj->handle = classobj ? 0 : GIB_Handle_New (obj);
obj->refs = 1;
obj->signals = Hash_NewTable (128, GIB_Signal_Get_Key,
GIB_Signal_Free, NULL);
obj->slots = llist_new (GIB_Slot_Free, NULL, NULL);
if (classobj) {
for (temp = class, i = class->depth; temp; temp = temp->parent, i--)
if (temp->class_construct)
obj->data[i] = temp->class_construct (obj);
} else {
for (temp = class, i = class->depth; temp; temp = temp->parent, i--)
if (temp->construct)
obj->data[i] = temp->construct (obj);
}
return obj;
}
static void
GIB_Object_Finish_Destroy (int argc, const char **argv, void *data)
{
gib_object_t *obj = (gib_object_t *) data;
int i;
gib_class_t *temp;
for (temp = obj->class, i = obj->class->depth; temp; temp = temp->parent, i--)
if (temp->destruct)
temp->destruct (obj->data[i]);
free (obj->data);
GIB_Handle_Free (obj->handle);
Hash_DelTable (obj->signals);
llist_delete (obj->slots);
free (obj);
}
/*
GIB_Object_Destroy
Sends an object a dispose message and then frees it upon reply.
*/
void
GIB_Object_Destroy (gib_object_t *obj)
{
const static char *dispose = "dispose";
GIB_Send (obj, 1, &dispose, GIB_Object_Finish_Destroy, obj);
}
void
GIB_Object_Incref (gib_object_t *obj)
{
if (obj->refs > 0)
obj->refs++;
}
void
GIB_Object_Decref (gib_object_t *obj)
{
if (obj->refs > 0 && !--obj->refs)
GIB_Object_Destroy (obj);
}
int
GIB_Send (gib_object_t *obj, int argc, const char **argv, gib_reply_handler reply, void *replydata)
{
gib_message_t message;
gib_method_t *method;
if (!(method = Hash_Find (obj->methods, *argv)))
return -1;
message.argc = argc;
message.argv = argv;
message.reply = reply;
message.replydata = replydata;
return method->func (obj, method, obj->data[method->class->depth], message);
}
int
GIB_SendToMethod (gib_object_t *obj, gib_method_t *method, int argc, const char **argv, gib_reply_handler reply, void *replydata)
{
gib_message_t message;
message.argc = argc;
message.argv = argv;
message.reply = reply;
message.replydata = replydata;
return method->func (obj, method, obj->data[method->class->depth], message);
}
gib_object_t *
GIB_Object_Get (const char *id)
{
gib_class_t *class;
if (isdigit ((byte) *id))
return GIB_Handle_Get (atoi (id));
else if ((class = Hash_Find (gib_classes, id)))
return class->classobj;
else
return NULL;
}
void
GIB_Object_Signal_Slot_Pair (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot)
{
gib_signal_t *si = malloc (sizeof (gib_signal_t));
gib_slot_t *sl = malloc (sizeof (gib_slot_t));
si->slot = sl;
sl->signal = si;
si->receiver = receiver;
sl->sender = sender;
si->name = strdup (signal);
sl->mesg = strdup (slot);
Hash_Add (sender->signals, si);
llist_append (receiver->slots, sl);
}
void
GIB_Object_Signal_Slot_Destroy (gib_object_t *sender, const char *signal,
gib_object_t *receiver, const char *slot)
{
gib_signal_t **list, **cur;
if ((list = (gib_signal_t **) Hash_FindList (sender->signals, signal))) {
for (cur = list; *cur; cur++) {
if ((*cur)->receiver == receiver && !strcmp
((*cur)->slot->mesg, slot)) {
Hash_Free (sender->signals, Hash_DelElement
(sender->signals, *cur));
break;
}
}
free (list);
}
}
void
GIB_Object_Signal_Emit (gib_object_t *sender, int argc, const char **argv)
{
gib_signal_t **list, **cur;
const char *old = *argv;
if ((list = (gib_signal_t **) Hash_FindList (sender->signals, *argv))) {
for (cur = list; *cur; cur++) {
*argv = (*cur)->slot->mesg;
GIB_Send ((*cur)->receiver, argc, argv, NULL, NULL);
}
free (list);
}
*argv = old;
}
void
GIB_Object_Init (void)
{
gib_classes = Hash_NewTable (1024, GIB_Class_Get_Key, GIB_Class_Free, 0);
GIB_Classes_Init ();
}