79c03e9274b048a3444b00c34371d5f354e6ae5b
[obnox/wireshark/wip.git] / plugins / lua / elua_gui.c
1 /*
2  *  lua_gui.c
3  *  
4  * (c) 2006, Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
5  * 
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #include "elua.h"
28
29 static const funnel_ops_t* ops = NULL;
30
31 struct _lua_menu_data {
32     lua_State* L;
33     int cb_ref;
34     int data_ref;
35 };
36
37 static int menu_cb_error_handler(lua_State* L) {
38     const gchar* error =  lua_tostring(L,1);
39     report_failure("Lua: Error During execution of Menu Callback:\n %s",error);
40     return 0;    
41 }
42
43 ELUA_FUNCTION lua_gui_enabled(lua_State* L) { /* Checks whether the GUI facility is enabled. */
44     lua_pushboolean(L,GPOINTER_TO_INT(ops));
45     ELUA_RETURN(1); /* A boolean: true if it is enabled, false if it isn't. */
46 }
47
48 void lua_menu_callback(gpointer data) {
49     struct _lua_menu_data* md = data;
50
51     lua_pushcfunction(md->L,menu_cb_error_handler);
52     lua_rawgeti(md->L, LUA_REGISTRYINDEX, md->cb_ref);
53     lua_rawgeti(md->L, LUA_REGISTRYINDEX, md->data_ref);
54         
55     lua_pcall(md->L,1,0,1);
56     
57     return;
58 }
59
60 ELUA_FUNCTION elua_register_menu(lua_State* L) { /*  Register a menu item in the Statistics menu. */
61 #define ELUA_ARG_register_menu_NAME 1 /* The name of the menu item. */
62 #define ELUA_ARG_register_menu_ACTION 2 /* The function to be called when the menu item is invoked. */
63 #define ELUA_OPTARG_register_menu_USERDATA 3 /* To be passed to the action. */
64         
65     const gchar* name = luaL_checkstring(L,ELUA_ARG_register_menu_NAME);
66     struct _lua_menu_data* md;
67     gboolean retap = FALSE;
68     
69         if(!name)
70                 ELUA_ARG_ERROR(register_menu,NAME,"must be a string");
71         
72     if (!lua_isfunction(L,ELUA_ARG_register_menu_ACTION)) 
73                 ELUA_ARG_ERROR(register_menu,ACTION,"must be a function");
74     
75     md = g_malloc(sizeof(struct _lua_menu_data));
76     md->L = L;
77     
78     lua_pushvalue(L, 2);
79     md->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
80     
81     if ( lua_gettop(L) > 2) {
82         lua_pushvalue(L, ELUA_OPTARG_register_menu_USERDATA);
83         md->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
84     } else {
85         md->data_ref = LUA_NOREF;
86     }
87
88     funnel_register_menu(name,
89                          REGISTER_STAT_GROUP_GENERIC,
90                          lua_menu_callback,
91                          md,
92                          retap);
93
94     ELUA_RETURN(0);
95 }
96
97
98
99
100 struct _dlg_cb_data {
101     lua_State* L;
102     int func_ref;
103     int data_ref;
104 };
105
106 static int dlg_cb_error_handler(lua_State* L) {
107     const gchar* error =  lua_tostring(L,1);
108     report_failure("Lua: Error During execution of dialog callback:\n %s",error);
109     return 0;
110 }
111
112 static void lua_dialog_cb(gchar** user_input, void* data) {
113     struct _dlg_cb_data* dcbd = data;
114     int i = 0;
115     gchar* input;
116     lua_State* L = dcbd->L;
117     
118     lua_settop(L,0);
119     lua_pushcfunction(L,dlg_cb_error_handler);
120     lua_rawgeti(L, LUA_REGISTRYINDEX, dcbd->func_ref);
121     lua_rawgeti(L, LUA_REGISTRYINDEX, dcbd->data_ref);
122     
123     for (i = 0; (input = user_input[i]) ; i++) {
124         lua_pushstring(L,input);
125         g_free(input);
126     }
127     
128     g_free(user_input);
129     
130     switch ( lua_pcall(L,i+1,0,1) ) {
131         case 0:
132             break;
133         case LUA_ERRRUN:
134             g_warning("Runtime error while calling dialog callback");
135             break;
136         case LUA_ERRMEM:
137             g_warning("Memory alloc error while calling dialog callback");
138             break;
139         default:
140             g_assert_not_reached();
141             break;
142     }
143     
144 }
145
146 ELUA_FUNCTION elua_new_dialog(lua_State* L) { /* Pops up a new dialog */
147 #define ELUA_ARG_new_dialog_TITLE 1 /* Title of the dialog's window. */
148 #define ELUA_ARG_new_dialog_ACTION 2 /* Action to be performed when OKd. */
149 /* ELUA_MOREARGS new_dialog A series of strings to be used as labels of the dialog's fields */
150
151     const gchar* title;
152     int top = lua_gettop(L);
153     int i;
154     GPtrArray* labels;
155     struct _dlg_cb_data* dcbd;
156     
157     if (! ops) {
158         luaL_error(L,"the GUI facility has to be enabled");
159         return 0;
160     }
161     
162     if (! (title  = luaL_checkstring(L,ELUA_ARG_new_dialog_TITLE)) ) {
163         ELUA_ARG_ERROR(new_dialog,TITLE,"must be a string");
164         return 0;
165     }
166     
167     if (! lua_isfunction(L,ELUA_ARG_new_dialog_ACTION)) {
168         ELUA_ARG_ERROR(new_dialog,ACTION,"must be a function");
169         return 0;
170     }
171     
172     if (top < 3) {
173         ELUA_ERROR(new_dialog,"at least one field required");
174         return 0;
175     }
176     
177     
178     dcbd = g_malloc(sizeof(struct _dlg_cb_data));
179     dcbd->L = L;
180     
181     lua_remove(L,1);
182     
183     lua_pushvalue(L, 1);
184     dcbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX);
185     lua_remove(L,1);
186     
187     lua_pushvalue(L, 1);
188     dcbd->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
189     lua_remove(L,1);
190     
191     labels = g_ptr_array_new();
192     
193     top -= 3;
194     
195     for (i = 1; i <= top; i++) {
196         gchar* label = (void*)luaL_checkstring(L,i);
197                 
198                 /* XXX leaks labels on error */
199                 if (! label) 
200                         ELUA_ERROR(new_dialog,"all fields must be strings");
201                 
202         g_ptr_array_add(labels,label);
203     }
204     
205     g_ptr_array_add(labels,NULL);
206     
207     ops->new_dialog(title, (const gchar**)labels->pdata, lua_dialog_cb, dcbd);
208     
209     g_ptr_array_free(labels,TRUE);
210     
211     ELUA_RETURN(0);
212 }
213
214
215
216 ELUA_CLASS_DEFINE(TextWindow,NOP) /* Manages a text window. */
217
218 ELUA_CONSTRUCTOR TextWindow_new(lua_State* L) { /* Creates a new TextWindow. */
219 #define ELUA_OPTARG_TextWindow_new_TITLE 1 /* Title of the new window. */
220
221     const gchar* title;
222     TextWindow tw;
223
224         title = luaL_optstring(L,ELUA_OPTARG_TextWindow_new_TITLE,"Untitled Window");
225     tw = ops->new_text_window(title);
226     pushTextWindow(L,tw);
227     
228         ELUA_RETURN(1); /* The newly created TextWindow object. */
229 }
230
231 struct _close_cb_data {
232     lua_State* L;
233     int func_ref;
234     int data_ref;
235 };
236
237 int text_win_close_cb_error_handler(lua_State* L) {
238     const gchar* error =  lua_tostring(L,1);
239     report_failure("Lua: Error During execution of TextWindow close callback:\n %s",error);
240     return 0;    
241 }
242
243 static void text_win_close_cb(void* data) {
244     struct _close_cb_data* cbd = data;
245     lua_State* L = cbd->L;
246
247     lua_pushcfunction(L,text_win_close_cb_error_handler);
248     lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref);
249     lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->data_ref);
250     
251     switch ( lua_pcall(L,1,0,1) ) {
252         case 0:
253             break;
254         case LUA_ERRRUN:
255             g_warning("Runtime error during execution of TextWindow close callback");
256             break;
257         case LUA_ERRMEM:
258             g_warning("Memory alloc error during execution of TextWindow close callback");
259             break;
260         default:
261             g_assert_not_reached();
262             break;
263     }
264 }
265
266 ELUA_METHOD TextWindow_set_atclose(lua_State* L) { /* Set the function that will be called when the window closes */
267 #define ELUA_ARG_TextWindow_at_close_ACTION 2 /* A function to be executed when the user closes the window */
268
269     TextWindow tw = checkTextWindow(L,1);
270     struct _close_cb_data* cbd;
271
272         if (!tw)
273                 ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow");
274
275     lua_settop(L,3);
276
277     if (! lua_isfunction(L,2))
278         ELUA_ARG_ERROR(TextWindow_at_close,ACTION,"must be a function");
279     
280     cbd = g_malloc(sizeof(struct _close_cb_data));
281
282     cbd->L = L;
283     cbd->data_ref = luaL_ref(L,  LUA_REGISTRYINDEX);
284     cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX);
285     
286     ops->set_close_cb(tw,text_win_close_cb,cbd);
287     
288     pushTextWindow(L,tw);
289         ELUA_RETURN(1); /* The TextWindow object. */
290 }
291
292 ELUA_METHOD TextWindow_set(lua_State* L) { /* Sets the text. */
293 #define ELUA_ARG_TextWindow_set_TEXT 2 /* The text to be used. */
294
295     TextWindow tw = checkTextWindow(L,1);
296     const gchar* text = luaL_checkstring(L,2);
297
298         if (!tw)
299                 ELUA_ERROR(TextWindow_set,"cannot be called for something not a TextWindow");
300
301     if (!text)
302                 ELUA_ARG_ERROR(TextWindow_set,TEXT,"must be a string");
303     
304     ops->set_text(tw,text);
305     
306     pushTextWindow(L,tw);
307         ELUA_RETURN(1); /* The TextWindow object. */
308 }
309
310 ELUA_METHOD TextWindow_append(lua_State* L) { /* Appends text */
311 #define ELUA_ARG_TextWindow_append_TEXT 2 /* The text to be appended */ 
312     TextWindow tw = checkTextWindow(L,1);
313     const gchar* text = luaL_checkstring(L,2);
314     
315         if (!tw)
316                 ELUA_ERROR(TextWindow_append,"cannot be called for something not a TextWindow");
317
318         if (!text)
319                 ELUA_ARG_ERROR(TextWindow_append,TEXT,"must be a string");
320
321     ops->append_text(tw,text);
322     
323     pushTextWindow(L,tw);
324         ELUA_RETURN(1); /* The TextWindow object. */
325 }
326
327 ELUA_METHOD TextWindow_prepend(lua_State* L) { /* Prepends text */
328 #define ELUA_ARG_TextWindow_prepend_TEXT 2 /* The text to be appended */ 
329     TextWindow tw = checkTextWindow(L,1);
330     const gchar* text = luaL_checkstring(L,2);
331     
332         if (!tw)
333                 ELUA_ERROR(TextWindow_prepend,"cannot be called for something not a TextWindow");
334
335         if (!text)
336                 ELUA_ARG_ERROR(TextWindow_prepend,TEXT,"must be a string");
337    
338     ops->prepend_text(tw,text);
339     
340     pushTextWindow(L,tw);
341         ELUA_RETURN(1); /* The TextWindow object. */
342 }
343
344 ELUA_METHOD TextWindow_clear(lua_State* L) { /* Errases all text in the window. */
345     TextWindow tw = checkTextWindow(L,1);
346     
347         if (!tw)
348                 ELUA_ERROR(TextWindow_clear,"cannot be called for something not a TextWindow");
349
350     ops->clear_text(tw);
351     
352     pushTextWindow(L,tw);
353         ELUA_RETURN(1); /* The TextWindow object. */
354 }
355
356 ELUA_METHOD TextWindow_get_text(lua_State* L) { /* Get the text of the window */
357     TextWindow tw = checkTextWindow(L,1);
358         const gchar* text;
359
360         if (!tw)
361                 ELUA_ERROR(TextWindow_get_text,"cannot be called for something not a TextWindow");
362
363         text = ops->get_text(tw);
364
365     lua_pushstring(L,text);
366         ELUA_RETURN(1); /* The TextWindow's text. */
367 }
368
369 static int TextWindow__gc(lua_State* L) {
370     TextWindow tw = checkTextWindow(L,1);
371
372         if (!tw)
373                 ELUA_ERROR(TextWindow_gc,"cannot be called for something not a TextWindow");
374
375     ops->destroy_text_window(tw);
376     return 1;
377 }
378
379 ELUA_METHOD TextWindow_set_editable(lua_State* L) { /* Set the function that will be called when the window closes */
380 #define ELUA_OPTARG_TextWindow_at_close_EDITABLE 2 /* A boolean flag, defaults to true */
381
382         TextWindow tw = checkTextWindow(L,1);
383         gboolean editable = luaL_optint(L,2,1);
384
385         if (!tw)
386                 ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow");
387
388         if (ops->set_editable)
389                 ops->set_editable(tw,editable);
390         
391         pushTextWindow(L,tw);
392         ELUA_RETURN(1); /* The TextWindow object. */
393 }
394
395 typedef struct _elua_bt_cb_t {
396         lua_State* L;
397         int func_ref;
398         int data_ref;
399 } elua_bt_cb_t;
400
401 static gboolean elua_button_callback(funnel_text_window_t* tw, void* data) {
402         elua_bt_cb_t* cbd = data;
403         lua_State* L = cbd->L;
404         
405         lua_settop(L,0);
406         lua_pushcfunction(L,dlg_cb_error_handler);
407         lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->func_ref);
408         pushTextWindow(L,tw);
409         lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->data_ref);
410         
411         switch ( lua_pcall(L,2,0,1) ) {
412                 case 0:
413                         break;
414                 case LUA_ERRRUN:
415                         g_warning("Runtime error while calling button callback");
416                         break;
417                 case LUA_ERRMEM:
418                         g_warning("Memory alloc error while calling button callback");
419                         break;
420                 default:
421                         g_assert_not_reached();
422                         break;
423         }
424         
425         return TRUE;
426 }
427
428 ELUA_METHOD TextWindow_add_button(lua_State* L) {
429 #define ELUA_ARG_TextWindow_add_button_LABEL 2 /* The label of the button */ 
430 #define ELUA_ARG_TextWindow_add_button_FUNCTION 3 /* The function to be called when clicked */ 
431 #define ELUA_ARG_TextWindow_add_button_DATA 4 /* The data to be passed to the function (other than the window) */ 
432         TextWindow tw = checkTextWindow(L,1);
433         const gchar* label = luaL_checkstring(L,ELUA_ARG_TextWindow_add_button_LABEL);
434         
435         funnel_bt_t* fbt;
436         elua_bt_cb_t* cbd;
437         
438         if (!tw)
439                 ELUA_ERROR(TextWindow_at_close,"cannot be called for something not a TextWindow");
440         
441         if (! lua_isfunction(L,ELUA_ARG_TextWindow_add_button_FUNCTION) )
442                 ELUA_ARG_ERROR(TextWindow_add_button,FUNCTION,"must be a function");
443
444         lua_settop(L,4);
445
446         if (ops->add_button) {
447                 fbt = ep_alloc(sizeof(funnel_bt_t));
448                 cbd = ep_alloc(sizeof(elua_bt_cb_t));
449
450                 fbt->tw = tw;
451                 fbt->func = elua_button_callback;
452                 fbt->data = cbd;
453                 
454                 cbd->L = L;
455                 cbd->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
456                 cbd->func_ref = luaL_ref(L, LUA_REGISTRYINDEX);
457
458                 ops->add_button(tw,fbt,label);
459         }
460         
461         pushTextWindow(L,tw);
462         ELUA_RETURN(1); /* The TextWindow object. */
463 }
464
465 ELUA_METHODS TextWindow_methods[] = {
466         ELUA_CLASS_FNREG(TextWindow,new),
467         ELUA_CLASS_FNREG(TextWindow,set),
468         ELUA_CLASS_FNREG(TextWindow,new),
469         ELUA_CLASS_FNREG(TextWindow,append),
470         ELUA_CLASS_FNREG(TextWindow,prepend),
471         ELUA_CLASS_FNREG(TextWindow,clear),
472         ELUA_CLASS_FNREG(TextWindow,set_atclose),
473         ELUA_CLASS_FNREG(TextWindow,set_editable),
474         ELUA_CLASS_FNREG(TextWindow,get_text),
475         ELUA_CLASS_FNREG(TextWindow,add_button),
476     {0, 0}
477 };
478
479 ELUA_META TextWindow_meta[] = {
480     {"__tostring", TextWindow_get_text},
481     {"__gc", TextWindow__gc},
482     {0, 0}
483 };
484
485 int TextWindow_register(lua_State* L) {
486     
487     ops = funnel_get_funnel_ops();
488     
489         ELUA_REGISTER_CLASS(TextWindow);
490     
491     return 1;
492 }
493
494
495 ELUA_FUNCTION elua_retap_packets(lua_State* L) {
496         /*
497          Rescan all packets and just run taps - don't reconstruct the display.
498          */
499         if ( ops->retap_packets ) {
500                 ops->retap_packets();
501         } else {
502                 ELUA_ERROR(elua_retap_packets, "does not work on tWireshark");
503         }
504         
505         return 0;
506 }
507
508