Event dispatching: Improvement & bugfix
authorRui Guo <firemeteor.guo@gmail.com>
Sun, 7 Jun 2009 16:03:38 +0000 (8 00:03 +0800)
committerRui Guo <firemeteor.guo@gmail.com>
Sun, 7 Jun 2009 16:03:38 +0000 (8 00:03 +0800)
1. The reg/unreg interfaces are now called hook/unhook, the listen_to method
no longer exists.
2. Implement unregister logic, The privlege is unsigned now, and default to 31.
3. Check to prevent double reg/unreg the same handler.
4. Modify cmdcallback demo to enable toogle of logging.

src/lua.c
src/script.c
src/script.h
src/scripts/cmdcallback.lua

index aabfd6e..490b96e 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
@@ -43,6 +43,7 @@ extern struct layer *flayer;
 
 static int LuaDispatch(void *handler, const char *params, va_list va);
 static int LuaRegEvent(lua_State *L);
+static int LuaUnRegEvent(lua_State *L);
 
 /** Template {{{ */
 
@@ -260,7 +261,7 @@ window_get_monitor_status(lua_State *L)
 
 static const luaL_reg window_methods[] = {
   {"get_monitor_status", window_get_monitor_status},
-  {"listen_to", LuaRegEvent},
+  {"hook", LuaRegEvent},
   {0, 0}
 };
 
@@ -592,7 +593,8 @@ static const luaL_reg screen_methods[] = {
   {"display", screen_get_display},
   {"command", screen_exec_command},
   {"append_msg", screen_append_msg},
-  {"listen_to", LuaRegEvent},
+  {"hook", LuaRegEvent},
+  {"unhook", LuaUnRegEvent},
   {0, 0}
 };
 
@@ -815,19 +817,20 @@ LuaCommandExecuted(const char *command, const char **args, int argc)
   return LuaCallProcess("command_executed", params);
 }*/
 
+#define SEVNAME_MAX 30
 static int
 LuaRegEvent(lua_State *L)
 {
-  /* signature: listen_to(obj, event, handler, priv);
-   *        or: listen_to(event, handler, priv)
+  /* signature: hook(obj, event, handler, priv);
+   *        or: hook(event, handler, priv)
    *   returns: A ticket for later unregister. */
   int idx = 1, argc = lua_gettop(L);
-  int priv = 0;
+  int priv = 31; /* Default privilege */
 
   char *obj = NULL;
   const char *objname = "global";
 
-  static char evbuf[30];
+  static char evbuf[SEVNAME_MAX];
   const char *event, *handler;
 
   struct script_event *sev;
@@ -845,7 +848,7 @@ LuaRegEvent(lua_State *L)
     }
 
   event = luaL_checkstring(L, idx++);
-  snprintf(evbuf, 30, "%s_%s", objname, event);
+  snprintf(evbuf, SEVNAME_MAX, "%s_%s", objname, event);
   handler = luaL_checkstring(L, idx++);
   if (idx <= argc)
     priv = luaL_checkinteger(L, idx);
@@ -860,7 +863,11 @@ LuaRegEvent(lua_State *L)
       l->handler = (void *)handler;
       l->priv = priv;
       l->dispatcher = LuaDispatch;
-      register_listener(sev, l); 
+      if (register_listener(sev, l))
+        {
+          free(l);
+          return luaL_error(L, "Handler %s has already been registered", handler);
+        }
       /*Return the handler for un-register*/
       lua_pushlightuserdata(L, l);
     }
