diff mcabber/mcabber/events.c @ 1685:1342df44c814

Improved events interface * User can pass additional arguments to event handler * MUC invitation reject now can be supplied a reason
author Myhailo Danylenko <isbear@ukrpost.net>
date Tue, 02 Feb 2010 22:44:18 +0100
parents 41c26b7d2890
children e6e89b1d7831
line wrap: on
line diff
--- a/mcabber/mcabber/events.c	Tue Jan 19 19:16:38 2010 +0200
+++ b/mcabber/mcabber/events.c	Tue Feb 02 22:44:18 2010 +0100
@@ -2,6 +2,7 @@
  * events.c     -- Events fonctions
  *
  * Copyright (C) 2006-2009 Mikael Berthe <mikael@lilotux.net>
+ * Copyrigth (C) 2010      Myhailo Danylenko <isbear@ukrposte.net>
  *
  * 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
@@ -24,157 +25,191 @@
 #include "events.h"
 #include "logprint.h"
 
+typedef struct {
+  char           *id;
+  char           *description;
+  time_t          timeout;
+  guint           source;
+  evs_callback_t  callback;
+  gpointer        data;
+  GDestroyNotify  notify;
+} evs_t;
+
 static GSList *evs_list; // Events list
 
-static eviqs *evs_find(const char *evid);
+static evs_t *evs_find(const char *evid);
+
+static gboolean evs_check_timeout (gpointer userdata)
+{
+  evs_t *event = userdata;
+  if (event->callback &&
+      !event->callback(EVS_CONTEXT_TIMEOUT, NULL, event->data)) {
+    evs_del(event->id);
+    return FALSE;
+  }
+  return TRUE; // XXX
+}
 
 //  evs_new(type, timeout)
-// Create an events structure.
-eviqs *evs_new(guint8 type, time_t timeout)
+// Create new event. If id is omitted, generates unique
+// numerical id (recommended). If timeout is specified, sets
+// up timeout source, that will call handler in timeout
+// context after specified number of seconds. If supplied id
+// already exists, returns NULL, calling destroy notifier, if
+// one is specified.
+const char *evs_new(const char *desc, const char *id, time_t timeout, evs_callback_t callback, gpointer udata, GDestroyNotify notify)
 {
   static guint evs_idn;
-  eviqs *new_evs;
-  time_t now_t;
+  evs_t *event;
   char *stridn;
 
-  if (!++evs_idn)
-    evs_idn = 1;
-  /* Check for wrapping, we shouldn't reuse ids */
-  stridn = g_strdup_printf("%d", evs_idn);
-  if (evs_find(stridn))  {
-    g_free(stridn);
-    // We could try another id but for now giving up should be fine...
+  if (!id) {
+    if (!++evs_idn)
+      evs_idn = 1;
+    /* Check for wrapping, we shouldn't reuse ids */
+    stridn = g_strdup_printf("%d", evs_idn);
+    if (evs_find(stridn))  {
+      g_free(stridn);
+      // We could try another id but for now giving up should be fine...
+      if (notify)
+        notify(udata);
+      return NULL;
+    }
+  } else if (!evs_find(id))
+    stridn = g_strdup(id);
+  else {
+    if (notify)
+      notify(udata);
     return NULL;
   }
 
-  new_evs = g_new0(eviqs, 1);
-  time(&now_t);
-  new_evs->ts_create = now_t;
+  event = g_new(evs_t, 1);
+
+  event->id          = stridn;
+  event->description = g_strdup(desc);
+  event->timeout     = timeout;
+  event->callback    = callback;
+  event->data        = udata;
+  event->notify      = notify;
+
   if (timeout)
-    new_evs->ts_expire = now_t + timeout;
-  new_evs->type = type;
-  new_evs->id = stridn;
+    g_timeout_add_seconds(timeout, evs_check_timeout, event);
 
-  if(!g_slist_length(evs_list))
-    g_timeout_add_seconds(20, evs_check_timeout, NULL);
-  evs_list = g_slist_append(evs_list, new_evs);
-  return new_evs;
+  evs_list = g_slist_append(evs_list, event);
+  return stridn;
 }
 
-int evs_del(const char *evid)
+static evs_t *evs_find(const char *evid)
 {
   GSList *p;
-  eviqs *i;
 
-  if (!evid) return 1;
+  if (!evid)
+    return NULL;
 
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
-    if (!strcmp(evid, i->id))
-      break;
-  }
-  if (p) {
-    g_free(i->id);
-    g_free(i->data);
-    g_free(i->desc);
-    g_free(i);
-    evs_list = g_slist_remove(evs_list, p->data);
-    return 0; // Ok, deleted
-  }
-  return -1;  // Not found
-}
-
-static eviqs *evs_find(const char *evid)
-{
-  GSList *p;
-  eviqs *i;
-
-  if (!evid) return NULL;
-
-  for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
+    evs_t *i = p->data;
     if (!strcmp(evid, i->id))
       return i;
   }
   return NULL;
 }
 
