Unhook the event handler if it causes error.
[screen-lua.git] / src / script.c
blob0a3ca5a641168c69b6044f4389c3593ca765ff81
1 /* Copyright (c) 2008 Sadrul Habib Chowdhury (sadrul@users.sf.net)
2 * 2009 Rui Guo (firemeteor.guo@gmail.com)
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3, or (at your option)
7 * any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program (see the file COPYING); if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19 ****************************************************************
22 #include "config.h"
23 #include "screen.h"
24 #include "extern.h"
25 #include <stddef.h>
27 /*Binding structure & functions*/
29 struct binding *bindings = NULL;
31 static void
32 register_binding (struct binding *new_binding)
34 if (!new_binding->registered)
36 new_binding->b_next = bindings;
37 bindings = new_binding;
38 new_binding->registered = 1;
42 #ifdef LUA_BINDING
43 extern struct binding lua_binding;
44 #endif
46 void LoadBindings(void)
48 #ifdef LUA_BINDING
49 register_binding(&lua_binding);
50 #endif
53 void
54 FinalizeBindings (void)
56 struct binding *binding=bindings;
57 while(binding)
59 if (binding->inited)
60 binding->bd_Finit();
61 binding = binding->b_next;
65 void
66 ScriptSource(int argc, const char **argv)
68 int ret = 0;
69 int async = 0;
70 const char *bd_select = 0, *script;
71 struct binding *binding = bindings;
73 /* Parse the commandline options
74 * script source [-async|-a] [-binding|-b <binding>] script
76 while (*argv && **argv == '-')
78 /* check for (-a | -async) */
79 const char *arg = *argv;
80 if ((arg[1] == 'a' && !arg[2])
81 || strcmp(arg, "-async") == 0)
82 async = 1;
83 /* check for (-b | -binding) */
84 else if ((arg[1] == 'b' && !arg[2])
85 || strcmp(arg, "-binding") == 0)
87 argv++;
88 bd_select = *argv;
90 argv++;
92 script = *argv;
94 while (binding)
96 if (!bd_select || strcmp(bd_select, binding->name) == 0)
98 /*dynamically initialize the binding*/
99 if (!binding->inited)
101 binding->bd_Init();
102 binding->inited = 1;
105 /*and source the script*/
106 ret = binding->bd_Source(script, async);
107 if (ret)
108 break;
110 binding = binding->b_next;
112 if (!ret)
113 LMsg(0, "Could not source specified script %s", script);
117 ScriptCall(int argc, const char **argv)
119 int ret = 0;
120 struct binding *binding = bindings;
121 const char *bd_select = 0, *func;
122 /* Parse the commandline options
123 * script call [-binding|-b <binding>] function
125 while (*argv && **argv == '-')
127 const char *arg = *argv;
128 /* check for (-b | -binding) */
129 if ((arg[1] == 'b' && !arg[2])
130 || strcmp(arg, "-binding") == 0)
132 argv++;
133 bd_select = *argv;
135 argv++;
137 func = *argv;
138 argv++;
140 while (binding)
142 if (!bd_select || strcmp(bd_select, binding->name) == 0)
144 ret = binding->bd_call(func, argv);
145 if (ret)
146 break;
148 binding = binding->b_next;
151 if (!ret)
152 LMsg(0, "Failed to run specified script coummand '%s'", func);
153 return ret;
156 void
157 ScriptCmd(int argc, const char **argv)
159 const char * sub = *argv;
160 argv++;argc--;
161 if (!strcmp(sub, "call"))
162 ScriptCall(argc, argv);
163 else if (!strcmp(sub, "source"))
164 ScriptSource(argc, argv);
167 /* Event notification handling */
169 struct gevents globalevents;
171 /* To add a new event, introduce a field for that event to the object in
172 * question, and don't forget to put an descriptor here. NOTE: keep the
173 * name field sorted in alphabet order, the searching relies on it.
175 * the params string specifies the expected parameters. The length of the
176 * string equals to the number of parameters. Each char specifies the type of
177 * the parameter, with its meaning similar to those in printf().
179 * s: string (char *)
180 * S: string array (char **)
181 * i: signed int
182 * d: display
183 * c: canvas
186 struct sev_description {
187 char *name;
188 char *params;
189 int offset;
190 } event_table[] = {
191 /* NOTE: Manually sorted in Alphabet order! */
193 /* Display events*/
194 {"display_onidle", "d", offsetof(struct display, d_sev.onidle)},
195 /* Global events */
196 {"global_cmdexecuted", "sS", offsetof(struct gevents, cmdexecuted)},
197 {"global_detached", "di", offsetof(struct gevents, detached)},
198 /* The command "detach" triggers both 'cmdexecuted' and 'detached' events.
199 However, we need the 'detached' event to trigger callbacks from remote detaches.
201 /* fore window changed to window w on display d. */
202 {"global_forechanged", "dw", offsetof(struct gevents, forechanged)},
203 {"global_onattach", "d", offsetof(struct gevents, onattach)},
204 {"global_oncreatewindow", "w", offsetof(struct gevents, window_create)},
205 {"global_processcaption", "c", offsetof(struct gevents, processcaption)},
207 /* Window events */
208 {"window_can_resize", "", offsetof(struct win, w_sev.canresize)},
209 {"window_onactivity", "w", offsetof(struct win, w_sev.onactivity)},
210 {"window_onclose", "w", offsetof(struct win, w_sev.onclose)},
211 {"window_onfocus", "dw", offsetof(struct win, w_sev.onfocus)},
212 /* last view of window w disappeared on display d. */
213 {"window_onhide", "dw", offsetof(struct win, w_sev.onhide)},
214 /* window w lost its focus on display d. */
215 {"window_onleave", "dw", offsetof(struct win, w_sev.onleave)},
216 {"window_onoutput", "ws", offsetof(struct win, w_sev.onoutput)},
217 {"window_onresize", "w", offsetof(struct win, w_sev.onresize)},
218 /* window w first showed up on display d. When there are regions, it can
219 * still be shown while losing focus. */
220 {"window_onshow", "dw", offsetof(struct win, w_sev.onshow)},
221 {"window_onsilent", "w", offsetof(struct win, w_sev.onsilent)},
224 /* Get the event queue with the given name in the obj. If the obj is NULL,
225 * global events are searched. If no event is found, a NULL is returned.
227 struct script_event *
228 object_get_event(char *obj, const char *name)
230 int lo, hi, n, cmp;
231 if (!obj)
232 obj = (char *)&globalevents;
234 lo = 0;
235 n = hi = sizeof(event_table) / sizeof(struct sev_description);
236 while (lo < hi)
238 int half;
239 half = (lo + hi) >> 1;
240 cmp = strcmp(name, event_table[half].name);
241 if (cmp > 0)
242 lo = half + 1;
243 else
244 hi = half;
247 if (lo >= n || strcmp(name, event_table[lo].name))
248 return 0;
249 else
251 /*found an entry.*/
252 struct script_event *res;
253 res = (struct script_event *) (obj + event_table[lo].offset);
254 /*Setup the parameter record.*/
255 res->params = event_table[lo].params;
256 return res;
260 /* Put a listener in a proper position in the chain
261 * according to the privlege.
262 * Not insert duplicate entry. return zero if successful.*/
264 register_listener(struct script_event *ev, struct listener *l)
266 unsigned int priv = l->priv;
267 struct listener *p, *iter = &ev->listeners;
269 while(iter->chain && priv >= iter->chain->priv)
271 iter = iter->chain;
273 p = iter;
275 l->chain = p->chain;
276 l->prev = p;
277 if (p->chain)
278 p->chain->prev = l;
279 p->chain = l;
280 return 0;
283 void
284 unregister_listener(struct listener *l, int unlink_only)
286 struct listener *p = l->prev;
287 p->chain = l->chain;
288 if (l->chain)
289 l->chain->prev = p;
290 l->chain = l->prev = 0;
291 l->handler = 0;
292 if (!unlink_only)
293 free(l);
296 /* Trigger event with given parameters.*/
298 trigger_sevent(struct script_event *ev, VA_DOTS)
300 int res = 0;
301 struct listener *chain, *tmp;
302 char *params;
303 int err;
304 VA_LIST(va);
305 /*invalid or un-registered event structure*/
306 if (!ev || !ev->params)
307 return 0;
309 /*process the chain in order, stop if any of the handler returns true.*/
310 chain = ev->listeners.chain;
311 params = ev->params;
312 while (chain)
314 VA_START(va, ev);
315 res = chain->bd->bd_dispatch(chain->handler, &err, params, va);
316 VA_END(va);
317 tmp = chain;
318 chain = chain->chain;
319 if (err) {
320 unregister_listener(tmp, 1);
321 } else if (res)
322 break;
325 return res;
328 #define ALL_SCRIPTS(fn, params, stop) do { \
329 struct binding *iter; \
330 for (iter = bindings; iter; iter = iter->b_next) \
332 if (iter->fns && iter->fns->fn && (ret = (iter->fns->fn params)) && stop) \
333 break; \
335 } while (0)
337 /* Broker functions */
339 /* Broker hash */
340 struct broker *b_hash[255];
342 #define DELETED ((struct broker*)1)
343 #define BHASH(o) (((int)(o) >> sizeof(struct broker *)) & 255)
344 struct broker **broker_from_obj(void *obj)
346 struct broker **bucket = b_hash + BHASH(obj);
347 while (*bucket && (*bucket)->obj != obj)
349 bucket = &(*bucket)->b_next;
351 return bucket;
354 struct broker *
355 get_obj_broker(void *obj)
357 struct broker **bucket = broker_from_obj(obj);
358 if (!obj)
359 return NULL;
361 /* Not found, allocate a new one */
362 if (!*bucket)
364 struct broker *tmp = (struct broker *)malloc(sizeof(struct broker));
365 if (!tmp)
366 return NULL;
368 *bucket = tmp;
369 tmp->obj = obj;
370 tmp->ref = 0;
371 tmp->valid = 1;
372 tmp->b_next = NULL;
374 else
376 /* Assertion */
377 if (!(*bucket)->valid)
378 exit(1);
380 (*bucket)->ref++;
382 return *bucket;
385 void * get_broker_obj(struct broker **pbroker)
387 struct broker *broker = *pbroker;
388 if (!broker)
389 return NULL;
391 if (broker->valid)
392 return broker->obj;
394 /* Clear the invalid reference, and decrease the ref-count. */
395 broker_unref(pbroker);
396 return NULL;
399 void broker_inv_obj(void *obj)
401 struct broker *n, **b = broker_from_obj(obj);
402 if (*b) {
403 (*b)->valid = 0;
404 /* Unplug the broker from the hash. */
405 n = *b;
406 *b = (*b)->b_next;
407 n->b_next = 0;
411 void broker_unref(struct broker **pbroker)
413 struct broker *broker = *pbroker;
414 *pbroker = NULL;
415 if (--broker->ref == 0)
417 if (broker->valid) {
418 struct broker *n, **b = broker_from_obj(broker->obj);
419 if (*b) {
420 n = (*b)->b_next;
421 free(*b);
422 *b = n;
423 } else {
424 /* FIXME:Should not happen! But act better.*/
425 exit(1);
427 } else {
428 /* It had been unplugged before. */
429 free(broker);