Add missing include.
[monky] / src / llua.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Copyright (c) 2009 Toni Spets
7  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
8  *      (see AUTHORS)
9  * All rights reserved.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "conky.h"
26 #include "logging.h"
27 #include "build.h"
28
29 #include <ctype.h>
30
31 #ifdef LUA_EXTRAS
32 #include <tolua++.h>
33 #endif /* LUA_EXTRAS */
34
35 #ifdef HAVE_SYS_INOTIFY_H
36 #include <sys/inotify.h>
37
38 void llua_append_notify(const char *name);
39 void llua_rm_notifies(void);
40 static int llua_block_notify = 0;
41 #endif /* HAVE_SYS_INOTIFY_H */
42
43 #define MIN(a, b) ( (a) < (b) ? (a) : (b) )
44
45 static char *draw_pre_hook = 0;
46 static char *draw_post_hook = 0;
47 static char *startup_hook = 0;
48 static char *shutdown_hook = 0;
49
50 lua_State *lua_L = NULL;
51
52 static int llua_conky_parse(lua_State *L)
53 {
54         int n = lua_gettop(L);    /* number of arguments */
55         char *str;
56         char *buf = calloc(1, max_user_text);
57         if (n != 1) {
58                 lua_pushstring(L, "incorrect arguments, conky_parse(string) takes exactly 1 argument");
59                 lua_error(L);
60         }
61         if (!lua_isstring(L, 1)) {
62                 lua_pushstring(L, "incorrect argument (expecting a string)");
63                 lua_error(L);
64         }
65         str = strdup(lua_tostring(L, 1));
66         evaluate(str, buf, max_user_text);
67         lua_pushstring(L, buf);
68         free(str);
69         free(buf);
70         return 1;                 /* number of results */
71 }
72
73 static int llua_conky_set_update_interval(lua_State *L)
74 {
75         int n = lua_gettop(L);    /* number of arguments */
76         double value;
77         if (n != 1) {
78                 lua_pushstring(L, "incorrect arguments, conky_set_update_interval(number) takes exactly 1 argument");
79                 lua_error(L);
80         }
81         if (!lua_isnumber(L, 1)) {
82                 lua_pushstring(L, "incorrect argument (expecting a number)");
83                 lua_error(L);
84         }
85         value = lua_tonumber(L, 1);
86         set_update_interval(value);
87         return 0;                 /* number of results */
88 }
89
90 void llua_init(void)
91 {
92         const char *libs = PACKAGE_LIBDIR"/lib?.so;";
93         char *old_path, *new_path;
94         if (lua_L) return;
95         lua_L = lua_open();
96
97         /* add our library path to the lua package.cpath global var */
98         luaL_openlibs(lua_L);
99         lua_getglobal(lua_L, "package");
100         lua_getfield(lua_L, -1, "cpath");
101         old_path = strdup(lua_tostring(lua_L, -1));
102         new_path = malloc(strlen(old_path) + strlen(libs) + 1);
103         strcpy(new_path, libs);
104         strcat(new_path, old_path);
105         lua_pushstring(lua_L, new_path);
106         lua_setfield(lua_L, -3, "cpath");
107         lua_pop(lua_L, 2);
108         free(old_path);
109         free(new_path);
110
111         lua_pushstring(lua_L, PACKAGE_NAME" "VERSION" compiled "BUILD_DATE" for "BUILD_ARCH);
112         lua_setglobal(lua_L, "conky_build_info");
113
114         lua_pushstring(lua_L, VERSION);
115         lua_setglobal(lua_L, "conky_version");
116
117         lua_pushstring(lua_L, BUILD_DATE);
118         lua_setglobal(lua_L, "conky_build_date");
119
120         lua_pushstring(lua_L, BUILD_ARCH);
121         lua_setglobal(lua_L, "conky_build_arch");
122
123         lua_pushstring(lua_L, current_config);
124         lua_setglobal(lua_L, "conky_config");
125
126         lua_pushcfunction(lua_L, &llua_conky_parse);
127         lua_setglobal(lua_L, "conky_parse");
128
129         lua_pushcfunction(lua_L, &llua_conky_set_update_interval);
130         lua_setglobal(lua_L, "conky_set_update_interval");
131
132 #if defined(X11) && defined(LUA_EXTRAS)
133         /* register tolua++ user types */
134         tolua_open(lua_L);
135         tolua_usertype(lua_L, "Drawable");
136         tolua_usertype(lua_L, "Visual");
137         tolua_usertype(lua_L, "Display");
138 #endif /* X11 */
139 }
140
141 void llua_load(const char *script)
142 {
143         int error;
144         char path[DEFAULT_TEXT_BUFFER_SIZE];
145
146         llua_init();
147
148         to_real_path(path, script);
149         error = luaL_dofile(lua_L, path);
150         if (error) {
151                 NORM_ERR("llua_load: %s", lua_tostring(lua_L, -1));
152                 lua_pop(lua_L, 1);
153 #ifdef HAVE_SYS_INOTIFY_H
154         } else if (!llua_block_notify && inotify_fd != -1) {
155                 llua_append_notify(path);
156 #endif /* HAVE_SYS_INOTIFY_H */
157         }
158 }
159
160 /*
161  * Returns the first space-delimited token of the string starting at position *len.
162  * On return *len contains the length of the token. Spaces inside brackets are ignored, so that
163  * eg. '${foo bar}' is treated as a single token. Sets *len to zero and *str points to the end of
164  * the string when there are no more tokens.
165  */
166 static const char* tokenize(const char *str, size_t *len)
167 {
168         str += *len;
169         *len = 0;
170         while(str && isspace(*str))
171                 ++str;
172
173         size_t level = 0;
174         while(str[*len] && (level > 0 || !isspace(str[*len]))) {
175                 switch(str[*len]) {
176                         case '{': ++level; break;
177                         case '}': --level; break;
178                 }
179                 ++*len;
180         }
181
182         if(!str[*len] && level > 0)
183                 NORM_ERR("tokenize: improperly nested token: %s", str);
184
185         return str;
186 }
187
188 /*
189    llua_do_call does a flexible call to any Lua function
190 string: <function> [par1] [par2...]
191 retc: the number of return values expected
192  */
193 static char *llua_do_call(const char *string, int retc)
194 {
195         static char func[64];
196         int argc = 0;
197
198         size_t len = 0;
199
200         const char *ptr = tokenize(string, &len);
201
202         /* proceed only if the function name is present */
203         if (!len) {
204                 return NULL;
205         }
206
207         /* call only conky_ prefixed functions */
208         if(strncmp(ptr, LUAPREFIX, strlen(LUAPREFIX)) != 0) {
209                 snprintf(func, sizeof func, "%s", LUAPREFIX);
210         } else
211                 *func = 0;
212         strncat(func, ptr, MIN(len, sizeof(func) - strlen(func) - 1));
213
214         /* push the function name to stack */
215         lua_getglobal(lua_L, func);
216
217         /* parse all function parameters from args and push them to the stack */
218         while( ptr = tokenize(ptr, &len), len) {
219                 lua_pushlstring(lua_L, ptr, len);
220                 argc++;
221         }
222
223         if(lua_pcall(lua_L, argc, retc, 0) != 0) {
224                 NORM_ERR("llua_do_call: function %s execution failed: %s", func, lua_tostring(lua_L, -1));
225                 lua_pop(lua_L, -1);
226                 return NULL;
227         }
228
229         return func;
230 }
231
232 #if 0
233 /*
234  * same as llua_do_call() except passes everything after func as one arg.
235  */
236 static char *llua_do_read_call(const char *function, const char *arg, int retc)
237 {
238         static char func[64];
239         snprintf(func, 64, "conky_%s", function);
240
241         /* push the function name to stack */
242         lua_getglobal(lua_L, func);
243
244         /* push function parameter to the stack */
245         lua_pushstring(lua_L, arg);
246
247         if (lua_pcall(lua_L, 1, retc, 0) != 0) {
248                 NORM_ERR("llua_do_call: function %s execution failed: %s", func, lua_tostring(lua_L, -1));
249                 lua_pop(lua_L, -1);
250                 return NULL;
251         }
252
253         return func;
254 }
255 #endif
256
257 /* call a function with args, and return a string from it (must be free'd) */
258 static char *llua_getstring(const char *args)
259 {
260         char *func;
261         char *ret = NULL;
262
263         if(!lua_L) return NULL;
264
265         func = llua_do_call(args, 1);
266         if (func) {
267                 if (!lua_isstring(lua_L, -1)) {
268                         NORM_ERR("llua_getstring: function %s didn't return a string, result discarded", func);
269                 } else {
270                         ret = strdup(lua_tostring(lua_L, -1));
271                         lua_pop(lua_L, 1);
272                 }
273         }
274
275         return ret;
276 }
277
278 #if 0
279 /* call a function with args, and return a string from it (must be free'd) */
280 static char *llua_getstring_read(const char *function, const char *arg)
281 {
282         char *func;
283         char *ret = NULL;
284
285         if(!lua_L) return NULL;
286
287         func = llua_do_read_call(function, arg, 1);
288         if (func) {
289                 if(!lua_isstring(lua_L, -1)) {
290                         NORM_ERR("llua_getstring_read: function %s didn't return a string, result discarded", func);
291                 } else {
292                         ret = strdup(lua_tostring(lua_L, -1));
293                         lua_pop(lua_L, 1);
294                 }
295         }
296
297         return ret;
298 }
299 #endif
300
301 /* call a function with args, and put the result in ret */
302 static int llua_getnumber(const char *args, double *ret)
303 {
304         char *func;
305
306         if(!lua_L) return 0;
307
308         func = llua_do_call(args, 1);
309         if(func) {
310                 if(!lua_isnumber(lua_L, -1)) {
311                         NORM_ERR("llua_getnumber: function %s didn't return a number, result discarded", func);
312                 } else {
313                         *ret = lua_tonumber(lua_L, -1);
314                         lua_pop(lua_L, 1);
315                         return 1;
316                 }
317         }
318         return 0;
319 }
320
321 void llua_close(void)
322 {
323 #ifdef HAVE_SYS_INOTIFY_H
324         llua_rm_notifies();
325 #endif /* HAVE_SYS_INOTIFY_H */
326         if (draw_pre_hook) {
327                 free(draw_pre_hook);
328                 draw_pre_hook = 0;
329         }
330         if (draw_post_hook) {
331                 free(draw_post_hook);
332                 draw_post_hook = 0;
333         }
334         if (startup_hook) {
335                 free(startup_hook);
336                 startup_hook = 0;
337         }
338         if (shutdown_hook) {
339                 free(shutdown_hook);
340                 shutdown_hook = 0;
341         }
342         if(!lua_L) return;
343         lua_close(lua_L);
344         lua_L = NULL;
345 }
346
347 #ifdef HAVE_SYS_INOTIFY_H
348 struct _lua_notify_s {
349         int wd;
350         char name[DEFAULT_TEXT_BUFFER_SIZE];
351         struct _lua_notify_s *next;
352 };
353 static struct _lua_notify_s *lua_notifies = 0;
354
355 static struct _lua_notify_s *llua_notify_list_do_alloc(const char *name)
356 {
357         struct _lua_notify_s *ret = malloc(sizeof(struct _lua_notify_s));
358         memset(ret, 0, sizeof(struct _lua_notify_s));
359         strncpy(ret->name, name, DEFAULT_TEXT_BUFFER_SIZE);
360         return ret;
361 }
362
363 void llua_append_notify(const char *name)
364 {
365         /* do it */
366         struct _lua_notify_s *new_tail = 0;
367         if (!lua_notifies) {
368                 /* empty, fresh new digs */
369                 new_tail = lua_notifies = llua_notify_list_do_alloc(name);
370         } else {
371                 struct _lua_notify_s *tail = lua_notifies;
372                 while (tail->next) {
373                         tail = tail->next;
374                 }
375                 // should be @ the end now
376                 new_tail = llua_notify_list_do_alloc(name);
377                 tail->next = new_tail;
378         }
379         new_tail->wd = inotify_add_watch(inotify_fd,
380                         new_tail->name,
381                         IN_MODIFY);
382 }
383
384 void llua_rm_notifies(void)
385 {
386         /* git 'er done */
387         struct _lua_notify_s *head = lua_notifies;
388         struct _lua_notify_s *next = 0;
389         if (!lua_notifies) return;
390         inotify_rm_watch(inotify_fd, head->wd);
391         if (head->next) next = head->next;
392         free(head);
393         while (next) {
394                 head = next;
395                 next = head->next;
396                 inotify_rm_watch(inotify_fd, head->wd);
397                 free(head);
398         }
399         lua_notifies = 0;
400 }
401
402 void llua_inotify_query(int wd, int mask)
403 {
404         struct _lua_notify_s *head = lua_notifies;
405         if (mask & IN_MODIFY || mask & IN_IGNORED) {
406                 /* for whatever reason, i keep getting IN_IGNORED when the file is
407                  * modified */
408                 while (head) {
409                         if (head->wd == wd) {
410                                 llua_block_notify = 1;
411                                 llua_load(head->name);
412                                 llua_block_notify = 0;
413                                 NORM_ERR("Lua script '%s' reloaded", head->name);
414                                 if (mask & IN_IGNORED) {
415                                         /* for some reason we get IN_IGNORED here
416                                          * sometimes, so we need to re-add the watch */
417                                         head->wd = inotify_add_watch(inotify_fd,
418                                                         head->name,
419                                                         IN_MODIFY);
420                                 }
421                                 return;
422                         }
423                         head = head->next;
424                 }
425         }
426 }
427 #endif /* HAVE_SYS_INOTIFY_H */
428
429 void llua_set_number(const char *key, double value)
430 {
431         lua_pushnumber(lua_L, value);
432         lua_setfield(lua_L, -2, key);
433 }
434
435 void llua_set_startup_hook(const char *args)
436 {
437         if (startup_hook) free(startup_hook);
438         startup_hook = strdup(args);
439 }
440
441 void llua_set_shutdown_hook(const char *args)
442 {
443         if (shutdown_hook) free(shutdown_hook);
444         shutdown_hook = strdup(args);
445 }
446
447 void llua_startup_hook(void)
448 {
449         if (!lua_L || !startup_hook) return;
450         llua_do_call(startup_hook, 0);
451 }
452
453 void llua_shutdown_hook(void)
454 {
455         if (!lua_L || !shutdown_hook) return;
456         llua_do_call(shutdown_hook, 0);
457 }
458
459 #ifdef X11
460 void llua_draw_pre_hook(void)
461 {
462         if (!lua_L || !draw_pre_hook) return;
463         llua_do_call(draw_pre_hook, 0);
464 }
465
466 void llua_draw_post_hook(void)
467 {
468         if (!lua_L || !draw_post_hook) return;
469         llua_do_call(draw_post_hook, 0);
470 }
471
472 void llua_set_draw_pre_hook(const char *args)
473 {
474         draw_pre_hook = strdup(args);
475 }
476
477 void llua_set_draw_post_hook(const char *args)
478 {
479         draw_post_hook = strdup(args);
480 }
481
482 #ifdef LUA_EXTRAS
483 void llua_set_userdata(const char *key, const char *type, void *value)
484 {
485         tolua_pushusertype(lua_L, value, type);
486         lua_setfield(lua_L, -2, key);
487 }
488 #endif /* LUA_EXTRAS */
489
490 void llua_setup_window_table(int text_start_x, int text_start_y, int text_width, int text_height)
491 {
492         if (!lua_L) return;
493         lua_newtable(lua_L);
494
495         if (output_methods & TO_X) {
496 #ifdef LUA_EXTRAS
497                 llua_set_userdata("drawable", "Drawable", (void*)&window.drawable);
498                 llua_set_userdata("visual", "Visual", window.visual);
499                 llua_set_userdata("display", "Display", display);
500 #endif /* LUA_EXTRAS */
501
502
503                 llua_set_number("width", window.width);
504                 llua_set_number("height", window.height);
505                 llua_set_number("border_inner_margin", window.border_inner_margin);
506                 llua_set_number("border_outer_margin", window.border_outer_margin);
507                 llua_set_number("border_width", window.border_width);
508
509                 llua_set_number("text_start_x", text_start_x);
510                 llua_set_number("text_start_y", text_start_y);
511                 llua_set_number("text_width", text_width);
512                 llua_set_number("text_height", text_height);
513
514                 lua_setglobal(lua_L, "conky_window");
515         }
516 }
517
518 void llua_update_window_table(int text_start_x, int text_start_y, int text_width, int text_height)
519 {
520         if (!lua_L) return;
521
522         lua_getglobal(lua_L, "conky_window");
523         if (lua_isnil(lua_L, -1)) {
524                 /* window table isn't populated yet */
525                 lua_pop(lua_L, 1);
526                 return;
527         }
528
529         llua_set_number("width", window.width);
530         llua_set_number("height", window.height);
531
532         llua_set_number("text_start_x", text_start_x);
533         llua_set_number("text_start_y", text_start_y);
534         llua_set_number("text_width", text_width);
535         llua_set_number("text_height", text_height);
536
537         lua_setglobal(lua_L, "conky_window");
538 }
539 #endif /* X11 */
540
541 void llua_setup_info(struct information *i, double u_interval)
542 {
543         if (!lua_L) return;
544         lua_newtable(lua_L);
545
546         llua_set_number("update_interval", u_interval);
547         llua_set_number("uptime", i->uptime);
548
549         lua_setglobal(lua_L, "conky_info");
550 }
551
552 void llua_update_info(struct information *i, double u_interval)
553 {
554         if (!lua_L) return;
555
556         lua_getglobal(lua_L, "conky_info");
557         if (lua_isnil(lua_L, -1)) {
558                 /* window table isn't populated yet */
559                 lua_pop(lua_L, 1);
560                 return;
561         }
562
563         llua_set_number("update_interval", u_interval);
564         llua_set_number("uptime", i->uptime);
565
566         lua_setglobal(lua_L, "conky_info");
567 }
568
569 void print_lua(struct text_object *obj, char *p, int p_max_size)
570 {
571         char *str = llua_getstring(obj->data.s);
572         if (str) {
573                 snprintf(p, p_max_size, "%s", str);
574                 free(str);
575         }
576 }
577
578 void print_lua_parse(struct text_object *obj, char *p, int p_max_size)
579 {
580         char *str = llua_getstring(obj->data.s);
581         if (str) {
582                 evaluate(str, p, p_max_size);
583                 free(str);
584         }
585 }
586
587 void print_lua_bar(struct text_object *obj, char *p, int p_max_size)
588 {
589         double per;
590         if (llua_getnumber(obj->data.s, &per)) {
591                 new_bar(obj, p, p_max_size, (per/100.0 * 255));
592         }
593 }
594
595 #ifdef X11
596 void print_lua_graph(struct text_object *obj, char *p, int p_max_size)
597 {
598         double per;
599
600         if (!p_max_size)
601                 return;
602
603         if (llua_getnumber(obj->data.s, &per)) {
604                 new_graph(obj, p, p_max_size, per);
605         }
606 }
607 #endif /* X11 */
608
609 void print_lua_gauge(struct text_object *obj, char *p, int p_max_size)
610 {
611         double per;
612
613         if (!p_max_size)
614                 return;
615
616         if (llua_getnumber(obj->data.s, &per)) {
617                 new_gauge(obj, p, p_max_size, (per/100.0 * 255));
618         }
619 }