@@ -874,27 +881,35 @@ static int
 LuaUnRegEvent(lua_State *L)
 {
   /* signature: release([obj], ticket, handler)
-   *   returns: zero of success, non-zero otherwise */
+   *   returns: true of success, false otherwise */
   int idx = 1;
   struct listener *l;
   const char *handler;
-  if (lua_isuserdata(L, idx))
-    idx++;
 
-  luaL_checktype(L, idx, LUA_TLIGHTUSERDATA);
+  /* If the param is not as expected */
+  if (!lua_islightuserdata(L, idx))
+    {
+      /* Then it should be the userdata to be ignore, but if not ...  */
+      if (!lua_isuserdata(L, idx))
+        luaL_checktype(L, idx, LUA_TLIGHTUSERDATA);
+      idx++;
+      luaL_checktype(L, idx, LUA_TLIGHTUSERDATA);
+    }
+
   l = (struct listener*)lua_touserdata(L, idx++);
   handler = luaL_checkstring(L, idx++);
 
   /*Validate the listener structure*/
-  if (strcmp((char *)handler, (char *)l->handler))
+  if (!l || !l->handler 
+      || strncmp((char *)handler, (char *)l->handler, SEVNAME_MAX))
     {
       /* invalid */
-      lua_pushinteger(L, 1);
+      lua_pushboolean(L,0);
     }
   else
     {
       unregister_listener(l);
-      lua_pushinteger(L, 0);
+      lua_pushboolean(L, 1);
     }
   
   return 1;
index 3faf4bc..4759c27 100644 (file)
@@ -187,32 +187,51 @@ object_get_event(char *obj, const char *name)
 }
 
 /* Put a listener in a proper position in the chain 
- * according to the privlege.*/
-#define PRIV_MIN  -31
-void
+ * according to the privlege.
+ * Not insert duplicate entry. return zero if successful.*/
+int
 register_listener(struct script_event *ev, struct listener *l)
 {
-  int priv;
-  struct listener head, *p;
-  head.chain = ev->listeners;
-  p = &head;
+  unsigned int priv = l->priv;
+  struct listener *p, *iter = &ev->listeners;
 
-  if (l->priv < PRIV_MIN)
-    l->priv = PRIV_MIN;
-  priv = l->priv;
+  while(iter->chain && priv >= iter->chain->priv)
+    {
+      iter = iter->chain;
+      /* return if duplicate found*/
+      if (iter->handler == l->handler
+          && iter->dispatcher == l->dispatcher)
+        return 1;
+    }
+  p = iter;
 
-  while (p->chain && priv >= p->chain->priv)
-    p = p->chain;
+  while(iter->chain)
+    {
+      iter = iter->chain;
+      /* return if duplicate found*/
+      if (iter->handler == l->handler
+          && iter->dispatcher == l->dispatcher)
+        return 1;
+    }
 
   l->chain = p->chain;
+  l->prev = p;
+  if (p->chain)
+    p->chain->prev = l;
   p->chain = l;
-  ev->listeners = head.chain;
+  return 0;
 }
 
 void
 unregister_listener(struct listener *l)
 {
-  /*TODO*/
+  struct listener *p = l->prev;
+  p->chain = l->chain;
+  if (l->chain)
+    l->chain->prev = p;
+  l->chain = l->prev = 0;
+  l->handler = 0;
+  free(l);
 }
 
 /* Trigger event with given parameters.*/
@@ -228,7 +247,7 @@ trigger_sevent(struct script_event *ev, VA_DOTS)
     return 0;
 
   /*process the chain in order, stop if any of the handler returns true.*/
-  chain = ev->listeners;
+  chain = ev->listeners.chain;
   params = ev->params;
   while (chain)
     {
@@ -237,6 +256,7 @@ trigger_sevent(struct script_event *ev, VA_DOTS)
       VA_END(va);
       if (res)
         break;
+      chain = chain->chain;
     }
 
   return res;
index 10762f8..c268eb5 100644 (file)
@@ -50,6 +50,7 @@ void ScriptCmd __P((int argc, const char **argv));
 
 /***Script events***/
 
+struct script_event;
 /* Script event listener */
 struct listener 
 {
@@ -63,8 +64,9 @@ struct listener
   int (*dispatcher) __P((void *handler, const char *params, va_list va)); 
   
   /* smaller means higher privilege.*/
-  int priv;
+  unsigned int priv;
   struct listener *chain;
+  struct listener *prev;
 };
 
 /* the script_event structure needs to be zeroed before using.
@@ -74,7 +76,7 @@ struct script_event
 {
   /* expected parameter description of this event. */
   char *params;
-  struct listener *listeners;
+  struct listener listeners;
 };
 struct script_event* object_get_event __P((char *obj, const char *name));
 int trigger_sevent(struct script_event *ev, VA_DOTS);
index 7fbd9bc..d905954 100644 (file)
@@ -12,4 +12,11 @@ function command_executed(name, args)
   f:close()
 end
 
-screen.listen_to("cmdexecuted", "command_executed")
+function toogle_cmd_log()
+  if  (type(ticket) == "nil") then
+    ticket = screen.hook("cmdexecuted", "command_executed")
+  else
+    screen.unhook(ticket, "command_executed")
+    ticket = nil
+  end
+end