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