aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/GuiLua/GuiLua.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/GuiLua/GuiLua.c')
-rw-r--r--src/GuiLua/GuiLua.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/GuiLua/GuiLua.c b/src/GuiLua/GuiLua.c
new file mode 100644
index 0000000..c7a368c
--- /dev/null
+++ b/src/GuiLua/GuiLua.c
@@ -0,0 +1,456 @@
1/* GuiLua - a GUI library that implements matrix-RAD style stuff.
2
3Provides the skang and widget Lua packages.
4
5In the initial intended use case, several applications will be using
6this all at once, with one central app hosting all the GUIs.
7
8Basically this should deal with "windows" and their contents. A
9"window" in this case is hosted in the central app as some sort of
10internal window, but the user can "tear off" those windows, then they
11get their own OS hosted window. This could be done by the hosting app
12sending the current window contents to the original app as a skang file.
13
14Between the actual GUI and the app might be a socket, or a stdin/out
15pipe. Just like matrix-RAD, this should be transparent to the app.
16Also just like matrix-RAD, widgets can be connected to variable /
17functions (C or Lua), and any twiddlings with those widgets runs the
18function / changes the variable, again transparent to the app, except
19for any registered get/set methods.
20
21This interface between the GUI and the app is "skang" files, which are
22basically Lua scripts. The GUI and the app can send skang files back
23and forth, usually the app sends actual GUI stuff, and usually the GUI
24sends variable twiddles or action calls. Usually.
25
26To start with, this will be used to support multiple apps hosting their
27windows in extantz, allowing the big viewer blob to be split up into
28modules. At some point converting LL XML based UI shit into skang could
29be done. Also, this should be an exntension to LuaSL, so in-world
30scripts can have a poper GUI for a change.
31
32
33NOTES and TODOs -
34
35Lua scripts do -
36 require 'widget' -> loads widget.c
37 Widget.c is a library like test_c.
38 It starts up GuiLua.c app, with stdin/stdout pipe.
39 Widget.c then acts as a proxy for all the widget stuff.
40 So Lua modules via C libraries can work with Elm code that has a special main and has to be an app.
41 Seems simplest.
42
43 Also -
44 Some of this gets shared with LuaSL, since it came from there anyway.
45
46 Finally -
47 Add a --gui command line option, that runs foo.skang.
48 Than change the hash bang to use it.
49 And if there's a matching module, load the module first, call gimmeSkin() on it.
50 So that any with an internal default skin get that instead.
51 Same if there's a module, but no skang file.
52
53Making these packages all a sub package of skang seems like a great
54idea. On the other hand, looks like most things are just getting folded
55into skang anyway. See
56http://www.inf.puc-rio.br/~roberto/pil2/chapter15.pdf part 15.5 for
57package details.
58
59See if I can use LuaJIT FFI here. Since this will be a library, and
60skang apps could be written in C or Lua, perhaps writing this library to
61be FFI friendly instead of the usual Lua C binding might be the way to
62go? LuaJIT is not ready yet, since it needs include files copied into
63Lua files, and does not support macros, which EFL uses a lot of.
64
65For the "GUI hosted in another app" case, we will need some sort of
66internal window manager running in that other app.
67
68This might end up running dozens of Lua scripts, and could use the LuaSL
69Lua script running system. Moving that into this library might be a
70sane idea I think? Or prehaps a separate library that both LuaSL and
71GuiLua use?
72
73Raster wants a method of sending Lua tables around as edje messages.
74Between C, Edje, Edje Lua, and Lua. Sending between threads, and across
75sockets. Using a new edje message type, or eet for sockets, was
76suggested, but perhaps Lua skang is a better choice?
77
78Somehow access to the edje_lua2.c bindings should be provided. And
79bindings to the rest of EFL when they are done. Assuming the other EFL
80developers do proper introspection stuff, or let me do it.
81
82The generic Lua binding helper functions I wrote for edje_lua2.c could
83be used here as well, and expanded as discussed on the E devs mailing
84list. This would include the thread safe Lua function stuff copied
85into the README.
86
87There will eventually be a built in editor, like the zen editor from
88matrix-RAD. It might be a separate app.
89
90NAWS should probably live in here to. If I ever get around to writing
91it. lol
92
93The pre tokenized widget structure thingy I had planned in the
94matrix-RAD TODO just wont work, as it uses symbols. On the other hand,
95we will be using Lua tables anyway. B-)
96
97The last half of http://passingcuriosity.com/2009/extending-lua-in-c/
98might be of use.
99
100*/
101
102
103/* thing package
104
105Currently this is in skang.lua, but should bring this in here later.
106
107*/
108
109
110/* skang package
111
112Currently this is in skang.lua, but should bring this in here later.
113
114*/
115
116
117/* stuff & squeal packages
118
119Currently Stuff is in skang.lua, but should bring this in here later.
120
121*/
122
123
124/* widget package
125
126Currently widget design is in skang.lua, but should bring this in here later.
127
128*/
129
130
131/* introspection
132
133As detailed in README, EFL introspection doesn't seem to really be on
134the radar, but I might get lucky, or I might have to write it myself.
135For quick and dirty early testing, I'll probably write a widget package
136that has hard coded mappings between some basic "label", "button", etc.
137and ordinary elementary widgets. Proper introspection can come later.
138
139*/
140
141
142
143#include "GuiLua.h"
144
145
146globals ourGlobals;
147static const char *globName = "ourGlobals";
148
149
150// TODO - These functions should be able to deal with multiple windows.
151// TODO - Should be able to open external and internal windows, and even switch between them on the fly.
152static void _on_done(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
153{
154// globals *ourGlobals = data;
155
156 // Tell the main loop to stop, which it will, eventually.
157 elm_exit();
158}
159
160
161/* Sooo, how to do this -
162widget has to be a light userdata
163The rest can be Lua sub things? Each with a C function to update the widget.
164
165win.quitter:colour(1,2,3,4) -> win.quitter.colour(win.quitter, 1,2,3,4) -> __call(win.quitter.colour, win.quitter, 1,2,3,4) -> skang.colour(win.quitter.colour, win.quitter, 1,2,3,4)
166win.quitter.colour.r = 5 -> direct access to the table, well "direct" via Thing and Mum. We eventually want to call skang.colour() though.
167*/
168
169struct _Widget
170{
171 char magic[8];
172 Evas_Object *obj;
173 char *label, *look, *action, *help;
174 // foreground / background colour
175 // thing
176 // types {}
177 // skangCoord x, y, w, h
178};
179
180static void _on_click(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
181{
182 globals *ourGlobals;
183 lua_State *L = data;
184 struct _Widget *wid;
185
186 lua_getfield(L, LUA_REGISTRYINDEX, globName);
187 ourGlobals = lua_touserdata(L, -1);
188 lua_pop(L, 1);
189
190 wid = evas_object_data_get(obj, "Widget");
191 if (wid)
192 {
193 PD("Doing action %s", wid->action);
194 if (0 != luaL_dostring(L, wid->action))
195 PE("Error running - %s", wid->action);
196 }
197}
198
199static int widget(lua_State *L)
200{
201 globals *ourGlobals;
202 char *type = "label";
203 char *title = ":";
204 int x = 1, y = 1, w = WIDTH/3, h = HEIGHT/3;
205
206 lua_getfield(L, LUA_REGISTRYINDEX, globName);
207 ourGlobals = lua_touserdata(L, -1);
208 lua_pop(L, 1);
209
210 pull_lua(L, 1, "$type $title %x %y %w %h", &type, &title, &x, &y, &w, &h);
211
212 // Poor mans introspection, until I write real introspection into EFL.
213 if (strcmp(type, "button") == 0)
214 {
215 struct _Widget *wid;
216
217 wid = calloc(1, sizeof(struct _Widget));
218 strcpy(wid->magic, "Widget");
219 wid->label = strdup(title);
220 wid->obj = elm_button_add(ourGlobals->win);
221 elm_object_text_set(wid->obj, title);
222 evas_object_smart_callback_add(wid->obj, "clicked", _on_click, L);
223 evas_object_resize(wid->obj, w, h);
224 evas_object_move(wid->obj, x, y);
225 evas_object_show(wid->obj);
226 evas_object_data_set(wid->obj, "Widget", wid);
227 /* Evas_Object *bt isn't a real pointer it seems. At least Lua bitches about it -
228 PANIC: unprotected error in call to Lua API (bad light userdata pointer)
229 So we wrap it.
230 */
231 lua_pushlightuserdata(L, (void *) wid);
232 return 1;
233 }
234
235 return 0;
236}
237
238static int action(lua_State *L)
239{
240 globals *ourGlobals;
241 struct _Widget *wid = lua_touserdata(L, 1);
242 char *action = "nada";
243
244 lua_getfield(L, LUA_REGISTRYINDEX, globName);
245 ourGlobals = lua_touserdata(L, -1);
246 lua_pop(L, 1);
247
248 pull_lua(L, 2, "$", &action);
249 if (wid && strcmp(wid->magic, "Widget") == 0)
250 {
251 PD("Setting action %s", action);
252 wid->action = strdup(action);
253 }
254 return 0;
255}
256
257static int colour(lua_State *L)
258{
259// TODO - This is just a stub for now.
260
261 return 0;
262}
263
264static int window(lua_State *L)
265{
266 globals *ourGlobals;
267 char *name = "GuiLua";
268 char *title = "GuiLua test harness";
269 int w = WIDTH, h = HEIGHT;
270
271 lua_getfield(L, LUA_REGISTRYINDEX, globName);
272 ourGlobals = lua_touserdata(L, -1);
273 lua_pop(L, 1);
274
275 pull_lua(L, 1, "%w %h $title $name", &w, &h, &title, &name);
276 PI("Setting window to %d %d %s", w, h, title);
277
278 if ((ourGlobals->win = elm_win_util_standard_add(name, title)))
279 {
280 evas_object_smart_callback_add(ourGlobals->win, "delete,request", _on_done, ourGlobals);
281 evas_object_resize(ourGlobals->win, w, h);
282 evas_object_move(ourGlobals->win, 0, 0);
283 evas_object_show(ourGlobals->win);
284
285 lua_pushlightuserdata(L, &ourGlobals->win);
286 return 1;
287 }
288
289 return 0;
290}
291
292static int clear(lua_State *L)
293{
294// TODO - This is just a stub for now.
295
296 return 0;
297}
298
299static int loopWindow(lua_State *L)
300{
301 globals *ourGlobals;
302
303 lua_getfield(L, LUA_REGISTRYINDEX, globName);
304 ourGlobals = lua_touserdata(L, -1);
305 lua_pop(L, 1);
306
307 if (ourGlobals->win)
308 elm_run();
309
310 return 0;
311}
312
313static int quit(lua_State *L)
314{
315 globals *ourGlobals;
316
317 lua_getfield(L, LUA_REGISTRYINDEX, globName);
318 ourGlobals = lua_touserdata(L, -1);
319 lua_pop(L, 1);
320
321 _on_done(ourGlobals, NULL, NULL);
322
323 return 0;
324}
325
326static int closeWindow(lua_State *L)
327{
328 globals *ourGlobals;
329
330 lua_getfield(L, LUA_REGISTRYINDEX, globName);
331 ourGlobals = lua_touserdata(L, -1);
332 lua_pop(L, 1);
333
334 // Elm will delete our buttons to, and EO will bitch four times for each.
335 if (ourGlobals->win)
336 evas_object_del(ourGlobals->win);
337
338 if (ourGlobals->logDom >= 0)
339 {
340 eina_log_domain_unregister(ourGlobals->logDom);
341 ourGlobals->logDom = -1;
342 }
343
344 // This shuts down Elementary, but keeps the main loop running until all ecore_evas are freed.
345 elm_shutdown();
346
347 return 0;
348}
349
350/* local widget = require 'libGuiLua'
351
352Lua's require() function will strip any stuff from the front of the name
353separated by a hyphen, so 'ClientHamr-GuiLua-libGuiLua' -> 'libGuiLua'. Then
354it will search through a path, and eventually find this libGuiLua.so (or
355libGuiLua.dll or whatever), then call luaopen_libGuiLua(), which should return
356a table. The argument (only thing on the stack) for this function will
357be 'libGuiLua'.
358
359Normally luaL_register() creates a table of functions, that is the table
360returned, but we want to do something different with skang.
361*/
362int luaopen_GuiLua(lua_State *L)
363{
364 int skang;
365
366 // In theory this function only ever gets called once.
367 memset(&ourGlobals, 0, sizeof(globals));
368 ourGlobals.logDom = loggingStartup("GuiLua", ourGlobals.logDom);
369
370 elm_policy_set(ELM_POLICY_EXIT, ELM_POLICY_EXIT_NONE);
371 elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_NONE);
372 elm_policy_set(ELM_POLICY_THROTTLE, ELM_POLICY_THROTTLE_HIDDEN_ALWAYS);
373
374 // These are set via the elementary_config tool, which is hard to find.
375 elm_config_finger_size_set(0);
376 elm_config_scale_set(1.0);
377
378// pseudo-indices, special tables that can be accessed like the stack -
379// LUA_GLOBALSINDEX - thread environment, where globals are
380// LUA_ENVIRONINDEX - C function environment, in this case luaopen_widget() is the C function
381// LUA_REGISTRYINDEX - C registry, global, for unique keys use the module name as a string, or a lightuserdata address to a C object in our module.
382// lua_upvalueindex(n) - C function upvalues
383
384 // Shove ourGlobals into the registry.
385 lua_pushlightuserdata(L, &ourGlobals);
386 lua_setfield(L, LUA_REGISTRYINDEX, globName);
387
388 // The skang module should have been loaded by now, so we can just grab it out of package.loaded[].
389 lua_getglobal(L, "package");
390 lua_getfield(L, lua_gettop(L), "loaded");
391 lua_remove(L, -2); // Removes "package"
392 lua_getfield(L, lua_gettop(L), SKANG);
393 lua_remove(L, -2); // Removes "loaded"
394 lua_setfield(L, LUA_REGISTRYINDEX, SKANG);
395 lua_getfield(L, LUA_REGISTRYINDEX, SKANG); // Puts the skang table back on the stack.
396 skang = lua_gettop(L);
397
398 // Define our functions.
399//thingasm{'window', 'The size and title of the application Frame.', window, 'x,y,name', acl='GGG'}
400 push_lua(L, "@ ( { = $ $ & $ $acl } )", skang, THINGASM, skang, "Cwindow", "Opens our window.", window, "number,number,string", "GGG", 0);
401 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "clear", "The current skin is cleared of all widgets.", clear, 0);
402 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "widget", "Create a widget.", widget, 0);
403 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "action", "Add an action to a widget.", action, 0);
404 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "Colour", "Change widget colours.", colour, 0);
405 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "loopWindow", "Run our windows main loop.", loopWindow, 0);
406 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "quit", "Quit, exit, remove thyself.", quit, 0);
407 push_lua(L, "@ ( = $ $ & )", skang, THINGASM, skang, "closeWindow", "Closes our window.", closeWindow, 0);
408
409 // A test of the array building stuff.
410 push_lua(L, "@ ( { = $ $ % $widget !required } )", skang, THINGASM, skang, "wibble", "It's wibbly!", 1, "'edit', 'The wibblinator:', 1, 1, 10, 50", 1, 0);
411
412 // Makes no difference what we return, but it's expecting something.
413 return 1;
414}
415
416
417void GuiLuaDo(int argc, char **argv)
418{
419 lua_State *L;
420 lua_Number i;
421
422 L = luaL_newstate();
423 if (L)
424 {
425 luaL_openlibs(L);
426
427 // Pass all our command line arguments to Lua.
428 i = 1;
429 lua_newtable(L);
430 while (--argc > 0 && *++argv != '\0')
431 {
432 lua_pushnumber(L, i++);
433 lua_pushstring(L, *argv);
434 lua_settable(L, -3);
435 }
436 lua_setfield(L, LUA_GLOBALSINDEX, "arg");
437
438
439 // When we do this, skang will process all the arguments passed to GuiLuaDo().
440 // This likely includes a module load, which likely opens a window.
441 lua_getglobal(L, "require");
442 lua_pushstring(L, SKANG);
443 lua_call(L, 1, 1);
444 lua_setfield(L, LUA_GLOBALSINDEX, SKANG);
445
446
447 // Run the main loop via a Lua call.
448 // This does nothing if no module opened a window.
449 if (0 != luaL_dostring(L, "skang.loopWindow()"))
450 PEm("Error running - skang.loopWindow()");
451 lua_pop(L, closeWindow(L));
452 lua_close(L);
453 }
454 else
455 fprintf(stderr, "Failed to start Lua!\n");
456}