summaryrefslogtreecommitdiff
path: root/src/libs/cdp/cdpapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/cdp/cdpapi.c')
-rw-r--r--src/libs/cdp/cdpapi.c864
1 files changed, 864 insertions, 0 deletions
diff --git a/src/libs/cdp/cdpapi.c b/src/libs/cdp/cdpapi.c
new file mode 100644
index 0000000..e50e39d
--- /dev/null
+++ b/src/libs/cdp/cdpapi.c
@@ -0,0 +1,864 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * 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 the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP API definitions
+ * the API is used by both the engine and modules
+ */
+
+#include "cdp.h"
+#include "port.h"
+#include "cdpint.h"
+#include "uqmversion.h"
+
+#define MAX_REG_ITFS 255
+#define MAX_REG_EVENTS 1023
+
+static cdp_Error cdp_api_error = CDPERR_NONE;
+
+static uint32 cdp_Host_GetApiVersion (void);
+static uint32 cdp_Host_GetVersion (void);
+static cdp_Error cdp_Host_GetApiError (void);
+static cdp_Itf* cdp_Host_GetItf (const char* name);
+static bool cdp_Host_GetItfs (cdp_ItfDef* defs);
+static cdp_ItfReg* cdp_Host_RegisterItf (const char* name,
+ cdp_ApiVersion ver_from, cdp_ApiVersion ver_to,
+ cdp_Itf*, cdp_Module*);
+static void cdp_Host_UnregisterItf (cdp_ItfReg*);
+static bool cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module*);
+static void cdp_Host_UnregisterItfs (cdp_ItfDef* defs);
+static cdp_Event cdp_Host_GetEvent (const char* name);
+static bool cdp_Host_GetEvents (cdp_EventDef* defs);
+static cdp_EventReg* cdp_Host_RegisterEvent (const char* name, cdp_Module*);
+static void cdp_Host_UnregisterEvent (cdp_EventReg*);
+static bool cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module*);
+static void cdp_Host_UnregisterEvents (cdp_EventDef* defs);
+static bool cdp_Host_SubscribeEvent (cdp_Event, cdp_EventProc, cdp_Module*);
+static void cdp_Host_UnsubscribeEvent (cdp_Event, cdp_EventProc);
+static bool cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module*);
+static void cdp_Host_UnsubscribeEvents (cdp_EventDef* defs);
+static cdp_EventResult cdp_Host_FireEvent (cdp_EventReg*, uint32, void*);
+
+// Interfaces
+cdp_Itf_HostVtbl_v1 cdp_host_itf_v1 =
+{
+ cdp_Host_GetApiVersion,
+ cdp_Host_GetVersion,
+ cdp_Host_GetApiError,
+ cdp_Host_GetItf,
+ cdp_Host_GetItfs,
+ cdp_Host_RegisterItf,
+ cdp_Host_UnregisterItf,
+ cdp_Host_RegisterItfs,
+ cdp_Host_UnregisterItfs,
+ cdp_Host_GetEvent,
+ cdp_Host_GetEvents,
+ cdp_Host_RegisterEvent,
+ cdp_Host_UnregisterEvent,
+ cdp_Host_RegisterEvents,
+ cdp_Host_UnregisterEvents,
+ cdp_Host_SubscribeEvent,
+ cdp_Host_UnsubscribeEvent,
+ cdp_Host_SubscribeEvents,
+ cdp_Host_UnsubscribeEvents,
+ cdp_Host_FireEvent,
+};
+
+cdp_Itf_MemoryVtbl_v1 cdp_memory_itf_v1 =
+{
+ HMalloc,
+ HFree,
+ HCalloc,
+ HRealloc,
+};
+
+cdp_Itf_IoVtbl_v1 cdp_io_itf_v1 =
+{
+ uio_fopen,
+ uio_fclose,
+ uio_fread,
+ uio_fwrite,
+ uio_fseek,
+ uio_ftell,
+ uio_fflush,
+ uio_feof,
+ uio_ferror,
+};
+
+cdp_Itf_SoundVtbl_v1 cdp_sound_itf_v1 =
+{
+ SoundDecoder_Register,
+ SoundDecoder_Unregister,
+ SoundDecoder_Lookup,
+};
+
+cdp_Itf_VideoVtbl_v1 cdp_video_itf_v1 =
+{
+ VideoDecoder_Register,
+ VideoDecoder_Unregister,
+ VideoDecoder_Lookup,
+};
+
+// the actual interface registration struct/handle
+struct cdp_ItfReg
+{
+ bool builtin;
+ bool used;
+ const char* name;
+ cdp_ApiVersion ver_from;
+ cdp_ApiVersion ver_to;
+ cdp_Itf* itfvtbl;
+ cdp_Module* module;
+};
+
+#define CDP_DECLARE_ITF(kind,vf,vt,vtbl) \
+ {true, true, CDPITF_KIND_##kind, \
+ CDPAPI_VERSION_##vf, CDPAPI_VERSION_##vt, vtbl, NULL}
+
+// Built-in interfaces + space for loadable
+cdp_ItfReg cdp_itfs[MAX_REG_ITFS + 1] =
+{
+ CDP_DECLARE_ITF (HOST, 1, 1, &cdp_host_itf_v1),
+ CDP_DECLARE_ITF (MEMORY, 1, 1, &cdp_memory_itf_v1),
+ CDP_DECLARE_ITF (IO, 1, 1, &cdp_io_itf_v1),
+ CDP_DECLARE_ITF (SOUND, 1, 1, &cdp_sound_itf_v1),
+ CDP_DECLARE_ITF (VIDEO, 1, 1, &cdp_video_itf_v1),
+ // TODO: put newly defined built-in interfaces here
+
+ {false, false, "", 0, 0, NULL} // term
+};
+
+// event bind descriptor
+typedef struct
+{
+ cdp_EventProc proc;
+ cdp_Module* module;
+
+} cdp_EventBind;
+
+#define EVENT_BIND_GROW 16
+
+// the actual event registration struct/handle
+struct cdp_EventReg
+{
+ bool builtin;
+ bool used;
+ const char* name;
+ cdp_EventBind* binds;
+ uint32 bindslots;
+ cdp_Module* module;
+};
+
+#define CDP_DECLARE_EVENT(name) \
+ {true, true, "UQM." #name, NULL, 0, NULL}
+
+// Built-in events + space for loadable
+// a cdp_Event handle is an index into this array
+cdp_EventReg cdp_evts[MAX_REG_EVENTS + 1] =
+{
+ // sample - no real events defined yet
+ CDP_DECLARE_EVENT (PlanetSide.TouchDown),
+ CDP_DECLARE_EVENT (PlanetSide.LiftOff),
+ // TODO: put newly defined built-in events here
+
+ {false, false, "", NULL, 0, NULL} // term
+};
+
+cdp_Error
+cdp_GetApiError (void)
+{
+ cdp_Error ret = cdp_api_error;
+ cdp_api_error = CDPERR_NONE;
+ return ret;
+}
+
+bool
+cdp_InitApi (void)
+{
+ int i;
+ cdp_Module* kernel;
+
+ // preprocess built-in itfs
+ kernel = cdp_LoadModule (NULL);
+
+ for (i = 0; cdp_itfs[i].builtin; ++i)
+ {
+ cdp_itfs[i].module = kernel;
+ }
+ // clear the rest
+ //memset (cdp_itfs + i, 0,
+ // sizeof (cdp_itfs) - sizeof (cdp_ItfReg) * i);
+
+ for (i = 0; cdp_evts[i].builtin; ++i)
+ {
+ cdp_evts[i].module = kernel;
+ cdp_evts[i].bindslots = 0;
+ cdp_evts[i].binds = NULL;
+ }
+
+ return true;
+}
+
+void
+cdp_UninitApi (void)
+{
+ cdp_ItfReg* itf;
+
+ // unregister custom interfaces
+ for (itf = cdp_itfs; itf->used; ++itf)
+ {
+ if (!itf->builtin)
+ {
+ itf->used = false;
+ if (itf->name)
+ HFree (itf->name);
+ itf->name = NULL;
+ itf->itfvtbl = NULL;
+ itf->module = NULL;
+ }
+ }
+}
+
+static uint32
+cdp_Host_GetApiVersion (void)
+{
+ return CDPAPI_VERSION;
+}
+
+static uint32
+cdp_Host_GetVersion (void)
+{
+ return (UQM_MAJOR_VERSION << 20) | (UQM_MINOR_VERSION << 15) |
+ UQM_PATCH_VERSION;
+}
+
+static cdp_Error
+cdp_Host_GetApiError (void)
+{
+ return cdp_GetApiError ();
+}
+
+static char*
+cdp_MakeContextName (const char* ctx, const char* name)
+{
+ int namelen;
+ char* id_name;
+
+ namelen = strlen(ctx) + strlen(name) + 2;
+ id_name = HMalloc (namelen);
+ strcpy(id_name, ctx);
+ strcat(id_name, ".");
+ strcat(id_name, name);
+
+ return id_name;
+}
+
+/***********************************************************
+ * Interface system *
+ ***********************************************************/
+
+cdp_ItfReg*
+cdp_GetInterfaceReg (const char* name, cdp_ApiVersion api_ver)
+{
+ cdp_ItfReg* itf;
+
+ for (itf = cdp_itfs; itf->used &&
+ (!itf->name || strcasecmp(itf->name, name) != 0 ||
+ api_ver < itf->ver_from || api_ver > itf->ver_to);
+ itf++)
+ ;
+ if (!itf->name)
+ {
+ cdp_api_error = CDPERR_NO_ITF;
+ return NULL;
+ }
+
+ return itf;
+}
+
+cdp_Itf*
+cdp_GetInterface (const char* name, cdp_ApiVersion api_ver)
+{
+ cdp_ItfReg* reg;
+
+ reg = cdp_GetInterfaceReg (name, api_ver);
+ return reg ? reg->itfvtbl : NULL;
+}
+
+static cdp_Itf*
+cdp_Host_GetItf (const char* name)
+{
+ return cdp_GetInterface (name, CDPAPI_VERSION_1);
+}
+
+static bool
+cdp_Host_GetItfs (cdp_ItfDef* defs)
+{
+ cdp_ItfDef* def;
+ cdp_ItfReg* reg;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ // registration handle is not returned
+ def->reg = NULL;
+
+ reg = cdp_GetInterfaceReg (def->name, CDPAPI_VERSION_1);
+ if (reg)
+ {
+ def->itf = reg->itfvtbl;
+ def->name = reg->name; // set to cannonical name
+ def->ver_from = reg->ver_from;
+ def->ver_to = reg->ver_to;
+ def->module = reg->module;
+ }
+ else
+ {
+ def->itf = NULL;
+ def->module = NULL;
+ def->ver_from = 0;
+ def->ver_to = 0;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static cdp_ItfReg*
+cdp_Host_RegisterItf (const char* name, cdp_ApiVersion ver_from,
+ cdp_ApiVersion ver_to, cdp_Itf* itfvtbl,
+ cdp_Module* owner)
+{
+ cdp_ItfReg* itfreg;
+ cdp_ItfReg* newslot = NULL;
+ char* id_name;
+ const char* ctx;
+
+ if (!owner)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "No owner info supplied\n");
+ //return NULL;
+ }
+ if (!name || !*name || !itfvtbl)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Null or invalid interface (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+ ctx = cdp_GetModuleContext (owner, false);
+ if (!ctx)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Null or invalid context (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ // TODO: review version policy (below)
+ // enforce version policy and do not allow obsolete interfaces
+ // POLICY: all modules MUST be aware of recent API changes and will not
+ // be allowed to expose interfaces that support and/or utilize obsoleted
+ // API versions
+ if (ver_from < CDPAPI_VERSION_MIN)
+ ver_from = CDPAPI_VERSION_MIN;
+ if (ver_to < CDPAPI_VERSION_MIN)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Obsolete interface %s (from %s)\n",
+ name, cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ id_name = cdp_MakeContextName (ctx, name);
+
+ // check if interface already registered
+ for (itfreg = cdp_itfs; itfreg->used &&
+ (!itfreg->name || strcasecmp(itfreg->name, id_name) != 0 ||
+ ver_from < itfreg->ver_from || ver_to > itfreg->ver_to);
+ ++itfreg)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !itfreg->name)
+ newslot = itfreg;
+ }
+
+ if (itfreg >= cdp_itfs + MAX_REG_ITFS)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Interfaces limit reached\n");
+ HFree (id_name);
+ return NULL;
+ }
+ else if (itfreg->name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Interface %s already registered for these versions, "
+ "%s denied\n",
+ name, cdp_GetModuleName (owner, true));
+ HFree (id_name);
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = itfreg;
+ newslot->used = true;
+ // make next one a term
+ itfreg[1].builtin = false;
+ itfreg[1].used = false;
+ itfreg[1].name = NULL;
+ itfreg[1].itfvtbl = NULL;
+ }
+
+ newslot->name = id_name;
+ newslot->ver_from = ver_from;
+ newslot->ver_to = ver_to;
+ newslot->itfvtbl = itfvtbl;
+ newslot->module = owner;
+
+ return newslot;
+}
+
+static void
+cdp_Host_UnregisterItf (cdp_ItfReg* itfreg)
+{
+ if (itfreg < cdp_itfs || itfreg >= cdp_itfs + MAX_REG_ITFS ||
+ !itfreg->name || !itfreg->itfvtbl)
+ {
+ fprintf (stderr, "cdp_Host_UnregisterItf(): "
+ "Invalid or expired interface passed\n");
+ return;
+ }
+
+ if (!itfreg->builtin)
+ {
+ HFree (itfreg->name);
+ }
+ itfreg->module = NULL;
+ itfreg->name = NULL;
+ itfreg->itfvtbl = NULL;
+}
+
+static bool
+cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module* owner)
+{
+ cdp_ItfDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ def->reg = cdp_Host_RegisterItf (def->name, def->ver_from,
+ def->ver_to, def->itf, owner);
+ if (def->reg)
+ {
+ def->module = owner;
+ }
+ else
+ {
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static void
+cdp_Host_UnregisterItfs (cdp_ItfDef* defs)
+{
+ cdp_ItfDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->reg)
+ cdp_Host_UnregisterItf (def->reg);
+ }
+}
+
+/***********************************************************
+ * Event system *
+ ***********************************************************/
+
+cdp_EventReg*
+cdp_GetEventReg (const char* name)
+{
+ cdp_EventReg* evt;
+
+ for (evt = cdp_evts; evt->used &&
+ (!evt->name || strcasecmp(evt->name, name) != 0);
+ evt++)
+ ;
+ if (!evt->name)
+ {
+ cdp_api_error = CDPERR_NO_EVENT;
+ return NULL;
+ }
+
+ return evt;
+}
+
+// hopefully inlinable
+static cdp_Event
+cdp_EventFromReg (cdp_EventReg* reg)
+{
+ return (reg - cdp_evts) / sizeof (cdp_EventReg);
+}
+
+// hopefully inlinable
+static cdp_EventReg*
+cdp_RegFromEvent (cdp_Event event)
+{
+ return cdp_evts + event;
+}
+
+cdp_Event
+cdp_GetEvent (const char* name)
+{
+ cdp_EventReg* reg;
+
+ reg = cdp_GetEventReg (name);
+ return reg ? cdp_EventFromReg (reg) : CDP_EVENT_INVALID;
+}
+
+static cdp_EventBind*
+cdp_AllocEventBinds (cdp_EventBind* binds, uint32 ccur, uint32 cnew)
+{
+ cdp_EventBind* newbinds;
+ uint32 newsize;
+
+ newsize = cnew * sizeof (cdp_EventBind);
+ if (binds)
+ newbinds = HRealloc (binds, newsize);
+ else
+ newbinds = HMalloc (newsize);
+
+ if (cnew > ccur)
+ memset (newbinds + ccur, 0,
+ (cnew - ccur) * sizeof (cdp_EventBind));
+
+ return newbinds;
+}
+
+static cdp_Event
+cdp_Host_GetEvent (const char* name)
+{
+ return cdp_GetEvent (name);
+}
+
+static bool
+cdp_Host_GetEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+ cdp_EventReg* reg;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ // registration handle is not returned
+ def->reg = NULL;
+
+ reg = cdp_GetEventReg (def->name);
+ if (reg)
+ {
+ def->event = cdp_EventFromReg(reg);
+ def->name = reg->name; // set to cannonical name
+ def->module = reg->module;
+ }
+ else
+ {
+ def->event = CDP_EVENT_INVALID;
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static cdp_EventReg*
+cdp_Host_RegisterEvent (const char* name, cdp_Module* owner)
+{
+ cdp_EventReg* evtreg;
+ cdp_EventReg* newslot = NULL;
+ char* id_name;
+ const char* ctx;
+
+ if (!owner)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "No owner info supplied\n");
+ //return NULL;
+ }
+ if (!name || !*name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Null or invalid event (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+ ctx = cdp_GetModuleContext (owner, false);
+ if (!ctx)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Null or invalid context (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ id_name = cdp_MakeContextName (ctx, name);
+
+ // check if event already registered
+ for (evtreg = cdp_evts; evtreg->used &&
+ (!evtreg->name || strcasecmp(evtreg->name, id_name) != 0);
+ ++evtreg)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !evtreg->name)
+ newslot = evtreg;
+ }
+
+ if (evtreg >= cdp_evts + MAX_REG_EVENTS)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Event limit reached\n");
+ HFree (id_name);
+ return NULL;
+ }
+ else if (evtreg->name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Event %s already registered, "
+ "%s denied\n",
+ name, cdp_GetModuleName (owner, true));
+ HFree (id_name);
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = evtreg;
+ newslot->used = true;
+ // make next one a term
+ evtreg[1].builtin = false;
+ evtreg[1].used = false;
+ evtreg[1].name = NULL;
+ }
+
+ newslot->name = id_name;
+ newslot->module = owner;
+ newslot->binds = NULL;
+ newslot->bindslots = 0;
+
+ return newslot;
+}
+
+static void
+cdp_Host_UnregisterEvent (cdp_EventReg* evtreg)
+{
+ if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS ||
+ !evtreg->name)
+ {
+ fprintf (stderr, "cdp_Host_UnregisterEvent(): "
+ "Invalid or expired event passed\n");
+ return;
+ }
+
+ if (!evtreg->builtin)
+ {
+ HFree (evtreg->name);
+ }
+ evtreg->module = NULL;
+ evtreg->name = NULL;
+ if (evtreg->binds)
+ HFree (evtreg->binds);
+ evtreg->binds = NULL;
+ evtreg->bindslots = 0;
+}
+
+static bool
+cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module* owner)
+{
+ cdp_EventDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ def->reg = cdp_Host_RegisterEvent (def->name, owner);
+ if (def->reg)
+ {
+ def->module = owner;
+ }
+ else
+ {
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static void
+cdp_Host_UnregisterEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->reg)
+ cdp_Host_UnregisterEvent (def->reg);
+ }
+}
+
+static bool
+cdp_Host_SubscribeEvent (cdp_Event event, cdp_EventProc proc, cdp_Module* module)
+{
+ cdp_EventReg* reg = cdp_RegFromEvent (event);
+ cdp_EventBind* bind = NULL;
+ uint32 i;
+
+ if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS ||
+ !reg->name)
+ {
+ fprintf (stderr, "cdp_Host_SubscribeEvent(): "
+ "Invalid or expired event passed\n");
+ return false;
+ }
+
+ if (reg->binds)
+ {
+ // check for duplicate or find a new slot
+ for (i = 0, bind = reg->binds; i < reg->bindslots &&
+ (!bind->proc || bind->proc != proc);
+ ++i, ++bind)
+ ;
+ if (i >= reg->bindslots)
+ { // full - add more slots
+ reg->binds = cdp_AllocEventBinds (reg->binds,
+ reg->bindslots, reg->bindslots + EVENT_BIND_GROW);
+ bind = reg->binds + reg->bindslots;
+ reg->bindslots += EVENT_BIND_GROW;
+ }
+ else if (bind->proc == proc)
+ { // already bound
+ return true;
+ }
+ }
+ else
+ {
+ reg->binds = cdp_AllocEventBinds (NULL, 0, EVENT_BIND_GROW);
+ reg->bindslots = EVENT_BIND_GROW;
+ bind = reg->binds;
+ }
+
+ bind->proc = proc;
+ bind->module = module;
+
+ return true;
+}
+
+static void
+cdp_Host_UnsubscribeEvent (cdp_Event event, cdp_EventProc proc)
+{
+ cdp_EventReg* reg = cdp_RegFromEvent (event);
+ cdp_EventBind* bind = NULL;
+ uint32 i;
+
+ if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS ||
+ !reg->name)
+ { // event either expired or invalid
+ return;
+ }
+
+ if (!reg->binds || !reg->bindslots)
+ return; // hmm, no bindings
+
+ // check for duplicate or find a new slot
+ for (i = 0, bind = reg->binds; i < reg->bindslots &&
+ bind->proc != proc;
+ ++i, ++bind)
+ ;
+ if (i >= reg->bindslots)
+ return; // binding not found
+
+ bind->proc = NULL;
+ bind->module = NULL;
+}
+
+static bool
+cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module* module)
+{
+ cdp_EventDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->event != CDP_EVENT_INVALID && def->proc)
+ if (!cdp_Host_SubscribeEvent (def->event, def->proc, module))
+ ++errors;
+ }
+ return !errors;
+}
+
+static void
+cdp_Host_UnsubscribeEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->event != CDP_EVENT_INVALID && def->proc)
+ cdp_Host_UnsubscribeEvent (def->event, def->proc);
+ }
+}
+
+static cdp_EventResult
+cdp_Host_FireEvent (cdp_EventReg* evtreg, uint32 iparam, void* pparam)
+{
+ bool bHandled = false;
+ cdp_EventResult ret = 0;
+ cdp_Event event;
+ cdp_EventBind* bind;
+ uint32 i;
+
+ if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS ||
+ !evtreg->name)
+ {
+#ifdef DEBUG
+ fprintf (stderr, "cdp_Host_FireEvent(): Invalid event\n");
+#endif
+ return 0;
+ }
+
+ if (!evtreg->binds)
+ return 0; // no subscribers
+
+ event = cdp_EventFromReg (evtreg);
+
+ // call event procs in opposite order of binding
+ for (i = evtreg->bindslots, bind = evtreg->binds + i - 1;
+ !bHandled && i > 0;
+ --i, --bind)
+ {
+ if (bind->proc)
+ ret = bind->proc (event, iparam, pparam, &bHandled);
+ }
+ return ret;
+}