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