-//  evs_callback(evid, evcontext)
-// Callback processing for the specified event.
-// Return 0 in case of success, -1 if the evid hasn't been found.
-int evs_callback(const char *evid, guint evcontext)
+//  evs_del(evid)
+// Deletes event.
+// This will not call event handler, however this will
+// call destroy notify function.
+// Returns 0 in case of success, -1 if the evid hasn't been found.
+int evs_del(const char *evid)
 {
-  eviqs *i;
+  evs_t *event = evs_find(evid);
+
+  if (!event)
+    return -1;
+
+  if (event->notify)
+    event->notify(event->data);
+  if (event->source)
+    g_source_remove(event->source);
 
-  i = evs_find(evid);
-  if (!i) return -1;
+  evs_list = g_slist_remove(evs_list, event);
+  g_free(event->id);
+  g_free(event->description);
+  g_free(event);
+
+  return 0; // Ok, deleted
+}
 
-  // IQ processing
-  // Note: If xml_result is NULL, this is a timeout
-  if (i->callback)
-    (void)(*i->callback)(i, evcontext);
+//  evs_callback(evid, evcontext, argument)
+// Callback processing for the specified event.
+// If event handler will return FALSE, event will be destroyed.
+// Return 0 in case of success, -1 if the evid hasn't been found.
+// evcontext and argument are transparently passed to event handler.
+int evs_callback(const char *evid, guint context, const char *arg)
+{
+  evs_t *event;
 
-  evs_del(evid);
+  event = evs_find(evid);
+  if (!event)
+    return -1;
+
+  if (event->callback &&
+      !event->callback(context, arg, event->data))
+    evs_del(evid);
   return 0;
 }
 
-gboolean evs_check_timeout()
-{
-  time_t now_t;
-  GSList *p;
-  eviqs *i;
-
-  time(&now_t);
-  p = evs_list;
-  if (!p)
-    return FALSE;
-  while (p) {
-    i = p->data;
-    // We must get next IQ eviqs element now because the current one
-    // could be freed.
-    p = g_slist_next(p);
-
-    if ((!i->ts_expire && now_t > i->ts_create + EVS_MAX_TIMEOUT) ||
-        (i->ts_expire && now_t > i->ts_expire)) {
-      evs_callback(i->id, EVS_CONTEXT_TIMEOUT);
-    }
-  }
-  return TRUE;
-}
-
+//  evs_display_list()
+// Prints list of events to mcabber log window.
 void evs_display_list(void)
 {
   GSList *p;
-  eviqs *i;
 
   scr_LogPrint(LPRINT_LOGNORM, "Events list:");
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
+    evs_t *i = p->data;
     scr_LogPrint(LPRINT_LOGNORM,
-                 "Id: %-3s %s", i->id, (i->desc ? i->desc : ""));
+                 "Id: %-3s %s", i->id,
+                 (i->description ? i->description : ""));
   }
   scr_LogPrint(LPRINT_LOGNORM, "End of events list.");
 }
 
-//  evs_geteventslist(bool comp)
-// Return a singly-linked-list of events ids, for the completion system.
-// If comp is true, the string "list" is added (it's a completion argument).
-// Note: the caller should free the list (and data) after use.
-GSList *evs_geteventslist(int compl)
+//  evs_geteventslist()
+// Return a singly-linked-list of events ids.
+// Data in list should not be modified and can disappear,
+// you must strdup them, if you want them to persist.
+// Note: the caller should free the list after use.
+GSList *evs_geteventslist(void)
 {
   GSList *evidlist = NULL, *p;
-  eviqs *i;
 
   for (p = evs_list; p; p = g_slist_next(p)) {
-    i = p->data;
-    evidlist = g_slist_append(evidlist, g_strdup(i->id));
+    evs_t *i = p->data;
+    evidlist = g_slist_append(evidlist, i->id);
   }
 
-  if (compl) {
-    // Last item is the "list" subcommand.
-    evidlist = g_slist_append(evidlist, g_strdup("list"));
-  }
   return evidlist;
 }
 
+//  evs_deinit()
+// Frees all events.
+void evs_deinit(void)
+{
+  GSList *eel;
+  for (eel = evs_list; eel; eel = eel->next) {
+    evs_t *event = eel->data;
+    if (event->notify)
+      event->notify(event->data);
+    if (event->source)
+      g_source_remove(event->source);
+  
+    evs_list = g_slist_remove(evs_list, event);
+    g_free(event->id);
+    g_free(event->description);
+    g_free(event);
+  }
+  g_slist_free(evs_list);
+  evs_list = NULL;
+}
+
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */