/* 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 #endif #ifdef HAVE_STRINGS_H # include #endif #include #include 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, NULL, 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, NULL, 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, gib_object_t *sender, 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; if (reply) GIB_Object_Incref (obj); return method->func (obj, method, obj->data[method->class->depth], sender, message); } int GIB_SendToMethod (gib_object_t *obj, gib_method_t *method, gib_object_t *sender, 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; if (reply) GIB_Object_Incref (obj); return method->func (obj, method, obj->data[method->class->depth], sender, 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, sender, 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 (); }