aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
authorDavid Walter Seikel2014-04-22 15:13:38 +1000
committerDavid Walter Seikel2014-04-22 15:13:38 +1000
commitdd009ccdfd62f9153dbc72f5f5de5d5f72979690 (patch)
tree50d62438c6d47dccf82f440986919b1ed3edbbd9 /src
parentMove most of the README's and other docs into the new docs directory. (diff)
downloadSledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.zip
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.gz
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.bz2
SledjHamr-dd009ccdfd62f9153dbc72f5f5de5d5f72979690.tar.xz
Move all source into the new src directory, and shuffle a few other things around.
Diffstat (limited to 'src')
-rw-r--r--src/GuiLua/GuiLua.c456
-rw-r--r--src/GuiLua/GuiLua.h35
-rwxr-xr-xsrc/GuiLua/build.lua23
-rw-r--r--src/GuiLua/skang.c16
-rw-r--r--src/GuiLua/skang.lua1686
-rw-r--r--src/GuiLua/test.lua211
-rw-r--r--src/GuiLua/test.properties1
-rwxr-xr-xsrc/GuiLua/test.sh3
-rw-r--r--src/GuiLua/test.skang14
-rw-r--r--src/GuiLua/test_c.c88
-rw-r--r--src/LuaSL/LuaSL.edc205
-rw-r--r--src/LuaSL/LuaSL.h82
-rw-r--r--src/LuaSL/LuaSL_LSL_tree.h443
-rw-r--r--src/LuaSL/LuaSL_compile.c2345
-rw-r--r--src/LuaSL/LuaSL_lemon_yaccer.y275
-rw-r--r--src/LuaSL/LuaSL_lexer.l164
-rw-r--r--src/LuaSL/LuaSL_main.c708
-rw-r--r--src/LuaSL/LuaSL_test.c467
-rw-r--r--src/LuaSL/LuaSL_threads.c496
-rw-r--r--src/LuaSL/LuaSL_threads.h54
-rw-r--r--src/LuaSL/LuaSL_utilities.c60
-rwxr-xr-xsrc/LuaSL/build.lua23
-rwxr-xr-xsrc/LuaSL/test.sh27
-rw-r--r--src/extantz/CDemo.cpp510
-rw-r--r--src/extantz/CDemo.h63
-rwxr-xr-xsrc/extantz/build.lua26
-rw-r--r--src/extantz/crappisspuke.cpp294
-rw-r--r--src/extantz/extantz.c1588
-rw-r--r--src/extantz/extantz.edc30
-rw-r--r--src/extantz/extantz.h215
-rw-r--r--src/extantz/extantzCamera.cpp282
-rw-r--r--src/extantz/extantzCamera.h101
32 files changed, 10991 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}
diff --git a/src/GuiLua/GuiLua.h b/src/GuiLua/GuiLua.h
new file mode 100644
index 0000000..156fa1a
--- /dev/null
+++ b/src/GuiLua/GuiLua.h
@@ -0,0 +1,35 @@
1
2#include <stdio.h>
3#include <ctype.h>
4
5#include <Elementary.h>
6
7#include <lua.h>
8#include <luajit.h>
9#include <lualib.h>
10#include <lauxlib.h>
11
12#include "LumbrJack.h"
13#include "Runnr.h"
14
15typedef struct _globals globals;
16
17
18#define WIDTH (300)
19#define HEIGHT (300)
20
21#define SKANG "skang"
22#define MODULEBEGIN "moduleBegin"
23#define MODULEEND "moduleEnd"
24#define THINGASM "thingasm"
25
26
27struct _globals
28{
29 Evas_Object *win; // Our Elm window.
30 int logDom; // Our logging domain.
31};
32
33
34int luaopen_widget(lua_State *L);
35void GuiLuaDo(int argc, char **argv);
diff --git a/src/GuiLua/build.lua b/src/GuiLua/build.lua
new file mode 100755
index 0000000..7e1da15
--- /dev/null
+++ b/src/GuiLua/build.lua
@@ -0,0 +1,23 @@
1#!/usr/bin/env lua
2
3local dir = ...
4
5if 'nil' == type(dir) then
6 local build, err = loadfile('../../build.lua')
7 if build then
8 setfenv(build, getfenv(2))
9 build(2)
10 else
11 print("ERROR - " .. err)
12 end
13 dir = workingDir
14end
15
16LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS
17
18removeFiles(dir, {'test_c.so', 'GuiLua.o', '../../libraries/libGuiLua.so', 'skang'})
19
20runCommand('C modules', dir, 'gcc ' .. CFLAGS .. ' -fPIC -shared -o test_c.so test_c.c')
21runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c GuiLua.c')
22runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libGuiLua.so -o ../../libraries/libGuiLua.so GuiLua.o')
23runCommand('C apps', dir, 'gcc ' .. CFLAGS .. ' -Wl,-export-dynamic -o skang skang.c ' .. LDFLAGS .. ' -lGuiLua ' .. libs)
diff --git a/src/GuiLua/skang.c b/src/GuiLua/skang.c
new file mode 100644
index 0000000..facc239
--- /dev/null
+++ b/src/GuiLua/skang.c
@@ -0,0 +1,16 @@
1#include "GuiLua.h"
2
3
4EAPI_MAIN int elm_main(int argc, char **argv)
5{
6 elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
7 elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
8 elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR);
9 elm_app_info_set(elm_main, "GuiLua", "skang.lua");
10
11 GuiLuaDo(argc, argv);
12
13 return 0;
14}
15
16ELM_MAIN()
diff --git a/src/GuiLua/skang.lua b/src/GuiLua/skang.lua
new file mode 100644
index 0000000..23549c3
--- /dev/null
+++ b/src/GuiLua/skang.lua
@@ -0,0 +1,1686 @@
1--[[ TODO - This should be in C, but so far development has been quite rapid doing it in Lua.
2
3C will let us -
4 Actually do the widget stuff.
5 Slap meta tables on all value types.
6 Which lets us put the meta table on the variable, instead of on the table, which I think is cleaner.
7 Figure out the directory separator.
8 Network stuff. No need to look at Lua socket stuff, we have Ecore_Con.
9 Database stuff. No need to look at Lua SQL stuff, we have esskyuehl. Maybe.
10
11 Actually, we could have the best of both worlds, since it is currently a C / Lua hybrid. B-)
12]]
13
14
15--[[ Skang package
16
17In here should live all the internals of matrix-RAD that don't
18specifically relate to widgets. This would include the "window" though.
19
20skang.module(Evas)
21skang.module(Elementary)
22skang.skang('some/skang/file.skang')
23
24This package is also what "apps" that use the system should "inherit"
25from, in the same way matrix-RAD apps did. Skang "apps" could be Lua
26modules. They could also be C code, like the extantz modules are likely
27to be. Skang "apps" would automatically be associated with skang files
28of the same name.
29
30For a .skang file, the skang command (written in C) would strip off the
31first line, add the two implied lines, then run it as Lua. The
32skang.load() command would do the same. So that skang C comand would
33just pass the file name to skang.load() in this library. B-)
34
35The old skang argument types are -
36
37{"name", "java.lang.String"},
38{"action", "java.lang.String"},
39{"type", "java.lang.String"},
40{"data", "java.lang.String"},
41{"URL", "java.lang.String"},
42{"file", "java.lang.String"},
43{"method", "java.lang.String"},
44{"lx", "java.lang.String"},
45{"ly", "java.lang.String"},
46{"lw", "java.lang.String"},
47{"lh", "java.lang.String"},
48{"normal", "java.lang.String"},
49{"ghost", "java.lang.String"},
50{"active", "java.lang.String"},
51{"toggle", "java.lang.String"},
52{"boolean","java.lang.Boolean"},
53{"number", "java.lang.Integer"},
54{"int", "java.lang.Integer"},
55{"x", "java.lang.Integer"},
56{"y", "java.lang.Integer"},
57{"w", "java.lang.Integer"},
58{"h", "java.lang.Integer"},
59{"r", "java.lang.Integer"},
60{"g", "java.lang.Integer"},
61{"b", "java.lang.Integer"},
62{"alpha", "java.lang.Integer"},
63{"acl", "net.matrix_rad.security.ACL"},
64]]
65
66
67-- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular.
68-- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module".
69do -- Only I'm not gonna indent this.
70
71mainSkin = {}
72
73-- TODO - This needs to be expanded a bit to cover things like 1.42
74local versions = {
75 '0%.0', 'unwritten', 'Just a stub, no code at all, or completely non-existant.',
76 '0%.1', 'prototype', 'Stuff that has only been prototyped, or partly written. There is some code, and it may even work, but it is not even close to the finished product.',
77 '%d%.3', 'written', 'Stuff that has already been written. It may not be perfect, but it is considered an aproximation of the finished product.',
78 '%d%.5', 'alpha', 'Version being tested by official alpha testers.',
79 '%d%.9', 'beta', 'Version passed alpha testing, but not ready for final release.',
80 '1%.0', 'final', 'Version ready for final release, fully tested.',
81 '3%.0', 'poetry', 'Near perfection has been acheived.',
82 '5%.0', 'nirvana', 'Perfection has been acheived.',
83 '9%.0', 'bible', 'This is the Whord of Ghod.',
84}
85
86-- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated.
87-- TODO - Should parse in license type to.
88moduleBegin = function (name, author, copyright, version, timestamp, skin, isLua)
89 local _M = {} -- This is what we return to require().
90 local level = 2
91
92 if 'nil' == type(isLua) then isLua = true end
93
94 package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end.
95 -- Returning it at the end does the same thing.
96 -- This is so that we can have all the module stuff at the top, in this function.
97 -- Should do this before any further require(), so that circular references don't blow out.
98
99 -- Save the callers environment.
100 local savedEnvironment
101 if isLua then
102 savedEnvironment = getfenv(level)
103 else
104 -- While the above works fine for test_c, it doesn't for GuiLua. Odd.
105 savedEnvironment = getfenv(1)
106 end
107
108 -- Clone the environment into _M, so the module can access everything as usual after the setfenv() below.
109 --[[ TODO - Check if this also clones _G or _ENV. And see if it leaks stuff in either direction.
110 local _G = _G -- Only sets a local _G for this function.
111 _M._G = _G -- This clone loop might do this, but we don't want to be able to access the old _G from outside via this leak.
112 In Lua 5.1 at least, _G was special. In 5.2, _ENV sorta replaces setfenv(), but no idea if this clone loop stomps on that.
113 ]]
114 for k, v in pairs(savedEnvironment) do
115 _M[k] = v
116 end
117
118 _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M.
119 _M._NAME = name
120 _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name.
121 _M.isLua = isLua
122
123 -- Parse in an entire copyright message, and strip that down into bits, to put back together.
124 local date, owner = string.match(copyright, '[Cc]opyright (%d%d%d%d) (.*)')
125 _M.AUTHOR = author or owner
126 _M.COPYRIGHT = 'Copyright ' .. date .. ' ' .. _M.AUTHOR
127 -- Translate the version number into a version string.
128 local versionName, versionDesc = ' ', ''
129 for i = 1, #versions / 3 do
130 if 1 == string.find(version, versions[i]) then
131 versionName = ' ' .. versions[i + 1] .. ' '
132 versionDesc = versions[i + 2]
133 break
134 end
135 end
136 _M.VERSION = version .. versionName .. timestamp
137 _M.VERSION_DESC = versionDesc
138 -- If there is a .skang file, read that in and override the passed in skin.
139 local f = io.open(name .. '.skang')
140 if f then
141 skin = f:read('*l')
142 if '#' == string.sub(skin, 1, 1) then skin = '' end
143 skin = skin .. f:read('*a')
144 f:close()
145 end
146 if skin then
147 skin = "local skang = require 'skang'\nlocal " .. name .. " = require '" .. name .. "'\n" .. skin
148 if nil == mainSkin._NAME then mainSkin = _M end
149 end
150 _M.DEFAULT_SKANG = skin
151
152 --_G[_M._NAME] = _M -- Stuff it into a global of the same name.
153 -- Not such a good idea to stomp on global name space.
154 -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway.
155 -- This is why module() is broken.
156 _M.savedEnvironment = savedEnvironment
157 -- NOTE - setfenv() wont work if the environment it refers to is a C function. Worse, getfenv() returns the global environment, so we can't tell.
158 if isLua then
159 -- setfenv() sets the environment for the FUNCTION, stack level deep.
160 -- The number is the stack level -
161 -- 0 running thread, 1 current function, 2 function that called this function, etc
162 setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names.
163 -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd().
164 -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time.
165 end
166
167 print('Loaded module ' .. _M._NAME .. ' version ' .. _M.VERSION .. ', ' .. _M.COPYRIGHT .. '.\n ' .. _M.VERSION_DESC)
168
169 return _M
170end
171
172
173--[[ Parse command line parameters.
174
175This is done in two parts. Skang will do an initial scan and tokenise,
176then each module gets a chance to pull it's own Things from the result.
177
178Make the command line parameter getting MUCH more intelligent, try to support the common
179command line interfaces -
180
181arg value
182a value
183/arg value
184/a value
185--arg value
186--a value
187-a value
188-ab ('a' and 'b' are both shortcuts.)
189arg=value
190a=value
191arg1=value1&arg2=value2
192arg1=value1|arg2=value2
193a=value1&a=value2
194+arg/-arg (Can't support this generically.)
195
196Ignore /,-,--,& except as arg introducers. Use = as value introducer. Expect
197arg or a. If type is String, expect a value. If type is integer, and next token is
198not an integer, increment current value, otherwise expect integer value. If type is
199boolean, value beginning with T, t, F, f, etc is true, otherwise value is false, unless
200next token starts with an introducer, then value is true.
201
202TODO - Finish supporting all of the above.
203 These all need deeper parsing, but we dunno if they might have been inside quoted strings from the shell.
204 arg=value Shell.
205 arg1=value1&arg2=value2 For URLs.
206 arg1=value1|arg2=value2 Can't remember why, probably the old skang multivalue syntax.
207 Test it all.
208 Skang command line should have standardish stuff, like --version, --help, --help module.thing.
209 Lua does these already, might be no need to do them ourselves -
210 -e 'some code'.
211 -i go interactive after running the script.
212 -v version.
213 - read from stdin non interactively.
214 LuaJIT also has this -
215 -- stop processing options.
216]]
217
218ARGS = {}
219lua = ''
220command = ''
221
222
223-- Do an initial scan and tokenise of the command line arguments.
224scanArguments = function (args)
225 if args then
226 lua = args[-1]
227 command = args[0]
228 for i, v in ipairs(args) do
229 local pre = ''
230 if '--' == string.sub(v, 1, 2) then pre = '--'; v = string.sub(v, 3, -1) end
231 if '-' == string.sub(v, 1, 1) then pre = '-'; v = string.sub(v, 2, -1) end
232 if '+' == string.sub(v, 1, 1) then pre = '+'; v = string.sub(v, 2, -1) end
233 -- TODO - Make this the opposite of the directory separator for what ever platform we are running on.
234 -- Which Lua can't figure out I think.
235 if '/' == string.sub(v, 1, 1) then pre = '/'; v = string.sub(v, 2, -1) end
236 if '=' == string.sub(v, 1, 1) then pre = '='; v = string.sub(v, 2, -1) end
237 if '&' == string.sub(v, 1, 1) then pre = '&'; v = string.sub(v, 2, -1) end
238 if '|' == string.sub(v, 1, 1) then pre = '|'; v = string.sub(v, 2, -1) end
239 if '' ~= v then ARGS[i] = {pre, v} end
240 end
241 end
242end
243
244parseType = function (module, thingy, v, value)
245 if 'string' == thingy.types[1] then
246 if value then
247 module[v[2] ] = value[2]
248 value[2] = nil -- Mark it as used.
249 else
250 print('ERROR - Expected a string value for ' .. thingy.names[1])
251 end
252 end
253
254 if 'number' == thingy.types[1] then
255 if value then
256 -- If the introducer is '-', then this should be a negative number.
257 if '-' == value[1] then value[1] = ''; value[2] = '-' .. value[2] end
258 -- Only parse the next value as a number if it doesn't have an introducer.
259 if ('' == value[1]) or ('=' == value[1]) then
260 value[2] = tonumber(value[2])
261 if value[2] then
262 module[v[2] ] = value[2]
263 value[2] = nil -- Mark it as used.
264 else
265 print('ERROR - Expected a number value for ' .. thingy.names[1])
266 end
267 else
268 module[v[2] ] = module[v[2] ] + 1
269 end
270 else
271 print('ERROR - Expected a number value for ' .. thingy.names[1])
272 end
273 end
274
275 if 'function' == thingy.types[1] then
276 local args = {}
277 -- TODO - Should allow more than one argument, but would need to pass in ARGS and i.
278 if 2 == #thingy.types then
279 if value then
280 -- TODO - Should check the type of the arguments.
281 args[#args + 1] = value[2]
282 module[v[2] ](args[1])
283 value[2] = nil -- Mark it as used.
284 else
285 print('ERROR - Expected an argument for ' .. thingy.names[1])
286 end
287 else
288 module[v[2] ]()
289 end
290 end
291
292 if 'boolean' == thingy.types[1] then
293 if value then
294 -- Only parse the next value as a boolean if it doesn't have an introducer.
295 if ('' == value[1]) or ('=' == value[1]) then
296 module[v[2] ] = isBoolean(value[2])
297 value[2] = nil -- Mark it as used.
298 else
299 module[v[2] ] = true
300 end
301 else
302 print('ERROR - Expected a boolean value for ' .. thingy.names[1])
303 end
304 end
305end
306
307pullArguments = function (module)
308 -- Look for our command line arguments.
309 local metaMum = getmetatable(module)
310 if metaMum and metaMum.__self then
311 for i, v in ipairs(ARGS) do
312 if v[2] then
313 local thingy = metaMum.__self.stuff[v[2] ]
314 -- Did we find one of ours?
315 if thingy then
316 parseType(module, thingy, v, ARGS[i + 1])
317 v[2] = nil -- Mark it as used.
318 else
319 -- Didn't find one directly, check for single letter matches in '-abc'.
320 for k, w in pairs(metaMum.__self.stuff) do
321 if 1 == #w.names[1] then
322 for j = 1, #v[2] do
323 if string.sub(v[2], j, 1) == w.names[1] then
324 if 1 == j then
325 v[2] = string.sub(v[2], 2, -1)
326 if 'boolean' == w.types[1] then module[v[2] ] = true end
327 elseif #v[2] == j then
328 v[2] = string.sub(v[2], 1, j - 1)
329 -- The one at the end is the only one that could have a following value.
330 parseType(module, w, v, ARGS[i + 1])
331 else
332 v[2] = string.sub(v[2], 1, j - 1) .. string.sub(v[2], j + 1, -1)
333 if 'boolean' == w.types[1] then module[v[2] ] = true end
334 end
335 if '' == v[2] then v[2] = nil end -- Mark it as used.
336 end
337 end
338 end
339 end
340 end
341 end
342 end
343 end
344end
345
346-- Restore the environment, and grab paramateres from standard places.
347moduleEnd = function (module)
348 -- See if there is a properties file, and run it in the modules environment.
349 local properties, err = loadfile(module._NAME .. '.properties')
350 if properties then
351 setfenv(properties, getfenv(2))
352 properties()
353 elseif 'cannot open ' ~= string.sub(err, 1, 12) then
354 print("ERROR - " .. err)
355 end
356
357 pullArguments(module)
358
359 -- Run the main skin, which is the first skin that is defined. In theory, the skin from the main module.
360 if mainSkin == module then
361 print("RUNNING SKIN FOR " .. module._NAME)
362 local skin, err = loadstring(module.DEFAULT_SKANG)
363 if skin then
364 setfenv(skin, getfenv(2))
365 skin()
366 else
367 print("ERROR - " .. err)
368 end
369 end
370
371 if module.isLua then setfenv(2, module.savedEnvironment) end
372end
373
374
375-- Call this now so that from now on, this is like any other module.
376local _M = moduleBegin('skang', 'David Seikel', 'Copyright 2014 David Seikel', '0.1', '2014-03-27 02:57:00')
377
378-- This works coz LuaJIT automatically loads the jit module.
379if type(jit) == 'table' then
380 print('Skang is being run by ' .. jit.version .. ' under ' .. jit.os .. ' on a ' .. jit.arch)
381else
382 print('Skang is being run by Lua version ' .. _VERSION)
383end
384
385scanArguments(arg)
386
387
388function printTableStart(table, space, name)
389 print(space .. name .. ": ")
390 print(space .. "{")
391 printTable(table, space .. " ")
392 print(space .. "}")
393 if '' == space then print('') end
394end
395
396function printTable(table, space)
397 if nil == table then return end
398 for k, v in pairs(table) do
399 if type(v) == "table" then
400 if v._NAME then
401 print(space .. "SKANG module " .. v._NAME .. ";")
402 else
403 printTableStart(v, space, k)
404 end
405 elseif type(v) == "string" then
406 print(space .. k .. ': "' .. v .. '";')
407 elseif type(v) == "function" then
408 print(space .. "function " .. k .. "();")
409 elseif type(v) == "userdata" then
410 print(space .. "userdata " .. k .. ";")
411 elseif type(v) == "boolean" then
412 if (v) then
413 print(space .. "boolean " .. k .. " TRUE ;")
414 else
415 print(space .. "boolean " .. k .. " FALSE ;")
416 end
417 else
418 print(space .. k .. ": " .. v .. ";")
419 end
420 end
421end
422
423
424csv2table = function (csv)
425 local result = {}
426 local i = 1
427
428 for v in string.gmatch(csv, ' *([^,]+)') do
429 result[i] = v
430 i = i + 1
431 end
432 return result
433end
434
435
436shiftLeft = function (tab)
437 local result = tab[1]
438 table.remove(tab, 1)
439 return result
440end
441
442
443-- My clever boolean check, this is the third language I've written this in. B-)
444-- true 1 yes ack ok one positive absolutely affirmative 'ah ha' 'shit yeah' 'why not'
445local isTrue = 't1aopswy'
446-- false 0 no nack nope zero negative nah 'no way' 'get real' 'uh uh' 'fuck off' 'bugger off'
447local isFalse = 'f0bgnuz'
448isBoolean = function (aBoolean)
449 local result = false
450
451 if type(aBoolean) ~= 'nil' then
452 -- The default case, presence of a value means it's true.
453 result = true
454 if type(aBoolean) == 'boolean' then result = aBoolean
455 elseif type(aBoolean) == 'function' then result = aBoolean()
456 elseif type(aBoolean) == 'number' then result = (aBoolean ~= 0)
457 elseif type(aBoolean) == 'string' then
458 if '' == aBoolean then
459 result = false
460 else
461 if 1 == string.find(string.lower(aBoolean), '^[' .. isTrue .. ']') then result = true end
462 if 1 == string.find(string.lower(aBoolean), '^[' .. isFalse .. ']') then result = false end
463 end
464 end
465 end
466 return result
467end
468
469
470--[[ Thing Java package
471
472matrix-RAD had Thing as the base class of everything.
473
474Each "users session" (matrix-RAD term that came from Java
475applets/servlets) has a ThingSpace, which is a tree that holds
476everything else. It holds the class cache, commands, loaded modules,
477variables and their values, widgets and their states. In matrix-RAD I
478built BonsiaTree and LeafLike, for the old FDO system I built dumbtrees.
479
480Other Thing things are -
481 get/set The getter and setter.
482 number No idea how this was useful.
483 skang The owning object, a Skang (actually got this, called module for now).
484 owner The owning object, a String (module._NAME).
485 clas Class of the Thing, a Class. (pointless)
486 type Class of the Thing, a String. (pointless)
487 realType Real Class of the Thing, a String. (pointless)
488 myRoot ThingSpace we are in, a ThingSpace.
489
490 Also various functions to wrap checking the security, like canDo, canRead, etc.
491]]
492
493
494--[[ Stuff Java package
495
496In matrix-RAD Stuff took care of multi value Things, like database rows.
497
498Stuff is an abstract class that gets extended by other classes, like
499SquealStuff, which was the only thing extending it. It dealt with the
500basic "collection of things" stuff. Each individual thing was called a
501stufflet. A final fooStuff would extend SquealStuff, and include an
502array of strings called "stufflets" that at least named the stufflets,
503but could also include metadata and links to other Stuffs.
504
505There was various infrastructure for reading and writing Stuff, throwing
506rows of Stuff into grids, having choices of Stuff, linking stufflets to
507individual widgets, having default Stuffs for windows, validating
508Stuffs, etc.
509
510In Lua, setting up stuff has been folded into the general Thing stuff.
511
512]]
513
514
515--[[ Thing structure -
516
517In the following, meta(table) is short for getmetatable(table).
518
519In Lua everything is supposed to be a first class value, and variables are just places to store values.
520All variables are in fact stored in tables, even if it's just the local environment table.
521Any variable that has a value of nil, doesn't actually exist. That's the definition.
522While non table things can have metatables, Lua can only set metatables on tables, C has no such restriction.
523meta(table).__index and __newindex only work on table entries that don't exist.
524 __index(table, key) is called if table.key is nil.
525 Though if __index is a table, then try __index[key].
526 __newindex(table, key, value) is called if table.key is nil.
527 Though if __newindex is a table, then try __newindex[key] = value.
528Using both __index and __newindex, and keeping the actual values elsewhere, is called a proxy table.
529meta(table).__call(table, ...) is called when trying to access table as a function - table(...).
530
531It's worth repeating -
532All variables in Lua are in some table somewhere, even if it's just the global environment table.
533Metatables are only associated vith values, not variables.
534Lua can only attach metatables to values that are tables, but C can attach metatables to any value.
535
536
537A Thing is a managed variable stored in a parent proxy table, which is usually empty.
538So the values stored in this Thing are actually stored in meta(parent)__values[thing].
539 parent[thing] -> __index (parent, thing) -> meta(parent).__values[thing]
540 parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value
541
542
543Each Thing has a description table that includes -
544 names - An array of names, the first one is the "official" name.
545 types - An array of types, the first one is the "official" type.
546 help - A descriptive text for humans to read.
547 default - The default value.
548 widget - A default widget definition.
549 required - If the Thing is required.
550 isValid - A function that tests if the Thing is valid.
551 errors - Any errors related to the Thing.
552 isKeyed - Is this a parent for Things that are stored under an arbitrary key.
553 stuff - An array of descriptions for sub Things, so Things that are tables can have their own Things.
554 and other things that aren't actually used yet.
555All things that a description doesn't have should be inherited from the Thing table.
556 setmetatable(aStuff, {__index = Thing})
557Descriptions should be able to be easily shared by various Things.
558
559
560A parent's metatable has __self, which is it's own description.
561A parent is free to use it's own name space for anything it wants.
562Only the variables it specifies as managed Things are dealt with here.
563
564
565Things that are tables are treated differently, in two different ways even.
566Ordinary table Things are basically treated recursively, the table is a parent, and it gets it's own Things.
567There is also 'Keyed' type table Things, where the keys to this table are arbitrary, but we still want to store Things in it.
568In this case, when a table is assigned to this Keyed Thing, via a new key, a new table Thing is created by copying the parent Thing description.
569
570
571TODO -
572 test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.stuff[foo], then return an empty table instead?
573 Do we still need a parent pointer?
574 Should be in __values I guess.
575 __values[key].value
576 __values[key].parent
577 Weak references might help in here somewhere.
578 Maybe try looking in the skang table for Things that are not found?
579 Maybe put Things in the skang table that are unique from modules?
580 I think this is what matrix-RAD Collisions was all about.
581]]
582
583-- There is no ThingSpace, or Stuff, now it's all just in this meta table.
584local Thing =
585{
586-- Default Thing values.
587 names = {'unknown'},
588 help = 'No description supplied.', -- help text describing this Thing.
589 default = '', -- The default value. This could be a funcion, making this a command.
590 types = {}, -- A list of types. The first is the type of the Thing itself, the rest are for multi value Things. Or argument types for commands.
591 required = false, -- Is this thing is required. TODO - Maybe fold this into types somehow, or acl?
592 widget = '', -- Default widget command arguments for creating this Thing as a widget.
593-- acl = '', -- Access Control List defining security restrains.
594-- boss = '', -- The Thing or person that owns this Thing, otherwise it is self owned.
595
596 action = 'nada', -- An optional action to perform.
597 tell = '', -- The skang command that created this Thing.
598 pattern = '.*', -- A pattern to restrict values.
599
600 isKeyed = false, -- Is this thing an arbitrarily Keyed table?
601 isReadOnly = false, -- Is this Thing read only?
602 isServer = false, -- Is this Thing server side?
603 isStub = false, -- Is this Thing a stub?
604 isStubbed = false, -- Is this Thing stubbed elsewhere?
605
606 hasCrashed = 0, -- How many times this Thing has crashed.
607
608 append = function (self,data) -- Append to the value of this Thing.
609 end,
610
611 stuff = {}, -- The sub things this Thing has, for modules, tables, and Keyed tables.
612 errors = {}, -- A list of errors returned by isValid().
613
614 isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors.
615 -- Anything that overrides this method, should call this super method first.
616 local name = self.names[1]
617 local metaMum = getmetatable(parent)
618 local value = metaMum.__values[name]
619 local mum = metaMum.__self.names[1]
620
621 local t = type(value) or 'nil'
622 self.errors = {}
623 -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions.
624 if 'nil' == t then
625 if self.required then table.insert(self.errors, mum .. '.' .. name .. ' is required!') end
626 else
627 if 'widget' == self.types[1] then
628 -- TODO - Should validate any attached Thing.
629 elseif self.types[1] ~= t then table.insert(self.errors, mum .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!')
630 else
631 if 'number' == t then value = '' .. value end
632 if ('number' == t) or ('string' == t) then
633 if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mum .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end
634 end
635 end
636 end
637
638 for k, v in pairs(self.stuff) do
639 if not v:isValid(value) then
640 for i, w in ipairs(v.errors) do
641 table.insert(self.errors, w)
642 end
643 end
644 end
645
646 return #(self.errors) == 0
647 end,
648
649 remove = function (self) -- Delete this Thing.
650 end,
651}
652
653
654getStuffed = function (parent, key)
655 local metaMum = getmetatable(parent)
656 local thingy
657
658 if metaMum and metaMum.__self then
659 thingy = metaMum.__self.stuff[key]
660
661 if not thingy then
662 -- Deal with getting a table entry.
663 if metaMum.__values[key] then
664 thingy = getmetatable(metaMum.__values[key]).__self
665 end
666 end
667 end
668 return metaMum, thingy
669end
670
671local Mum =
672{
673
674__index = function (parent, key)
675 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist.
676
677 -- First see if this is a Thing.
678 local metaMum, thingy = getStuffed(parent, key)
679
680 if thingy then
681 return metaMum.__values[thingy.names[1] ] or thingy.default
682 end
683
684 -- Then see if we can inherit it from Thing.
685 return Thing[key]
686end,
687
688__newindex = function (parent, key, value)
689 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist.
690
691 -- First see if this is a Thing.
692 local metaMum, thingy = getStuffed(parent, key)
693
694 if not thingy then
695 if metaMum.__self.isKeyed then
696 -- Deal with setting a new Keyed table entry.
697 local newThing = copy(parent, key)
698 local newSelf = getmetatable(newThing).__self
699 rawset(metaMum.__values, key, newThing)
700 thingy = {}
701 for k, v in pairs(newSelf) do
702 thingy[k] = v
703 end
704 thingy.names={key}
705 thingy.types={'table'}
706 setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default.
707 end
708 end
709
710 if thingy then
711 local name = thingy.names[1]
712 local oldMum
713
714 if 'table' == type(value) then
715 -- Coz setting it via metaMum screws with the __index stuff somehow.
716 local oldValue = metaMum.__values[name]
717 if 'table' == type(oldValue) then
718 oldMum = getmetatable(oldValue)
719 if oldMum then
720 -- TODO - This SHOULD work, but doesn't -
721 --setmetatable(value, oldMum)
722 -- Instead we do this -
723 -- Clear out any values in the old table.
724 for k, v in pairs(oldMum.__values) do
725 oldMum.__values[k] = nil
726 end
727 for k, v in pairs(value) do
728 local newK = oldMum.__self.stuff[k]
729 if newK then newK = newK.names[1] else newK = k end
730 oldMum.__values[newK] = v
731 end
732 end
733 end
734 end
735 if nil == oldMum then metaMum.__values[name] = value end
736 -- NOTE - invalid values are still stored, this is by design.
737 if not thingy:isValid(parent) then
738 for i, v in ipairs(thingy.errors) do
739 print('ERROR - ' .. v)
740 end
741 end
742 -- TODO - Go through it's linked things and set them to.
743 -- Done, don't fall through to the rawset()
744 return
745 end
746
747 rawset(parent, key, value) -- Stuff it normally.
748end,
749
750__call = function (func, ...)
751 return thingasm(func, ...)
752end,
753
754}
755
756newMum = function ()
757 local result = {}
758--[[ From an email by Mike Pall -
759"Important: create the metatable and its metamethods once and reuse
760the _same_ metatable for _every_ instance."
761
762This is for LuaJIT, he's the author, and concerns performance.
763
764TODO - Which is the exact opposite of what we are doing. Perhaps we can fix that?
765]]
766 for k, v in pairs(Mum) do
767 result[k] = v
768 end
769 result.__self = {stuff={}}
770 result.__values = {}
771 return result
772end
773
774
775-- skang.thingasm() Creates a new Thing, or changes an existing one.
776--[[ It can be called in many different ways -
777
778It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss)
779Or it can be called with a table - {names, help, pattern='.*', acl='rwx'}
780
781The first argument can be another Thing (the parent), or a string list of names (see below).
782
783It can be called by itself, with no parent specified -
784 thingasm('foo', 'help text)
785In this case the surrounding Lua environment becomes the parent of foo.
786 If the first argument (or first in the table) is a string, then it's this form.
787All others include the parent as the first argument, which would be a table.
788
789It can be called by calling the parent as a function -
790 foo('bar', 'some help', types='table') -- ___call(foo, 'bar', ...) And now foo is the parent.
791 foo.bar{'baz', types='Keyed'} -- thingasm({foo.bar, 'baz', ...})
792 foo.bar.baz{'field0'} -- thingasm({foo.bar.baz, 'field0'})
793 foo.bar.baz{'field1'}
794]]
795
796-- names - a comma seperated list of names, aliases, and shortcuts. The first one is the official name.
797-- If this is not a new thing, then only the first one is used to look it up.
798-- So to change names, use skang.thingasm{'oldName', names='newName,otherNewName'}
799thingasm = function (names, ...)
800 local params = {...}
801 local new = false
802 local parent
803 local set = true
804
805 -- Check how we were called, and re arrange stuff to match.
806 if 0 == #params then
807 if ('table' == type(names)) then -- thingasm{...}
808 params = names
809 names = shiftLeft(params)
810 if 'table' == type(names) then -- thingasm{parent, 'foo', ...}
811 parent = names
812 names = shiftLeft(params)
813 end
814 end -- thingasm("foo") otherwise
815 else
816 if 'table' == type(names) then
817 parent = names
818 if 'string' == type(...) then params = {...} -- C or __call(table, string, ..)
819 elseif 'table' == type(...) then params = ... -- __call(table, table)
820 end
821 names = shiftLeft(params)
822 end -- thingasm('foo', ...) otherwise
823 end
824
825 -- Break out the names.
826 names = csv2table(names)
827 local name = names[1]
828 local oldNames = {}
829
830 -- TODO - Double check this comment - No need to bitch and return if no names, this will crash for us.
831
832 -- Grab the environment of the calling function if no parent was passed in.
833 parent = parent or getfenv(2)
834 local metaMum = getmetatable(parent)
835 -- Coz at module creation time, Thing is an empty table, or in case this is for a new parent.
836 if nil == metaMum then
837 metaMum = newMum()
838 metaMum.__self.names = {parent._NAME or 'NoName'}
839 if parent._NAME then metaMum.__self.types = {'Module'} end
840 setmetatable(parent, metaMum)
841 end
842
843 local thingy = metaMum.__self.stuff[name]
844 if not thingy then -- This is a new Thing.
845 new = true
846 thingy = {}
847 thingy.names = names
848 thingy.stuff = {}
849 setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default.
850 end
851
852 -- Pull out positional arguments.
853 thingy.help = params[1] or thingy.help
854 thingy.default = params[2] or thingy.default
855 local types = params[3] or table.concat(thingy.types or {}, ',')
856
857 -- Pull out named arguments.
858 for k, v in pairs(params) do
859 if 'string' == type(k) then
860 if 'types' == k then types = v
861 elseif 'names' == k then
862 oldNames = thingy.names
863 thingy.names = cvs2table(v)
864 elseif 'required' == k then
865 if isBoolean(v) then thingy.required = true end
866 else thingy[k] = v
867 end
868 end
869 end
870
871 -- Find type, default to string, then break out the other types.
872 local typ = type(thingy.default)
873 if 'nil' == typ then typ = 'string' end
874 if 'function' == typ then types = typ .. ',' .. types end
875 if '' == types then types = typ end
876 thingy.types = csv2table(types)
877
878 if 'widget' == thingy.types[1] then
879 set = false
880 local args, err = loadstring('return ' .. thingy.widget)
881 if args then
882 setfenv(args, parent)
883 thingy.Cwidget = widget(args())
884print('\nNO IDEA WHY this does isValid() three times on the action, and the first one being a string.')
885 parent.W[name] = thingy
886 else
887 print("ERROR - " .. err)
888 end
889 end
890
891 -- Deal with Keyed and tables.
892 if 'Keyed' == thingy.types[1] then
893 set = false
894 thingy.types[1] = 'table'
895 thingy.isKeyed = true
896 end
897 if 'table' == thingy.types[1] then
898 -- Default in this case becomes a parent.
899 if '' == thingy.default then thingy.default = {} end
900 local thisMum = newMum()
901 thisMum.__self = thingy
902 setmetatable(thingy.default, thisMum)
903 end
904
905 if 'userdata' == thingy.types[1] then
906 set = false
907 end
908
909 -- Remove old names, then stash the Thing under all of it's new names.
910 for i, v in ipairs(oldNames) do
911 metaMum.__self.stuff[v] = nil
912 end
913 for i, v in ipairs(thingy.names) do
914 metaMum.__self.stuff[v] = thingy
915 end
916
917 -- This triggers the Mum.__newindex metamethod above. If nothing else, it triggers thingy.isValid()
918 if new and set then parent[name] = thingy.default end
919end
920
921
922fixNames = function (module, name)
923 local stuff = getmetatable(module)
924 if stuff then
925 stuff.__self.names[1] = name
926 for k, v in pairs(stuff.__self.stuff) do
927 if 'table' == v.types[1] then
928 local name = v.names[1]
929 print(name .. ' -> ' .. name)
930 fixNames(stuff.__values[name], name)
931 end
932 end
933 end
934end
935
936
937copy = function (parent, name)
938 local result = {}
939 local stuff = getmetatable(parent).__self.stuff
940
941 for k, v in pairs(stuff) do
942 local temp = {}
943 for l, w in pairs(v) do
944 temp[l] = w
945 end
946 temp[1] = table.concat(temp.names, ',')
947 temp.names = nil
948 temp.types = table.concat(temp.types, ',')
949 temp.errors = nil
950 thingasm(result, temp)
951 end
952 getmetatable(result).__self.names = {name}
953
954-- TODO - Should we copy values to?
955
956 return result
957end
958
959module = function (name)
960 _G[name] = require(name)
961 return _G[name]
962end
963
964stuff = function (aThingy, aStuff)
965 return getmetatable(aThingy).__self.stuff[aStuff]
966end
967
968
969get = function (stuff, key, name)
970 local result
971 if name then
972 local thingy = getmetatable(stuff)
973 if thingy then
974 local this = thingy.__self.stuff[key]
975 if this then result = this[name] end
976 end
977 else
978 result = stuff[key]
979 end
980 return result
981end
982
983
984reset = function (stuff, key, name)
985 if name then
986 local thingy = getmetatable(stuff)
987 if thingy then
988 local this = thingy.__self.stuff[key]
989 if this then this[name] = nil end
990 end
991 else
992 stuff[key] = nil
993 end
994end
995
996
997set = function (stuff, key, name, value)
998 if 'nil' ~= type(value) then
999 local thingy = getmetatable(stuff)
1000 if thingy then
1001 local this = thingy.__self.stuff[key]
1002 if this then this[name] = value end
1003 end
1004 else
1005 -- In this case, value isn't there, so we are reusing the third argument as the value.
1006 stuff[key] = name
1007 end
1008end
1009
1010
1011-- Get our C functions installed into skang.
1012-- This has to be after thingasm is defined.
1013package.cpath = package.cpath .. ';../../libraries/lib?.so'
1014local GuiLua = require 'GuiLua'
1015
1016
1017thingasm('module,l', 'Load a module.', module, 'file')
1018thingasm('get', 'Get the current value of an existing Thing or metadata.', get, 'thing,key,name')
1019thingasm('reset', 'Reset the current value of an existing Thing or metadata.', reset, 'thing,key,name')
1020thingasm('set', 'Set the current value of an existing Thing or metadata.', set, 'thing,key,name,data')
1021
1022thingasm('nada', 'Do nothing.', function () --[[ This function intentionally left blank. ]] end)
1023
1024
1025
1026-- Widget wrappers.
1027-- TODO - Fix this up so we don't need .W
1028local widgets = {}
1029--thingasm{widgets, 'window', 'The window.', types='userdata'}
1030thingasm{widgets, 'W', 'Holds all the widgets', types='Keyed'}
1031widgets.W{'Cwidget', 'The widget.', types='userdata'}
1032widgets.W{'action,a', 'The action for the widget.', 'nada', types='string'}
1033local aIsValid = function (self, parent)
1034 local result = Thing.isValid(self, parent)
1035
1036 if result then
1037 local value = parent[self.names[1] ]
1038print('NEW ACTION - ' .. self.names[1] .. ' = ' .. value .. ' ' .. type(parent.Cwidget))
1039 action(parent.Cwidget, value)
1040 end
1041 return result
1042end
1043
1044widgets.W{'look,l', 'The look of the widget.', types='string'}
1045--[[
1046widgets.W{'colour,color,c', 'The colours of the widget.', types='table'}
1047widgets.W.c{'red,r', 'The red colour of the widget.', 255, types='number'}
1048widgets.W.c{'green,g', 'The green colour of the widget.', 255, types='number'}
1049widgets.W.c{'blue,b', 'The blue colour of the widget.', 255, types='number'}
1050widgets.W.c{'alpha,a', 'The alpha colour of the widget.', 255, types='number'}
1051-- At this point we want to change widgets.W.c() to go to a different __call, coz right now it's going to the Mum.__call, which wraps thingasm.
1052-- TODO - Keep an eye on this if we change to a single Mum, instead of the shallow copy we use now.
1053local wMum = getmetatable(widgets.W.c)
1054wMum.__call = function (func, ...)
1055 return Colour(func, ...)
1056end
1057]]
1058
1059window = function(w, h, title, name)
1060 name = name or 'myWindow'
1061 local win = {}
1062 win = copy(widgets, name)
1063 local wMum, wThingy = getStuffed(win.W, 'a')
1064 wThingy.isValid = aIsValid
1065 win.window = Cwindow(w, h, title, name)
1066 return win
1067end
1068
1069thingasm{'window', 'Specifies the size and title of the application Frame.', window, 'number,number,string', acl="GGG"}
1070
1071
1072-- TODO - Some function stubs, for now. Fill them up later.
1073skang = function (name)
1074end
1075
1076thingasm('skang', 'Parse the contents of a skang file or URL.', skang, 'URL')
1077
1078
1079moduleEnd(_M)
1080
1081end
1082
1083-- Boss is the person that owns a Thing.
1084
1085--[[ The original Skang parameters and commands.
1086 public final static String MY_USAGE[][] =
1087 {
1088 {"skinURL", "skinURL", "Y", "s", null, "URL of skin file.", "", "RI-"},
1089 {"debug", "debug", "N", "", "0", "Set debugging level to :\n\t-1 - errors and warnings only (-q)\n\t0 - basic information\n\t1 - advanced information (-v)\n\t2 - trace functions\n\t3 - trace protocol\n\t4 - dump packets + stuff\n\t5 - detail", "", ""},
1090 {"browser", "browser", "N", "", "mozilla %f", "Browser to run.", "", ""},
1091 {"downloaddir", "downloadDir", "N", "", "download", "Download directory.", "", ""},
1092 {"sessionID", "sessionID", "N", "", null, "SessionID from servlet.", "", ""},
1093 {"JSESSIONID", "JSESSIONID", "N", "", null, "JSESSIONID from servlet engine.", "", ""},
1094 {"servletpath", "servletPath", "N", "", "matrix_rad", "Servlet path.", "", ""},
1095 {"servletport", "servletPort", "N", "", null, "Servlet port.", "", ""},
1096 {"servletsslport", "servletSSLPort", "N", "", null, "Servlet SSL port.", "", ""},
1097 {"HTML", "HTML", "N", "", "false", "Output to HTML?", "", ""},
1098 {"PHP", "PHP", "N", "", "false", "Output though the PHP wrapper", "", ""},
1099 {"inbrowser", "inBrowser", "N", "", "true", "Run in browser window?", "", ""},
1100 {"SSL", "SSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""},
1101 {"NOSSL", "NOSSL", "N", "", null, "Dummy to avoid a web server bug.", "", ""},
1102 {"corporate", "corporate", "N", "", null, "Are we doing corporate shit?", "", ""},
1103 {"", "", "", "", "", "", "", ""}
1104 };
1105 public final static String MY_SKANG[][] =
1106 {
1107-- {"module", "addModule", "file,data", "Load a module.", "", ""},
1108 {"append", "appendThing", "name,data", "Append to the current value of a Thing.", "", ""},
1109 {"#!java", "bash", "name,name,name,name,name,name,name", "A not so clever unix script compatability hack.", "", ""},
1110 {"pending", "pendingDoThing", "action", "Do an action when you are ready.", "", ""},
1111 {"applet", "doIfApplet", "action", "Only do this if we are an applet.", "", ""},
1112 {"application", "doIfApplication", "action", "Only do this if we are an application.", "", ""},
1113 {"corporateshit", "doIfCorporateShit", "action", "Only do this if we are doing corporate shit.", "", ""},
1114 {"realworld", "doIfRealWorld", "action", "Only do this if we are in the real world.", "", ""},
1115 {"servlet", "doIfServlet", "action", "Only do this if we are a servlet.", "", ""},
1116 {"do", "doThing", "action", "Do this action.", "", ""},
1117 {"grab", "getFile", "URL", "Grab a file from a URL.", "", ""},
1118-- {"get", "getThing", "name", "Get the current value of an existing thing.", "", ""},
1119 {"gimmeskin", "gimmeSkin", "", "Returns the modules default skin.", "", ""},
1120 {"help", "helpThing", "file", "Show help page.", "", ""},
1121-- {"nada", "nothing", "data", "Does nothing B-).", "", ""},
1122 {"postshow", "postShowThings", "URL,name", "POST the values of all Things to the URL, show the returned content.", "", ""},
1123 {"post", "postThings", "URL", "POST the values of all Things to the URL, return the content.", "", ""},
1124 {"postparse", "postParseThings", "URL", "POST the values of all Things to the URL, parse the returned content.", "", ""},
1125 {"quiet", "quiet", "", "Output errors and warnings only.", "", ""},
1126 {"remove", "removeThing", "name", "Remove an existing thing.", "", ""},
1127 {"sethelp", "setHelp", "name,data", "Change the help for something.", "", ""},
1128-- {"set", "setThing", "name,data", "Set the current value of an existing Thing.", "", ""},
1129-- {"skang", "skangRead", "URL", "Parse the contents of a skang file or URL.", "", ""},
1130-- {"quit", "startQuit", "", "Quit, exit, remove thyself.", "", ""},
1131 {"stopwhinging", "stopWhinging", "", "Clear all messages.", "", ""},
1132 {"tell", "tellThing", "name", "Returns details of an existing Thing.", "", ""},
1133 {"togglebug", "toggleIgnoreBrowserBug", "", "Toggle ignorance of a certain browser bug.", "", ""},
1134 {"verbose", "verbose", "", "Output advanced information.", "", ""},
1135 {"", "", "", "", "", ""}
1136]]
1137
1138--[[ The original SkangAWT parameters and commands.
1139 public final static String MY_USAGE[][] =
1140 {
1141 {"", "", "", "", "", "", "", ""}
1142 };
1143 public final static String MY_SKANG[][] =
1144 {
1145 {"taction", "tactionWidget", "name,action", "Set the alternative action for a widget.", "", ""},
1146 {"action", "actionWidget", "name,action", "Set the action for a widget.", "", ""},
1147 {"pane", "addPane", "name,x,y,w,h,data", "Add a pane to the current module.", "", ""},
1148 {"widget", "addWidget", "name,type,lx,ly,lw,lh,data,data", "Add a widget to the current skin.", "", ""},
1149 {"checkboxgroup", "checkBoxGroup", "number", "Make the next 'number' Checkboxes part of a check box group.", "", ""},
1150-- {"clear", "clearWidgets", "", "The current skin is cleared of all widgets.", "", ""},
1151 {"colour", "colourWidget", "name,r,g,b,alpha,r,g,b,alpha", "Set widget's background and foreground colour.", "", "GGG"},
1152 {"doaction", "doWidget", "name", "Do a widgets action.", "", "GGG"},
1153 {"disable", "disableWidget", "name", "Disable a widget.", "", "GGG"},
1154 {"enable", "enableWidget", "name", "Enable a widget.", "", "GGG"},
1155 {"hide", "hideWidget", "name", "Hide a widget.", "", "GGG"},
1156 {"hideall", "hideAllWidgets", "name,lx,ly,lw,lh", "Hide all widgets.", "", "GGG"},
1157 {"look", "lookWidget", "name,normal,ghost,active,toggle", "Set the current look of an existing widget.", "", "GGG"},
1158 {"mask", "maskWidget", "name,data", "Set the mask for a widget.", "", ""},
1159 {"onmouse", "onMouse", "name,data", "Do something on mouse hover.", "", ""},
1160 {"offmouse", "offMouse", "name,data", "Do something off mouse hover.", "", ""},
1161 {"popup", "popupWidget", "name,data,data,data,data", "Create a popup.", "", "GGG"},
1162 {"readonly", "readOnlyWidget", "name", "Make a widget read only.", "", "GGG"},
1163 {"writeonly", "writeOnlyWidget", "name", "Make a widget write only.", "", "GGG"},
1164 {"satori", "satori", "x,y", "Give me the developers menu.", "", "GGG"},
1165 {"showloginwindow", "showLoginWindow", "", "Show user login window.", "", "GGG"},
1166 {"show", "showWidget", "name", "Show a widget.", "", "GGG"},
1167-- {"window", "setSkangFrame", "x,y,name", "Specifies the size and title of the application Frame.", "", "GGG"},
1168 {"stuff", "stuffWidget", "name,data", "Set the stuff for a widget's pane.", "", ""},
1169 {"stufflet", "stuffWidget", "name,data,data", "Set the stufflet for a widget.", "", ""},
1170 {"stufflist", "stuffListWidget", "name,data", "List the stuff in this widget.", "", ""},
1171 {"stuffload", "stuffLoadWidget", "name,data,data", "Load the stuff for a widget.", "", ""},
1172 {"stuffsave", "stuffSaveWidget", "name,data,data", "Save the stuff for a widget.", "", ""},
1173 {"stuffdelete", "stuffDeleteWidget", "name,data,data", "Delete the stuff for a widget.", "", "SSS"},
1174 {"stuffclear", "stuffClearWidget", "name,data", "Clear the stuff for a widget.", "", "SSS"},
1175 {"rowtowidgets", "rowToWidgets", "name", "Copy Grid row to matching widgets.", "", ""},
1176 {"widgetstorow", "widgetsToRow", "name,data", "Copy matching widgets to Grid row.", "", ""},
1177 {"clearrow", "clearRow", "name", "Clear Grid row and matching widgets.", "", ""},
1178 {"clearrowwidgets", "clearRowWidgets", "name", "Clear only the Grid row matching widgets.", "", ""},
1179 {"", "", "", "", "", ""}
1180 };
1181]]
1182
1183
1184--[[ security package
1185
1186Java skang could run as a stand alone applicion, as an applet in a web
1187page, or as a servlet on a web server. This was pretty much all
1188transparent to the user. The security system reflected that. Lua skang
1189wont run in web pages, but can still have client / server behaviour.
1190The general idea was, and still is, that the GUI is the client side (in
1191web page, in extantz GUI) that sends values back to the server side
1192(servlet, actual Lua package running as a separate process, or the world
1193server for in world scripts). Client side can request that server side
1194runs commands. Serevr side can send values and commands back to the
1195client. Mostly it all happenes automatically through the ACLs.
1196
1197Bouncer is the Java skang security manager, it extended the Java
1198SecurityManager. Lua has no such thing, though C code running stuff in
1199a sandbox does a similar job. Fascist is the Java security supervisor,
1200again should go into the C sandbox.
1201
1202Human is used for authenticating a human, Puter for authenticating a
1203computer, Suits for corporate style authentication, and they all
1204extended Who, the base authentication module.
1205
1206For now, I have no idea how this all translates into Lua, but putting
1207this here for a reminder to think about security during the design
1208stage.
1209
1210
1211This is the old Java ACL definition -
1212 acl - access control list.
1213Owner is usually the person running the Thingspace.
1214RWX~,---,Rwxgroup1,r--group2,r-xgroup3,rw-group4,--X~user1
1215rwx~ is for the owner. The second one is the default. The rest are per group or per user.
1216Capital letters mean that they get access from the network to.
1217--- No access at all.
1218RWX Full access.
1219R-- Read only access.
1220r-x Read and execute, but only locally.
1221rw- Read and write a field, but don't execute a method.
1222-w- A password.
1223-a- An append only log file.
1224-A- An append only log file on the server.
1225Ri- read, but only set from init (ei. skinURL not set from properties or skang files).
1226RI- As above, but applet.init() can set it too.
1227--x Thing is both method and field, only execution of the method is allowed.
1228--p Run as owner (Pretend).
1229--P Run across the network as owner (can run in applet triggered by server).
1230s-- Read only, but not even visible to applets.
1231sss Only visible to servlets and applications.
1232--S Send to servlet to execute if applet, otherwise execute normally.
1233S-- Read only, but ignore local version and get it from server.
1234ggg GUI Thing, only visible to Applets and applications.
1235GGG GUI Thing, but servlets can access them across the net.
1236
1237For servlet only modules from an applet, the applet only loads the skanglet class, using it for all
1238access to the module.
1239
1240
1241Lua Security best practices -
1242
1243 From an email by Mike Pall -
1244
1245"The only reasonably safe way to run untrusted/malicious Lua scripts is
1246to sandbox it at the process level. Everything else has far too many
1247loopholes."
1248
1249http://lua-users.org/lists/lua-l/2011-02/msg01595.html
1250http://lua-users.org/lists/lua-l/2011-02/msg01609.html
1251http://lua-users.org/lists/lua-l/2011-02/msg01097.html
1252http://lua-users.org/lists/lua-l/2011-02/msg01106.html
1253
1254So that's processes, not threads like LuaProc does. B-(
1255
1256However, security in depth is good, so still worthwhile looking at it from other levels as well.
1257
1258General solution is to create a new environment, which we are doing
1259anyway, then whitelist the safe stuff into it, instead of blacklisting
1260unsafe stuff. Coz you never know if new unsafe stuff might exist.
1261
1262Different between 5.1 (setfenv) and 5.2 (_ENV)
1263
1264http://lua-users.org/wiki/SandBoxes -
1265
1266------------------------------------------------------
1267-- make environment
1268local env = -- add functions you know are safe here
1269{
1270 ipairs = ipairs,
1271 next = next,
1272 pairs = pairs,
1273 pcall = pcall,
1274 tonumber = tonumber,
1275 tostring = tostring,
1276 type = type,
1277 unpack = unpack,
1278 coroutine = { create = coroutine.create, resume = coroutine.resume,
1279 running = coroutine.running, status = coroutine.status,
1280 wrap = coroutine.wrap },
1281 string = { byte = string.byte, char = string.char, find = string.find,
1282 format = string.format, gmatch = string.gmatch, gsub = string.gsub,
1283 len = string.len, lower = string.lower, match = string.match,
1284 rep = string.rep, reverse = string.reverse, sub = string.sub,
1285 upper = string.upper },
1286 table = { insert = table.insert, maxn = table.maxn, remove = table.remove,
1287 sort = table.sort },
1288 math = { abs = math.abs, acos = math.acos, asin = math.asin,
1289 atan = math.atan, atan2 = math.atan2, ceil = math.ceil, cos = math.cos,
1290 cosh = math.cosh, deg = math.deg, exp = math.exp, floor = math.floor,
1291 fmod = math.fmod, frexp = math.frexp, huge = math.huge,
1292 ldexp = math.ldexp, log = math.log, log10 = math.log10, max = math.max,
1293 min = math.min, modf = math.modf, pi = math.pi, pow = math.pow,
1294 rad = math.rad, random = math.random, sin = math.sin, sinh = math.sinh,
1295 sqrt = math.sqrt, tan = math.tan, tanh = math.tanh },
1296 os = { clock = os.clock, difftime = os.difftime, time = os.time },
1297}
1298
1299-- run code under environment [Lua 5.1]
1300local function run(untrusted_code)
1301 if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end
1302 local untrusted_function, message = loadstring(untrusted_code)
1303 if not untrusted_function then return nil, message end
1304 setfenv(untrusted_function, env)
1305 return pcall(untrusted_function)
1306end
1307
1308-- run code under environment [Lua 5.2]
1309local function run(untrusted_code)
1310 local untrusted_function, message = load(untrusted_code, nil, 't', env)
1311 if not untrusted_function then return nil, message end
1312 return pcall(untrusted_function)
1313end
1314------------------------------------------------------
1315
1316Also includes a table of safe / unsafe stuff.
1317
1318
1319While whitelisting stuff, could also wrap unsafe stuff to make them more safe.
1320
1321print() -> should reroute to our output widgets.
1322rawget/set() -> don't bypass the metatables, but gets tricky and recursive.
1323require -> don't allow bypassing the sandbox to get access to restricted modules
1324package.loaded -> ditto
1325
1326
1327Other things to do -
1328
1329debug.sethook() can be used to call a hook every X lines, which can help with endless loops and such.
1330Have a custom memory allocater, like edje_lua2 does.
1331
1332------------------------------------------------------
1333------------------------------------------------------
1334
1335The plan -
1336
1337 Process level -
1338 Have a Lua script runner C program / library.
1339 It does the LuaProc thing, and the edje_lua2 memory allocater thing.
1340 Other code feeds scripts to it via a pipe.
1341 Unless they are using this as a library.
1342 It can be chrooted, ulimited, LXC containered, etc.
1343 It has an internal watchdog thread.
1344 The truly paranoid can have a watchdog timer process watch it.
1345 Watches for a "new Lua state pulled off the queue" signal.
1346 This could be done from the App that spawned it.
1347
1348 It runs a bunch of worker threads, with a queue of ready Lua states.
1349 Each Lua state being run has lua_sethook() set to run each X lines, AND a watchdog timer set.
1350 If either is hit, then the Lua state is put back on the queue.
1351 (Currently LuaProc states go back an the queue when waiting for a "channel message", which does a lua_yeild().)
1352 NOTE - apparently "compiled code" bypasses hooks, though there's an undocumented LuaJIT compile switch for that. http://lua-users.org/lists/lua-l/2011-02/msg01106.html
1353 EFL is event based.
1354 LSL is event based.
1355 LuaSL is event based.
1356 Java skang is event based, with anything that has long running stuff overriding runBit().
1357 Coz Java AWT is event based, the "events" are over ridden methods in each widget class.
1358 Should all work if we keep this as event based.
1359 An "event" is a bit of Lua script in a string, at Input trust level usually.
1360 Configurable for this script runner is -
1361 IP address & port, or pipe name.
1362 Security level to run at, defaults to Network.
1363 Number of worker threads, defaults to number of CPUs.
1364 Memory limit per Lua state.
1365 Lines & time per tick for Lua states.
1366
1367 Different levels of script trust -
1368 System - the local skang and similar stuff.
1369 -> No security at all.
1370 App - Lua scripts and C from the running application.
1371 -> Current module level security.
1372 Each has it's own environment, with no cross environment leakage.
1373 Runs in the Apps process, though that might be the script runner as a library.
1374 Or could be skang's main loop.
1375 Local - Lua scripts and skang files sent from the client apps running on the same system.
1376 -> As per App.
1377 Runs in a script runner, maybe? Or just the Apps script runner.
1378 Limit OS and file stuff, the client app can do those itself.
1379 Network - Lua scripts and skang files sent from the network.
1380 -> Runs in a script runner.
1381 Option to chroot it, ulimit it, etc.
1382 Heavily Lua sandboxed via environment.
1383 It can access nails, but via network derived credentials.
1384 Can have very limited local storage, not direct file access though, but via some sort of security layer.
1385 Can have network access.
1386 Can have GUI access, but only to it's own window.
1387 Config - Lua scripts run as configuration files.
1388 -> Almost empty local environment, can really only do math and set Things.
1389 Input - Lua scripts run as a result of hitting skang widgets on the other end of a socket.
1390 -> As per Config, but can include function calls.
1391 Also would need sanitizing, as this can come from the network.
1392 Microsoft - Not to be trusted at all.
1393 Apple - Don't expect them to trust us.
1394
1395 NOTE - edje_lua2 would be roughly Local trust level.
1396
1397 So we need to be able to pass Lua between processes -
1398 Have to be able to do it from C, from Lua, and from Lua embedded in C.
1399 edje_lua2 - uses Edje messages / signals.
1400 LuaSL - uses Ecore_Con, in this case a TCP port, even though it's only local.
1401 LuaSL mainloop for it's scripts is to basically wait for these messages from LuaProc.
1402 Which yield's until we get one.
1403 Eventually gets Lua code as a string -> loadstring() -> setfenv() -> pcall()
1404 The pcall returns a string that we send back as a LuaProc message.
1405 Extantz - we want to use stdin/stdout for the pipe, but otherwise LuaSL style semantics.
1406
1407 Hmm, Extantz could run external skang modules in two ways -
1408 Run the module as a separate app, with stdin/stdout.
1409 Require the module, and run it internally.
1410 Stuff like the in world editor and radar would be better as module, since they are useless outside Extantz?
1411 Radar is useless outside Extantz, editor could be used to edit a local file.
1412 Stuff like woMan would be better off as a separate application, so it can start and stop extantz.
1413
1414]]
1415
1416
1417--[[
1418The main loop.
1419 A Lua skang module is basically a table, with skang managed fields.
1420 Once it calls moduleEnd(), it's expected to have finished.
1421 test.lua is currently an exception, it runs test stuff afterwards.
1422 So their code just registers Things and local variables.
1423 Some of those Things might be functions for manipulating internal module state.
1424 A C module has it's variables managed outside of it by Lua.
1425 So would have to go via LUA_GLOBALSINDEX to get to them.
1426 Unless they are userdata, which are C things anyway.
1427 Though it's functions are obviously inside the C module.
1428 Normal Lua module semantics expect them to return to the require call after setting themselves up.
1429 So test.lua is really an aberation.
1430
1431 Soooo, where does the main loop go?
1432 The skang module itself could define the main loop.
1433 Should not actually call it though, coz skang is itself a module.
1434
1435 In Java we had different entry points depending on how it was called.
1436 If it was called as an applet or application, it got it's own thread with a mainloop.
1437 That main loop just slept and called runBit() on all registered modules.
1438 If it was loaded as a module, it bypassed that stuff.
1439 I don't think Lua provides any such mechanism.
1440 In theory, the first call to moduleBegin would be the one that was started as an application.
1441 So we could capture that, and test against it in moduleEnd to find when THAT module finally got to the end.
1442 THEN we can start the main loop (test.lua being the exception that fucks this up).
1443 Though test.lua could include a runBits() that quits the main loop, then starts the main loop at the very end once more?
1444 The problem with this is applications that require skang type modules.
1445 The first one they require() isn't going to return.
1446 Eventually skang itself will get loaded. It looks at the "arg" global, which SHOULD be the command line.
1447 Including the name of the application, which we could use to double check.
1448 Extantz / script runner would set this arg global itself.
1449
1450 Skang applications have one main loop per app.
1451 Skang modules use the main loop of what ever app called them.
1452 Non skang apps have the option of calling skangs main loop.
1453 Extantz starts many external skang apps, which each get their own main loop.
1454 Extantz has it's own Ecore main loop.
1455 LuaSL still has one main loop per script.
1456 LSL scripts get their own main loop, but LSL is stupid and doesn't have any real "module" type stuff.
1457
1458What does skang main loop actually do?
1459 In Java it just slept for a bit, then called runBit() from each module, and the only module that had one was watch.
1460 Actually better off using Ecore timers for that sort of thing.
1461 Skang main loop has nothing to do? lol
1462 Well, except for the "wait for LuaProc channel messages" thing.
1463 Widget main loop would be Ecore's main loop.
1464
1465 Extantz loads a skang module.
1466 Module runs in extantz script runner.
1467 Module runs luaproc message main loop from skang.
1468 Evas / Elm widgets send signals, call C callbacks.
1469 Extantz sends Lua input scripts via luaproc messages to module.
1470 Extantz runs separate skang module.
1471 Module runs in it's own process.
1472 Module runs it's own message loop on the end of stdin.
1473 Evas / Elm widgets send signals, call C callbacks.
1474 Extantz sends Lua Input scripts to module via stdout.
1475 Module runs stand alone.
1476 Module runs in it's own process.
1477 Module has to have widget start Ecore's main loop.
1478 Module runs it's own message loop, waiting for widget to send it messages.
1479 Evas / Elm widgets send signals, call C callbacks.
1480 Widget sends Lua Input scripts to module.
1481
1482Alternate plan -
1483 Skang has no main loop, modules are just tables.
1484 OK, so sometimes skang has to start up an Ecore main loop.
1485 With either Ecore_Con, or Evas and Elm.
1486 skang.message(string)
1487 Call it to pass a bit of Lua to skang, which is likely Input.
1488 Widget twiddles happen in Ecore's main loop, via signals and call backs.
1489 Eventually these call backs hit the widgets action string.
1490 Send that action string to skang.message().
1491
1492 Extantz loads a skang module.
1493 Extantz has a skang Lua state.
1494 Module is just another table in skang.
1495 Widget -> callback -> action string -> skang.message()
1496 Extantz runs separate skang module.
1497 Skang module C code runs an Ecore main loop.
1498 Module is just another table in skang.
1499 Skang C uses Ecore_Con to get messages from Extantz' Ecore_Con.
1500 Widget -> callback -> action string -> Ecore_Con -> skang.message()
1501 OOORRRR ....
1502 Skang runs a main loop reading stdin.
1503 Widget -> callback -> action string -> stdout -> skang.message()
1504 Module runs stand alone.
1505 Skang module C code runs an Ecore main loop.
1506 Module is just another table in skang.
1507 Widget -> callback -> action string -> skang.message()
1508 Extantz loads a skang module from the network.
1509 Skang module runs on the server with it's own Ecore main loop somehow.
1510 Module is just another table in skang.
1511 Extantz uses Ecore_Con to get messages from Extantz' Ecore_Con, via TCP.
1512 Widget -> callback -> action string -> Ecore_Con -> skang.message()
1513 OOORRRR ....
1514 Remember, these are untrusted, so that's what the script runner is for.
1515 Skang module runs in the script runner, with some sort of luaproc interface.
1516 Widget -> callback -> action string -> Ecore_Con -> luaproc -> skang.message()
1517
1518 Skang running as a web server.
1519 Skang runs an Ecore main loop.
1520 Skang has an Ecore_Con server attached to port 80.
1521 Skang loads modules as usual.
1522 Skang responds to specific URLs with HTML forms generated from Skang files.
1523 Skang gets post data back, which kinda looks like Input. B-)
1524
1525 Extantz runs a LuaSL / LSL script from the network.
1526 Send this one to the LuaSL script runner.
1527 Coz LSL scripts tend to have lots of copies with the same name in different objects.
1528 Could get too huge to deal with via "just another table".
1529 In this case, compiling the LSL might be needed to.
1530
1531]]
1532
1533
1534--[[ TODO
1535 NOTE that skang.thingasm{} doesn't care what other names you pass in, they all get assigned to the Thing.
1536
1537
1538 Widget -
1539 Should include functions for actually dealing with widgets, plus a way
1540 of creating widgets via introspection. Should also allow access to
1541 widget internals via table access. Lua code could look like this -
1542
1543 foo = widget('label', 0, "0.1", 0.5, 0, 'Text goes here :")
1544 -- Method style.
1545 foo:colour(255, 255, 255, 0, 0, 100, 255, 0)
1546 foo:hide()
1547 foo:action("skang.load(some/skang/file.skang)")
1548 -- Table style.
1549 foo.action = "skang.load('some/skang/file.skang')"
1550 foo.colour.r = 123
1551 foo.look('some/edje/file/somewhere.edj')
1552 foo.help = 'This is a widget for labelling some foo.'
1553
1554 Widgets get a type as well, which would be label, button, edit, grid, etc.
1555 A grid could even have sub types - grid,number,string,button,date.
1556 A required widget might mean that the window HAS to have one.
1557 Default for a widget could be the default creation arguments - '"Press me", 1, 1, 10, 50'.
1558
1559 skang.thingasm{'myButton', types='Button', rectangle={1, 1, 10, 50}, title='Press me', ...}
1560
1561 skang.thingasm('foo,s,fooAlias', 'Foo is a bar, not the drinking type.', function () print('foo') end, '', '"button", "The foo :"' 1, 1, 10, 50')
1562 myButton = skang.widget('foo') -- Gets the default widget creation arguments.
1563 myButton:colour(1, 2, 3, 4)
1564 -- Use generic positional / named arguments for widget to, then we can do -
1565 myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, look='edit.edj', colour={blue=20}, action='...'}
1566 -- Using the Thing alias stuff, maybe we can do the "first stage tokenise" step after all -
1567 myEditor = skang.widget{'foo', "edit", "Edit foo :", 5, 15, 10, 100, l='edit.edj', c={b=20}, a='...'}
1568 myEditor:colour(1, 2, 3, 4, 5, 6, 7, 8)
1569 myButton = 'Not default' -- myEditor and foo change to. Though now foo is a command, not a parameter, so maybe don't change that.
1570 -- Though the 'quit' Thing could have a function that does quitting, this is just an example of NOT linking to a Thing.
1571 -- If we had linked to this theoretical 'quit' Thing, then pushing that Quit button would invoke it's Thing function.
1572 quitter = skang.widget(nil, 'button', 'Quit', 0.5, 0.5, 0.5, 0.5)
1573 quitter:action('quit')
1574
1575 For widgets with "rows", which was handled by Stuff in skang, we could
1576 maybe use the Lua concat operator via metatable. I think that works by
1577 having the widget (a table) on one side of the concat or the other, and
1578 the metatable function gets passed left and right sides, then must
1579 return the result. Needs some experimentation, but it might look like
1580 this -
1581
1582 this.bar = this.bar .. 'new choice'
1583 this.bar = 'new first choice' .. this.bar
1584
1585
1586 coordinates and sizes -
1587
1588 Originally skang differentiated between pixels and character cells,
1589 using plain integers to represent pixels, and _123 to represent
1590 character cells. The skang TODO wanted to expand that to percentages
1591 and relative numbers. We can't use _123 in Lua, so some other method
1592 needs to be used. Should include those TODO items in this new design.
1593
1594 Specifying character cells should be done as strings - "123"
1595
1596 Percentages can be done as small floating point numbers between 0 and 1,
1597 which is similar to Edje. Since Lua only has a floating point number
1598 type, both 0 and 1 should still represent pixels / character cells -
1599
1600 0.1, 0.5, "0.2", "0.9"
1601
1602 Relative numbers could be done as strings, with the widget to be
1603 relative to, a + or -, then the number. This still leaves the problem
1604 of telling if the number is pixels or character cells. Also, relative
1605 to what part of the other widget? Some more thought needs to be put
1606 into this.
1607
1608 Another idea for relative numbers could be to have a coord object with
1609 various methods, so we could have something like -
1610
1611 button:bottom(-10):right(5) -- 10 pixels below the bottom of button, 5 pixels to the right of the right edge of button.
1612 button:width("12") -- 12 characters longer than the width of button.
1613
1614 But then how do we store that so that things move when the thing they are
1615 relative to moves?
1616
1617
1618 Squeal -
1619 Squeal was the database driver interface for SquealStuff, the database
1620 version of Stuff. Maybe we could wrap esskyuehl? Not really in need of
1621 database stuff for now, but should keep it in mind.
1622 For SquealStuff, the metadata would be read from the SQL database automatically.
1623
1624 squeal.database('db', 'host', 'someDb', 'user', 'password') -> Should return a Squeal Thing.
1625 local db = require 'someDbThing' -> Same as above, only the database details are encodode in the someDbThing source, OR come from someDbThing.properties.
1626 db:getTable('stuff', 'someTable') -> Grabs the metadata, but not the rows.
1627 db:read('stuff', 'select * from someTable'} -> Fills stuff up with several rows, including setting up the metadata for the columns.
1628 stuff[1].field1 -> Is a Thing, with a proper metatable, that was created automatically from the database meta data.
1629 stuff:read('someIndex') -> Grabs a single row that has the key 'someIndex', or perhaps multiple rows since this might have SQL under it.
1630 stuff = db:read('stuff', 'select * from someTable where key='someIndex')
1631
1632 stuff:write() -> Write all rows to the database table.
1633 stuff:write(1) -> Write one row to the database table.
1634 stuff:stuff('field1').isValid = someFunction -- Should work, all stuff[key] share the same Thing description.
1635 stuff:isValid(db) -> Validate the entire Thing against it's metadata at least.
1636 window.stuff = stuff -> Window gets stuff as it's default 'Keyed' table, any widgets with same names as the table fields get linked.
1637 grid.stuff = stuff -> Replace contents of this grid widget with data from all the rows in stuff.
1638 choice.stuff = stuff -> As in grid, but only using the keys.
1639 widget.stuff = stuff:stuff('field1') -> This widget gets a particular stufflet.
1640 widget would have to look up getmetatable(window.stuff).parent. Or maybe this should work some other way?
1641
1642 In all these cases above, stuff is a 'Keyed' table that has a Thing metatable, so it has sub Things.
1643 Should include some way of specifyings details like key name, where string, etc.
1644 getmetatable(stuff).__keyName
1645 getmetatable(stuff).__squeal.where
1646 And a way to link this database table to others, via the key of the other, as a field in this Stuff.
1647 stuff:stuff('field0').__link = {parent, key, index}
1648 In Java we had this -
1649
1650public class PersonStuff extends SquealStuff
1651{
1652
1653...
1654
1655 public final static String FULLNAME = "fullname";
1656
1657 public static final String keyField = "ID"; // Name of key field/s.
1658 public static final String where = keyField + "='%k'";
1659 public static final String listName = "last";
1660 public static final String tables = "PEOPLE";
1661 public static final String select = null;
1662 public static final String stufflets[] =
1663 {
1664 keyField,
1665 "PASSWD_ID|net.matrix_rad.squeal.PasswdStuff|,OTHER",
1666 "QUALIFICATION_IDS|net.matrix_rad.people.QualificationStuff|,OTHER",
1667 "INTERESTING_IDS|net.matrix_rad.people.InterestingStuff|,OTHER",
1668 "title",
1669 "first",
1670 "middle",
1671 "last",
1672 "suffix",
1673
1674...
1675
1676 FULLNAME + "||,VARCHAR,512"
1677 };
1678}
1679
1680]]
1681
1682
1683-- Gotta check out this _ENV thing, 5.2 only. Seems to replace the need for setfenv(). Seems like setfenv should do what we want, and is more backward compatible.
1684-- "_ENV is not supported directly in 5.1, so its use can prevent a module from remaining compatible with 5.1.
1685-- Maybe you can simulate _ENV with setfenv and trapping gets/sets to it via __index/__newindex metamethods, or just avoid _ENV."
1686-- LuaJIT doesn't support _ENV anyway.
diff --git a/src/GuiLua/test.lua b/src/GuiLua/test.lua
new file mode 100644
index 0000000..705f7ad
--- /dev/null
+++ b/src/GuiLua/test.lua
@@ -0,0 +1,211 @@
1-- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular.
2-- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module".
3do -- Only I'm not gonna indent this.
4
5local skang = require 'skang'
6local _M = skang.moduleBegin('test', nil, 'Copyright 2014 David Seikel', '0.1', '2014-03-27 03:57:00', [[
7 local win = skang.window(500, 500, "G'day planet.", 'testWindow')
8 skang.thingasm{win, 'quitter', 'Quits the skang window', types = 'widget', widget='"button", "Quit", 10, 10, 100, 30'}
9 win.W.quitter.action = 'skang.quit()' -- TODO Should look it up in ThingSpace.commands, and translat 'quit' into the Lua 'skang.quit()'?
10]])
11
12print('code')
13
14-- A variable that is private to this module.
15local fool = 22
16
17-- TODO - Could have a table of tables, and ipair through the top level, passing the inner ones to skang.thingasm{}.
18
19skang.thingasm{'fooble,f', 'Help text goes here', 1, widget='"edit", "The fooble:", 1, 1, 10, 50', required=true}
20skang.thingasm('bar', 'Help text', "Default")
21skang.thingasm('foo')
22
23-- We can use inline functions if we don't need the function internally.
24skang.thingasm('ffunc', 'Help Text', function (arg1, arg2)
25 print('Inside test.ffunc(' .. arg1 .. ', ' .. arg2 .. ')')
26end, 'number,string')
27
28print('Ending soon')
29skang.moduleEnd(_M)
30
31end
32
33
34-- Test it.
35local skang = require 'skang'
36local test = require 'test'
37local test_c = require 'test_c'
38local copy = skang.copy(test, 'copy')
39
40print('End ' .. test.bar .. ' ' .. test.VERSION .. ' ' .. skang.get(test, 'ffunc', 'help') .. ' ->> ' .. skang.get(test, 'f', 'action'))
41
42print('')
43
44print('foo = ' .. test.foo .. ' ->> ' .. skang.get(test, 'foo', 'help'))
45print('fooble = ' .. test.fooble)
46print('cfooble = ' .. test_c.cfooble .. ' ->> ' .. skang.get(test_c, 'cfooble', 'help') .. ' [' .. skang.get(test_c, 'cfooble', 'widget') .. ']')
47print('cfunc ->> ' .. skang.get(test_c, 'cfunc', 'help'))
48test.ffunc('one', 2)
49test_c.cfunc(0, 'zero')
50print('')
51
52test.f = 42
53print('f is now ' .. test.fooble .. ' ' .. test.f)
54print('copy_f is now ' .. copy.fooble .. ' ' .. copy.f)
55copy.f = 24
56print('f is now ' .. test.fooble .. ' ' .. test.f)
57print('copy_f is now ' .. copy.fooble .. ' ' .. copy.f)
58test.f = nil
59print('f is now ' .. test.fooble .. ' ' .. test.f)
60test.fooble = 42
61print('f is now ' .. test.fooble .. ' ' .. test.f)
62test.fooble = nil
63print('f is now ' .. test.fooble .. ' ' .. test.f)
64print('')
65
66print(skang.isBoolean(true))
67print(skang.isBoolean(1))
68print(skang.isBoolean('1'))
69print(skang.isBoolean('true'))
70print(skang.isBoolean('Yep'))
71print(skang.isBoolean('?'))
72print(skang.isBoolean(test))
73print(skang.isBoolean(function (a) return true end))
74print('')
75print(skang.isBoolean(false))
76print(skang.isBoolean(nil))
77print(skang.isBoolean(0))
78print(skang.isBoolean(''))
79print(skang.isBoolean('0'))
80print(skang.isBoolean('false'))
81print(skang.isBoolean('Nope'))
82print(skang.isBoolean(function (a) return false end))
83print('')
84
85-- Make it required, even though it was anyway.
86skang.set(test, 'f', 'required', true)
87-- Disable the default value, so we see "is required" errors.
88skang.reset(test, 'f', 'default')
89test.fooble = 42
90test.fooble = 'Should fail.'
91test.fooble = 42
92test.fooble = nil
93test.fooble = 42
94test.fooble = true
95test.f = 42
96test.f = nil
97test.bar = 123
98print('')
99
100skang.set(test, 'f', 'required', false)
101test.f = 42
102test.f = nil
103skang.set(test, 'f', 'default', 999)
104test.f = 42
105test.f = nil
106print(test.fooble .. ' ' .. test.f)
107print(skang.get(test, 'f', 'default'))
108print('')
109
110local stuff = {}
111stuff.t = {}
112
113skang.thingasm{stuff, 'a', 'A test stufflet'}
114skang.thingasm{stuff.t, 'b', 'A sub stufflet'}
115skang.thingasm{stuff.t, 'c', 'Another sub stufflet'}
116skang.thingasm{stuff, 's', 'A Stuff', types='table'}
117stuff.s{'sa,a', 'A stufflet in a Stuff'}
118stuff.s{'sb,b', 'Another stufflet in a Stuff'}
119skang.thingasm{stuff, 'S', 'A database table of Stuff', types='Keyed'}
120stuff.S{'field0', 'The first field of the db table.'}
121stuff.S{'field1', 'The second field of the db table.'}
122
123print('*********************************')
124skang.fixNames(skang, 'skang')
125skang.fixNames(test, 'test')
126skang.fixNames(test_c, 'test_c')
127skang.fixNames(stuff, 'stuff')
128print('*********************************')
129
130print(skang.get(stuff, 'a', 'help'))
131print(skang.get(stuff.t, 'b', 'help'))
132print(skang.get(stuff.t, 'c', 'help'))
133print(skang.get(stuff, 's', 'help'))
134print(skang.get(stuff.s, 'sa', 'help'))
135print(skang.get(stuff.s, 'sb', 'help'))
136print(skang.get(stuff.S, 'field0', 'help'))
137print(skang.get(stuff.S, 'field1', 'help'))
138skang.thingasm{test, 'baz,b', 'A test stufflet for test'}
139print(skang.get(test, 'b', 'help'))
140print(skang.get(test, 'f', 'help'))
141-- Should fail isValid()
142stuff.a = 1
143stuff.t.b = '2'
144stuff.t.c = '3'
145test.b = '422222'
146test.f = 5
147test_c.cbar = '666'
148-- This one doesn't actually exist.
149test_c.bar = '7'
150stuff.s.sa = true
151stuff.s.sb = 22
152stuff.s.b = 33
153print('')
154-- TODO - This triggers isValid() twice for each table element.
155stuff.s = {a=8, sb='9'}
156print('')
157stuff.s.sb = 99
158-- NOTE - Yet this doesn't trigger isValid() twice.
159stuff.S['record0'] = {field0=0, field1='zero'}
160stuff.S['record1'] = {field0='1', field1='one'}
161stuff.S['record2'] = {field0='2', field1='two'}
162
163print('')
164
165print(skang.get(stuff, 'a'))
166print(skang.get(stuff.t, 'b'))
167print(skang.get(stuff.t, 'c'))
168print(skang.get(test, 'b'))
169print(skang.get(test, 'baz'))
170print(skang.get(test, 'f'))
171print(skang.get(test, 'fooble'))
172print(skang.get(test_c, 'cbar'))
173print(skang.get(test_c, 'bar'))
174print(type(skang.get(stuff, 's')))
175print(skang.get(stuff.s, 'sa'))
176print(skang.get(stuff.s, 'sb'))
177print('')
178
179print(stuff.a)
180print(stuff.t.b)
181print(stuff.t.c)
182print(test.b)
183print(test.baz)
184print(test.f)
185print(test.fooble)
186print(test_c.cbar)
187print(test_c.bar)
188print(test_c.c)
189print(test_c.cfooble)
190print(stuff.s.sa)
191print(stuff.s.sb)
192print('')
193
194--skang.printTableStart(stuff.s, '', 'stuff.s')
195--skang.printTableStart(stuff.S, '', 'stuff.S')
196--skang.printTableStart(getmetatable(stuff.S), '', 'stuff.S metatable')
197
198print(stuff.S['record0'].field1)
199print(stuff.S['record1'].field0)
200print(stuff.S['record2'].field1)
201
202--skang.printTableStart(stuff.S['record0'], '', 'stuff.S[record0]')
203--skang.printTableStart(getmetatable(stuff.S['record0']), '', 'metatable stuff.S[record0]')
204--skang.printTableStart(getmetatable(stuff.S['record1']), '', 'metatable stuff.S[record1]')
205--skang.printTableStart(getmetatable(stuff.S['record2']), '', 'metatable stuff.S[record2]')
206
207--skang.printTableStart(getmetatable(stuff.s), '', 'stuff.s metatable')
208--skang.printTableStart(getmetatable(stuff), '', 'stuff metatable')
209--skang.printTableStart(getmetatable(stuff.S), '', 'stuff.S metatable')
210
211--skang.printTableStart(getmetatable(test), '', 'test metatable')
diff --git a/src/GuiLua/test.properties b/src/GuiLua/test.properties
new file mode 100644
index 0000000..71a371d
--- /dev/null
+++ b/src/GuiLua/test.properties
@@ -0,0 +1 @@
fooble = 'forty two'
diff --git a/src/GuiLua/test.sh b/src/GuiLua/test.sh
new file mode 100755
index 0000000..1effdb3
--- /dev/null
+++ b/src/GuiLua/test.sh
@@ -0,0 +1,3 @@
1#! /bin/bash
2
3./skang -l test -foo "argy bargy"
diff --git a/src/GuiLua/test.skang b/src/GuiLua/test.skang
new file mode 100644
index 0000000..10382b5
--- /dev/null
+++ b/src/GuiLua/test.skang
@@ -0,0 +1,14 @@
1#!/usr/bin/env skang -l test -- Lua allows this shell hack.
2
3-- There's an implied local skang = require 'skang'
4-- There's an implied local test = require 'test'
5
6-- This is a bit more verbose than I wanted. lol
7local win = skang.window(500, 500, "G'day planet.", 'testWindow')
8skang.thingasm{win, 'quitter', 'Quits the skang window', types = 'widget', widget='"button", "Quit", 10, 10, 100, 30'}
9win.W.quitter.action = 'skang.quit()' -- TODO Should look it up in ThingSpace.commands, and translate 'quit' into the Lua 'skang.quit()'?
10
11skang.thingasm{win, 'ffuncer', 'Calls ffunc', types = 'widget', widget='"button", "ffunc()", 10, 40, 100, 30', action='test.ffunc(3, 4)'}
12
13test.bar = 'things'
14test.ffunc(1, 'two')
diff --git a/src/GuiLua/test_c.c b/src/GuiLua/test_c.c
new file mode 100644
index 0000000..5328bda
--- /dev/null
+++ b/src/GuiLua/test_c.c
@@ -0,0 +1,88 @@
1/* Should be a Lua skang module, roughly the same as test.lua
2
3Seems to be several problems with linking in various OSes, here's some
4possibly helpful links -
5
6http://lua.2524044.n2.nabble.com/C-Lua-modules-not-compatible-with-every-Lua-interpreter-td7647522.html
7http://lua-users.org/wiki/LuaProxyDllFour
8http://stackoverflow.com/questions/11492194/how-do-you-create-a-lua-plugin-that-calls-the-c-lua-api?rq=1
9http://lua-users.org/lists/lua-l/2008-01/msg00671.html
10*/
11
12
13#include "GuiLua.h"
14
15
16static const char *ourName = "test_c";
17int skang, _M;
18
19static int cfunc (lua_State *L)
20{
21 double arg1 = luaL_checknumber(L, 1);
22 const char *arg2 = luaL_checkstring(L, 2);
23
24 printf("Inside %s.cfunc(%f, %s)\n", ourName, arg1, arg2);
25 return 0;
26}
27
28/* local test_c = require 'test_c'
29
30Lua's require() function will strip any stuff from the front of the name
31separated by a hyphen, so 'ClientHamr-GuiLua-test_c' -> 'test_c'. Then
32it will search through a path, and eventually find this test_c.so (or
33test_c.dll or whatever), then call luaopen_test_c(), which should return
34a table. The argument (only thing on the stack) for this function will
35be 'test_c'.
36
37Normally luaL_register() creates a table of functions, that is the table
38returned, but we want to do something different with skang.
39*/
40int luaopen_test_c(lua_State *L)
41{
42 // In theory, the only thing on the stack now is 'test_c' from the require() call.
43
44// pseudo-indices, special tables that can be accessed like the stack -
45// LUA_GLOBALSINDEX - thread environment, where globals are
46// LUA_ENVIRONINDEX - C function environment, in this case luaopen_test_c() is the C function
47// 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.
48// lua_upvalueindex(n) - C function upvalues
49
50// The only locals we care about are skang and _M.
51// All modules go into package.loaded[name] as well.
52// skang is essentially a global anyway.
53// _M we pass back as the result, and our functions get added to it by skang.thingasm()
54// Not entirely true, _M is a proxy table, getmetatable(_M).__values[cfunc] would be our function.
55
56// local skang = require 'skang'
57 lua_getglobal(L, "require");
58 lua_pushstring(L, SKANG);
59 lua_call(L, 1, 1);
60 lua_setfield(L, LUA_REGISTRYINDEX, SKANG);
61 lua_getfield(L, LUA_REGISTRYINDEX, SKANG);
62 skang = lua_gettop(L);
63
64// local _M = skang.moduleBegin('test_c', nil, 'Copyright 2014 David Seikel', '0.1', '2014-03-27 03:57:00', nil, false)
65 push_lua(L, "@ ( $ ~ $ $ $ ~ ! )", skang, MODULEBEGIN, ourName, "Copyright 2014 David Seikel", "0.1", "2014-03-27 03:57:00", 0, 1);
66 lua_setfield(L, LUA_REGISTRYINDEX, ourName);
67 lua_getfield(L, LUA_REGISTRYINDEX, ourName);
68 _M = lua_gettop(L);
69
70// This uses function{} style.
71// skang.thingasm{_M, 'cfooble,c', 'cfooble help text', 1, widget=\"'edit', 'The cfooble:', 1, 1, 10, 50\", required=true}
72 push_lua(L, "@ ( { = $ $ % $widget !required } )", skang, THINGASM, _M, "cfooble,c", "cfooble help text", 1, "'edit', 'The cfooble:', 1, 1, 10, 50", 1, 0);
73
74// skang.thing(_M, 'cbar', 'Help text', 'Default')
75 push_lua(L, "@ ( = $ $ $ )", skang, THINGASM, _M, "cbar", "Help text", "Default", 0);
76
77// skang.thingasm(_M, 'cfoo')
78 push_lua(L, "@ ( = $ )", skang, THINGASM, _M, "cfoo", 0);
79
80// skang.thingasm(_M, 'cfunc', 'cfunc does nothing really', cfunc, 'number,string')
81 push_lua(L, "@ ( = $ $ & $ )", skang, THINGASM, _M, "cfunc", "cfunc does nothing really", cfunc, "number,string", 0);
82
83// skang.moduleEnd(_M)
84 push_lua(L, "@ ( = )", skang, MODULEEND, _M, 0);
85
86 // Return _M, the table itself, not the index.
87 return 1;
88}
diff --git a/src/LuaSL/LuaSL.edc b/src/LuaSL/LuaSL.edc
new file mode 100644
index 0000000..844fc8e
--- /dev/null
+++ b/src/LuaSL/LuaSL.edc
@@ -0,0 +1,205 @@
1color_classes {
2 color_class { name: "test_colour"; color: 255 255 255 255; }
3}
4
5//fonts {
6// font: "Vera.ttf" "default";
7//}
8
9images {
10 image: "bubble.png" COMP;
11 image: "test.png" COMP;
12}
13
14collections {
15 group {
16 name: "main";
17 lua_script_only: 1;
18 lua_script {
19 --// stick object private/local vars here
20 local D;
21 local text_geom;
22
23 --// init object here
24 D = {}; --// data is empty table to start
25
26 --// send some random edje message
27 edje.messagesend(7, "none" );
28 edje.messagesend(7, "sig", "signal", "source");
29 edje.messagesend(7, "str", "hello world");
30 edje.messagesend(7, "int", 987);
31 edje.messagesend(7, "float", 987.321);
32 edje.messagesend(7, "strset", {"hello", "there", "world"});
33 edje.messagesend(7, "intset", {1, 2, 3});
34 edje.messagesend(7, "floatset", {1.1, 2.2, 3.3});
35 edje.messagesend(7, "strint", "hello world", 7);
36 edje.messagesend(7, "strfloat", "hello world", 7.654);
37 edje.messagesend(7, "strintset","hello world", {1, 2, 3});
38
39 D.edje = edje.edje();
40 D.edje:file("plain/edje/group");
41 D.edje:show();
42
43 D.text = edje.text();
44 D.text:geom (50, 5, 150, 50);
45 D.text:color (255, 0, 0, 255);
46 D.text:font("Sans:style=Bold", 32);
47 D.text:text("Lua rocks!");
48 text_geom = D.text:geom();
49--// print(D.text:text());
50 D.text:show();
51
52
53 --// shutdown func - generally empty or not there. everything garbage collected for you
54 function shutdown ()
55--// print("lua::shutdown");
56 end
57
58 function show ()
59--// print("lua::show");
60 end
61
62 function hide ()
63--// print("lua::hide");
64 end
65
66 function move (x, y)
67--// print("lua::move x=" .. x .. " x=" .. y);
68 D.edje:move(0, 0);
69 end
70
71 function resize (w, h)
72--// print("lua::resize w=" .. w .. " h=" .. h);
73 D.text:move((w - text_geom.w) / 2, (h - text_geom.h) / 8);
74 D.edje:resize(w, h);
75 end
76
77 function message (id, type, ...)
78 print("lua::message id=" .. id .. " type=" .. type);
79 --// handle your message type here. check id + type then use the
80 --// vararg appropriately. they are the same as the params passed
81 --// to edje:messagesend() (if any are passed at all). Any array
82 --// arguments are passed as a single table.
83
84 if ("none" == type) then
85 print("lua::message no args");
86 elseif ("strset" == type) then
87 strs = ... ;
88 print_table_start(strs, "", "lua::message strings");
89 elseif ("intset" == type) then
90 ints = ... ;
91 print_table_start(ints, "", "lua::message ints");
92 elseif ("floatset" == type) then
93 floats = ... ;
94 print_table_start(floats, "", "lua::message floats");
95 elseif ("strintset" == type) then
96 str, ints = ... ;
97 print("lua::message " .. str);
98 print_table_start(ints, "", "lua::message ints");
99 elseif ("strfloatset" == type) then
100 str, floats = ... ;
101 print("lua::message " .. str);
102 print_table_start(floats, "", "lua::message floats");
103 else
104 print("lua::message " .. ... );
105 end
106 end
107
108 function signal (sig, src)
109 print("lua::signal sig= " .. sig .. " src= " .. src);
110 end
111 }
112 }
113
114 // The group name NEEDS a / in it,
115 // or the part below that tries to swallow it won't work.
116 // Leaving just the lua part visible.
117 group {
118 name: "bubbles/lua";
119 lua_script_only: 1;
120 lua_script {
121 local bubbles = { };
122 local bubbleCols = 8;
123 local bubbleRows = 6;
124
125 for i = 1, bubbleRows do
126 row = { };
127 for j = 1, bubbleCols do
128 image = edje.image();
129 image:image("bubble.png");
130 image:show();
131 table.insert(row, image);
132 end
133 table.insert(bubbles, row);
134 end
135
136 function resize (w, h)
137 for i = 1, bubbleRows do
138 for j = 1, bubbleCols do
139 w1 = w / bubbleCols;
140 h1 = h / bubbleRows;
141 bubbles[i][j]:geom((j - 1) * w1, (i - 1) * h1, w1, h1);
142 if ((1 == i) or (1 == j) or (bubbleRows == i) or (bubbleCols == j)) then
143 bubbles[i][j]:color(0, 255, 0, 200);
144 else
145 bubbles[i][j]:color(math.random(200) + 55, 0, math.random(255) + 55, 200);
146 end
147 end
148 end
149 end
150 }
151 }
152
153 group {
154 name: "plain/edje/group";
155 parts {
156 part {
157 name: "background";
158 type: RECT;
159 mouse_events: 0;
160 description {
161 state: "default" 0.0;
162 color: 0 0 0 255;
163 }
164 }
165
166 // A lua group embedded in an edje group.
167 part {
168 name: "bubbles_lua";
169 type: GROUP;
170 source: "bubbles/lua";
171 mouse_events: 0;
172 description { state: "default" 0.0; }
173 }
174
175 part {
176 name: "background_image";
177 type: IMAGE;
178 mouse_events: 0;
179 description {
180 state: "default" 0.0;
181 aspect_preference: HORIZONTAL;
182 color_class: "test_colour";
183 image { normal: "test.png"; }
184 }
185 }
186
187 part {
188 name: "some_text";
189 type: TEXT;
190 mouse_events: 0;
191 description {
192 state: "default" 0;
193 text
194 {
195 text: "This is test text.";
196 text_class: "test_text_class";
197 }
198 }
199 }
200
201 }
202 }
203
204}
205
diff --git a/src/LuaSL/LuaSL.h b/src/LuaSL/LuaSL.h
new file mode 100644
index 0000000..d1f448e
--- /dev/null
+++ b/src/LuaSL/LuaSL.h
@@ -0,0 +1,82 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#else
4//#define PACKAGE_EXAMPLES_DIR "."
5#define __UNUSED__
6#endif
7
8#include <Eet.h>
9#include <Ecore.h>
10#include <Ecore_Con.h>
11#include <Ecore_Evas.h>
12#include <Ecore_File.h>
13#include <Edje.h>
14#include <stdio.h>
15#include <ctype.h>
16
17#include <lua.h>
18#include <luajit.h>
19#include <lualib.h>
20#include <lauxlib.h>
21
22typedef struct _script script; // Define this here, so LuaSL_threads.h can use it.
23typedef struct _gameGlobals gameGlobals; // Define this here, so LuaSL_threads.h can use it.
24
25#include "LuaSL_threads.h"
26#include "LumbrJack.h"
27
28
29#define WIDTH (512)
30#define HEIGHT (384)
31
32
33#define TABLE_WIDTH 7
34#define TABLE_HEIGHT 42
35
36
37struct _gameGlobals
38{
39 Ecore_Evas *ee; // Our window.
40 Evas *canvas; // The canvas for drawing directly onto.
41 Evas_Object *bg; // Our background edje, also the game specific stuff.
42 Evas_Object *edje; // The edje of the background.
43 Ecore_Con_Server *server;
44 Eina_Hash *scripts, *names;
45 int logDom;
46 const char *address;
47 int port;
48 boolean ui; // Wether we actually start up the UI.
49};
50
51struct _script
52{
53 Eina_Clist node;
54 gameGlobals *game;
55 char SID[PATH_MAX];
56 char fileName[PATH_MAX];
57 lua_State *L;
58 struct timeval startTime;
59 float compileTime, timerTime;
60 int bugs, warnings;
61 boolean running;
62 int status;
63 int args;
64 Eina_Clist messages;
65 Ecore_Con_Client *client;
66 Ecore_Timer *timer;
67};
68
69typedef struct
70{
71 Eina_Clist node;
72 script *script;
73 const char message[PATH_MAX];
74} scriptMessage;
75
76
77void scriptSendBack(void * data);
78void sendBack(gameGlobals *ourGlobals, Ecore_Con_Client *client, const char *SID, const char *message, ...);
79void sendForth(gameGlobals *ourGlobals, const char *SID, const char *message, ...);
80float timeDiff(struct timeval *now, struct timeval *then);
81
82#include "LuaSL_LSL_tree.h"
diff --git a/src/LuaSL/LuaSL_LSL_tree.h b/src/LuaSL/LuaSL_LSL_tree.h
new file mode 100644
index 0000000..5415228
--- /dev/null
+++ b/src/LuaSL/LuaSL_LSL_tree.h
@@ -0,0 +1,443 @@
1
2#ifndef __LUASL_TREE_H__
3#define __LUASL_TREE_H__
4
5#define LUASL_DEBUG 0
6#define LUASL_DIFF_CHECK 0
7
8
9#include <stddef.h> // So we can have NULL defined.
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13
14#include "assert.h"
15#include <unistd.h>
16#include <stdlib.h>
17#include <stdio.h>
18#include <limits.h> // For PATH_MAX.
19
20#include "LuaSL_lemon_yaccer.h"
21
22#define YYERRCODE 256
23#define YYDEBUG 1
24
25
26// http://w-hat.com/stackdepth is a useful discussion about some aspects of the LL parser.
27
28
29typedef struct _allowedTypes allowedTypes;
30typedef struct _LSL_Token LSL_Token;
31typedef struct _LSL_Text LSL_Text;
32typedef struct _LSL_Leaf LSL_Leaf;
33typedef struct _LSL_Numby LSL_Numby;
34typedef struct _LSL_Parenthesis LSL_Parenthesis;
35typedef struct _LSL_Identifier LSL_Identifier;
36typedef struct _LSL_Statement LSL_Statement;
37typedef struct _LSL_Block LSL_Block;
38typedef struct _LSL_Function LSL_Function;
39typedef struct _LSL_FunctionCall LSL_FunctionCall;
40typedef struct _LSL_State LSL_State;
41typedef struct _LSL_Script LSL_Script;
42
43extern LSL_Token **tokens;
44extern int lowestToken;
45
46typedef int LSL_Type;
47
48typedef enum
49{
50 OM_LSL,
51 OM_LUA
52} outputMode;
53
54typedef void (*outputToken) (FILE *file, outputMode mode, LSL_Leaf *content);
55
56//#ifndef FALSE
57//typedef enum
58//{
59// FALSE = 0,
60// TRUE = 1
61//} boolean;
62//#endif
63
64typedef enum
65{
66 LSL_NONE = 0,
67 LSL_LEFT2RIGHT = 1,
68 LSL_RIGHT2LEFT = 2,
69 LSL_INNER2OUTER = 4,
70 LSL_UNARY = 8,
71 LSL_ASSIGNMENT = 16,
72 LSL_CREATION = 32,
73 LSL_NOIGNORE = 64,
74 LSL_TYPE = 128
75} LSL_Flags;
76
77
78// VERY IMPORTANT to keep this in sync with allowedTypes allowed[] from LuaSL_compile.c!
79typedef enum
80{
81 OT_nothing,
82
83 OT_bool,
84 OT_integer,
85 OT_float,
86 OT_key,
87 OT_list,
88 OT_rotation,
89 OT_string,
90 OT_vector,
91 OT_other,
92
93 OT_boolBool,
94 OT_intBool,
95 OT_intInt,
96 OT_intFloat,
97 OT_floatInt,
98 OT_floatFloat,
99 OT_keyKey,
100 OT_keyString,
101 OT_stringKey,
102 OT_stringString,
103 OT_listList,
104 OT_listBool,
105 OT_listInt,
106 OT_listFloat,
107 OT_listString,
108 OT_intList,
109 OT_floatList,
110 OT_listOther,
111 OT_vectorVector,
112 OT_vectorFloat,
113 OT_vectorRotation,
114 OT_rotationRotation,
115 OT_otherOther,
116 OT_undeclared,
117 OT_invalid
118} opType;
119
120typedef enum
121{
122 ST_NONE = 0,
123 ST_ASSIGNMENT = 1, // -= *= /=
124 ST_BIT_NOT = 2, // ~
125 ST_BOOL_NOT = 4, // !
126 ST_BITWISE = 8, // & | ^ << >>
127 ST_BOOLEAN = 16, // && !!
128 ST_COMPARISON = 32, // < > <= >=
129 ST_CONCATENATION = 64, // = +=
130 ST_EQUALITY = 128, // == !=
131 ST_ADD = 512, // +
132 ST_SUBTRACT = 1024, // -
133 ST_NEGATE = 2048, // -
134 ST_MULTIPLY = 4096, // * /
135 ST_MODULO = 8192 // % %=
136} opSubType;
137
138typedef enum
139{
140 MF_NONE = 0,
141 MF_LOCAL = 1,
142 MF_NOASSIGN = 2, // These two are for completely different purposes. This one is for variable definitions with no assignment.
143 MF_ASSIGNEXP = 4, // These two are for completely different purposes. This one is for assignments being used as expressions.
144 MF_WRAPFUNC = 8,
145 MF_PREDEC = 16,
146 MF_PREINC = 32,
147 MF_POSTDEC = 64,
148 MF_POSTINC = 128,
149 MF_LSLCONST = 256,
150 MF_TYPECAST = 512
151} miscFlags;
152
153struct _allowedTypes
154{
155 opType result;
156 const char *name;
157 int subTypes;
158};
159
160struct _LSL_Token
161{
162 LSL_Type type;
163 opSubType subType;
164 const char *toKen;
165 LSL_Flags flags;
166 outputToken output;
167};
168
169struct _LSL_Text
170{
171 const char *text;
172#if LUASL_DIFF_CHECK
173 Eina_Strbuf *ignorable;
174#endif
175};
176
177struct _LSL_Leaf
178{
179 LSL_Leaf *left;
180 LSL_Leaf *right;
181 LSL_Token *toKen;
182#if LUASL_DIFF_CHECK
183 Eina_Strbuf *ignorable;
184#endif
185 int line, column, len;
186 opType basicType;
187 miscFlags flags;
188 union
189 {
190 float floatValue;
191 float vectorValue[3];
192 float rotationValue[4];
193 int integerValue;
194 LSL_Numby *numbyValue;
195 LSL_Leaf *listValue;
196 const char *stringValue;
197 opType operationValue;
198 LSL_Parenthesis *parenthesis;
199 LSL_Identifier *identifierValue;
200 LSL_Statement *statementValue;
201 LSL_Block *blockValue;
202 LSL_Function *functionValue;
203 LSL_FunctionCall *functionCallValue;
204 LSL_State *stateValue;
205 LSL_Script *scriptValue;
206 } value;
207};
208
209struct _LSL_Numby
210{
211 LSL_Text text;
212 LSL_Type type;
213 union
214 {
215 float floatValue;
216 int integerValue;
217 } value;
218};
219
220struct _LSL_Parenthesis
221{
222 LSL_Leaf *contents;
223#if LUASL_DIFF_CHECK
224 Eina_Strbuf *rightIgnorable;
225#endif
226 LSL_Type type;
227 miscFlags flags;
228};
229
230struct _LSL_Identifier // For variables and function parameters.
231{
232 LSL_Text name;
233 LSL_Statement *definition;
234 Eina_Strbuf *ignorable;
235 const char *sub;
236 LSL_Leaf value;
237 miscFlags flags;
238};
239
240struct _LSL_Statement
241{
242 Eina_Clist statement; // For block statement lists, this is the entry.
243 LSL_Text identifier;
244 LSL_Parenthesis *parenthesis;
245 LSL_Leaf *expressions; // A for statement will have three expressions, and two semicolons, everything else has zero or one.
246 LSL_Block *block;
247 LSL_Statement *single; // For single statement "blocks".
248 LSL_Statement *elseBlock;
249 LSL_Type type; // Expression type.
250#if LUASL_DIFF_CHECK
251 Eina_Strbuf **ignorable; // Can be up to five of these I think.
252#endif
253 miscFlags flags;
254};
255
256struct _LSL_Block
257{
258 LSL_Block *outerBlock;
259 Eina_Clist statements; // For statement lists, this is the HEAD.
260 Eina_Hash *variables; // Those variables in this scope.
261 LSL_Function *function; // A pointer to the function if this block is a function.
262#if LUASL_DIFF_CHECK
263 Eina_Strbuf *openIgnorable;
264 Eina_Strbuf *closeIgnorable;
265#endif
266};
267
268struct _LSL_Function
269{
270 LSL_Text name;
271 LSL_Text type;
272 const char *state;
273#if LUASL_DIFF_CHECK
274// LSL_Leaf *params; // So we store the parenthesis, and their ignorables.
275 // This points to the params leaf, which is a function, pointing to this structure. The actual params are in vars.
276#endif
277 Eina_Inarray vars; // Eina Inarray has not been released yet (Eina 1.2).
278 LSL_Block *block;
279 miscFlags flags;
280};
281
282struct _LSL_FunctionCall
283{
284 LSL_Function *function;
285 Eina_Inarray params; // Eina Inarray has not been released yet (Eina 1.2).
286 Eina_Clist dangler; // Entry for function calls used before the function is defined.
287 LSL_Leaf *call; // This is to stash the details for dangling ones, to search later.
288 // The line and column details are needed for bitching, so we need the leaf.
289 // Also need the stringValue for the search.
290 // On top of all that, the leaf is still used in expressions, so need to keep it around and update it when resolving danglers.
291};
292
293struct _LSL_State
294{
295 LSL_Text name;
296 LSL_Text state;
297 LSL_Block *block;
298 Eina_Hash *handlers;
299};
300
301struct _LSL_Script
302{
303 const char *name;
304 Eina_Hash *functions;
305 Eina_Hash *states;
306 Eina_Hash *variables;
307 int bugCount, warningCount;
308};
309
310/* Tracking variables.
311
312There are global variables, block local variables, and function parameters.
313
314For outputting Lua, which is the ultimate goal -
315 track order, name, and type.
316
317For looking them up during the compile -
318 quick access from name.
319
320For validating them during compile -
321 track type.
322
323For outputting LSL to double check -
324 track order, name, type, and white space.
325
326For executing directly from the AST -
327 track order, name, type, and value.
328 In this case, order is only important for functions.
329
330We can assume that names are stringshared. This means we only have to
331compare pointers. It also means the same name stored at diffferent
332scopes, must be stored in separate structures, coz the pointers are the
333same.
334
335Order is taken care of by the AST anyway, but for somethings we want to
336condense the AST down to something more efficient.
337
338On the other hand, no need to micro optimise it just yet, we should be
339able to try out other data structures at a later date, then benchmark
340them with typical scripts.
341
342Right now I see nothing wrong with the current use of hash for script
343and block variables. The same for script states and functions, as well
344as state functions. Though in the near future, they will have similar
345problems to functions I think - the need to track order and white
346space.
347
348Function params got unwieldy. Cleaned that up now.
349
350*/
351
352/* General design.
353
354NOTE We can remove the white space tracking at compile time, as it's
355only a debugging aid. Will be a performance and memory gain for
356productidon use. Tracking values on the other hand will still be useful
357for constants.
358
359The compile process starts with turning tokens into AST nodes connected
360in a tree. During that process the parser wants to condense nodes down
361to more efficient data structures. This is a good idea, as we will
362spend a fair amount of time looking up names, no matter which part of
363the process is important at the time.
364
365Once the parser has condensed things down, it only deals with the
366condensed nodes. So we can get rid of some of the AST parts at this
367time, so long as we keep the relevant information. This is what the
368other data structures above are for. Lemon tries to free the no longer
369needed AST nodes itself, even if we are still using them internally.
370Need to do something about that.
371
372*/
373
374// Define the type for flex and lemon.
375#define YYSTYPE LSL_Leaf
376
377typedef struct
378{
379 gameGlobals *game;
380 Ecore_Con_Client *client;
381 void *scanner; // This should be of type yyscan_t, which is typedef to void * anyway, but that does not get defined until LuaSL_lexer.h, which depends on this struct being defined first.
382 int argc;
383 char **argv;
384 char SID[37];
385 char fileName[PATH_MAX];
386 FILE *file;
387 LSL_Leaf *ast;
388 LSL_Script script;
389 LSL_State state;
390#if LUASL_DIFF_CHECK
391 Eina_Strbuf *ignorable;
392#endif
393 LSL_Leaf *lval;
394 LSL_Block *currentBlock;
395 LSL_Function *currentFunction;
396 Eina_Clist danglingCalls; // HEAD for function calls used before the function is defined.
397 int column, line;
398 int undeclared;
399 boolean inState;
400} LuaSL_compiler;
401
402
403#ifndef excludeLexer
404 #include "LuaSL_lexer.h"
405#endif
406
407
408
409boolean compilerSetup(gameGlobals *ourGlobals);
410boolean compileLSL(gameGlobals *ourGlobals, Ecore_Con_Client *client, char *SID, char *script, boolean doConstants);
411void burnLeaf(void *data);
412
413LSL_Leaf *addBlock(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right);
414LSL_Leaf *addCrement(LuaSL_compiler *compiler, LSL_Leaf *variable, LSL_Leaf *crement, LSL_Type type);
415LSL_Leaf *addFor(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr0, LSL_Leaf *stat0, LSL_Leaf *expr1, LSL_Leaf *stat1, LSL_Leaf *expr2, LSL_Leaf *right, LSL_Leaf *block);
416LSL_Leaf *addFunction(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close);
417LSL_Leaf *addFunctionBody(LuaSL_compiler *compiler, LSL_Leaf *function, LSL_Leaf *block);
418LSL_Leaf *addFunctionCall(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close);
419LSL_Leaf *addIfElse(LuaSL_compiler *compiler, LSL_Leaf *ifBlock, LSL_Leaf *elseBlock);
420LSL_Leaf *addList(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right);
421LSL_Leaf *addNumby(LSL_Leaf *numby);
422LSL_Leaf *addOperation(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right);
423LSL_Leaf *addParameter(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *newParam);
424LSL_Leaf *addParenthesis(LSL_Leaf *lval, LSL_Leaf *expr, LSL_Type type, LSL_Leaf *rval);
425LSL_Leaf *addRotVec(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right);
426LSL_Leaf *addState(LuaSL_compiler *compiler, LSL_Leaf *state, LSL_Leaf *identifier, LSL_Leaf *block);
427LSL_Leaf *addStatement(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr, LSL_Leaf *right, LSL_Leaf *block, LSL_Leaf *identifier);
428LSL_Leaf *addTypecast(LSL_Leaf *lval, LSL_Leaf *type, LSL_Leaf *rval, LSL_Leaf *expr);
429LSL_Leaf *addVariable(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *assignment, LSL_Leaf *expr);
430
431LSL_Leaf *beginBlock(LuaSL_compiler *compiler, LSL_Leaf *block);
432LSL_Leaf *checkVariable(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *dot, LSL_Leaf *sub);
433LSL_Leaf *collectArguments(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *arg);
434LSL_Leaf *collectParameters(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *newParam);
435LSL_Leaf *collectStatements(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *newStatement);
436
437void *ParseAlloc(void *(*mallocProc)(size_t));
438void ParseTrace(FILE *TraceFILE, char *zTracePrompt);
439void Parse(void *yyp, int yymajor, LSL_Leaf *yyminor, LuaSL_compiler *compiler);
440void ParseFree(void *p, void (*freeProc)(void*));
441
442
443#endif // __LUASL_LSL_TREE_H__
diff --git a/src/LuaSL/LuaSL_compile.c b/src/LuaSL/LuaSL_compile.c
new file mode 100644
index 0000000..771888e
--- /dev/null
+++ b/src/LuaSL/LuaSL_compile.c
@@ -0,0 +1,2345 @@
1
2#include "LuaSL.h"
3
4/* TODO - problem de jour
5*/
6
7
8static void outputBitOp(FILE *file, outputMode mode, LSL_Leaf *leaf);
9static void outputBlockToken(FILE *file, outputMode mode, LSL_Leaf *content);
10static void outputCrementsToken(FILE *file, outputMode mode, LSL_Leaf *content);
11static void outputFloatToken(FILE *file, outputMode mode, LSL_Leaf *content);
12static void outputFunctionToken(FILE *file, outputMode mode, LSL_Leaf *content);
13static void outputFunctionCallToken(FILE *file, outputMode mode, LSL_Leaf *content);
14static void outputIntegerToken(FILE *file, outputMode mode, LSL_Leaf *content);
15static void outputIdentifierToken(FILE *file, outputMode mode, LSL_Leaf *content);
16static void outputListToken(FILE *file, outputMode mode, LSL_Leaf *content);
17static void outputParameterListToken(FILE *file, outputMode mode, LSL_Leaf *content);
18static void outputParenthesisToken(FILE *file, outputMode mode, LSL_Leaf *content);
19static void outputStateToken(FILE *file, outputMode mode, LSL_Leaf *content);
20static void outputStatementToken(FILE *file, outputMode mode, LSL_Leaf *content);
21static void outputStringToken(FILE *file, outputMode mode, LSL_Leaf *content);
22
23LSL_Token LSL_Tokens[] =
24{
25 // Various forms of "space".
26 {LSL_COMMENT, ST_NONE, "/*", LSL_NONE, NULL},
27 {LSL_COMMENT_LINE, ST_NONE, "//", LSL_NONE, NULL},
28 {LSL_SPACE, ST_NONE, " ", LSL_NONE, NULL},
29
30 // Operators, in order of precedence, low to high
31 // Left to right, unless otherwise stated.
32 // According to http://wiki.secondlife.com/wiki/Category:LSL_Operators, which was obsoleted by http://wiki.secondlife.com/wiki/LSL_Operators but that has less info.
33
34 {LSL_ASSIGNMENT_CONCATENATE,ST_CONCATENATION, "+=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
35 {LSL_ASSIGNMENT_ADD, ST_CONCATENATION, "+=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
36 {LSL_ASSIGNMENT_SUBTRACT, ST_ASSIGNMENT, "-=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
37 {LSL_ASSIGNMENT_MULTIPLY, ST_ASSIGNMENT, "*=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
38 {LSL_ASSIGNMENT_MODULO, ST_MODULO, "%=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
39 {LSL_ASSIGNMENT_DIVIDE, ST_ASSIGNMENT, "/=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
40 {LSL_ASSIGNMENT_PLAIN, ST_CONCATENATION, "=", LSL_RIGHT2LEFT | LSL_ASSIGNMENT, NULL},
41
42 {LSL_BOOL_AND, ST_BOOLEAN, "&&", LSL_RIGHT2LEFT, NULL},
43// QUIRK - Seems to be some disagreement about BOOL_AND/BOOL_OR precedence. Either they are equal, or OR is higher.
44// QUIRK - No boolean short circuiting.
45// LUA - Short circiuts boolean operations, and goes left to right.
46// LUA - "and" returns its first argument if it is false, otherwise, it returns its second argument. "or" returns its first argument if it is not false, otherwise it returns its second argument.
47// Note that the above means that "and/or" can return any type.
48 {LSL_BOOL_OR, ST_BOOLEAN, "||", LSL_RIGHT2LEFT, NULL},
49 {LSL_BIT_OR, ST_BITWISE, "|", LSL_LEFT2RIGHT, outputBitOp},
50 {LSL_BIT_XOR, ST_BITWISE, "^", LSL_LEFT2RIGHT, outputBitOp},
51 {LSL_BIT_AND, ST_BITWISE, "&", LSL_LEFT2RIGHT, outputBitOp},
52// QUIRK - Booleans and conditionals are executed right to left. Or maybe not, depending on who you believe.
53 {LSL_NOT_EQUAL, ST_EQUALITY, "!=", LSL_RIGHT2LEFT, NULL},
54 {LSL_EQUAL, ST_EQUALITY, "==", LSL_RIGHT2LEFT, NULL},
55 {LSL_GREATER_EQUAL, ST_COMPARISON, ">=", LSL_RIGHT2LEFT, NULL},
56 {LSL_LESS_EQUAL, ST_COMPARISON, "<=", LSL_RIGHT2LEFT, NULL},
57 {LSL_GREATER_THAN, ST_COMPARISON, ">", LSL_RIGHT2LEFT, NULL},
58 {LSL_LESS_THAN, ST_COMPARISON, "<", LSL_RIGHT2LEFT, NULL},
59// LUA - comparisons are always false if they are different types. Tables, userdata, and functions are compared by reference. Strings compare in alphabetical order, depending on current locale.
60// LUA - really only has three conditionals, as it translates a ~= b to not (a == b), a > b to b < a, and a >= b to b <= a.
61 {LSL_RIGHT_SHIFT, ST_BITWISE, ">>", LSL_LEFT2RIGHT, outputBitOp},
62 {LSL_LEFT_SHIFT, ST_BITWISE, "<<", LSL_LEFT2RIGHT, outputBitOp},
63 {LSL_CONCATENATE, ST_ADD, "+", LSL_LEFT2RIGHT, NULL},
64 {LSL_ADD, ST_ADD, "+", LSL_LEFT2RIGHT, NULL},
65 {LSL_SUBTRACT, ST_SUBTRACT, "-", LSL_LEFT2RIGHT, NULL},
66 {LSL_CROSS_PRODUCT, ST_NONE, "%", LSL_LEFT2RIGHT, NULL},
67 {LSL_DOT_PRODUCT, ST_NONE, "*", LSL_LEFT2RIGHT, NULL},
68 {LSL_MULTIPLY, ST_MULTIPLY, "*", LSL_LEFT2RIGHT, NULL},
69 {LSL_MODULO, ST_MODULO, "%", LSL_LEFT2RIGHT, NULL},
70 {LSL_DIVIDE, ST_MULTIPLY, "/", LSL_LEFT2RIGHT, NULL},
71 {LSL_NEGATION, ST_NEGATE, "-", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
72 {LSL_BOOL_NOT, ST_BOOL_NOT, "!", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
73 {LSL_BIT_NOT, ST_BIT_NOT, "~", LSL_RIGHT2LEFT | LSL_UNARY, outputBitOp},
74
75// LUA precedence - (it has no bit operators, at least not until 5.2, but LuaJIT has them as table functions.)
76// or
77// and
78// < > <= >= ~= ==
79// ..
80// + -
81// * /
82// not negate
83// exponentiation (^)
84
85 {LSL_TYPECAST_CLOSE, ST_NONE, ")", LSL_RIGHT2LEFT | LSL_UNARY, NULL},
86 {LSL_TYPECAST_OPEN, ST_NONE, "(", LSL_RIGHT2LEFT | LSL_UNARY, outputParenthesisToken},
87 {LSL_ANGLE_CLOSE, ST_NONE, ">", LSL_LEFT2RIGHT | LSL_CREATION, NULL},
88 {LSL_ANGLE_OPEN, ST_NONE, "<", LSL_LEFT2RIGHT | LSL_CREATION, NULL},
89 {LSL_BRACKET_CLOSE, ST_NONE, "]", LSL_INNER2OUTER | LSL_CREATION, NULL},
90 {LSL_BRACKET_OPEN, ST_NONE, "[", LSL_INNER2OUTER | LSL_CREATION, NULL},
91 {LSL_PARENTHESIS_CLOSE, ST_NONE, ")", LSL_INNER2OUTER, NULL},
92 {LSL_PARENTHESIS_OPEN, ST_NONE, "(", LSL_INNER2OUTER, outputParenthesisToken},
93 {LSL_DOT, ST_NONE, ".", LSL_RIGHT2LEFT, NULL},
94 {LSL_DECREMENT_POST, ST_NONE, "--", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
95 {LSL_DECREMENT_PRE, ST_NONE, "--", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
96 {LSL_INCREMENT_POST, ST_NONE, "++", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
97 {LSL_INCREMENT_PRE, ST_NONE, "++", LSL_RIGHT2LEFT | LSL_UNARY, outputCrementsToken},
98 {LSL_COMMA, ST_NONE, ",", LSL_LEFT2RIGHT, NULL},
99
100 {LSL_EXPRESSION, ST_NONE, "expression", LSL_NONE , NULL},
101
102 // Types.
103 {LSL_FLOAT, ST_NONE, "float", LSL_NONE, outputFloatToken},
104 {LSL_INTEGER, ST_NONE, "integer", LSL_NONE, outputIntegerToken},
105 {LSL_KEY, ST_NONE, "key", LSL_NONE, outputStringToken},
106 {LSL_LIST, ST_NONE, "list", LSL_NONE, outputListToken},
107 {LSL_ROTATION, ST_NONE, "rotation", LSL_NONE, outputListToken},
108 {LSL_STRING, ST_NONE, "string", LSL_NONE, outputStringToken},
109 {LSL_VECTOR, ST_NONE, "vector", LSL_NONE, outputListToken},
110
111 // Types names.
112 {LSL_TYPE_FLOAT, ST_NONE, "float", LSL_TYPE, NULL},
113 {LSL_TYPE_INTEGER, ST_NONE, "integer", LSL_TYPE, NULL},
114 {LSL_TYPE_KEY, ST_NONE, "key", LSL_TYPE, NULL},
115 {LSL_TYPE_LIST, ST_NONE, "list", LSL_TYPE, NULL},
116 {LSL_TYPE_ROTATION, ST_NONE, "rotation", LSL_TYPE, NULL},
117 {LSL_TYPE_STRING, ST_NONE, "string", LSL_TYPE, NULL},
118 {LSL_TYPE_VECTOR, ST_NONE, "vector", LSL_TYPE, NULL},
119
120 // Then the rest of the syntax tokens.
121 {LSL_FUNCTION_CALL, ST_NONE, "funccall", LSL_NONE, outputFunctionCallToken},
122 {LSL_IDENTIFIER, ST_NONE, "identifier", LSL_NONE, outputIdentifierToken},
123 {LSL_VARIABLE, ST_NONE, "variable", LSL_NONE, outputIdentifierToken},
124
125 {LSL_LABEL, ST_NONE, "@", LSL_NONE, NULL},
126
127 {LSL_DO, ST_NONE, "do", LSL_NONE, NULL},
128 {LSL_FOR, ST_NONE, "for", LSL_NONE, NULL},
129 {LSL_ELSE, ST_NONE, "else", LSL_NONE, NULL},
130 {LSL_ELSEIF, ST_NONE, "elseif", LSL_NONE, NULL},
131 {LSL_IF, ST_NONE, "if", LSL_NONE, NULL},
132 {LSL_JUMP, ST_NONE, "jump", LSL_NONE, NULL},
133 {LSL_RETURN, ST_NONE, "return", LSL_NONE, NULL},
134 {LSL_STATE_CHANGE, ST_NONE, "state", LSL_NONE, NULL},
135 {LSL_WHILE, ST_NONE, "while", LSL_NONE, NULL},
136 {LSL_STATEMENT, ST_NONE, ";", LSL_NOIGNORE, outputStatementToken},
137
138 {LSL_BLOCK_CLOSE, ST_NONE, "}", LSL_NONE, NULL},
139 {LSL_BLOCK_OPEN, ST_NONE, "{", LSL_NONE, outputBlockToken},
140 {LSL_PARAMETER, ST_NONE, "parameter", LSL_NONE, outputIdentifierToken},
141 {LSL_PARAMETER_LIST, ST_NONE, "plist", LSL_NONE, outputParameterListToken},
142 {LSL_FUNCTION, ST_NONE, "function", LSL_NONE, outputFunctionToken},
143 {LSL_DEFAULT, ST_NONE, "default", LSL_NONE, outputStateToken},
144 {LSL_STATE, ST_NONE, "state", LSL_NONE, outputStateToken},
145 {LSL_SCRIPT, ST_NONE, "", LSL_NONE, NULL},
146
147 {LSL_UNKNOWN, ST_NONE, "unknown", LSL_NONE, NULL},
148
149 // A sentinal.
150 {999999, ST_NONE, NULL, LSL_NONE, NULL}
151};
152
153// VERY IMPORTANT to keep this in sync with enum opType from LuaSL_LSL_tree.h!
154allowedTypes allowed[] =
155{
156 {OT_nothing, "nothing", (ST_NONE)}, //
157
158 {OT_bool, "boolean", (ST_BOOL_NOT)}, // bool !
159 {OT_integer, "integer", (ST_BOOL_NOT | ST_BIT_NOT | ST_NEGATE)}, // int ! - ~
160 {OT_float, "float", (ST_BOOL_NOT | ST_NEGATE)}, // float ! -
161 {OT_key, "key", (ST_BOOL_NOT)}, // key !
162 {OT_list, "list", (ST_NONE)}, //
163 {OT_rotation, "rotation", (ST_NONE)}, //
164 {OT_string, "string", (ST_BOOL_NOT)}, // string !
165 {OT_vector, "vector", (ST_NONE)}, //
166 {OT_other, "other", (ST_NONE)}, //
167
168 {OT_bool, "boolean", (ST_BOOLEAN | ST_EQUALITY)}, // bool bool == != = && ||
169
170 {OT_integer, "integer", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO | ST_BITWISE)}, // int boolean * / + - % == != < > <= >= = += -= *= /= %= & | ^ << >>
171 {OT_integer, "integer", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO | ST_BITWISE)}, // int int * / + - % == != < > <= >= = += -= *= /= %= & | ^ << >>
172 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // int float cast to float float
173 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // float int cast to float float
174 {OT_float, "float", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT)}, // float float * / + - == != < > <= >= = += -= *= /=
175
176 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // key key cast to string string
177 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // key string cast to string string
178 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // string key cast to string string
179 {OT_string, "string", (ST_ADD | ST_EQUALITY | ST_CONCATENATION)}, // string string + == != = +=
180
181 {OT_list, "list", (ST_ADD | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT )}, // list list + == != = +=
182 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list boolean + < > <= >= = +=
183 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list integer + < > <= >= = +=
184 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list float + < > <= >= = +=
185 {OT_list, "list", (ST_ADD | ST_COMPARISON | ST_CONCATENATION | ST_ASSIGNMENT )}, // list string + < > <= >= = +=
186 {OT_integer, "integer", (ST_ADD | ST_COMPARISON)}, // integer list + < > <= >=
187 {OT_float, "float", (ST_ADD | ST_COMPARISON)}, // float list + < > <= >=
188 {OT_list, "list", (ST_ADD | ST_CONCATENATION)}, // list other + = +=
189
190 {OT_vector, "vector", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT | ST_MODULO)}, // vector vector * / + - % == != = += -= *= /= %=
191 {OT_vector, "vector", (ST_MULTIPLY | ST_ASSIGNMENT)}, // vector float * / *= /=
192 {OT_vector, "vector", (ST_MULTIPLY)}, // vector rotation * /
193
194 {OT_rotation, "rotation", (ST_MULTIPLY | ST_ADD | ST_SUBTRACT | ST_EQUALITY | ST_CONCATENATION | ST_ASSIGNMENT)}, // rotation rotation * / + - == != = += -= *= /=
195
196 {OT_other, "other", (ST_NONE)}, //
197 {OT_undeclared, "undeclared", (ST_NONE)}, //
198 {OT_invalid, "invalid", (ST_NONE)} //
199};
200
201opType opExpr[][10] =
202{
203 {OT_nothing, OT_bool, OT_integer, OT_float, OT_key, OT_list, OT_rotation, OT_string, OT_vector, OT_other},
204 {OT_bool, OT_boolBool, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
205 {OT_integer, OT_intBool, OT_intInt, OT_intFloat, OT_invalid, OT_intList, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
206 {OT_float, OT_invalid, OT_floatInt, OT_floatFloat, OT_invalid, OT_floatList, OT_invalid, OT_invalid, OT_invalid, OT_invalid},
207 {OT_key, OT_invalid, OT_invalid, OT_invalid, OT_keyKey, OT_invalid, OT_invalid, OT_keyString, OT_invalid, OT_invalid},
208 {OT_list, OT_listBool, OT_listInt, OT_listFloat, OT_invalid, OT_listList, OT_invalid, OT_listString, OT_invalid, OT_listOther},
209 {OT_rotation, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_rotationRotation, OT_invalid, OT_invalid, OT_invalid},
210 {OT_string, OT_invalid, OT_invalid, OT_invalid, OT_stringKey, OT_invalid, OT_invalid, OT_stringString, OT_invalid, OT_invalid},
211 {OT_vector, OT_invalid, OT_invalid, OT_vectorFloat, OT_invalid, OT_invalid, OT_vectorRotation, OT_invalid, OT_vectorVector, OT_invalid},
212 {OT_other, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_invalid, OT_otherOther}
213};
214
215
216LSL_Token **tokens = NULL;
217LSL_Script constants;
218int lowestToken = 999999;
219
220
221static LSL_Leaf *newLeaf(LSL_Type type, LSL_Leaf *left, LSL_Leaf *right)
222{
223 LSL_Leaf *leaf = calloc(1, sizeof(LSL_Leaf));
224
225 if (leaf)
226 {
227 leaf->left = left;
228 leaf->right = right;
229 leaf->toKen = tokens[type - lowestToken];
230 }
231
232 return leaf;
233}
234
235void burnLeaf(void *data)
236{
237 LSL_Leaf *leaf = data;
238
239 if (leaf)
240 {
241// TODO - the problem here is that lemon wants to free these after a reduce, but we might want to keep them around. Should ref count them or something.
242// burnLeaf(leaf->left);
243// burnLeaf(leaf->right);
244 // TODO - Should free up the value to.
245//#if LUASL_DIFF_CHECK
246// eina_strbuf_free(leaf->ignorable);
247//#endif
248// free(leaf);
249 }
250}
251
252static LSL_Leaf *findFunction(LuaSL_compiler *compiler, const char *name)
253{
254 LSL_Leaf *func = NULL;
255
256 if (name)
257 {
258 if (NULL == func)
259 func = eina_hash_find(constants.functions, name);
260 if (NULL != func)
261 {
262 func->flags |= MF_LSLCONST;
263 func->value.functionValue->flags |= MF_LSLCONST;
264 }
265 else
266 func = eina_hash_find(compiler->script.functions, name);
267
268 }
269
270 return func;
271}
272
273static LSL_Leaf *findVariable(LuaSL_compiler *compiler, const char *name)
274{
275 LSL_Leaf *var = NULL;
276
277 if (name)
278 {
279 LSL_Block *block = compiler->currentBlock;
280
281 while ((block) && (NULL == var))
282 {
283 if (block->function)
284 {
285 LSL_Leaf *param = NULL;
286 EINA_INARRAY_FOREACH((&(block->function->vars)), param)
287 {
288 if ((param) && (LSL_PARAMETER == param->toKen->type))
289 {
290// if (name == param->value.identifierValue->name.text) // Assuming they are stringshares.
291 if (0 == strcmp(name, param->value.identifierValue->name.text)) // Not assuming they are stringeshares. They should be.
292 var = param;
293 }
294 }
295 }
296 if ((NULL == var) && block->variables)
297 var = eina_hash_find(block->variables, name);
298 block = block->outerBlock;
299 }
300
301 if (NULL == var)
302 {
303 var = eina_hash_find(constants.variables, name);
304 if (var)
305 {
306 var->flags |= MF_LSLCONST;
307 var->value.identifierValue->flags |= MF_LSLCONST;
308 }
309 }
310 if (NULL == var)
311 var = eina_hash_find(compiler->script.variables, name);
312 }
313
314 return var;
315}
316
317LSL_Leaf *checkVariable(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *dot, LSL_Leaf *sub)
318{
319 gameGlobals *ourGlobals = compiler->game;
320 const char *search;
321
322 if (dot)
323 search = identifier->value.identifierValue->name.text;
324 else
325 search = identifier->value.stringValue;
326
327 if (identifier)
328 {
329 LSL_Leaf *var = findVariable(compiler, search);
330
331 if (var)
332 {
333 if (LUASL_DEBUG)
334 PI("Found %s!", identifier->value.stringValue);
335 identifier->value.identifierValue = var->value.identifierValue;
336 identifier->basicType = var->basicType;
337 if ((dot) && (sub))
338 {
339 LSL_Identifier *id = calloc(1, sizeof(LSL_Identifier));
340
341 if (id)
342 {
343 memcpy(id, var->value.identifierValue, sizeof(LSL_Identifier));
344 identifier->value.identifierValue = id;
345 if (LSL_ROTATION == var->toKen->type)
346 {
347 // TODO - check if it's one of x, y, z, or s.
348 }
349 if (LSL_VECTOR == var->toKen->type)
350 {
351 // TODO - check if it's one of x, y, or z.
352 }
353 identifier->value.identifierValue->sub = sub->value.stringValue;
354 identifier->basicType = OT_float;
355 }
356 }
357 }
358 else
359 {
360 compiler->script.bugCount++;
361 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerError(%d,%d,NOT found %s)", identifier->line, identifier->column, identifier->value.stringValue);
362 }
363 }
364
365 return identifier;
366}
367
368LSL_Leaf *addOperation(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right)
369{
370 gameGlobals *ourGlobals = compiler->game;
371
372 if (lval)
373 {
374 opType lType, rType;
375
376 lval->left = left;
377 lval->right = right;
378
379 // Convert subtract to negate if needed.
380 if ((NULL == left) && (LSL_SUBTRACT == lval->toKen->type))
381 lval->toKen = tokens[LSL_NEGATION - lowestToken];
382
383 // Try to figure out what type of operation this is.
384 if (NULL == left)
385 lType = OT_nothing;
386 else
387 {
388 if ((left->toKen) && (LSL_IDENTIFIER == left->toKen->type) && (left->value.identifierValue))
389 {
390 LSL_Leaf *var = findVariable(compiler, left->value.identifierValue->name.text);
391
392 if (var)
393 lType = var->basicType;
394 if (left->value.identifierValue->sub)
395 {
396 // TODO - keep an eye on this, but I think all the sub types are floats.
397 lType = OT_float;
398 }
399 }
400 else
401 lType = left->basicType;
402 if (OT_undeclared == lType)
403 {
404 compiler->script.warningCount++;
405 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerWarning(%d,%d,Undeclared identifier issue, deferring this until the second pass)", lval->line, lval->column);
406 lval->basicType = OT_undeclared;
407 return lval;
408 }
409 if (OT_vector < lType)
410 lType = allowed[lType].result;
411 }
412 if (NULL == right)
413 rType = OT_nothing;
414 else
415 {
416 if ((right->toKen) && (LSL_IDENTIFIER == right->toKen->type) && (right->value.identifierValue))
417 {
418 LSL_Leaf *var = findVariable(compiler, right->value.identifierValue->name.text);
419
420 if (var)
421 rType = var->basicType;
422 if (right->value.identifierValue->sub)
423 {
424 // TODO - keep an eye on this, but I think all the sub types are floats.
425 rType = OT_float;
426 }
427 }
428 else
429 rType = right->basicType;
430 if (OT_undeclared == rType)
431 {
432 compiler->script.warningCount++;
433 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerWarning(%d,%d,Undeclared identifier issue, deferring this until the second pass)", lval->line, lval->column);
434 lval->basicType = OT_undeclared;
435 return lval;
436 }
437 if (OT_vector < rType)
438 rType = allowed[rType].result;
439 }
440
441 // Convert add to concatenate if needed.
442 if ((LSL_ADD == lval->toKen->type) && (OT_string == lType) && (OT_string == rType))
443 lval->toKen = tokens[LSL_CONCATENATE - lowestToken];
444
445 switch (lval->toKen->subType)
446 {
447 case ST_BOOLEAN :
448 case ST_COMPARISON :
449 case ST_EQUALITY :
450 lval->basicType = OT_bool;
451 break;
452 default :
453 // The basic lookup.
454 lval->basicType = opExpr[lType][rType];
455 if (OT_invalid != lval->basicType)
456 {
457 // Check if it's an allowed operation.
458 if (0 == (lval->toKen->subType & allowed[lval->basicType].subTypes))
459 lval->basicType = OT_invalid;
460 else
461 {
462 // Double check the corner cases.
463 switch (lval->toKen->subType)
464 {
465 case ST_MULTIPLY :
466 if (OT_vectorVector == lval->basicType)
467 {
468 if (LSL_MULTIPLY == lval->toKen->type)
469 {
470 lval->basicType = OT_float;
471 lval->toKen = tokens[LSL_DOT_PRODUCT - lowestToken];
472 }
473 else
474 lval->basicType = OT_vector;
475 }
476 break;
477 default :
478 break;
479 }
480 }
481 }
482 break;
483 }
484
485 /* Flag assignments for the "assignments are statements, which can't happen inside expressions" test.
486 *
487 * Assignments in the middle of expressions are legal in LSL, but not in Lua.
488 * The big complication is that they often happen in the conditionals of flow control statements. That's a big bitch.
489 *
490 * So things like -
491 *
492 * while ((x = doSomething()) == foo)
493 * {
494 * buggerAround();
495 * }
496 *
497 * Turns into -
498 *
499 * x = doSomething();
500 * while (x == foo)
501 * {
502 * buggerAround();
503 * x = doSomething();
504 * }
505 *
506 * http://lua-users.org/wiki/StatementsInExpressions was helpful. Which suggests something like this -
507 *
508 * while ( (function() x = doSomething(); return x; end)() == foo)
509 * {
510 * buggerAround();
511 * }
512 *
513 * The remaining problem is when to recognise the need to do that.
514 * That's what this code and the matching code in addParenthesis() does.
515 */
516 // TODO - Only got one of these in my test scripts, so leave all this debugging shit in until it's been tested more.
517 if (left)
518 {
519 if (left->flags & MF_ASSIGNEXP)
520 {
521//if ((left) && (right))
522// printf("%s %s %s\n", left->toKen->toKen, lval->toKen->toKen, right->toKen->toKen);
523//else if (left)
524// printf("%s %s NORIGHT\n", left->toKen->toKen, lval->toKen->toKen);
525//else if (right)
526// printf("NOLEFT %s %s\n", lval->toKen->toKen, right->toKen->toKen);
527//else
528// printf("NOLEFT %s NORIGHT\n", lval->toKen->toKen);
529// printf("############################################################################## left\n");
530 left->flags |= MF_WRAPFUNC;
531 if (LSL_PARENTHESIS_OPEN == left->toKen->type)
532 left->value.parenthesis->flags |= MF_WRAPFUNC;
533 }
534 }
535 if (lval)
536 {
537// if (lval->flags & MF_ASSIGNEXP)
538// printf("############################################################################## lval %s %s\n", left->toKen->toKen, right->toKen->toKen);
539 if (LSL_ASSIGNMENT & lval->toKen->flags)
540 {
541 lval->flags |= MF_ASSIGNEXP;
542//// printf("******************* lval %s %s\n", left->toKen->toKen, right->toKen->toKen);
543 if (LSL_IDENTIFIER == left->toKen->type) // It always should be.
544 {
545 left->flags |= MF_ASSIGNEXP;
546//// printf("$$$$$$$$$$$$$$$$$ lval\n");
547 }
548 }
549 }
550 // TODO - Don't think I have to do this on the right.
551 if (right)
552 {
553 if (right->flags & MF_ASSIGNEXP)
554 {
555if ((left) && (right))
556 printf("%s %s %s\n", left->toKen->toKen, lval->toKen->toKen, right->toKen->toKen);
557else if (left)
558 printf("%s %s NORIGHT\n", left->toKen->toKen, lval->toKen->toKen);
559else if (right)
560 printf("NOLEFT %s %s\n", lval->toKen->toKen, right->toKen->toKen);
561else
562 printf("NOLEFT %s NORIGHT\n", lval->toKen->toKen);
563 printf("############################################################################## right\n");
564 right->flags |= MF_WRAPFUNC;
565 }
566 }
567
568 if (OT_invalid == lval->basicType)
569 {
570 const char *leftType = "", *rightType = "", *leftToken = "", *rightToken = "";
571
572 if (left)
573 {
574 if (left->toKen)
575 leftToken = left->toKen->toKen;
576 else
577 PE("BROKEN LEFT TOKEN!!!!!!!!!!!!!!!!!!");
578 leftType = allowed[left->basicType].name;
579 }
580 if (right)
581 {
582 if (right->toKen)
583 rightToken = right->toKen->toKen;
584 else
585 PE("BROKEN RIGHT TOKEN!!!!!!!!!!!!!!!!!!");
586 rightType = allowed[right->basicType].name;
587 }
588
589 compiler->script.bugCount++;
590 sendBack(ourGlobals, compiler->client, compiler->SID, "compilerError(%d,%d,Invalid operation [%s(%s) %s %s(%s)])", lval->line, lval->column, leftType, leftToken, lval->toKen->toKen, rightType, rightToken);
591 }
592 }
593
594 return lval;
595}
596
597LSL_Leaf *addBlock(LuaSL_compiler *compiler, LSL_Leaf *left, LSL_Leaf *lval, LSL_Leaf *right)
598{
599 // Damn, look ahead. The } symbol is getting read (and thus endBlock called) before the last statement in the block is reduced (which actually calls the add*() functions).
600 compiler->currentBlock = compiler->currentBlock->outerBlock;
601#if LUASL_DIFF_CHECK
602 if ((left) && (right))
603 {
604 left->value.blockValue->closeIgnorable = right->ignorable;
605 right->ignorable = NULL;
606 }
607#endif
608 return lval;
609}
610
611LSL_Leaf *addCrement(LuaSL_compiler *compiler, LSL_Leaf *variable, LSL_Leaf *crement, LSL_Type type)
612{
613 if ((variable) && (crement))
614 {
615 crement->value.identifierValue = variable->value.identifierValue;
616#if LUASL_DIFF_CHECK
617 crement->value.identifierValue->ignorable = variable->ignorable;
618 variable->ignorable = NULL;
619#endif
620 crement->basicType = variable->basicType;
621 crement->toKen = tokens[type - lowestToken];
622 switch (crement->toKen->type)
623 {
624 case LSL_DECREMENT_PRE : variable->value.identifierValue->flags |= MF_PREDEC; break;
625 case LSL_INCREMENT_PRE : variable->value.identifierValue->flags |= MF_PREINC; break;
626 case LSL_DECREMENT_POST : variable->value.identifierValue->flags |= MF_POSTDEC; break;
627 case LSL_INCREMENT_POST : variable->value.identifierValue->flags |= MF_POSTINC; break;
628 }
629 variable->value.identifierValue->definition->flags = variable->value.identifierValue->flags;
630 }
631
632 return crement;
633}
634
635LSL_Leaf *addParameter(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier)
636{
637 LSL_Identifier *result = calloc(1, sizeof(LSL_Identifier));
638
639 if ( (identifier) && (result))
640 {
641 result->name.text = identifier->value.stringValue;
642#if LUASL_DIFF_CHECK
643 result->name.ignorable = identifier->ignorable;
644 identifier->ignorable = NULL;
645#endif
646 result->value.toKen = tokens[LSL_UNKNOWN - lowestToken];
647 identifier->value.identifierValue = result;
648 identifier->toKen = tokens[LSL_PARAMETER - lowestToken];
649 identifier->left = type;
650 if (type)
651 {
652 identifier->basicType = type->basicType;
653 result->value.basicType = type->basicType;
654 result->value.toKen = type->toKen; // This is the LSL_TYPE_* toKen instead of the LSL_* toKen. Not sure if that's a problem.
655 }
656 }
657 return identifier;
658}
659
660LSL_Leaf *collectParameters(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *newParam)
661{
662 LSL_Function *func = NULL;
663
664 if (NULL == list)
665 list = newLeaf(LSL_FUNCTION, NULL, NULL);
666
667 if (list)
668 {
669 func = list->value.functionValue;
670 if (NULL == func)
671 {
672 func = calloc(1, sizeof(LSL_Function));
673 if (func)
674 {
675 list->value.functionValue = func;
676 eina_inarray_step_set(&(func->vars), sizeof(Eina_Inarray), sizeof(LSL_Leaf), 3);
677 }
678 }
679
680 if (func)
681 {
682 if (newParam)
683 {
684 if (LUASL_DIFF_CHECK)
685 {
686 // Stash the comma for diff later.
687 if (comma)
688 eina_inarray_push(&(func->vars), comma);
689 }
690 eina_inarray_push(&(func->vars), newParam);
691 // At this point, pointers to newParams are not pointing to the one in func->vars, AND newParam is no longer needed.
692 }
693 }
694 }
695 return list;
696}
697
698LSL_Leaf *addFunction(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close)
699{
700 LSL_Function *func = NULL;
701
702 if (params)
703 {
704 func = params->value.functionValue;
705 // At this point, params is no longer needed, except if we are doing diff.
706 // open and close are not needed either if we are not doing diff.
707 if (func)
708 {
709 if (identifier)
710 {
711 func->name.text = identifier->value.stringValue;
712#if LUASL_DIFF_CHECK
713 func->name.ignorable = identifier->ignorable;
714 identifier->ignorable = NULL;
715#endif
716 identifier->toKen = tokens[LSL_FUNCTION - lowestToken];
717 identifier->value.functionValue = func;
718 if (type)
719 {
720 func->type.text = type->toKen->toKen;
721#if LUASL_DIFF_CHECK
722 func->type.ignorable = type->ignorable;
723 type->ignorable = NULL;
724#endif
725 identifier->basicType = type->basicType;
726 }
727 else
728 identifier->basicType = OT_nothing;
729 if (compiler->inState)
730 eina_hash_add(compiler->state.handlers, func->name.text, func);
731 else
732 eina_hash_add(compiler->script.functions, func->name.text, identifier);
733#if LUASL_DIFF_CHECK
734// func->params = addParenthesis(open, params, LSL_PARAMETER_LIST, close);
735#endif
736 }
737 compiler->currentFunction = func;
738 }
739 }
740
741 return identifier;
742}
743
744LSL_Leaf *addFunctionBody(LuaSL_compiler *compiler, LSL_Leaf *function, LSL_Leaf *block)
745{
746 LSL_Leaf *statement = NULL;
747
748 if (function)
749 {
750 function->value.functionValue->block = block->value.blockValue;
751 statement = addStatement(compiler, NULL, function, NULL, function, NULL, NULL, NULL);
752 }
753
754 return statement;
755}
756
757LSL_Leaf *collectArguments(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *comma, LSL_Leaf *arg)
758{
759 LSL_FunctionCall *call = NULL;
760
761 if (NULL == list)
762 list = newLeaf(LSL_FUNCTION_CALL, NULL, NULL);
763
764 if (list)
765 {
766 call = list->value.functionCallValue;
767 if (NULL == call)
768 {
769 call = calloc(1, sizeof(LSL_FunctionCall));
770 if (call)
771 {
772 list->value.functionCallValue = call;
773 eina_inarray_step_set(&(call->params), sizeof(Eina_Inarray), sizeof(LSL_Leaf), 3);
774 }
775 }
776
777 if (call)
778 {
779 if (arg)
780 {
781 if (LUASL_DIFF_CHECK)
782 {
783 // Stash the comma for diff later.
784 if (comma)
785 eina_inarray_push(&(call->params), comma);
786 }
787 eina_inarray_push(&(call->params), arg);
788 // At this point, pointers to arg are not pointing to the one in call->params, AND arg is no longer needed.
789 }
790 }
791 }
792 return list;
793}
794
795LSL_Leaf *addFunctionCall(LuaSL_compiler *compiler, LSL_Leaf *identifier, LSL_Leaf *open, LSL_Leaf *params, LSL_Leaf *close)
796{
797 LSL_Leaf *func = findFunction(compiler, identifier->value.stringValue);
798 LSL_FunctionCall *call = NULL;
799
800 if (params)
801 {
802 call = params->value.functionCallValue;
803 }
804 else
805 call = calloc(1, sizeof(LSL_FunctionCall));
806
807 if (func)
808 {
809 if (call)
810 {
811 call->function = func->value.functionValue;
812 eina_clist_element_init(&(call->dangler));
813 }
814 identifier->value.functionCallValue = call;
815 identifier->toKen = tokens[LSL_FUNCTION_CALL - lowestToken];
816 identifier->basicType = func->basicType;
817 }
818 else
819 {
820 // It may be declared later, so store it and check later.
821 if (call)
822 {
823 eina_clist_add_tail(&(compiler->danglingCalls), &(call->dangler));
824 call->call = identifier;
825 }
826 // Here the identifier stringValue needs to be kept for later searching.
827 identifier->toKen = tokens[LSL_UNKNOWN - lowestToken];
828 identifier->basicType = OT_undeclared;
829 compiler->undeclared = TRUE;
830 }
831
832 return identifier;
833}
834
835LSL_Leaf *addList(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right)
836{
837 left = addParenthesis(left, list, LSL_LIST, right);
838 left->toKen = tokens[LSL_LIST - lowestToken];
839 left->basicType = OT_list;
840 return left;
841}
842
843LSL_Leaf *addRotVec(LSL_Leaf *left, LSL_Leaf *list, LSL_Leaf *right)
844{
845 LSL_Type type = LSL_ROTATION;
846 opType otype = OT_rotation;
847
848 // TODO - count the members of list to see if it's a vector.
849 left = addParenthesis(left, list, type, right);
850 left->toKen = tokens[type - lowestToken];
851 left->basicType = otype;
852 return left;
853}
854
855LSL_Leaf *addNumby(LSL_Leaf *numby)
856{
857 LSL_Numby *num = calloc(1, sizeof(LSL_Numby));
858
859 if ((numby) && (num))
860 {
861 num->text.text = numby->value.stringValue;
862#if LUASL_DIFF_CHECK
863 num->text.ignorable = numby->ignorable;
864 numby->ignorable = NULL;
865#endif
866 switch (numby->toKen->type)
867 {
868 case LSL_FLOAT :
869 {
870 num->value.floatValue = atof(num->text.text);
871 numby->basicType = OT_float;
872 break;
873 }
874 case LSL_INTEGER :
875 {
876 num->value.integerValue = atoi(num->text.text);
877 numby->basicType = OT_integer;
878 break;
879 }
880 default:
881 break;
882 }
883 numby->value.numbyValue = num;
884 num->type = numby->basicType;
885 }
886 return numby;
887}
888
889LSL_Leaf *addParenthesis(LSL_Leaf *lval, LSL_Leaf *expr, LSL_Type type, LSL_Leaf *rval)
890{
891 LSL_Parenthesis *parens = calloc(1, sizeof(LSL_Parenthesis));
892
893 if (parens)
894 {
895 parens->contents = expr;
896 parens->type = type;
897#if LUASL_DIFF_CHECK
898 parens->rightIgnorable = rval->ignorable;
899 // Actualy, at this point, rval is no longer needed.
900 rval->ignorable = NULL;
901#endif
902 if (lval)
903 {
904 lval->value.parenthesis = parens;
905 if (expr)
906 {
907 lval->basicType = expr->basicType;
908 // Propagate these flags inwards and outwards.
909 if (MF_ASSIGNEXP & expr->flags)
910 lval->flags |= MF_ASSIGNEXP;
911 if (MF_WRAPFUNC & expr->flags)
912 parens->flags |= MF_WRAPFUNC;
913 }
914 }
915 }
916 return lval;
917}
918
919LSL_Leaf *addState(LuaSL_compiler *compiler, LSL_Leaf *state, LSL_Leaf *identifier, LSL_Leaf *block)
920{
921 LSL_State *result = calloc(1, sizeof(LSL_State));
922
923 if ((identifier) && (result))
924 {
925 Eina_Iterator *handlers;
926 LSL_Function *func;
927
928 memcpy(result, &(compiler->state), sizeof(LSL_State));
929 compiler->state.block = NULL;
930 compiler->state.handlers = NULL;
931 result->name.text = identifier->value.stringValue;
932#if LUASL_DIFF_CHECK
933 result->name.ignorable = identifier->ignorable;
934 identifier->ignorable = NULL;
935#endif
936 handlers = eina_hash_iterator_data_new(result->handlers);
937 while(eina_iterator_next(handlers, (void **) &func))
938 {
939 func->state = result->name.text;
940 }
941 result->block = block->value.blockValue;
942 if (state)
943 {
944 result->state.text = state->toKen->toKen;
945#if LUASL_DIFF_CHECK
946 result->state.ignorable = state->ignorable;
947 state->ignorable = NULL;
948#endif
949 }
950 identifier->value.stateValue = result;
951 identifier->toKen = tokens[LSL_STATE - lowestToken];
952 eina_hash_add(compiler->script.states, result->name.text, identifier);
953 compiler->inState = FALSE;
954 }
955
956 return identifier;
957}
958
959LSL_Leaf *addIfElse(LuaSL_compiler *compiler, LSL_Leaf *ifBlock, LSL_Leaf *elseBlock)
960{
961 if (ifBlock->value.statementValue->elseBlock)
962 {
963 LSL_Statement *oldElseIf = ifBlock->value.statementValue->elseBlock;
964
965 while (oldElseIf->elseBlock)
966 oldElseIf = oldElseIf->elseBlock;
967
968 oldElseIf->elseBlock = elseBlock->value.statementValue;
969 }
970 else
971 ifBlock->value.statementValue->elseBlock = elseBlock->value.statementValue;
972 return ifBlock;
973}
974
975LSL_Leaf *addFor(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr0, LSL_Leaf *stat0, LSL_Leaf *expr1, LSL_Leaf *stat1, LSL_Leaf *expr2, LSL_Leaf *right, LSL_Leaf *block)
976{
977 LSL_Leaf **exprs = calloc(5, sizeof(LSL_Leaf *));
978
979 if (exprs)
980 {
981 lval = addStatement(compiler, lval, flow, left, expr0, right, block, NULL);
982 exprs[0] = expr0;
983 exprs[1] = stat0;
984 exprs[2] = expr1;
985 exprs[3] = stat1;
986 exprs[4] = expr2;
987 lval->value.statementValue->expressions = (LSL_Leaf *) exprs;
988 }
989 return lval;
990}
991
992LSL_Leaf *addStatement(LuaSL_compiler *compiler, LSL_Leaf *lval, LSL_Leaf *flow, LSL_Leaf *left, LSL_Leaf *expr, LSL_Leaf *right, LSL_Leaf *block, LSL_Leaf *identifier)
993{
994 gameGlobals *ourGlobals = compiler->game;
995 LSL_Statement *stat = calloc(1, sizeof(LSL_Statement));
996 boolean justOne = FALSE;
997
998 if (NULL == lval)
999 lval = newLeaf(LSL_STATEMENT, NULL, NULL);
1000
1001 if (stat)
1002 {
1003 stat->type = flow->toKen->type;
1004 stat->expressions = expr;
1005 if (block)
1006 {
1007 if (LSL_BLOCK_OPEN == block->toKen->type)
1008 stat->block = block->value.blockValue;
1009 else
1010 stat->single = block->value.statementValue;
1011 }
1012 eina_clist_element_init(&(stat->statement));
1013 if (identifier)
1014 {
1015 stat->identifier.text = identifier->value.stringValue;
1016#if LUASL_DIFF_CHECK
1017 stat->identifier.ignorable = identifier->ignorable;
1018 identifier->ignorable = NULL;
1019#endif
1020 }
1021 if (left)
1022 {
1023 LSL_Leaf *parens = addParenthesis(left, expr, LSL_EXPRESSION, right);
1024
1025 if (parens)
1026 stat->parenthesis = parens->value.parenthesis;
1027 }
1028
1029 switch (stat->type)
1030 {
1031 case LSL_EXPRESSION :
1032 {
1033 break;
1034 }
1035 case LSL_FUNCTION :
1036 {
1037 break;
1038 }
1039 case LSL_DO :
1040 {
1041 break;
1042 }
1043 case LSL_FOR :
1044 {
1045 justOne = TRUE;
1046 break;
1047 }
1048 case LSL_IF :
1049 {
1050 justOne = TRUE;
1051 break;
1052 }
1053 case LSL_ELSE :
1054 {
1055 justOne = TRUE;
1056 break;
1057 }
1058 case LSL_ELSEIF :
1059 {
1060 justOne = TRUE;
1061 break;
1062 }
1063 case LSL_JUMP :
1064 {
1065 justOne = TRUE;
1066 break;
1067 }
1068 case LSL_LABEL :
1069 {
1070 justOne = TRUE;
1071 break;
1072 }
1073 case LSL_RETURN :
1074 {
1075 justOne = TRUE;
1076 break;
1077 }
1078 case LSL_STATE_CHANGE :
1079 {
1080 justOne = TRUE;
1081 break;
1082 }
1083 case LSL_STATEMENT :
1084 {
1085 break;
1086 }
1087 case LSL_WHILE :
1088 {
1089 stat->identifier.text = NULL;
1090 justOne = TRUE;
1091 break;
1092 }
1093 case LSL_IDENTIFIER :
1094 {
1095 break;
1096 }
1097 case LSL_VARIABLE :
1098 {
1099 if (identifier)
1100 {
1101 stat->identifier.text = identifier->value.identifierValue->name.text;
1102 identifier->value.identifierValue->definition = stat;
1103 stat->flags = identifier->value.identifierValue->flags;
1104 }
1105 break;
1106 }
1107 default :
1108 {
1109 compiler->script.bugCount++;
1110 PE("Should not be here %d.", stat->type);
1111 break;
1112 }
1113 }
1114
1115#if LUASL_DIFF_CHECK
1116 if (justOne && (flow))
1117 {
1118 stat->ignorable = calloc(2, sizeof(Eina_Strbuf *));
1119 if (stat->ignorable)
1120 {
1121 stat->ignorable[1] = flow->ignorable;
1122 flow->ignorable = NULL;
1123 }
1124 }
1125#endif
1126
1127 if (lval)
1128 {
1129#if LUASL_DIFF_CHECK
1130 if (NULL == stat->ignorable)
1131 stat->ignorable = calloc(1, sizeof(Eina_Strbuf *));
1132 if (stat->ignorable)
1133 {
1134 stat->ignorable[0] = lval->ignorable;
1135 lval->ignorable = NULL;
1136 }
1137#endif
1138 lval->value.statementValue = stat;
1139 }
1140
1141#if LUASL_DIFF_CHECK
1142 if (left)
1143 {
1144 if (NULL == stat->ignorable)
1145 stat->ignorable = calloc(3, sizeof(Eina_Strbuf *));
1146 else
1147 stat->ignorable = realloc(stat->ignorable, 3 * sizeof(Eina_Strbuf *));
1148 if (stat->ignorable)
1149 {
1150 stat->ignorable[2] = left->ignorable;
1151 left->ignorable = NULL;
1152 }
1153 }
1154#endif
1155 }
1156
1157 return lval;
1158}
1159
1160LSL_Leaf *collectStatements(LuaSL_compiler *compiler, LSL_Leaf *list, LSL_Leaf *statement)
1161{
1162// Guess this is not needed after all, and seemed to cause the "states with only one function get dropped" bug.
1163// boolean wasNull = FALSE;
1164
1165 if (NULL == list)
1166 {
1167 list = newLeaf(LSL_BLOCK_OPEN, NULL, NULL);
1168// wasNull = TRUE;
1169 }
1170
1171 if (list)
1172 {
1173 if (statement)
1174 {
1175// if (!wasNull)
1176 list->value.blockValue = compiler->currentBlock; // Maybe NULL.
1177
1178 if ((compiler->inState) && (LSL_FUNCTION == statement->value.statementValue->type))
1179 {
1180 eina_clist_add_tail(&(compiler->state.block->statements), &(statement->value.statementValue->statement));
1181 }
1182 else if (list->value.blockValue)
1183 {
1184 eina_clist_add_tail(&(list->value.blockValue->statements), &(statement->value.statementValue->statement));
1185 }
1186 }
1187 }
1188
1189 return list;
1190}
1191
1192/* Typecasting
1193
1194LSL is statically typed, so stored values are not converted, only the values used in expressions are.
1195Lua is dynamically typed, so stored values are changed (sometimes I think).
1196
1197LSL implicitly typecasts - There is a shitload of QUIRKs about this. Apparently some don't work anyway.
1198 integer -> float (Says in lslwiki that precision is never lost, which is bullshit, since they are both 32 bit. Would be true if the float is 64 bit. Lua suggest to use 64 bit floats to emulate 32 bit integers.)
1199 string -> key
1200 Some functions need help with this or the other way around.
1201 string -> vector (Maybe, should test that.)
1202 vector -> string (Maybe, should test that.)
1203 Also happens when getting stuff from lists.
1204
1205Explicit type casting -
1206 string -> integer
1207 Leading spaces are ignored, as are any characters after the run of digits.
1208 All other strings convert to 0.
1209 Which means "" and " " convert to 0.
1210 Strings in hexadecimal format will work, same in Lua (though Lua can't handle "0x", but "0x0" is fine).
1211 keys <-> string
1212 No other typecasting can be done with keys.
1213 float -> string
1214 You get a bunch of trailing 0s.
1215
1216QUIRK - I have seen cases where a double explicit typecast was needed in SL, but was considered to be invalid syntax in OS.
1217
1218Any binary operation involving a float and an integer implicitly casts the integer to float.
1219
1220A boolean operation deals with TRUE (1) and FALSE (0). Any non zero value is a TRUE (generally sigh).
1221On the other hand, in Lua, only false and nil are false, everything else is true. 0 is true. sigh
1222Bitwise operations only apply to integers. Right shifts are arithmetic, not logical.
1223
1224integer = integer0 % integer1; // Apparently only applies to integers, but works fine on floats in OS.
1225string = string0 + string1; // Concatenation.
1226list = list0 + list1; // Concatenation. Also works if either is not a list, it's promoted to a list first.
1227list = (list=[]) + list + ["new_item"]; // Voodoo needed for old LSL, works in Mono but not needed, does not work in OS. Works for strings to.
1228bool = list == != int // Only compares the lengths, probably applies to the other conditionals to.
1229vector = vector0 + vector1; // Add elements together.
1230vector = vector0 - vector1; // Subtract elements of vector1 from elements of vector0.
1231float = vector0 * vector1; // A dot product of the vectors.
1232vector = vector0 % vector1; // A cross product of the vectors.
1233vector = vector * float; // Scale the vector, works the other way around I think. Works for integer to, but it will end up being cast to float.
1234vector = vector / float; // Scale the vector, works the other way around I think. Works for integer to, but it will end up being cast to float.
1235vector = vector * rotation; // Rotate the vector by the rotation. Other way around wont compile.
1236vector = vector / rotation; // Rotate the vector by the rotation, in the opposite direction. Other way around wont compile.
1237rotation = llGetRot() * rotation; // Rotate an object around the global axis.
1238rotation = rotation * llGetLocalRot(); // Rotate an object around the local axis.
1239rotation = rotation0 * rotation1; // Add two rotations, so the result is as if you applied each rotation one after the other.
1240 // Division rotates in the opposite direction.
1241rotation = rotation0 + rotation1; // Similar to vector, but it's a meaningless thing as far as rotations go.
1242rotation = rotation0 - rotation1; // Similar to vector, but it's a meaningless thing as far as rotations go.
1243
1244A boolean operator results in a boolean value. (any types)
1245A comparison operator results in a boolean value. (any types)
1246A bitwise operator results in an integer value. (intInt or int)
1247A dot product operator results in a float value. (vector * vector)
1248A vectorFloat results in a vector value.
1249
1250*/
1251
1252LSL_Leaf *addTypecast(LSL_Leaf *lval, LSL_Leaf *type, LSL_Leaf *rval, LSL_Leaf *expr)
1253{
1254 addParenthesis(lval, expr, LSL_TYPECAST_OPEN, rval);
1255 if (lval)
1256 {
1257 if (type)
1258 {
1259 lval->basicType = type->basicType;
1260 if ((expr) && (OT_integer == type->basicType)) // TODO - Should be from string, but I guess I'm not propagating basic types up from function calls and parenthesis?
1261 lval->value.parenthesis->flags |= MF_TYPECAST;
1262 }
1263 // Actualy, at this point, type is no longer needed.
1264 lval->toKen = tokens[LSL_TYPECAST_OPEN - lowestToken];
1265 }
1266// if (rval)
1267// rval->toKen = tokens[LSL_TYPECAST_CLOSE - lowestToken];
1268
1269 return lval;
1270}
1271
1272LSL_Leaf *addVariable(LuaSL_compiler *compiler, LSL_Leaf *type, LSL_Leaf *identifier, LSL_Leaf *assignment, LSL_Leaf *expr)
1273{
1274 LSL_Identifier *result = calloc(1, sizeof(LSL_Identifier));
1275
1276 if ( (identifier) && (result))
1277 {
1278 result->name.text = identifier->value.stringValue;
1279#if LUASL_DIFF_CHECK
1280 result->name.ignorable = identifier->ignorable;
1281 identifier->ignorable = NULL;
1282#endif
1283 result->value.toKen = tokens[LSL_UNKNOWN - lowestToken];
1284 identifier->value.identifierValue = result;
1285 identifier->toKen = tokens[LSL_VARIABLE - lowestToken];
1286 identifier->left = type;
1287 identifier->right = assignment;
1288 if (assignment)
1289 assignment->right = expr;
1290 else
1291 identifier->flags |= MF_NOASSIGN;
1292 if (type)
1293 {
1294 if (compiler->currentBlock)
1295 {
1296 identifier->flags |= MF_LOCAL;
1297 result->flags |= MF_LOCAL;
1298 type->flags |= MF_LOCAL;
1299 }
1300 identifier->basicType = type->basicType;
1301 result->value.basicType = type->basicType;
1302 result->value.toKen = type->toKen; // This is the LSL_TYPE_* toKen instead of the LSL_* toKen. Not sure if that's a problem.
1303 }
1304 if (compiler->currentBlock)
1305 eina_hash_add(compiler->currentBlock->variables, result->name.text, identifier);
1306 else
1307 eina_hash_add(compiler->script.variables, result->name.text, identifier);
1308 }
1309
1310 return identifier;
1311}
1312
1313LSL_Leaf *beginBlock(LuaSL_compiler *compiler, LSL_Leaf *block)
1314{
1315 LSL_Block *blok = calloc(1, sizeof(LSL_Block));
1316
1317 if (blok)
1318 {
1319 eina_clist_init(&(blok->statements));
1320 blok->variables = eina_hash_stringshared_new(burnLeaf);
1321 block->value.blockValue = blok;
1322 if ((NULL == compiler->currentBlock) && (NULL == compiler->currentFunction))
1323 {
1324 compiler->inState = TRUE;
1325 compiler->state.block=blok;
1326 compiler->state.handlers = eina_hash_stringshared_new(free);
1327 }
1328 blok->outerBlock = compiler->currentBlock;
1329 compiler->currentBlock = blok;
1330 blok->function = compiler->currentFunction;
1331 compiler->currentFunction = NULL;
1332#if LUASL_DIFF_CHECK
1333 blok->openIgnorable = block->ignorable;
1334 block->ignorable = NULL;
1335#endif
1336 }
1337 return block;
1338}
1339
1340static void secondPass(LuaSL_compiler *compiler, LSL_Leaf *leaf)
1341{
1342 if (leaf)
1343 {
1344 secondPass(compiler, leaf->left);
1345 if (OT_undeclared == leaf->basicType)
1346 leaf = addOperation(compiler, leaf->left, leaf, leaf->right);
1347 secondPass(compiler, leaf->right);
1348 }
1349}
1350
1351static void outputLeaf(FILE *file, outputMode mode, LSL_Leaf *leaf)
1352{
1353 if (leaf)
1354 {
1355 if ((OM_LUA == mode) &&(ST_BITWISE != leaf->toKen->subType))
1356 outputLeaf(file, mode, leaf->left);
1357#if LUASL_DIFF_CHECK
1358 if ((!(LSL_NOIGNORE & leaf->toKen->flags)) && (leaf->ignorable))
1359 fwrite(eina_strbuf_string_get(leaf->ignorable), 1, eina_strbuf_length_get(leaf->ignorable), file);
1360#endif
1361 if (leaf->toKen->output)
1362 leaf->toKen->output(file, mode, leaf);
1363 else
1364 {
1365 if (OM_LUA == mode)
1366 {
1367 if (MF_WRAPFUNC & leaf->flags)
1368 {
1369// TODO - Leaving this here in case we trip over one.
1370if ((leaf->left) && (leaf->right))
1371 printf("%s %s %s\n", leaf->left->toKen->toKen, leaf->toKen->toKen, leaf->right->toKen->toKen);
1372else if (leaf->left)
1373 printf("%s %s NORIGHT\n", leaf->left->toKen->toKen, leaf->toKen->toKen);
1374else if (leaf->right)
1375 printf("NOLEFT %s %s\n", leaf->toKen->toKen, leaf->right->toKen->toKen);
1376else
1377 printf("NOLEFT %s NORIGHT\n", leaf->toKen->toKen);
1378 }
1379 if ((LSL_ASSIGNMENT & leaf->toKen->flags) && (LSL_ASSIGNMENT_PLAIN != leaf->toKen->type))
1380 {
1381 if (leaf->left->value.identifierValue->sub)
1382 fprintf(file, " --[[%s]] = %s.%s %.1s ", leaf->toKen->toKen, leaf->left->value.identifierValue->name.text, leaf->left->value.identifierValue->sub, leaf->toKen->toKen);
1383 else
1384 fprintf(file, " --[[%s]] = %s %.1s ", leaf->toKen->toKen, leaf->left->value.identifierValue->name.text, leaf->toKen->toKen);
1385 }
1386 else if (LSL_TYPE & leaf->toKen->flags)
1387 {
1388 if (MF_LOCAL & leaf->flags)
1389 fprintf(file, " local ");
1390 fprintf(file, " --[[%s]] ", leaf->toKen->toKen);
1391 }
1392 else if (LSL_BOOL_AND == leaf->toKen->type)
1393 fprintf(file, " and ");
1394 else if (LSL_BOOL_OR == leaf->toKen->type)
1395 fprintf(file, " or ");
1396 else if (LSL_BOOL_NOT == leaf->toKen->type)
1397 fprintf(file, " not ");
1398 else if (LSL_CONCATENATE == leaf->toKen->type)
1399 fprintf(file, " .. ");
1400 else if (LSL_NOT_EQUAL == leaf->toKen->type)
1401 fprintf(file, " ~= ");
1402 else
1403 fprintf(file, "%s", leaf->toKen->toKen);
1404 }
1405 else
1406 fprintf(file, "%s", leaf->toKen->toKen);
1407 }
1408 if ((OM_LUA == mode) &&(ST_BITWISE != leaf->toKen->subType))
1409 outputLeaf(file, mode, leaf->right);
1410 }
1411}
1412
1413// Circular references, so declare this one first.
1414static void outputRawStatement(FILE *file, outputMode mode, LSL_Statement *statement);
1415
1416static void outputRawBlock(FILE *file, outputMode mode, LSL_Block *block, boolean doEnd)
1417{
1418 if (block)
1419 {
1420 LSL_Statement *stat = NULL;
1421
1422#if LUASL_DIFF_CHECK
1423 if (block->openIgnorable)
1424 fwrite(eina_strbuf_string_get(block->openIgnorable), 1, eina_strbuf_length_get(block->openIgnorable), file);
1425 if (OM_LSL == mode)
1426 fprintf(file, "{");
1427#else
1428 if (OM_LSL == mode)
1429 fprintf(file, "\n{\n");
1430 else if (doEnd && (OM_LUA == mode))
1431 fprintf(file, "\n");
1432#endif
1433 EINA_CLIST_FOR_EACH_ENTRY(stat, &(block->statements), LSL_Statement, statement)
1434 {
1435 outputRawStatement(file, mode, stat);
1436 }
1437#if LUASL_DIFF_CHECK
1438 if (block->closeIgnorable)
1439 fwrite(eina_strbuf_string_get(block->closeIgnorable), 1, eina_strbuf_length_get(block->closeIgnorable), file);
1440#endif
1441 if (OM_LSL == mode)
1442 fprintf(file, "}");
1443 else if (doEnd && (OM_LUA == mode))
1444 fprintf(file, "end ");
1445 }
1446}
1447
1448// TODO - should clean this up by refactoring the bits in the switch outside.
1449static void outputRawParenthesisToken(FILE *file, outputMode mode, LSL_Parenthesis *parenthesis, const char *typeName)
1450{
1451 if ((OM_LUA == mode) && (LSL_TYPECAST_OPEN == parenthesis->type))
1452 {
1453 if (MF_TYPECAST & parenthesis->flags)
1454 fprintf(file, " _LSL.%sTypecast(", typeName);
1455 else
1456 fprintf(file, " --[[%s]] ", typeName);
1457 outputLeaf(file, mode, parenthesis->contents);
1458 if (MF_TYPECAST & parenthesis->flags)
1459 fprintf(file, ") ");
1460 return;
1461 }
1462
1463 if ((OM_LUA == mode) && (MF_WRAPFUNC & parenthesis->flags))
1464 fprintf(file, " (function() ");
1465 else
1466 fprintf(file, "(");
1467 if (LSL_TYPECAST_OPEN == parenthesis->type)
1468 fprintf(file, "%s", typeName); // TODO - We are missing the type ignorable text here.
1469 else
1470 outputLeaf(file, mode, parenthesis->contents);
1471 if ((OM_LUA == mode) && (MF_WRAPFUNC & parenthesis->flags))
1472 fprintf(file, "; return x; end)() ");
1473 else
1474 {
1475#if LUASL_DIFF_CHECK
1476 fprintf(file, "%s)", eina_strbuf_string_get(parenthesis->rightIgnorable));
1477#else
1478 fprintf(file, ")");
1479#endif
1480 }
1481
1482 if (LSL_TYPECAST_OPEN == parenthesis->type)
1483 outputLeaf(file, mode, parenthesis->contents);
1484}
1485
1486static void outputText(FILE *file, LSL_Text *text, boolean ignore)
1487{
1488 if (text->text)
1489 {
1490#if LUASL_DIFF_CHECK
1491 if (ignore && (text->ignorable))
1492 fwrite(eina_strbuf_string_get(text->ignorable), 1, eina_strbuf_length_get(text->ignorable), file);
1493#endif
1494 fprintf(file, "%s", text->text);
1495 }
1496}
1497
1498static void outputRawStatement(FILE *file, outputMode mode, LSL_Statement *statement)
1499{
1500 boolean isBlock = FALSE;
1501
1502 if (statement)
1503 {
1504 switch (statement->type)
1505 {
1506 case LSL_EXPRESSION :
1507 {
1508 break;
1509 }
1510 case LSL_FUNCTION :
1511 {
1512 isBlock = TRUE;
1513 break;
1514 }
1515 case LSL_DO :
1516 {
1517 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1518 break;
1519 }
1520 case LSL_FOR :
1521 {
1522#if LUASL_DIFF_CHECK
1523 if ((statement->ignorable) && (statement->ignorable[1]))
1524 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1525#endif
1526 if (OM_LSL == mode)
1527 {
1528 isBlock = TRUE;
1529 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1530 }
1531 else if (OM_LUA == mode)
1532 {
1533 LSL_Leaf **exprs = (LSL_Leaf **) statement->expressions;
1534
1535 outputLeaf(file, mode, exprs[0]);
1536 fprintf(file, ";\nwhile (");
1537 outputLeaf(file, mode, exprs[2]);
1538#if LUASL_DIFF_CHECK
1539 fprintf(file, "%s)\n", eina_strbuf_string_get(statement->parenthesis->rightIgnorable));
1540#else
1541 fprintf(file, ") do\n");
1542#endif
1543 if (statement->block)
1544 outputRawBlock(file, mode, statement->block, FALSE);
1545 if (statement->single)
1546 outputRawStatement(file, mode, statement->single);
1547 fprintf(file, "\n");
1548 outputLeaf(file, mode, exprs[4]);
1549 fprintf(file, ";\nend\n");
1550 return;
1551 }
1552 break;
1553 }
1554 case LSL_IF :
1555 case LSL_ELSE :
1556 case LSL_ELSEIF :
1557 {
1558 isBlock = TRUE;
1559#if LUASL_DIFF_CHECK
1560 if ((statement->ignorable) && (statement->ignorable[1]))
1561 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1562#endif
1563 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1564 if (OM_LUA == mode)
1565 {
1566 fprintf(file, " ");
1567 if (LSL_ELSE != statement->type)
1568 {
1569 if (statement->parenthesis)
1570 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1571 else
1572 outputLeaf(file, mode, statement->expressions);
1573 fprintf(file, " then\n");
1574 }
1575 if (statement->block)
1576 outputRawBlock(file, mode, statement->block, FALSE);
1577 if (statement->single)
1578 outputRawStatement(file, mode, statement->single);
1579 if (statement->elseBlock)
1580 outputRawStatement(file, mode, statement->elseBlock);
1581 if (LSL_IF == statement->type)
1582 {
1583#if 1
1584 fprintf(file, " end\n");
1585#else
1586 fprintf(file, " end --[[");
1587 if (statement->parenthesis)
1588 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1589 else
1590 outputLeaf(file, mode, statement->expressions);
1591 fprintf(file, "]]\n");
1592#endif
1593 }
1594 return;
1595 }
1596 break;
1597 }
1598 case LSL_JUMP :
1599 {
1600#if LUASL_DIFF_CHECK
1601 if ((statement->ignorable) && (statement->ignorable[1]))
1602 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1603#endif
1604 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1605 break;
1606 }
1607 case LSL_LABEL :
1608 {
1609#if LUASL_DIFF_CHECK
1610 if ((statement->ignorable) && (statement->ignorable[1]))
1611 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1612#endif
1613 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1614 break;
1615 }
1616 case LSL_RETURN :
1617 {
1618#if LUASL_DIFF_CHECK
1619 if ((statement->ignorable) && (statement->ignorable[1]))
1620 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1621#endif
1622 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1623 break;
1624 }
1625 case LSL_STATE_CHANGE :
1626 {
1627#if LUASL_DIFF_CHECK
1628 if ((statement->ignorable) && (statement->ignorable[1]))
1629 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1630#endif
1631 if (OM_LSL == mode)
1632 {
1633 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1634 if (statement->identifier.text)
1635 outputText(file, &(statement->identifier), TRUE);
1636 }
1637 else if (OM_LUA == mode)
1638 {
1639 fprintf(file, "return _LSL.stateChange(_");
1640 if (statement->identifier.text)
1641 outputText(file, &(statement->identifier), TRUE);
1642 fprintf(file, "State)");
1643 }
1644 break;
1645 }
1646 case LSL_STATEMENT :
1647 {
1648 break;
1649 }
1650 case LSL_WHILE :
1651 {
1652 isBlock = TRUE;
1653#if LUASL_DIFF_CHECK
1654 if ((statement->ignorable) && (statement->ignorable[1]))
1655 fwrite(eina_strbuf_string_get(statement->ignorable[1]), 1, eina_strbuf_length_get(statement->ignorable[1]), file);
1656#else
1657 if (OM_LUA == mode)
1658 fprintf(file, "\n");
1659#endif
1660 fprintf(file, "%s", tokens[statement->type - lowestToken]->toKen);
1661 if (OM_LUA == mode)
1662 {
1663 if (statement->parenthesis)
1664 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1665 fprintf(file, " do ");
1666 if (statement->block)
1667 outputRawBlock(file, mode, statement->block, TRUE);
1668 if (statement->single)
1669 outputRawStatement(file, mode, statement->single);
1670 fprintf(file, "\n");
1671 return;
1672 }
1673 break;
1674 }
1675 case LSL_IDENTIFIER :
1676 {
1677 break;
1678 }
1679 case LSL_VARIABLE :
1680 {
1681 break;
1682 }
1683 default :
1684 {
1685 fprintf(file, "@@Should not be here %s.@@", tokens[statement->type - lowestToken]->toKen);
1686 break;
1687 }
1688 }
1689
1690#if LUASL_DIFF_CHECK
1691 if ((statement->ignorable) && (statement->ignorable[2]))
1692 fwrite(eina_strbuf_string_get(statement->ignorable[2]), 1, eina_strbuf_length_get(statement->ignorable[2]), file);
1693#else
1694 if (OM_LUA == mode)
1695 fprintf(file, " ");
1696#endif
1697 if (LSL_FOR == statement->type)
1698 {
1699 LSL_Leaf **exprs = (LSL_Leaf **) statement->expressions;
1700 int i;
1701
1702 fprintf(file, "(");
1703 for (i = 0; i < 5; i++)
1704 {
1705 outputLeaf(file, mode, exprs[i]);
1706 if (i % 2)
1707 fprintf(file, ";");
1708 }
1709#if LUASL_DIFF_CHECK
1710 fprintf(file, "%s)", eina_strbuf_string_get(statement->parenthesis->rightIgnorable));
1711#else
1712 fprintf(file, ")");
1713#endif
1714 }
1715 else if (statement->parenthesis)
1716 outputRawParenthesisToken(file, mode, statement->parenthesis, "");
1717 else
1718 outputLeaf(file, mode, statement->expressions);
1719
1720 if (statement->block)
1721 outputRawBlock(file, mode, statement->block, TRUE);
1722 if (statement->single)
1723 outputRawStatement(file, mode, statement->single);
1724
1725#if LUASL_DIFF_CHECK
1726 if ((statement->ignorable) && (statement->ignorable[0]))
1727 fwrite(eina_strbuf_string_get(statement->ignorable[0]), 1, eina_strbuf_length_get(statement->ignorable[0]), file);
1728#endif
1729
1730 if (!isBlock)
1731 {
1732 fprintf(file, ";");
1733 if (!LUASL_DIFF_CHECK)
1734 fprintf(file, "\n");
1735 }
1736
1737 if ((LSL_VARIABLE == statement->type) && (OM_LUA == mode) && (MF_LOCAL & statement->flags))
1738 {
1739 const char *name = statement->identifier.text;
1740
1741// if ((MF_PREDEC | MF_PREINC | MF_POSTDEC | MF_POSTINC) & statement->flags)
1742// fprintf(file, "\n");
1743 if (MF_PREDEC & statement->flags) fprintf(file, "local function _preDecrement_%s() %s = %s - 1; return %s; end\n", name, name, name, name);
1744 if (MF_PREINC & statement->flags) fprintf(file, "local function _preIncrement_%s() %s = %s + 1; return %s; end\n", name, name, name, name);
1745 if (MF_POSTDEC & statement->flags) fprintf(file, "local function _postDecrement_%s() local _temp = %s; %s = %s - 1; return _temp; end\n", name, name, name, name);
1746 if (MF_POSTINC & statement->flags) fprintf(file, "local function _postDecrement_%s() local _temp = %s; %s = %s + 1; return _temp; end\n", name, name, name, name);
1747 }
1748
1749 if (statement->elseBlock)
1750 outputRawStatement(file, mode, statement->elseBlock);
1751 }
1752}
1753
1754static void outputBitOp(FILE *file, outputMode mode, LSL_Leaf *leaf)
1755{
1756 if (OM_LSL == mode)
1757 outputLeaf(file, mode, leaf);
1758 else if (OM_LUA == mode)
1759 {
1760 switch (leaf->toKen->type)
1761 {
1762 case LSL_BIT_AND : fprintf(file, " _bit.band("); break;
1763 case LSL_BIT_OR : fprintf(file, " _bit.bor("); break;
1764 case LSL_BIT_XOR : fprintf(file, " _bit.xor("); break;
1765 case LSL_BIT_NOT : fprintf(file, " _bit.bnot("); break;
1766 case LSL_LEFT_SHIFT : fprintf(file, " _bit.lshift("); break;
1767 case LSL_RIGHT_SHIFT : fprintf(file, " _bit.arshift("); break;
1768 default : break;
1769 }
1770 outputLeaf(file, mode, leaf->left);
1771 if (LSL_BIT_NOT != leaf->toKen->type)
1772 {
1773 fprintf(file, ", ");
1774 outputLeaf(file, mode, leaf->right);
1775 }
1776 fprintf(file, ") ");
1777 }
1778}
1779
1780static void outputBlockToken(FILE *file, outputMode mode, LSL_Leaf *content)
1781{
1782 if (content)
1783 outputRawBlock(file, mode, content->value.blockValue, TRUE);
1784}
1785
1786static void outputCrementsToken(FILE *file, outputMode mode, LSL_Leaf *content)
1787{
1788 if (content)
1789 {
1790 if (OM_LSL == mode)
1791 {
1792 switch (content->toKen->type)
1793 {
1794 case LSL_DECREMENT_PRE :
1795 case LSL_INCREMENT_PRE :
1796 {
1797 fprintf(file, "%s", content->toKen->toKen);
1798#if LUASL_DIFF_CHECK
1799 if (content->value.identifierValue->ignorable)
1800 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1801#endif
1802 outputText(file, &(content->value.identifierValue->name), FALSE);
1803 break;
1804 }
1805 case LSL_DECREMENT_POST :
1806 case LSL_INCREMENT_POST :
1807 {
1808#if LUASL_DIFF_CHECK
1809 if (content->value.identifierValue->ignorable)
1810 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1811#endif
1812 outputText(file, &(content->value.identifierValue->name), FALSE);
1813 fprintf(file, "%s", content->toKen->toKen);
1814 break;
1815 }
1816 default :
1817 break;
1818 }
1819 }
1820 else if (OM_LUA == mode)
1821 {
1822 if (MF_LOCAL & content->value.identifierValue->flags)
1823 fprintf(file, " _");
1824 else
1825 fprintf(file, " _LSL.");
1826 switch (content->toKen->type)
1827 {
1828 case LSL_DECREMENT_PRE : fprintf(file, "preDecrement"); break;
1829 case LSL_INCREMENT_PRE : fprintf(file, "preIncrement"); break;
1830 case LSL_DECREMENT_POST : fprintf(file, "postDecrement"); break;
1831 case LSL_INCREMENT_POST : fprintf(file, "postIncrement"); break;
1832 default :
1833 break;
1834 }
1835 if (MF_LOCAL & content->value.identifierValue->flags)
1836 fprintf(file, "_");
1837 else
1838 fprintf(file, "(\"");
1839#if LUASL_DIFF_CHECK
1840 if (content->value.identifierValue->ignorable)
1841 fwrite(eina_strbuf_string_get(content->value.identifierValue->ignorable), 1, eina_strbuf_length_get(content->value.identifierValue->ignorable), file);
1842#endif
1843 outputText(file, &(content->value.identifierValue->name), FALSE);
1844 if (MF_LOCAL & content->value.identifierValue->flags)
1845 fprintf(file, "()");
1846 else
1847 fprintf(file, "\")");
1848 }
1849 }
1850}
1851
1852static void outputFloatToken(FILE *file, outputMode mode, LSL_Leaf *content)
1853{
1854 if (content)
1855 outputText(file, &(content->value.numbyValue->text), !(LSL_NOIGNORE & content->toKen->flags));
1856}
1857
1858static void outputFunctionToken(FILE *file, outputMode mode, LSL_Leaf *content)
1859{
1860 if (content)
1861 {
1862 LSL_Function *func = content->value.functionValue;
1863 LSL_Leaf *param = NULL;
1864 int first = TRUE;
1865
1866 if (OM_LSL == mode)
1867 {
1868 outputText(file, &(func->type), !(LSL_NOIGNORE & content->toKen->flags));
1869 outputText(file, &(func->name), !(LSL_NOIGNORE & content->toKen->flags));
1870 // TODO - should print comma and parenthesis ignorables.
1871 fprintf(file, "(");
1872 EINA_INARRAY_FOREACH((&(func->vars)), param)
1873 {
1874 if (!LUASL_DIFF_CHECK)
1875 {
1876 if (!first)
1877 fprintf(file, ", ");
1878 }
1879 outputLeaf(file, mode, param);
1880 first = FALSE;
1881 }
1882 fprintf(file, ")");
1883 outputRawBlock(file, mode, func->block, TRUE);
1884 if (!LUASL_DIFF_CHECK)
1885 fprintf(file, "\n");
1886 }
1887 else if (OM_LUA == mode)
1888 {
1889 if (func->state)
1890 fprintf(file, "\n\n_%sState.%s = function(", func->state, func->name.text);
1891 else
1892 {
1893 fprintf(file, "\n\nfunction ");
1894 if (func->type.text)
1895 fprintf(file, " --[[%s]] ", func->type.text);
1896 fprintf(file, "%s(", func->name.text);
1897 }
1898 EINA_INARRAY_FOREACH((&(func->vars)), param)
1899 {
1900 // Commenting out the types is done in outputLeaf() which outputs all the types.
1901 if (!LUASL_DIFF_CHECK)
1902 {
1903 if (!first)
1904 fprintf(file, ", ");
1905 }
1906 outputLeaf(file, mode, param);
1907 first = FALSE;
1908 }
1909 fprintf(file, ")");
1910 outputRawBlock(file, mode, func->block, TRUE);
1911 }
1912 }
1913}
1914
1915static void outputFunctionCallToken(FILE *file, outputMode mode, LSL_Leaf *content)
1916{
1917 if (content)
1918 {
1919 LSL_FunctionCall *call = content->value.functionCallValue;
1920 LSL_Leaf *param = NULL;
1921 boolean first = TRUE;
1922
1923 // TODO - should output it's own ignorable here.
1924 if ((OM_LUA == mode) && (MF_LSLCONST & call->function->flags))
1925 fprintf(file, "_LSL.");
1926 outputText(file, &(call->function->name), FALSE); // Don't output the function definitions ignorable.
1927 fprintf(file, "(");
1928 EINA_INARRAY_FOREACH((&(call->params)), param)
1929 {
1930 if ((OM_LUA == mode) && (!first))
1931 fprintf(file, ", ");
1932 outputLeaf(file, mode, param);
1933 first = FALSE;
1934 }
1935 fprintf(file, ")");
1936 }
1937}
1938
1939static void outputIntegerToken(FILE *file, outputMode mode, LSL_Leaf *content)
1940{
1941 if (content)
1942 outputText(file, &(content->value.numbyValue->text), !(LSL_NOIGNORE & content->toKen->flags));
1943}
1944
1945static void outputIdentifierToken(FILE *file, outputMode mode, LSL_Leaf *content)
1946{
1947 if (content)
1948 {
1949 if (LSL_IDENTIFIER == content->toKen->type)
1950 {
1951 if ((OM_LUA == mode) && (MF_LSLCONST & content->value.identifierValue->flags))
1952 fprintf(file, "_LSL.");
1953 outputText(file, &(content->value.identifierValue->name), FALSE);
1954 if (content->value.identifierValue->sub)
1955 fprintf(file, ".%s", content->value.identifierValue->sub);
1956 }
1957 else
1958 if ((LSL_VARIABLE == content->toKen->type) && (MF_NOASSIGN & content->flags))
1959 {
1960 outputText(file, &(content->value.identifierValue->name), !(LSL_NOIGNORE & content->toKen->flags));
1961 if (OM_LUA == mode)
1962 {
1963 switch (content->basicType)
1964 {
1965 case OT_bool : fprintf(file, " = false"); break;
1966 case OT_integer : fprintf(file, " = 0"); break;
1967 case OT_float : fprintf(file, " = 0.0"); break;
1968 case OT_key : fprintf(file, " = _LSL.NULL_KEY"); break;
1969 case OT_list : fprintf(file, " = {}"); break;
1970 case OT_rotation : fprintf(file, " = _LSL.ZERO_ROTATION"); break;
1971 case OT_string : fprintf(file, " = \"\""); break;
1972 case OT_vector : fprintf(file, " = _LSL.ZERO_VECTOR"); break;
1973 default : fprintf(file, " = nil"); break;
1974 }
1975 }
1976 }
1977 else
1978 outputText(file, &(content->value.identifierValue->name), !(LSL_NOIGNORE & content->toKen->flags));
1979 }
1980}
1981
1982static void outputListToken(FILE *file, outputMode mode, LSL_Leaf *content)
1983{
1984 if (content)
1985 {
1986 LSL_Parenthesis *parens = content->value.parenthesis;
1987
1988 if (parens->contents)
1989 {
1990 LSL_FunctionCall *call = parens->contents->value.functionCallValue;
1991 LSL_Leaf *param = NULL;
1992 const char *ig = "";
1993
1994 // TODO - should output it's own ignorable here.
1995 if (OM_LSL == mode)
1996 {
1997 switch (parens->type)
1998 {
1999 case LSL_LIST : fprintf(file, "["); break;
2000 case LSL_ROTATION :
2001 case LSL_VECTOR : fprintf(file, "<");
2002 default : break;
2003 }
2004 }
2005 else if (OM_LUA == mode)
2006 {
2007 switch (parens->type)
2008 {
2009 case LSL_LIST : fprintf(file, "{"); break;
2010 case LSL_ROTATION :
2011 case LSL_VECTOR : fprintf(file, "{");
2012 default : break;
2013 }
2014 }
2015 EINA_INARRAY_FOREACH((&(call->params)), param)
2016 {
2017 outputLeaf(file, mode, param);
2018 if (OM_LUA == mode)
2019 fprintf(file, ", ");
2020 }
2021#if LUASL_DIFF_CHECK
2022 ig = eina_strbuf_string_get(parens->rightIgnorable);
2023#endif
2024 if (OM_LSL == mode)
2025 {
2026 switch (parens->type)
2027 {
2028 case LSL_LIST : fprintf(file, "%s]", ig); break;
2029 case LSL_ROTATION :
2030 case LSL_VECTOR : fprintf(file, "%s>", ig);
2031 default : break;
2032 }
2033 }
2034 else if (OM_LUA == mode)
2035 {
2036 switch (parens->type)
2037 {
2038 case LSL_LIST : fprintf(file, "%s}", ig); break;
2039 case LSL_ROTATION :
2040 case LSL_VECTOR : fprintf(file, "%s}", ig);
2041 default : break;
2042 }
2043 }
2044 }
2045 }
2046}
2047
2048static void outputParameterListToken(FILE *file, outputMode mode, LSL_Leaf *content)
2049{
2050 if (content)
2051 outputLeaf(file, mode, content->value.listValue);
2052 // TODO - Should go through the list, and output any crements functions we need at the top of the block.
2053}
2054
2055static void outputParenthesisToken(FILE *file, outputMode mode, LSL_Leaf *content)
2056{
2057 if (content)
2058 outputRawParenthesisToken(file, mode, content->value.parenthesis, allowed[content->basicType].name);
2059}
2060
2061static void outputStateToken(FILE *file, outputMode mode, LSL_Leaf *content)
2062{
2063 if (content)
2064 {
2065 LSL_State *state = content->value.stateValue;
2066
2067 if (state)
2068 {
2069 if (OM_LSL == mode)
2070 {
2071 outputText(file, &(state->state), !(LSL_NOIGNORE & content->toKen->flags));
2072 outputText(file, &(state->name), !(LSL_NOIGNORE & content->toKen->flags));
2073 outputRawBlock(file, mode, state->block, TRUE);
2074 }
2075 else if (OM_LUA == mode)
2076 {
2077 fprintf(file, "\n\n--[[state]] _");
2078 outputText(file, &(state->name), !(LSL_NOIGNORE & content->toKen->flags));
2079 fprintf(file, "State = {};");
2080 outputRawBlock(file, mode, state->block, FALSE);
2081 }
2082 }
2083 }
2084}
2085
2086static void outputStatementToken(FILE *file, outputMode mode, LSL_Leaf *content)
2087{
2088 if (content)
2089 outputRawStatement(file, mode, content->value.statementValue);
2090}
2091
2092static void outputStringToken(FILE *file, outputMode mode, LSL_Leaf *content)
2093{
2094 if (content)
2095 fprintf(file, "%s", content->value.stringValue); // The quotes are part of the string value already.
2096}
2097
2098boolean compilerSetup(gameGlobals *ourGlobals)
2099{
2100 int i;
2101
2102 // Figure out what numbers lemon gave to our tokens.
2103 for (i = 0; LSL_Tokens[i].toKen != NULL; i++)
2104 {
2105 if (lowestToken > LSL_Tokens[i].type)
2106 lowestToken = LSL_Tokens[i].type;
2107 }
2108 tokens = calloc(i + 1, sizeof(LSL_Token *));
2109 if (tokens)
2110 {
2111 char buf[PATH_MAX];
2112
2113 // Sort the token table.
2114 for (i = 0; LSL_Tokens[i].toKen != NULL; i++)
2115 {
2116 int j = LSL_Tokens[i].type - lowestToken;
2117
2118 tokens[j] = &(LSL_Tokens[i]);
2119 }
2120
2121 // Compile the constants.
2122 snprintf(buf, sizeof(buf), "lua -e 'require(\"LSL\").gimmeLSL()' > %s/libraries/constants.lsl", PACKAGE_DATA_DIR);
2123 system(buf);
2124 snprintf(buf, sizeof(buf), "%s/libraries/constants.lsl", PACKAGE_DATA_DIR);
2125 compileLSL(ourGlobals, NULL, "FAKE_SID", buf, TRUE);
2126
2127 return TRUE;
2128 }
2129 else
2130 PC("No memory for tokens!");
2131
2132 return FALSE;
2133}
2134
2135static int luaWriter(lua_State *L, const void* p, size_t sz, void* ud)
2136{
2137 FILE *out = ud;
2138 int result = 0;
2139
2140 if (sz != fwrite(p, 1, sz, out))
2141 result = -1;
2142 return result;
2143}
2144
2145boolean compileLSL(gameGlobals *ourGlobals, Ecore_Con_Client *client, char *SID, char *script, boolean doConstants)
2146{
2147 boolean result = FALSE;
2148 LuaSL_compiler compiler;
2149 void *pParser = ParseAlloc(malloc);
2150 int yv;
2151
2152// Parse the LSL script, validating it and reporting errors.
2153// Just pass all LSL constants and ll*() )function names through to Lua, assume they are globals there.
2154
2155 memset(&compiler, 0, sizeof(LuaSL_compiler));
2156 compiler.game = ourGlobals;
2157 compiler.client = client;
2158 compiler.script.functions = eina_hash_stringshared_new(burnLeaf);
2159 compiler.script.states = eina_hash_stringshared_new(burnLeaf);
2160 compiler.script.variables = eina_hash_stringshared_new(burnLeaf);
2161 eina_clist_init(&(compiler.danglingCalls));
2162#if LUASL_DIFF_CHECK
2163 compiler.ignorable = eina_strbuf_new();
2164#endif
2165
2166 strncpy(compiler.SID, SID, 36);
2167 compiler.SID[36] = '\0';
2168 strncpy(compiler.fileName, script, PATH_MAX - 1);
2169 compiler.fileName[PATH_MAX - 1] = '\0';
2170 compiler.file = fopen(compiler.fileName, "r");
2171 if (NULL == compiler.file)
2172 {
2173 PE("Error opening file %s.", compiler.fileName);
2174 return FALSE;
2175 }
2176 compiler.ast = NULL;
2177 compiler.lval = newLeaf(LSL_UNKNOWN, NULL, NULL);
2178 // Text editors usually start counting at 1, even programmers editors. mcedit is an exception, but you can deal with that yourself.
2179 compiler.column = 1;
2180 compiler.line = 1;
2181
2182 if (yylex_init_extra(&compiler, &(compiler.scanner)))
2183 return result;
2184 if (LUASL_DEBUG)
2185 {
2186 yyset_debug(1, compiler.scanner);
2187 ParseTrace(stdout, "LSL_lemon ");
2188 }
2189 yyset_in(compiler.file, compiler.scanner);
2190 // on EOF yylex will return 0
2191 while((yv = yylex(compiler.lval, compiler.scanner)) != 0)
2192 {
2193 Parse(pParser, yv, compiler.lval, &compiler);
2194 if (LSL_SCRIPT == yv)
2195 break;
2196 compiler.lval = newLeaf(LSL_UNKNOWN, NULL, NULL);
2197 }
2198
2199 yylex_destroy(compiler.scanner);
2200 Parse (pParser, 0, compiler.lval, &compiler);
2201 ParseFree(pParser, free);
2202
2203 if (compiler.undeclared)
2204 {
2205// PW("A second pass is needed to check if functions where used before they where declared. To avoid this second pass, don't do that.");
2206 if (eina_clist_count(&(compiler.danglingCalls)))
2207 {
2208 LSL_FunctionCall *call = NULL;
2209
2210 EINA_CLIST_FOR_EACH_ENTRY(call, &(compiler.danglingCalls), LSL_FunctionCall, dangler)
2211 {
2212 LSL_Leaf *func = findFunction(&(compiler), call->call->value.stringValue);
2213
2214 if (func)
2215 {
2216 call->function = func->value.functionValue;
2217 // Coz the leaf still had the stringValue from before.
2218 call->call->value.functionCallValue = call;
2219 call->call->toKen = tokens[LSL_FUNCTION_CALL - lowestToken];
2220 call->call->basicType = func->basicType;
2221 }
2222 else
2223 sendBack(ourGlobals, compiler.client, compiler.SID, "compilerError(%d,%d,Undeclared function %s called)", call->call->line, call->call->column, call->call->value.stringValue);
2224 }
2225 }
2226 secondPass(&compiler, compiler.ast);
2227// PD("Second pass completed.");
2228 }
2229
2230 if (doConstants)
2231 {
2232 memcpy(&constants, &(compiler.script), sizeof(LSL_Script));
2233 result = TRUE;
2234 }
2235 else
2236 {
2237 result = FALSE;
2238
2239 if (compiler.ast)
2240 {
2241 FILE *out;
2242 char buffer[PATH_MAX];
2243 char outName[PATH_MAX];
2244 char luaName[PATH_MAX];
2245 int count;
2246
2247 if (LUASL_DIFF_CHECK)
2248 {
2249 strcpy(outName, compiler.fileName);
2250 strcat(outName, "2");
2251 out = fopen(outName, "w");
2252 if (out)
2253 {
2254 char diffName[PATH_MAX];
2255
2256 strcpy(diffName, compiler.fileName);
2257 strcat(diffName, ".diff");
2258 outputLeaf(out, OM_LSL, compiler.ast);
2259 fclose(out);
2260 sprintf(buffer, "diff -u \"%s\" \"%s\" > \"%s\"", compiler.fileName, outName, diffName);
2261 count = system(buffer);
2262 if (0 != count)
2263 PE("LSL output file is different - %s!", outName);
2264 else
2265 result = TRUE;
2266 }
2267 else
2268 PC("Unable to open file %s for writing!", outName);
2269 }
2270 strcpy(luaName, compiler.fileName);
2271 strcat(luaName, ".lua");
2272 out = fopen(luaName, "w");
2273 // Generate the Lua source code.
2274 if (out)
2275 {
2276 lua_State *L;
2277 int err;
2278 char *ext;
2279
2280 fprintf(out, "--// Generated code goes here.\n\n");
2281 fprintf(out, "local _bit = require(\"bit\")\n");
2282 fprintf(out, "local _LSL = require(\"LSL\")\n\n");
2283 fprintf(out, "local _SID = [=[%s]=]\n\n", compiler.SID);
2284 strcpy(buffer, basename(compiler.fileName));
2285 if ((ext = rindex(buffer, '.')))
2286 ext[0] = '\0';
2287 fprintf(out, "local _scriptName = [=[%s]=]\n\n", buffer);
2288 outputLeaf(out, OM_LUA, compiler.ast);
2289 fprintf(out, "\n\n_LSL.mainLoop(_SID, _scriptName, _defaultState)\n"); // This actually starts the script running.
2290 fprintf(out, "\n--// End of generated code.\n\n");
2291 fclose(out);
2292
2293 // Compile the Lua source code.
2294 L = luaL_newstate();
2295 luaL_openlibs(L);
2296 // This ends up pushing a function onto the stack. The function is the compiled code.
2297 err = luaL_loadfile(L, luaName);
2298 if (err)
2299 {
2300 compiler.script.bugCount++;
2301 if (LUA_ERRSYNTAX == err)
2302 PE("Lua syntax error in %s: %s", luaName, lua_tostring(L, -1));
2303 else if (LUA_ERRFILE == err)
2304 PE("Lua compile file error in %s: %s", luaName, lua_tostring(L, -1));
2305 else if (LUA_ERRMEM == err)
2306 PE("Lua compile memory allocation error in %s: %s", luaName, lua_tostring(L, -1));
2307 }
2308 else
2309 {
2310 // Write the compiled code to a file.
2311 strcat(luaName, ".out");
2312 out = fopen(luaName, "w");
2313 if (out)
2314 {
2315 err = lua_dump(L, luaWriter, out);
2316 if (err)
2317 {
2318 compiler.script.bugCount++;
2319 PE("Lua compile file error writing to %s", luaName);
2320 }
2321 fclose(out);
2322 }
2323 else
2324 PC("Unable to open file %s for writing!", luaName);
2325 }
2326 }
2327 else
2328 PC("Unable to open file %s for writing!", luaName);
2329 }
2330
2331 if (0 == compiler.script.bugCount)
2332 result = TRUE;
2333 }
2334
2335 if (NULL != compiler.file)
2336 {
2337 fclose(compiler.file);
2338 compiler.file = NULL;
2339 }
2340
2341 if (!doConstants)
2342 burnLeaf(compiler.ast);
2343
2344 return result;
2345}
diff --git a/src/LuaSL/LuaSL_lemon_yaccer.y b/src/LuaSL/LuaSL_lemon_yaccer.y
new file mode 100644
index 0000000..182789f
--- /dev/null
+++ b/src/LuaSL/LuaSL_lemon_yaccer.y
@@ -0,0 +1,275 @@
1%include
2{
3 #include "LuaSL.h"
4}
5
6%extra_argument {LuaSL_compiler *compiler}
7
8%stack_size 1024
9
10%token_type {LSL_Leaf *}
11%default_type {LSL_Leaf *}
12%token_destructor {burnLeaf($$);}
13%default_destructor {burnLeaf($$);}
14
15// The start symbol, just coz we need one.
16
17// Lemon does not like the start symbol to be on the RHS, so give it a dummy start symbol.
18program ::= script LSL_SCRIPT(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
19
20// Various forms of "space". The lexer takes care of them for us.
21
22%nonassoc LSL_SPACE LSL_COMMENT LSL_COMMENT_LINE LSL_UNKNOWN.
23
24// Basic script structure.
25
26%nonassoc LSL_SCRIPT.
27script ::= script state(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
28script ::= script functionBody(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
29script ::= script statement(A). { if (NULL != A) A->left = compiler->ast; compiler->ast = A; }
30script ::= .
31
32// State definitions.
33
34%nonassoc LSL_BLOCK_OPEN LSL_BLOCK_CLOSE LSL_STATE.
35stateBlock(A) ::= beginBlock(L) functionList(B) LSL_BLOCK_CLOSE(R). { A = addBlock(compiler, L, B, R); }
36state(A) ::= LSL_DEFAULT(I) stateBlock(B). { A = addState(compiler, NULL, I, B); }
37state(A) ::= LSL_STATE_CHANGE(S) LSL_IDENTIFIER(I) stateBlock(B). { A = addState(compiler, S, I, B); }
38
39// Function definitions.
40
41%nonassoc LSL_PARAMETER LSL_PARAMETER_LIST LSL_FUNCTION.
42functionList(A) ::= functionList(B) functionBody(C). { A = collectStatements(compiler, B, C); }
43functionList(A) ::= functionBody(C). { A = collectStatements(compiler, NULL, C); }
44// No such thing as a function list with no functions.
45//functionList(A) ::= . { A = collectStatements(compiler, NULL, NULL); }
46
47functionBody(A) ::= function(F) block(B). { A = addFunctionBody(compiler, F, B); } // addFunctionBody returns an implied addStatement(compiler, NULL, F, NULL, F, NULL, NULL);
48
49parameterList(A) ::= parameterList(B) LSL_COMMA(C) parameter(D). { A = collectParameters(compiler, B, C, D); }
50parameterList(A) ::= parameter(D). { A = collectParameters(compiler, NULL, NULL, D); }
51parameterList(A) ::= . { A = collectParameters(compiler, NULL, NULL, NULL); }
52parameter(A) ::= type(B) LSL_IDENTIFIER(C). { A = addParameter(compiler, B, C); }
53function(A) ::= LSL_IDENTIFIER(C) LSL_PARENTHESIS_OPEN(D) parameterList(E) LSL_PARENTHESIS_CLOSE(F). { A = addFunction(compiler, NULL, C, D, E, F); }
54function(A) ::= type(B) LSL_IDENTIFIER(C) LSL_PARENTHESIS_OPEN(D) parameterList(E) LSL_PARENTHESIS_CLOSE(F). { A = addFunction(compiler, B, C, D, E, F); }
55
56// Blocks.
57
58block(A) ::= beginBlock(L) statementList(B) LSL_BLOCK_CLOSE(R). { A = addBlock(compiler, L, B, R); }
59
60// Various forms of statement.
61
62%nonassoc LSL_STATEMENT.
63statementList(A) ::= statementList(B) statement(C). { A = collectStatements(compiler, B, C); }
64// This causes infinite loops (and about 150 conflicts) with - if () single statement;
65//statementList(A) ::= statement(C). { A = collectStatements(compiler, NULL, C); }
66// Yes, you can have an empty block.
67statementList(A) ::= . { A = collectStatements(compiler, NULL, NULL); }
68
69%nonassoc LSL_DO LSL_FOR LSL_IF LSL_JUMP LSL_RETURN LSL_STATE_CHANGE LSL_WHILE.
70%nonassoc LSL_ELSE LSL_ELSEIF.
71statement(A) ::= LSL_DO(F) block(B) LSL_WHILE(W) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, L, E, R, B, W); }
72statement(A) ::= LSL_FOR(F) LSL_PARENTHESIS_OPEN(L) expr(E0) LSL_STATEMENT(S0) expr(E1) LSL_STATEMENT(S1) expr(E2) LSL_PARENTHESIS_CLOSE(R) block(B). { A = addFor(compiler, NULL, F, L, E0, S0, E1, S1, E2, R, B); }
73statement(A) ::= LSL_FOR(F) LSL_PARENTHESIS_OPEN(L) expr(E0) LSL_STATEMENT(S0) expr(E1) LSL_STATEMENT(S1) expr(E2) LSL_PARENTHESIS_CLOSE(R) statement(S). { A = addFor(compiler, NULL, F, L, E0, S0, E1, S1, E2, R, S); }
74
75statement(A) ::= ifBlock(B). { A = B; }
76ifBlock(A) ::= ifBlock(B) elseIfBlock(E). { A = addIfElse(compiler, B, E); }
77ifBlock(A) ::= ifBlock(B) elseBlock(E). { A = addIfElse(compiler, B, E); }
78ifBlock(A) ::= LSL_IF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). [LSL_ELSE] { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
79ifBlock(A) ::= LSL_IF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). [LSL_ELSE] { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
80elseIfBlock(A) ::= LSL_ELSEIF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). [LSL_ELSEIF] { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
81elseIfBlock(A) ::= LSL_ELSEIF(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). [LSL_ELSEIF] { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
82elseBlock(A) ::= LSL_ELSE(F) block(B). { A = addStatement(compiler, NULL, F, NULL, NULL, NULL, B, NULL); }
83elseBlock(A) ::= LSL_ELSE(F) statement(S). { A = addStatement(compiler, NULL, F, NULL, NULL, NULL, S, NULL); }
84
85statement(A) ::= LSL_JUMP(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
86statement(A) ::= LSL_RETURN(F) expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, E, NULL, NULL, NULL); }
87statement(A) ::= LSL_RETURN(F) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, NULL); }
88statement(A) ::= LSL_STATE_CHANGE(F) LSL_DEFAULT(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
89statement(A) ::= LSL_STATE_CHANGE(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
90statement(A) ::= LSL_WHILE(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) block(B). { A = addStatement(compiler, NULL, F, L, E, R, B, NULL); }
91statement(A) ::= LSL_WHILE(F) LSL_PARENTHESIS_OPEN(L) expr(E) LSL_PARENTHESIS_CLOSE(R) statement(S). { A = addStatement(compiler, NULL, F, L, E, R, S, NULL); }
92
93%nonassoc LSL_LABEL.
94statement(A) ::= LSL_LABEL(F) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, F, NULL, NULL, NULL, NULL, I); }
95
96beginBlock(A) ::= LSL_BLOCK_OPEN(B). { A = beginBlock(compiler, B); }
97
98// This might be bogus, or might be valid LSL, but it lets us test the expression parser by evaluating them.
99statement(A) ::= expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, S, NULL, E, NULL, NULL, NULL); }
100
101// Various forms of expression.
102
103// Used for function call params, and list contents.
104exprList(A) ::= exprList(B) LSL_COMMA(C) expr(D). { A = collectArguments(compiler, B, C, D); }
105exprList(A) ::= expr(D). { A = collectArguments(compiler, NULL, NULL, D); }
106exprList(A) ::= . { A = collectArguments(compiler, NULL, NULL, NULL); }
107
108// Assignments and variable declarations.
109
110%right LSL_ASSIGNMENT_CONCATENATE LSL_ASSIGNMENT_ADD LSL_ASSIGNMENT_SUBTRACT LSL_ASSIGNMENT_MULTIPLY LSL_ASSIGNMENT_MODULO LSL_ASSIGNMENT_DIVIDE LSL_ASSIGNMENT_PLAIN.
111// Variables and dealing with them.
112
113expr(A) ::= identifier(B). { A = B; }
114
115// Yes, these can be expressions, and can happen in if statements and such. In Lua they are NOT expressions. sigh
116expr(A) ::= identifier(B) LSL_ASSIGNMENT_CONCATENATE(C) expr(D). { A = addOperation(compiler, B, C, D); }
117expr(A) ::= identifier(B) LSL_ASSIGNMENT_ADD(C) expr(D). { A = addOperation(compiler, B, C, D); }
118expr(A) ::= identifier(B) LSL_ASSIGNMENT_SUBTRACT(C) expr(D). { A = addOperation(compiler, B, C, D); }
119expr(A) ::= identifier(B) LSL_ASSIGNMENT_MULTIPLY(C) expr(D). { A = addOperation(compiler, B, C, D); }
120expr(A) ::= identifier(B) LSL_ASSIGNMENT_MODULO(C) expr(D). { A = addOperation(compiler, B, C, D); }
121expr(A) ::= identifier(B) LSL_ASSIGNMENT_DIVIDE(C) expr(D). { A = addOperation(compiler, B, C, D); }
122expr(A) ::= identifier(B) LSL_ASSIGNMENT_PLAIN(C) expr(D). { A = addOperation(compiler, B, C, D); }
123
124// Hmm think this can have commas seperating the assignment parts, or is that only in C?. If so, best to separate them when converting to Lua, as it uses that syntax for something else.
125// Well, not in OpenSim at least, nor in SL. So we are safe. B-)
126// On the other hand, it might be legal to have comma separated bits in a for loop - for ((i = 1), (j=1); ...
127statement(A) ::= type(T) LSL_IDENTIFIER(I) LSL_ASSIGNMENT_PLAIN(D) expr(E) LSL_STATEMENT(S). { A = addStatement(compiler, S, I, NULL, addVariable(compiler, T, I, D, E), NULL, NULL, I); }
128statement(A) ::= type(T) LSL_IDENTIFIER(I) LSL_STATEMENT(S). { A = addStatement(compiler, S, I, NULL, addVariable(compiler, T, I, NULL, NULL), NULL, NULL, I); }
129
130// Basic operators.
131
132%right LSL_BOOL_AND.
133expr(A) ::= expr(B) LSL_BOOL_AND(C) expr(D). { A = addOperation(compiler, B, C, D); }
134%right LSL_BOOL_OR.
135expr(A) ::= expr(B) LSL_BOOL_OR(C) expr(D). { A = addOperation(compiler, B, C, D); }
136
137%left LSL_BIT_AND LSL_BIT_XOR LSL_BIT_OR.
138expr(A) ::= expr(B) LSL_BIT_OR(C) expr(D). { A = addOperation(compiler, B, C, D); }
139expr(A) ::= expr(B) LSL_BIT_XOR(C) expr(D). { A = addOperation(compiler, B, C, D); }
140expr(A) ::= expr(B) LSL_BIT_AND(C) expr(D). { A = addOperation(compiler, B, C, D); }
141
142%right LSL_EQUAL LSL_NOT_EQUAL.
143expr(A) ::= expr(B) LSL_NOT_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
144expr(A) ::= expr(B) LSL_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
145%right LSL_LESS_THAN LSL_GREATER_THAN LSL_LESS_EQUAL LSL_GREATER_EQUAL.
146expr(A) ::= expr(B) LSL_GREATER_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
147expr(A) ::= expr(B) LSL_LESS_EQUAL(C) expr(D). { A = addOperation(compiler, B, C, D); }
148expr(A) ::= expr(B) LSL_GREATER_THAN(C) expr(D). { A = addOperation(compiler, B, C, D); }
149expr(A) ::= expr(B) LSL_LESS_THAN(C) expr(D). { A = addOperation(compiler, B, C, D); }
150
151%left LSL_LEFT_SHIFT LSL_RIGHT_SHIFT.
152expr(A) ::= expr(B) LSL_RIGHT_SHIFT(C) expr(D). { A = addOperation(compiler, B, C, D); }
153expr(A) ::= expr(B) LSL_LEFT_SHIFT(C) expr(D). { A = addOperation(compiler, B, C, D); }
154
155%left LSL_SUBTRACT LSL_ADD LSL_CONCATENATE.
156expr(A) ::= expr(B) LSL_ADD(C) expr(D). { A = addOperation(compiler, B, C, D); }
157expr(A) ::= expr(B) LSL_SUBTRACT(C) expr(D). { A = addOperation(compiler, B, C, D); }
158%left LSL_DIVIDE LSL_MODULO LSL_MULTIPLY LSL_DOT_PRODUCT LSL_CROSS_PRODUCT.
159expr(A) ::= expr(B) LSL_MULTIPLY(C) expr(D). { A = addOperation(compiler, B, C, D); }
160expr(A) ::= expr(B) LSL_MODULO(C) expr(D). { A = addOperation(compiler, B, C, D); }
161expr(A) ::= expr(B) LSL_DIVIDE(C) expr(D). { A = addOperation(compiler, B, C, D); }
162
163%right LSL_BIT_NOT LSL_BOOL_NOT LSL_NEGATION.
164expr(A) ::= LSL_BIT_NOT(B) expr(C). { A = addOperation(compiler, NULL, B, C); }
165expr(A) ::= LSL_BOOL_NOT(B) expr(C). { A = addOperation(compiler, NULL, B, C); }
166expr(A) ::= LSL_SUBTRACT(B) expr(C). [LSL_NEGATION] { A = addOperation(compiler, NULL, B, C); }
167
168// Types, typecasts, and expression reordering.
169
170%right LSL_TYPECAST_OPEN LSL_TYPECAST_CLOSE.
171%nonassoc LSL_TYPE_FLOAT LSL_TYPE_INTEGER LSL_TYPE_KEY LSL_TYPE_LIST LSL_TYPE_ROTATION LSL_TYPE_STRING LSL_TYPE_VECTOR.
172type(A) ::= LSL_TYPE_FLOAT(B). { B->basicType = OT_float; A = B; }
173type(A) ::= LSL_TYPE_INTEGER(B). { B->basicType = OT_integer; A = B; }
174type(A) ::= LSL_TYPE_KEY(B). { B->basicType = OT_key; A = B; }
175type(A) ::= LSL_TYPE_LIST(B). { B->basicType = OT_list; A = B; }
176type(A) ::= LSL_TYPE_ROTATION(B). { B->basicType = OT_rotation; A = B; }
177type(A) ::= LSL_TYPE_STRING(B). { B->basicType = OT_string; A = B; }
178type(A) ::= LSL_TYPE_VECTOR(B). { B->basicType = OT_vector; A = B; }
179
180%left LSL_ANGLE_OPEN LSL_ANGLE_CLOSE.
181%nonassoc LSL_BRACKET_OPEN LSL_BRACKET_CLOSE.
182%nonassoc LSL_PARENTHESIS_OPEN LSL_PARENTHESIS_CLOSE LSL_EXPRESSION.
183
184expr(A) ::= LSL_PARENTHESIS_OPEN(B) expr(C) LSL_PARENTHESIS_CLOSE(D). { A = addParenthesis(B, C, LSL_EXPRESSION, D); }
185expr(A) ::= LSL_PARENTHESIS_OPEN(B) type(C) LSL_PARENTHESIS_CLOSE(D) expr(E). { A = addTypecast(B, C, D, E); }
186
187%right LSL_DOT LSL_IDENTIFIER LSL_FUNCTION_CALL LSL_VARIABLE.
188identifier(A) ::= identifier(I) LSL_DOT(D) LSL_IDENTIFIER(B). { A = checkVariable(compiler, I, D, B); }
189identifier(A) ::= LSL_IDENTIFIER(B). { A = checkVariable(compiler, B, NULL, NULL); }
190
191%right LSL_DECREMENT_PRE LSL_INCREMENT_PRE LSL_DECREMENT_POST LSL_INCREMENT_POST.
192expr(A) ::= identifier(B) LSL_DECREMENT_PRE(C). { A = addCrement(compiler, B, C, LSL_DECREMENT_POST); }
193expr(A) ::= identifier(B) LSL_INCREMENT_PRE(C). { A = addCrement(compiler, B, C, LSL_INCREMENT_POST); }
194expr(A) ::= LSL_DECREMENT_PRE(C) identifier(B). { A = addCrement(compiler, B, C, LSL_DECREMENT_PRE); }
195expr(A) ::= LSL_INCREMENT_PRE(C) identifier(B). { A = addCrement(compiler, B, C, LSL_INCREMENT_PRE); }
196
197// Function call.
198
199// Causes a conflict when exprList is empty with a function definition with no type and no parameters.
200expr(A) ::= LSL_IDENTIFIER(B) LSL_PARENTHESIS_OPEN(C) exprList(D) LSL_PARENTHESIS_CLOSE(E). { A = addFunctionCall(compiler, B, C, D, E); }
201
202%nonassoc LSL_COMMA.
203
204// Values.
205
206%nonassoc LSL_FLOAT.
207expr(A) ::= LSL_FLOAT(B). { A = addNumby(B); }
208%nonassoc LSL_INTEGER.
209expr(A) ::= LSL_INTEGER(B). { A = addNumby(B); }
210%nonassoc LSL_KEY.
211expr(A) ::= LSL_KEY(B). { B->basicType = OT_key; A = B; }
212%nonassoc LSL_LIST.
213expr(A) ::= LSL_BRACKET_OPEN(L) exprList(E) LSL_BRACKET_CLOSE(R). [LSL_BRACKET_OPEN] { A = addList(L, E, R); }
214%nonassoc LSL_ROTATION LSL_VECTOR.
215// Uses the same symbol for less than, greater than, and the rotation / vector delimiters.
216expr(A) ::= LSL_LESS_THAN(L) exprList(E) LSL_GREATER_THAN(R). [LSL_ANGLE_OPEN] { A = addRotVec(L, E, R); }
217%nonassoc LSL_STRING.
218expr(A) ::= LSL_STRING(B). { B->basicType = OT_string; A = B; }
219
220
221// Parser callbacks.
222
223%parse_accept
224{
225// gameGlobals *ourGlobals = compiler->game;
226
227// PI("Parsing complete.");
228}
229
230%parse_failure
231{
232 gameGlobals *ourGlobals = compiler->game;
233
234 compiler->script.bugCount++;
235 PE("Giving up. Parser is hopelessly lost!");
236}
237
238%stack_overflow
239{
240 gameGlobals *ourGlobals = compiler->game;
241
242 compiler->script.bugCount++;
243 PE("Giving up. Parser stack overflow @ line %d, column %d!", yypMinor->yy0->line, yypMinor->yy0->column); // Gotta love consistancy, if it ever happens.
244}
245
246%syntax_error
247{
248 gameGlobals *ourGlobals = compiler->game;
249
250 compiler->script.bugCount++;
251 PE("Syntax error @ line %d, column %d!", yyminor.yy0->line, yyminor.yy0->column);
252}
253
254
255/* Undocumented shit that might be useful later. Pffft
256
257** The next table maps tokens into fallback tokens. If a construct
258** like the following:
259**.
260** %fallback ID X Y Z.
261**
262** appears in the grammar, then ID becomes a fallback token for X, Y,
263** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
264** but it does not parse, the type of the token is changed to ID and
265** the parse is retried before an error is thrown.
266
267%wildcard
268%code
269
270%ifdef
271%endif
272%ifndef
273%endif
274
275*/
diff --git a/src/LuaSL/LuaSL_lexer.l b/src/LuaSL/LuaSL_lexer.l
new file mode 100644
index 0000000..85b2821
--- /dev/null
+++ b/src/LuaSL/LuaSL_lexer.l
@@ -0,0 +1,164 @@
1%{
2
3#define excludeLexer
4#include "LuaSL.h"
5
6int common(YYSTYPE *lval, char *text, int len, LuaSL_compiler *compiler, boolean checkIgnorable, int type);
7
8%}
9
10%option reentrant never-interactive batch
11%option bison-bridge 8bit
12%option noreject noyymore
13%option backup debug perf-report perf-report verbose warn
14%option align full
15%option extra-type="LuaSL_compiler *"
16
17HEX [[:xdigit:]]
18DECIMAL [[:digit:]]
19 /* LSL has no octal integer type. */
20INTEGER ({DECIMAL}+)|(0[xX]{HEX}+)
21EXPONANT [eE][+-]?{DECIMAL}+
22 /* Floats can be "0." or".0", but "." is not valid. At least in OpenSim. A single dot should be caught by the LSL_Dot rule first anyway.*/
23FLOAT {DECIMAL}*"."{DECIMAL}*{EXPONANT}?[fF]?
24 /* Variable identifiers can have these extra characters at the end or beginning, they will be ignored- #$`'\? */
25 /* Function identifiers can start with $ */
26IDENTIFIER (_|[[:alpha:]])(_|[[:alpha:]]|[[:digit:]])*
27CHAR '(\\.|[^\\'\n])+'
28KEY \"{HEX}{8}-{HEX}{4}-{HEX}{4}-{HEX}{4}-{HEX}{12}\"
29STRING \"(\\.|[^\\"\n])*\"
30
31%%
32
33 /* The order here is important, in mysterious ways. The more specific the lower in case of ambiguities like "floats contain integers". I think, not tested that well yet. */
34
35 /* Ignorables. */
36[[:space:]]+ %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_SPACE); %}
37 /* Yes I know this will have problems with huge comments, just being simple to get it to work for now. */
38"/*"([^"*/"]*)"*/" %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_COMMENT); %}
39"//"[^\n]* %{ common(yylval, yytext, yyleng, yyextra, FALSE, LSL_COMMENT_LINE); %}
40
41 /* Operations. */
42"&&" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_AND); }
43"||" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_OR); }
44"|" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_OR); }
45"^" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_XOR); }
46"&" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_AND); }
47"!=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_NOT_EQUAL); }
48"==" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_EQUAL); }
49">=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_GREATER_EQUAL); }
50"<=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LESS_EQUAL); }
51">" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_GREATER_THAN); }
52"<" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LESS_THAN); }
53">>" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_RIGHT_SHIFT); }
54"<<" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LEFT_SHIFT); }
55"+" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ADD); }
56"-" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_SUBTRACT); }
57"*" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_MULTIPLY); }
58"%" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_MODULO); }
59"/" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DIVIDE); }
60"!" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BOOL_NOT); }
61"~" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BIT_NOT); }
62"[" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BRACKET_OPEN); }
63"]" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BRACKET_CLOSE); }
64"(" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_PARENTHESIS_OPEN); }
65")" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_PARENTHESIS_CLOSE); }
66"+=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_ADD); }
67"-=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_SUBTRACT); }
68"*=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_MULTIPLY); }
69"%=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_MODULO); }
70"/=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_DIVIDE); }
71"=" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ASSIGNMENT_PLAIN); }
72"." { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DOT); }
73"--" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DECREMENT_PRE); }
74"++" { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_INCREMENT_PRE); }
75"," { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_COMMA); }
76
77 /* Other symbols. */
78"@" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_LABEL); %}
79"{" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BLOCK_OPEN); %}
80"}" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_BLOCK_CLOSE); %}
81";" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STATEMENT); %}
82
83 /* Type keywords. */
84"float" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_FLOAT); %}
85"integer" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_INTEGER); %}
86"key" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_KEY); %}
87"list" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_LIST); %}
88"quaternion" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_ROTATION); %}
89"rotation" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_ROTATION); %}
90"string" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_STRING); %}
91"vector" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_TYPE_VECTOR); %}
92
93 /* Statement keywords. */
94"default" %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DEFAULT); %}
95"do" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_DO); %}
96"for" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_FOR); %}
97"else" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ELSE); %}
98"if" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_IF); %}
99"else"[[:space:]]+"if" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_ELSEIF); %} /* TODO - deal with people that slap a comment in between them. */
100"jump" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_JUMP); %}
101"return" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_RETURN); %}
102"state" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STATE_CHANGE); %}
103"while" %{ return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_WHILE); %}
104
105{IDENTIFIER} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_IDENTIFIER); %}
106
107 /* Types. */
108{INTEGER} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_INTEGER); %}
109{FLOAT} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_FLOAT); %}
110{KEY} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_KEY); %}
111{STRING} %{ yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_STRING); %}
112
113<<EOF>> { return common(yylval, yytext, yyleng, yyextra, TRUE, LSL_SCRIPT); }
114
115 /* Everything else */
116. %{ printf(" unexpected character.\n"); yylval->value.stringValue = eina_stringshare_add_length(yytext, yyleng); common(yylval, yytext, yyleng, yyextra, TRUE, LSL_UNKNOWN); %}
117
118%%
119
120int common(YYSTYPE *lval, char *text, int len, LuaSL_compiler *compiler, boolean checkIgnorable, int type)
121{
122 char *p;
123
124 lval->toKen = tokens[type - lowestToken];
125 lval->line = compiler->line;
126 lval->column = compiler->column;
127 lval->len = len;
128
129 for (p = text; *p; p++)
130 {
131 if ('\n' == *p)
132 {
133 compiler->line++;
134 compiler->column = 1;
135 }
136 else
137 compiler->column++;
138 }
139
140#if LUASL_DIFF_CHECK
141 if (checkIgnorable)
142 {
143 lval->ignorable = compiler->ignorable;
144 compiler->ignorable = eina_strbuf_new();
145 }
146 else
147 eina_strbuf_append_length(compiler->ignorable, text, len);
148#endif
149
150 return type;
151}
152
153int yywrap(yyscan_t yyscanner) // This as actually useless for our needs, as it is called BEFORE the last token is dealt with.
154{
155#ifdef FLEX_SCANNER
156 #ifndef LL_WINDOWS
157 // Get gcc to stop complaining about lack of use of yyunput and input.
158 (void) yyunput;
159 (void) input;
160 #endif
161#endif
162
163 return 1;
164}
diff --git a/src/LuaSL/LuaSL_main.c b/src/LuaSL/LuaSL_main.c
new file mode 100644
index 0000000..0a40712
--- /dev/null
+++ b/src/LuaSL/LuaSL_main.c
@@ -0,0 +1,708 @@
1
2#include "LuaSL.h"
3
4
5static int CPUs = 4;
6static Eina_Strbuf *clientStream;
7
8
9static Eina_Bool _sleep_timer_cb(void *data)
10{
11 script *script = data;
12 gameGlobals *ourGlobals = script->game;
13
14 PD("Waking up %s", script->SID);
15 sendToChannel(ourGlobals, script->SID, "return 0.0");
16 return ECORE_CALLBACK_CANCEL;
17}
18
19static Eina_Bool _timer_timer_cb(void *data)
20{
21 script *script = data;
22 gameGlobals *ourGlobals = script->game;
23
24 PD("Timer for %s", script->SID);
25 sendToChannel(ourGlobals, script->SID, "events.timer()");
26 return ECORE_CALLBACK_RENEW;
27}
28
29static script *findThem(gameGlobals *ourGlobals, const char *base, const char *text)
30{
31 char name[PATH_MAX];
32 char *temp;
33
34 strncpy(name, base, PATH_MAX);
35 if ((temp = rindex(name, '/')))
36 temp[1] = '\0';
37 strcat(name, text);
38 if ((temp = rindex(name, '"')))
39 temp[0] = '\0';
40 strcat(name, ".lsl");
41 return eina_hash_find(ourGlobals->names, name);
42}
43
44static void resetScript(script *victim)
45{
46 gameGlobals *ourGlobals = victim->game;
47
48 PD("Resetting %s", victim->fileName);
49 // TODO - now what?
50}
51
52void scriptSendBack(void * data)
53{
54 scriptMessage *message = data;
55 gameGlobals *ourGlobals = message->script->game;
56
57 if (0 == strncmp(message->message, "llSleep(", 8))
58 ecore_timer_add(atof(&(message->message)[8]), _sleep_timer_cb, message->script);
59 else if (0 == strncmp(message->message, "llSetTimerEvent(", 16))
60 {
61 message->script->timerTime = atof(&(message->message)[16]);
62 if (0.0 == message->script->timerTime)
63 {
64 if (message->script->timer)
65 ecore_timer_del(message->script->timer);
66 message->script->timer = NULL;
67 }
68 else
69 message->script->timer = ecore_timer_add(message->script->timerTime, _timer_timer_cb, message->script);
70 }
71 else if (0 == strncmp(message->message, "llSetScriptState(", 17))
72 {
73 script *them;
74
75 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[18]))))
76 {
77 char *temp = rindex(&(message->message[18]), ',');
78
79 if (temp)
80 {
81 temp++;
82 while (isspace(*temp))
83 temp++;
84 if ('1' == *temp)
85 sendToChannel(ourGlobals, them->SID, "start()");
86 else
87 sendToChannel(ourGlobals, them->SID, "stop()");
88 PD("Stopped %s", them->fileName);
89 }
90 else
91 PE("Missing script state in llSetScriptState(%s, )", them->fileName);
92 }
93 else
94 {
95 char *temp = rindex(&(message->message[18]), '"');
96
97 if (temp)
98 *temp = '\0';
99 PE("Can't stop script, can't find %s", &(message->message[18]));
100 }
101 }
102 else if (0 == strncmp(message->message, "llResetOtherScript(", 19))
103 {
104 script *them;
105
106 if ((them = findThem(ourGlobals, message->script->fileName, &(message->message[20]))))
107 resetScript(them);
108 }
109 else if (0 == strncmp(message->message, "llResetScript(", 14))
110 resetScript(message->script);
111 else
112 sendBack(ourGlobals, message->script->client, message->script->SID, message->message);
113 free(message);
114}
115
116static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Add *ev)
117{
118 ecore_con_client_timeout_set(ev->client, 0);
119 return ECORE_CALLBACK_RENEW;
120}
121
122static Eina_Bool _data(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Data *ev)
123{
124 gameGlobals *ourGlobals = data;
125 char buf[PATH_MAX];
126 char SID[PATH_MAX];
127 const char *command;
128 char *ext;
129
130 eina_strbuf_append_length(clientStream, ev->data, ev->size);
131 command = eina_strbuf_string_get(clientStream);
132 while ((ext = index(command, '\n')))
133 {
134 int length = ext - command;
135
136 strncpy(SID, command, length + 1);
137 SID[length] = '\0';
138 eina_strbuf_remove(clientStream, 0, length + 1);
139 ext = index(SID, '.');
140 if (ext)
141 {
142 ext[0] = '\0';
143 command = ext + 1;
144 if (0 == strncmp(command, "compile(", 8))
145 {
146 char *temp;
147 char *file;
148
149 strcpy(buf, &command[8]);
150 temp = buf;
151 file = temp;
152 while (')' != temp[0])
153 temp++;
154 temp[0] = '\0';
155
156 PD("Compiling %s, %s.", SID, file);
157 if (compileLSL(ourGlobals, ev->client, SID, file, FALSE))
158 {
159 script *me = calloc(1, sizeof(script));
160
161 gettimeofday(&me->startTime, NULL);
162 strncpy(me->SID, SID, sizeof(me->SID));
163 strncpy(me->fileName, file, sizeof(me->fileName));
164 me->game = ourGlobals;
165 me->client = ev->client;
166 eina_hash_add(ourGlobals->scripts, me->SID, me);
167 eina_hash_add(ourGlobals->names, me->fileName, me);
168 sendBack(ourGlobals, ev->client, SID, "compiled(true)");
169 }
170 else
171 sendBack(ourGlobals, ev->client, SID, "compiled(false)");
172 }
173 else if (0 == strcmp(command, "run()"))
174 {
175 script *me;
176 char buf[PATH_MAX];
177
178 me = eina_hash_find(ourGlobals->scripts, SID);
179 if (me)
180 {
181 sprintf(buf, "%s.lua.out", me->fileName);
182 newProc(buf, TRUE, me);
183 }
184 }
185 else if (0 == strcmp(command, "exit()"))
186 {
187 PD("Told to exit.");
188 ecore_main_loop_quit();
189 }
190 else
191 {
192 const char *status = NULL;
193
194 status = sendToChannel(ourGlobals, SID, command);
195 if (status)
196 PE("Error sending command %s to script %s : %s", command, SID, status);
197 }
198 }
199
200 // Get the next blob to check it.
201 command = eina_strbuf_string_get(clientStream);
202 }
203
204 return ECORE_CALLBACK_RENEW;
205}
206
207static Eina_Bool _del(void *data, int type __UNUSED__, Ecore_Con_Event_Client_Del *ev)
208{
209 gameGlobals *ourGlobals = data;
210
211 if (ev->client)
212 {
213 PD("No more clients, exiting.");
214 ecore_con_client_del(ev->client);
215 ecore_main_loop_quit();
216 }
217 return ECORE_CALLBACK_RENEW;
218}
219
220int main(int argc, char **argv)
221{
222 gameGlobals ourGlobals;
223 int result = EXIT_FAILURE;
224
225 memset(&ourGlobals, 0, sizeof(gameGlobals));
226 ourGlobals.address = "127.0.0.1";
227 ourGlobals.port = 8211;
228
229 if (eina_init())
230 {
231 ourGlobals.logDom = loggingStartup("LuaSL", ourGlobals.logDom);
232 ourGlobals.scripts = eina_hash_string_superfast_new(NULL);
233 ourGlobals.names = eina_hash_string_superfast_new(NULL);
234 if (ecore_con_init())
235 {
236 if (ecore_con_init())
237 {
238 if ((ourGlobals.server = ecore_con_server_add(ECORE_CON_REMOTE_TCP, ourGlobals.address, ourGlobals.port, &ourGlobals)))
239 {
240 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb) _add, &ourGlobals);
241 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb) _data, &ourGlobals);
242 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb) _del, &ourGlobals);
243 ecore_con_server_timeout_set(ourGlobals.server, 0);
244 ecore_con_server_client_limit_set(ourGlobals.server, -1, 0);
245 ecore_con_server_timeout_set(ourGlobals.server, 10);
246 ecore_con_server_client_limit_set(ourGlobals.server, 3, 0);
247 clientStream = eina_strbuf_new();
248
249 if (edje_init())
250 {
251 int i;
252
253 result = 0;
254 compilerSetup(&ourGlobals);
255 luaprocInit();
256 for (i = 0; i < CPUs; i++)
257 {
258 if ( sched_create_worker( ) != LUAPROC_SCHED_OK )
259 PEm("Error creating luaproc worker thread.");
260 }
261 ecore_main_loop_begin();
262
263 // TODO - this is what hangs the system, should change from raw pthreads to ecore threads.
264 sched_join_workerthreads();
265 edje_shutdown();
266 }
267 else
268 PCm("Failed to init edje!");
269 }
270 else
271 PCm("Failed to add server!");
272 ecore_con_shutdown();
273 }
274 else
275 PCm("Failed to init ecore_con!");
276 ecore_shutdown();
277 }
278 else
279 PCm("Failed to init ecore!");
280 }
281 else
282 fprintf(stderr, "Failed to init eina!");
283
284 return result;
285}
286
287
288/* Stuff to be merged in later.
289
290#ifdef _WIN32
291# define FMT_SIZE_T "%Iu"
292#else
293# define FMT_SIZE_T "%zu"
294#endif
295
296#define MAX_LUA_MEM (64 * 1024)) // LSL Mono is 64KB, edje Lua is 4MB. (4 * (1024 * 1024))
297
298#define _edje_lua2_error(L, err_code) _edje_lua2_error_full(__FILE__, __FUNCTION__, __LINE__, L, err_code)
299
300
301typedef struct _Edje_Lua_Alloc Edje_Lua_Alloc;
302
303struct _Edje_Lua_Alloc
304{
305 size_t max, cur;
306};
307
308static void *
309_elua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
310{
311 Edje_Lua_Alloc *ela = ud;
312 void *ptr2 = NULL;
313
314 if (ela)
315 {
316 ela->cur += nsize - osize;
317 if (ela->cur > ela->max)
318 {
319 printf("Lua memory limit of " FMT_SIZE_T " bytes reached (" FMT_SIZE_T " allocated)", ela->max, ela->cur);
320 }
321 else if (nsize == 0)
322 {
323 free(ptr);
324 }
325 else
326 {
327 ptr2 = realloc(ptr, nsize);
328 if (NULL == ptr2)
329 printf("Lua cannot re-allocate " FMT_SIZE_T " bytes", nsize);
330 }
331 }
332 else
333 printf("Lua cannoct allocate memory, no Edje_Lua_Alloc");
334
335 return ptr2;
336}
337
338static int panics = 0;
339static int
340_elua_custom_panic(lua_State *L) // Stack usage [-0, +0, m]
341{
342 // If we somehow manage to have multiple panics, it's likely due to being out
343 // of memory in the following lua_tostring() call.
344 panics++;
345 if (panics)
346 {
347 printf("Lua PANICS!!!!!");
348 }
349 else
350 {
351 printf("Lua PANIC!!!!!: %s", lua_tostring(L, -1)); // Stack usage [-0, +0, m]
352 }
353 // The docs say that this will cause an exit(EXIT_FAILURE) if we return,
354 // and that we we should long jump some where to avoid that. This is only
355 // called for things not called from a protected environment. We always
356 // use pcalls though, except for the library load calls. If we can't load
357 // the standard libraries, then perhaps a crash is the right thing.
358 return 0;
359}
360
361static void
362_edje_lua2_error_full(const char *file, const char *fnc, int line,
363 lua_State *L, int err_code) // Stack usage [-0, +0, m]
364{
365 const char *err_type;
366
367 switch (err_code)
368 {
369 case LUA_ERRRUN:
370 err_type = "runtime";
371 break;
372 case LUA_ERRSYNTAX:
373 err_type = "syntax";
374 break;
375 case LUA_ERRMEM:
376 err_type = "memory allocation";
377 break;
378 case LUA_ERRERR:
379 err_type = "error handler";
380 break;
381 default:
382 err_type = "unknown";
383 break;
384 }
385 printf("Lua %s error: %s\n", err_type, lua_tostring(L, -1)); // Stack usage [-0, +0, m]
386}
387
388static int errFunc(lua_State *L)
389{
390 int i = 0;
391 lua_Debug ar;
392
393 while (lua_getstack(L, i++, &ar))
394 {
395 if (lua_getinfo(L, "nSlu", &ar))
396 {
397 if (NULL == ar.name)
398 ar.name = "DUNNO";
399 printf("Lua error in the %s %s %s @ line %d in %s\n%s!", ar.what, ar.namewhat, ar.name, ar.currentline, ar.short_src, ar.source);
400 }
401 }
402 return 0;
403}
404
405void runLuaFile(gameGlobals *ourGlobals, const char *filename)
406{
407 PD("Starting %s", filename);
408 newProc(filename, TRUE);
409
410// TODO, should set up our panic and errfunc as below. Plus the other TODO stuff.
411
412
413// TODO - hack up LuaJIT so that we can limit memory per state.
414// lua_setallocf(L, _elua_alloc, &ela); // LuaJIT uses a heavily hacked up dlmalloc. Seems that standard realloc is not so thread safe?
415 lua_atpanic(L, _elua_custom_panic);
416// TODO - Sandbox out what this opens. See lib_init.c from LuaJIT.
417// Just noticed this in the LuaJIT docs - "To change or extend the list of standard libraries to load, copy src/lib_init.c to your project and modify it accordingly. Make sure the jit library is loaded or the JIT compiler will not be activated."
418 luaL_openlibs(L);
419
420 lua_pushcfunction(L, errFunc);
421 ...
422 if ((err = lua_pcall(L, 0, 0, -2)))
423 _edje_lua2_error(L, err);
424}
425*/
426
427
428
429
430/* What we need to do, and how we might do it.
431 *
432 * Get configuration info.
433 *
434 * Some of the configuration info we need is tucked away in OpenSim .ini files. So we have to read that.
435 * Eet is probably not so useful here, as we would have to write a UI tool, and there's no UI otherwise.
436 *
437 * Multi task!
438 *
439 * Luaproc starts up X worker threads. Each one takes the next ready Lua state and runs it until it blocks waiting for a channel message, or yields.
440 * The current mainloop waits for events and commands from the message channel, executes them, then goes back to waiting.
441 * So that is fine in general, so long as the LSL event handlers actually return in a reasonable time.
442 * We need to be able to yield at suitable places, or be able to force a yield. Both without slowing things down too much.
443 *
444 * Watchdog thread.
445 *
446 * It's easy enough to have a watchdog thread wake up every now and then to check if any given Lua state has been hogging it's CPU for too long.
447 * The hard part is forcing Lua states to yield cleanly, without slowing performance too much.
448 *
449 * Identifying scripts. - OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
450 *
451 * We need to be able to identify scripts so that messages can get to the right ones. Also the objects they are in.
452 * And do it all in a way that OpenSim is happy with.
453 * Copies of the same script in the same prim would have the same asset UUID, but different name, though they would run independently.
454 * Copies of the same script in different prims could have the same asset UUID AND the same name.
455 * OpenSim seems to be using a UUID to identify single scripts, and a uint to identify single prims, when sending events for either case.
456 * The UUID is from the script item in the prims inventory.
457 * There is also an asset UUID (the one printed out on the console at script startup time) that points to the source code in the prim.
458 * Which will be identical to the asset UUID for the multiple copies of the same script.
459 *
460 * script assetID
461 * UUID of the script source in the grids asset database, also the script source in the prim.
462 *
463 * script itemID
464 * UUID of this instance of the running script.
465 * UUID of the scripts binary in the prims inventory.
466 * This is the one used to identify the running script.
467 *
468 * prim uint localID
469 * Some sort of number representing the prim the script is running in.
470 * Events are sometimes sent to this.
471 *
472 * path/filename
473 * An invention of LuaSL, coz we store stuff as files.
474 *
475 * OpenSim says "compile this assetID for this itemID, in this prim uint"
476 * Current infrastructure does not allow easy sending of the script source, but we don't have ROBUST code to get it either.
477 * ROBUST is the way to go though, coz we can sneakily start to suck other stuff, like prim contents across when needed.
478 * Though that sort of thing needs access to the local sim databases to lookup the prim and it's other contents. sigh
479 * I think that new script and notecard contents get new assetIDs anyway, so keeping an eye on assets.create_time or asset_access_time wont help much.
480 *
481 * OpenSim says "start / stop this itemID"
482 * Already catered for.
483 *
484 * What does OpenSim REALLY do?
485 *
486 * Region/Framework/Scenes/Scene.Inventory.cs - CapsUpdateTaskInventoryScriptAsset(IClientAPI remoteClient, UUID itemId, UUID primId, bool isScriptRunning, byte[] data)
487 * remoteClient
488 * itemID - UUID of the script source.
489 * primID - UUID of the prim it is in.
490 * isScriptRunning
491 * data - the script source code.
492 * Called when a user saves the script. itemID stays the same, but we get a new assetID, for the new source code asset.
493 * Looks up the item in the prim.
494 * AssetBase asset = CreateAsset(item.Name, item.Description, (sbyte)AssetType.LSLText, data, remoteClient.AgentId);
495 * AssetService.Store(asset);
496 * stashes the new assetID in the item
497 * updates the item in the prim
498 * if (isScriptRunning)
499 * part.Inventory.RemoveScriptInstance(item.ItemID, false);
500 * part.Inventory.CreateScriptInstance(item.ItemID, 0, false, DefaultScriptEngine, 0);
501 * errors = part.Inventory.GetScriptErrors(item.ItemID);
502 *
503 * CreateScriptInstance() is generally called to start scripts, part.ParentGroup.ResumeScripts(); is usually called after CreateScriptInstance()
504 *
505 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(UUID itemId, int startParam, bool postOnRez, string engine, int stateSource)
506 * looks up the itemID, then calls the real one -
507 * Region/Framework/Scenes/SceneObjectPartInventory.cs - CreateScriptInstance(TaskInventoryItem item, int startParam, bool postOnRez, string engine, int stateSource)
508 * get the asset from the asset database using the items assetID
509 * restores script state if needed
510 * converts asset.data to a string called script
511 * m_part.ParentGroup.Scene.EventManager.TriggerRezScript(m_part.LocalId, item.ItemID, script, startParam, postOnRez, engine, stateSource);
512 * QUIRK - if it's a sim border crossing, TriggerRezScript() gets called with empty script source.
513 *
514 * Region/ScriptEngine/XEngine/XEngine.cs - AddRegion(Scene scene)
515 * m_log.InfoFormat("[XEngine] Initializing scripts in region {0}", scene.RegionInfo.RegionName);
516 * gets the script config info, which is the same damn stuff for each sim. Pffft
517 * Think it relies on the scenes event manager to call OnRezScript() -
518 * m_Scene.EventManager.OnRezScript += OnRezScript;
519 *
520 * Region/Framework/Scenes/EventManager.cs - TriggerRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
521 * Loops through Scene.EventManager.OnRezScript calling them.
522 *
523 * Region/ScriptEngine/XEngine/XEngine.cs - OnRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
524 * Looks at the script source to figure out if it's an XEngine script.
525 * Either queues the script for later, or does it direct.
526 * Region/ScriptEngine/XEngine/XEngine.cs - DoOnRezScript() is passed an array holding -
527 * localID is a uint that represents the containing prim in the current scene
528 * itemID is the UUID of the script in the prims contents
529 * script is the script source code.
530 * startParam is the scripts startParam
531 * postOnRez
532 * stateSource is an integer saying how we where started, used to trigger the appropriate startup events.
533 * uses localID to look up the prim in the scene, then looks inside that for the itemID to find the assetID.
534 * m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assembly, out linemap);
535 * Which is in Region/ScriptEngine/Shared/CodeTools/Compiler.cs
536 * instance = new ScriptInstance(this, part, itemID, assetID, assembly, m_AppDomains[appDomain], part.ParentGroup.RootPart.Name, item.Name, startParam, postOnRez, stateSource, m_MaxScriptQueue);
537 * Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - ScriptInstance(IScriptEngine engine, SceneObjectPart part, UUID itemID, UUID assetID, string assembly, AppDomain dom, string primName, string scriptName, int startParam, bool postOnRez, StateSource stateSource, int maxScriptQueue)
538 * inits all the APIs
539 * loads in any saved state if it can find one
540 * m_log.DebugFormat("[XEngine] Loaded script {0}.{1}, script UUID {2}, prim UUID {3} @ {4}.{5}", part.ParentGroup.RootPart.Name, item.Name, assetID, part.UUID, part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
541 *
542 * Soooo, when a script is saved -
543 * the new source is saved in the asset database
544 * The script item in the prim gets the new assetID
545 * if the script is running -
546 * remove the old script instance (item.ItemID)
547 * create a new one (item.ItemID)
548 * get the source code from the asset database (item.assetID)
549 * restore script state
550 * TriggerRezOnScript()
551 * Loop through all those that are interested, incuding XEngine.onRezScript()
552 *** check the first line to see if it's an XEngine script
553 * sooner or later passes it to XEngine.DoOnRezScript()
554 * looks up localID to get the prim
555 * looks inside prim to get the script from itemID
556 * gets the assetID from the script item
557 * compiles the script
558 * creates the script instance
559 * loads up the APIs
560 * restores any script state
561 * calls instance.Init() which is Region/ScriptEngine/Shared/Instance/ScriptInstance.cs - Init()
562 * passes the usual startup events to the script.
563 * part.ParentGroup.ResumeScripts()
564 *
565 * At the *** marked point, LuaSL.onRezScript should -
566 * check the first line to see if it's an LuaSL script
567 * looks up localID to get the prim
568 * looks inside prim to get the script from itemID
569 * gets the assetID from the script item
570 * filename encode the sim name, object name, and script name
571 * replace anything less than 0x21, DEL " * / : < > ? \ | + [ ] - , . ( ) $ % # @ from - http://en.wikipedia.org/wiki/Filename plus a few more
572 * THEN reduce to 254 characters
573 * NOTE the object names might be identical, disambiguate them.
574 * write the script to a file - /script/engine/path/sim_name/objects/object_name/script_name
575 * send the itemID.compile(/script/engine/path/sim_name/objects/object_name/script_name) message to the script engine's socket
576 *
577 *
578 * Object inventory "cache".
579 *
580 * This code currently pretends that there is a local file based sim object store available.
581 * I think it would be a good idea to abuse the OpenSim cache system to produce that file based object store.
582 * It will help with the "damn OpenSim's asset database has to be a bottomless pit" monster design flaw.
583 * Prim contents must all be unique names anyway, and there are SOME constraints on contents names, so probably don't have to do much to convert an item name to a legal file name.
584 * Oops, names can have directory slashes in them. lol
585 * On the other hand, sim objects CAN have the same name.
586 *
587 * So we got sim directories, with an objects directory inside it, with object directories inside that. The object directories have object files in them. This is all like the test setup that is here.
588 * We need metadata. Sim metadata, object metadata, and object contents metadata. That can be done with a "foo.omg" file at each level.
589 * sim/index.omg - the list of object name.UUIDs, their X,Y,Z location, size, and rotation.
590 * sim/objects/objectName_UUID/index.omg - the list of contents names, item UUIDs, asset UUIDs, and types.
591 * sim/objects/objectName/subObjectName - the list of ITS contents names, item UUIDs, asset UUIDs, and types.
592 *
593 * Script start, stop, reset. - OpenSim/Region/ScriptEngine/Interfaces/IScriptEngine.cs
594 *
595 * Scripts and users have to be able to start, stop, and reset scripts.
596 * Users have to be able to see the start / stopped status of scripts from the viewer.
597 * Which means if the script does it, we have to tell OpenSim.
598 * Naturally, if OpenSim does it, it has to tell us.
599 * Should be able to do both by passing textual Lua function calls between OpenSim and LuaSL.
600 *
601 * Event handling, llDetect*() functions.
602 *
603 * OpenSim will generate events at random times and send us relevant information for llDetect*() functions and the handler calls.
604 * These should come through the scripts main loop eventually.
605 *
606 * Send messages from / to OpenSim and ROBUST.
607 *
608 * ROBUST uses HTTP for the communications, and some sort of XML, probably XML-RPC.
609 * OpenSim has some sort of generic mechanism for talking to script engines in C#. I want to turn that into a socket based, pass textual Lua function calls, type system.
610 * That assumes C# has some sort of semi decent introspection or reflection system.
611 * After a minimum of research, I have come to the conclusion that C# has suitable introspection, and will go ahead with my plans, leaving that problem to the C# coders.
612 * Looks like OpenSim is already using a bit of C# introspection for ll*() functions anyway.
613 * The scripts main loop can already deal with incoming commands as a string with a Lua function call in it.
614 *
615 * Send messages from / to Lua or C, and wait or not wait.
616 *
617 * Luaproc channels are distinct from Lua states, but some Lua state has to create them before they can be used.
618 * On the other hand, looks like broadcasting messages is not really catered for, it's first come first served.
619 * luaproc.send() can send multiple messages to a single channel. It blocks if no one is listening.
620 * This was done to simplify things for the luaproc programmers, who suggest creating more Lua states to deal with asynchronous message sending.
621 * luaprog.receive() gets a channel message. It can block waiting, or not block.
622 * I already hacked up C code to send and not block. I might have broken the luaproc.send() ability to send multiple messages.
623 * Before I hacked it up, actual message sending was done by copying the contents of the sending Lua states stack to the receiver states stack.
624 * This is the simple method for the luaproc programmers, as both states are in the context of a luaproc call, so both stacks are available.
625 * My hacked up version either takes one message from the sender, or is passed one from C. The C call just returns if there is no one waiting on that channel.
626 * luaproc.send() calls that C function after taking a single message from the stack, and block waits as usual if the C call cannot deliver.
627 * Don't think there is C to receive messages, luaproc seems to be lacking entirely in C side API.
628 * NOTE - Sending from C means that the message goes nowhere if no one is waiting for it.
629 * SOOOO, we may need to queue messages to.
630 * Just chuck them in a FIFO per channel, and destroy the FIFO when the channel get's destroyed.
631 * See if I can create the SID channel in C before I start the Lua state running.
632 * Edje messages might have to be used instead, or some hybrid.
633 *
634 * Main loop is waiting on messages, and that's the main driver. Luaproc is fine with that. Good for events.
635 * End of event handler -
636 * just wait for the next event.
637 * Stop a script from LSL -
638 * gotta find it's SID from it's name, and the prim UUID
639 * send the message
640 * wait for it to get the message - BUT we don't really want to wait.
641 * Stop a script from OpenSim -
642 * we should have it's SID from OpenSim, just send the message from C, no need to wait.
643 * Start a script -
644 * if it's stopped, it's already waiting for the message.
645 * if it's not stopped, then we don't care. BUT then we might be waiting for it to get the message if starting it from LSL.
646 * Reset a script -
647 * probably should be done from C anyway, and can reuse the libraries like luaproc likes to do.
648 * ask C to reset it.
649 * LSL calls a function we have to hand to OpenSim -
650 * send the message to C, wait.
651 * C eventually sends a message back.
652 * Sleep -
653 * tell C it's waiting for the wake up message.
654 * wait for the wake up message.
655 *
656 * C needs -
657 * Lua call for stop script.
658 * get the SID from the name, and the prim UUID.
659 * send the stop message to the SID.
660 * send something to OpenSim so it knows.
661 * return to Lua.
662 * Lua call for start script.
663 * get the SID from the name, and the prim UUID.
664 * send the start message to the SID.
665 * send something to OpenSim so it knows.
666 * return to Lua.
667 * Lua call for reset other script.
668 * get the SID from the name, and the prim UUID.
669 * figure out which Lua state it is.
670 * fall through to "reset this script", only with the script set to the found one.
671 * Lua call for reset this script.
672 * get luaproc to close this Lua state
673 * reload the script file
674 * start it again, reusing the previous Lua state, or which ever one luaproc wants to use.
675 * Lua call for sending a function to OpenSim.
676 * Lua first strings up the function call and args, with SID.
677 * C packs it off to opensim.
678 * C puts Lua state on the "waiting for message" queue if a return value is needed.
679 * OpenSim sends back the return value, business as usual.
680 * Lua call for sleep.
681 * setup an ecore timer callback
682 * put the Lua state into "waiting for message" queue.
683 * ecore timer callback sends the wake up message.
684 *
685 * Time and timers, plus deal with time dilation.
686 *
687 * Various LSL functions deal with time, that's no problem.
688 * Some might deal with time dilation, which means dealing with that info through OpenSim.
689 * There is a timer event, which should be done through ecore timers and whatever message system we end up with.
690 * Finally, a sleep call, which can be done with ecore timer and messages to.
691 *
692 * Deal directly with MySQL and SQLite databases.
693 *
694 * The script engine can run on the same computer as the sim server, that's how OpenSim does stuff. So we can directly access the database the sim server has, which gives us access to sim object metadata.
695 * Changing that metadata might require us to inform OpenSim about the changes. It's entirely possible that different changes do or do not need that.
696 * Esskyuehl may be suitable, though it's still in the prototype stage.
697 *
698 * Serialise the script state, send it somewhere.
699 *
700 * Lua can generally serialise itself as as a string of code to be executed at the destination. There might be some C side state that needs to be take care of as well. We shall see.
701 *
702 * Email, HTTP, XML-RPC?
703 *
704 * LSL has functions for using these as communications methods. We should implement them ourselves eventually, but use the existing OpenSim methods for now.
705 * Note that doing it ourselves may cause issues with OpenSim doing it for some other script engine.
706 * Azy might be suitable, but it's also in prototype.
707 *
708*/
diff --git a/src/LuaSL/LuaSL_test.c b/src/LuaSL/LuaSL_test.c
new file mode 100644
index 0000000..58d6225
--- /dev/null
+++ b/src/LuaSL/LuaSL_test.c
@@ -0,0 +1,467 @@
1
2#include "LuaSL.h"
3
4
5static Eina_Strbuf *clientStream;
6static int scriptCount = 0;
7static int compiledCount = 0;
8static float compileTime = 0.0;
9static struct timeval startTime;
10static int timedEvent = 0;
11static char *ownerKey = "12345678-1234-4321-abcd-0123456789ab";
12static char *ownerName = "onefang rejected";
13
14static const char *names[] =
15{
16 "bub1", "sh1",
17 "bub2", "sh2",
18 "bub3", "sh3",
19};
20
21
22static void
23_edje_signal_cb(void *data, Evas_Object *obj __UNUSED__, const char *emission, const char *source)
24{
25// gameGlobals *ourGlobals = data;
26}
27
28static
29Eina_Bool anim(void *data)
30{
31 gameGlobals *ourGlobals = data;
32 Evas_Object *bub, *sh;
33 Evas_Coord x, y, w, h, vw, vh;
34 double t, xx, yy, zz, r, fac;
35 double lx, ly;
36 unsigned int i;
37
38 evas_output_viewport_get(ourGlobals->canvas, 0, 0, &vw, &vh);
39 r = 48;
40 t = ecore_loop_time_get();
41 fac = 2.0 / (double)((sizeof(names) / sizeof(char *) / 2));
42 evas_pointer_canvas_xy_get(ourGlobals->canvas, &x, &y);
43 lx = x;
44 ly = y;
45
46 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
47 {
48 bub = evas_object_data_get(ourGlobals->bg, names[i * 2]);
49 sh = evas_object_data_get(ourGlobals->bg, names[(i * 2) + 1]);
50 zz = (((2 + sin(t * 6 + (M_PI * (i * fac)))) / 3) * 64) * 2;
51 xx = (cos(t * 4 + (M_PI * (i * fac))) * r) * 2;
52 yy = (sin(t * 6 + (M_PI * (i * fac))) * r) * 2;
53
54 w = zz;
55 h = zz;
56 x = (vw / 2) + xx - (w / 2);
57 y = (vh / 2) + yy - (h / 2);
58
59 evas_object_move(bub, x, y);
60 evas_object_resize(bub, w, h);
61
62 x = x - ((lx - (x + (w / 2))) / 4);
63 y = y - ((ly - (y + (h / 2))) / 4);
64
65 evas_object_move(sh, x, y);
66 evas_object_resize(sh, w, h);
67 evas_object_raise(sh);
68 evas_object_raise(bub);
69 }
70 return ECORE_CALLBACK_RENEW;
71}
72
73static void
74_on_delete(Ecore_Evas *ee __UNUSED__)
75{
76 ecore_main_loop_quit();
77}
78
79static void dirList_compile(const char *name, const char *path, void *data)
80{
81 gameGlobals *ourGlobals = data;
82
83 char *ext = rindex(name, '.');
84
85 if (ext)
86 {
87 if (0 == strcmp(ext, ".lsl"))
88 {
89 script *me = calloc(1, sizeof(script));
90
91 scriptCount++;
92 gettimeofday(&me->startTime, NULL);
93 snprintf(me->SID, sizeof(me->SID), "%08lx-%04lx-%04lx-%04lx-%012lx", random(), random() % 0xFFFF, random() % 0xFFFF, random() % 0xFFFF, random());
94 snprintf(me->fileName, sizeof(me->fileName), "%s/%s", path, name);
95 eina_hash_add(ourGlobals->scripts, me->SID, me);
96 sendForth(ourGlobals, me->SID, "compile(%s)", me->fileName);
97 }
98 }
99}
100
101static Eina_Bool _timer_cb(void *data)
102{
103 gameGlobals *ourGlobals = data;
104 Eina_Iterator *scripts;
105 script *me;
106 boolean exit = FALSE;
107
108 scripts = eina_hash_iterator_data_new(ourGlobals->scripts);
109 while(eina_iterator_next(scripts, (void **) &me))
110 {
111 switch (timedEvent)
112 {
113 case 5 :
114 {
115 sendForth(ourGlobals, me->SID, "events.detectedKeys({\"%s\"})", ownerKey);
116 sendForth(ourGlobals, me->SID, "events.detectedNames({\"%s\"})", ownerName);
117 sendForth(ourGlobals, me->SID, "events.touch_start(1)");
118 break;
119 }
120 case 9 :
121 {
122 sendForth(ourGlobals, me->SID, "quit()");
123 break;
124 }
125 case 11 :
126 {
127 exit = TRUE;
128 break;
129 }
130 }
131 }
132 timedEvent++;
133
134 if (exit)
135 {
136 sendForth(ourGlobals, ownerKey, "exit()");
137 ecore_main_loop_quit();
138 return ECORE_CALLBACK_CANCEL;
139 }
140 return ECORE_CALLBACK_RENEW;
141}
142
143static Eina_Bool _add(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Add *ev)
144{
145 gameGlobals *ourGlobals = data;
146 char buf[PATH_MAX];
147
148 ourGlobals->server = ev->server;
149 gettimeofday(&startTime, NULL);
150 snprintf(buf, sizeof(buf), "%s/media/Test sim/objects", PACKAGE_DATA_DIR);
151 eina_file_dir_list(buf, EINA_TRUE, dirList_compile, ourGlobals);
152 // Wait awhile, then start sending events for testing.
153 ecore_timer_add(0.5, _timer_cb, ourGlobals);
154 return ECORE_CALLBACK_RENEW;
155}
156
157static Eina_Bool _data(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Data *ev)
158{
159 gameGlobals *ourGlobals = data;
160
161 char buf[PATH_MAX];
162 char SID[PATH_MAX];
163 const char *command;
164 char *ext;
165
166 eina_strbuf_append_length(clientStream, ev->data, ev->size);
167 command = eina_strbuf_string_get(clientStream);
168 while ((ext = index(command, '\n')))
169 {
170 int length = ext - command;
171
172 strncpy(SID, command, length + 1);
173 SID[length] = '\0';
174 eina_strbuf_remove(clientStream, 0, length + 1);
175 ext = index(SID, '.');
176 if (ext)
177 {
178 script *me;
179
180 ext[0] = '\0';
181 command = ext + 1;
182 me = eina_hash_find(ourGlobals->scripts, SID);
183 if (0 == strncmp(command, "compilerWarning(", 16))
184 {
185 char *temp;
186 char *line;
187 char *column;
188 char *text;
189
190 strcpy(buf, &command[16]);
191 temp = buf;
192 line = temp;
193 while (',' != temp[0])
194 temp++;
195 temp[0] = '\0';
196 column = ++temp;
197 while (',' != temp[0])
198 temp++;
199 temp[0] = '\0';
200 text = ++temp;
201 while (')' != temp[0])
202 temp++;
203 temp[0] = '\0';
204 PW("%s @ line %s, column %s.", text, line, column);
205 if (me)
206 me->warnings++;
207 }
208 else if (0 == strncmp(command, "compilerError(", 14))
209 {
210 char *temp;
211 char *line;
212 char *column;
213 char *text;
214
215 strcpy(buf, &command[14]);
216 temp = buf;
217 line = temp;
218 while (',' != temp[0])
219 temp++;
220 temp[0] = '\0';
221 column = ++temp;
222 while (',' != temp[0])
223 temp++;
224 temp[0] = '\0';
225 text = ++temp;
226 while (')' != temp[0])
227 temp++;
228 temp[0] = '\0';
229 PE("%s @ line %s, column %s.", text, line, column);
230 if (me)
231 me->bugs++;
232 }
233 else if (0 == strcmp(command, "compiled(false)"))
234 PE("The compile of %s failed!", SID);
235 else if (0 == strcmp(command, "compiled(true)"))
236 {
237 if (me)
238 {
239 struct timeval now;
240
241 me->compileTime = timeDiff(&now, &me->startTime);
242 me->running = TRUE;
243 compiledCount++;
244 compileTime += me->compileTime;
245// PD("Average compile speed is %f scripts per second", compiledCount / compileTime);
246 if (compiledCount == scriptCount)
247 PD("TOTAL compile speed is %f scripts per second", compiledCount / timeDiff(&now, &startTime));
248 }
249// PD("The compile of %s worked, running it now.", SID);
250 sendForth(ourGlobals, SID, "run()");
251 }
252 else
253 {
254 // Send back some random or fixed values for testing.
255 if (0 == strcmp(command, "llGetKey()"))
256 sendForth(ourGlobals, SID, "return \"%08lx-%04lx-%04lx-%04lx-%012lx\"", random(), random() % 0xFFFF, random() % 0xFFFF, random() % 0xFFFF, random());
257 else if (0 == strcmp(command, "llGetOwner()"))
258 sendForth(ourGlobals, SID, "return \"%s\"", ownerKey);
259 else if (0 == strcmp(command, "llGetPos()"))
260 sendForth(ourGlobals, SID, "return {x=128.0, y=128.0, z=128.0}");
261 else if (0 == strcmp(command, "llGetRot()"))
262 sendForth(ourGlobals, SID, "return {x=0.0, y=0.0, z=0.0, s=1.0}");
263 else if (0 == strcmp(command, "llGetObjectDesc()"))
264 sendForth(ourGlobals, SID, "return \"\"");
265 else if (0 == strncmp(command, "llGetAlpha(", 11))
266 sendForth(ourGlobals, SID, "return 1.0");
267 else if (0 == strcmp(command, "llGetInventoryNumber(7)"))
268 sendForth(ourGlobals, SID, "return 3");
269 else if (0 == strcmp(command, "llGetInventoryName(7, 2)"))
270 sendForth(ourGlobals, SID, "return \".readme\"");
271 else if (0 == strcmp(command, "llGetInventoryName(7, 1)"))
272 sendForth(ourGlobals, SID, "return \".POSITIONS\"");
273 else if (0 == strcmp(command, "llGetInventoryName(7, 0)"))
274 sendForth(ourGlobals, SID, "return \".MENUITEMS\"");
275 else
276 PI("Script %s sent command %s", SID, command);
277 }
278 }
279
280 // Get the next blob to check it.
281 command = eina_strbuf_string_get(clientStream);
282 }
283
284 return ECORE_CALLBACK_RENEW;
285}
286
287static Eina_Bool _del(void *data, int type __UNUSED__, Ecore_Con_Event_Server_Del *ev)
288{
289 gameGlobals *ourGlobals = data;
290
291 if (ev->server)
292 {
293 ourGlobals->server = NULL;
294 ecore_con_server_del(ev->server);
295 if (!ourGlobals->ui)
296 ecore_main_loop_quit();
297 }
298
299 return ECORE_CALLBACK_RENEW;
300}
301
302int main(int argc, char **argv)
303{
304 /* put here any init specific to this app like parsing args etc. */
305 gameGlobals ourGlobals;
306 char *programName = argv[0];
307 boolean badArgs = FALSE;
308 int result = EXIT_FAILURE;
309
310 memset(&ourGlobals, 0, sizeof(gameGlobals));
311 ourGlobals.address = "127.0.0.1";
312 ourGlobals.port = 8211;
313
314 if (eina_init())
315 {
316 ourGlobals.logDom = loggingStartup("LuaSL_test", ourGlobals.logDom);
317 ourGlobals.scripts = eina_hash_string_superfast_new(NULL);
318
319 if (ecore_con_init())
320 {
321 if ((ourGlobals.server = ecore_con_server_connect(ECORE_CON_REMOTE_TCP, ourGlobals.address, ourGlobals.port, &ourGlobals)))
322 {
323 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb) _add, &ourGlobals);
324 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb) _data, &ourGlobals);
325 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb) _del, &ourGlobals);
326 clientStream = eina_strbuf_new();
327
328 if (ecore_evas_init())
329 {
330 if (edje_init())
331 {
332 // get the arguments passed in
333 while (--argc > 0 && *++argv != '\0')
334 {
335 if (*argv[0] == '-')
336 {
337 // point to the characters after the '-' sign
338 char *s = argv[0] + 1;
339
340 switch (*s)
341 {
342 case 'u':
343 {
344 ourGlobals.ui = TRUE;
345 break;
346 }
347 default:
348 badArgs = TRUE;
349 }
350 }
351 else
352 badArgs = TRUE;
353 }
354
355 if (badArgs)
356 {
357 // display the program usage to the user as they have it wrong
358 printf("Usage: %s [-u]\n", programName);
359 printf(" -u: Show the test UI.\n");
360 }
361 else
362 // else if ((ourGlobals.config) && (ourGlobals.data))
363 {
364 unsigned int i;
365 Evas_Object *bub, *sh;
366 Ecore_Animator *ani;
367 char *group = "main";
368 char buf[PATH_MAX];
369
370 if (ourGlobals.ui)
371 {
372 /* this will give you a window with an Evas canvas under the first engine available */
373 ourGlobals.ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
374 if (!ourGlobals.ee)
375 {
376 PEm("You got to have at least one evas engine built and linked up to ecore-evas for this example to run properly.");
377 edje_shutdown();
378 ecore_evas_shutdown();
379 return -1;
380 }
381 ourGlobals.canvas = ecore_evas_get(ourGlobals.ee);
382 ecore_evas_title_set(ourGlobals.ee, "LuaSL test harness");
383 ecore_evas_show(ourGlobals.ee);
384
385 ourGlobals.bg = evas_object_rectangle_add(ourGlobals.canvas);
386 evas_object_color_set(ourGlobals.bg, 255, 255, 255, 255); /* white bg */
387 evas_object_move(ourGlobals.bg, 0, 0); /* at canvas' origin */
388 evas_object_size_hint_weight_set(ourGlobals.bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
389 evas_object_resize(ourGlobals.bg, WIDTH, HEIGHT); /* covers full canvas */
390 evas_object_show(ourGlobals.bg);
391 ecore_evas_object_associate(ourGlobals.ee, ourGlobals.bg, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);
392 evas_object_focus_set(ourGlobals.bg, EINA_TRUE);
393
394 ourGlobals.edje = edje_object_add(ourGlobals.canvas);
395 snprintf(buf, sizeof(buf), "%s/media/%s.edj", PACKAGE_DATA_DIR, "LuaSL");
396 if (!edje_object_file_set(ourGlobals.edje, buf, group))
397 {
398 int err = edje_object_load_error_get(ourGlobals.edje);
399 const char *errmsg = edje_load_error_str(err);
400 PEm("Could not load '%s' from %s: %s\n", group, buf, errmsg);
401
402 evas_object_del(ourGlobals.edje);
403 ecore_evas_free(ourGlobals.ee);
404 edje_shutdown();
405 ecore_evas_shutdown();
406 return -2;
407 }
408 evas_object_move(ourGlobals.edje, 0, 0);
409 evas_object_resize(ourGlobals.edje, WIDTH, HEIGHT);
410 evas_object_show(ourGlobals.edje);
411
412 snprintf(buf, sizeof(buf), "%s/media/bubble_sh.png", PACKAGE_DATA_DIR);
413 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
414 {
415 sh = evas_object_image_filled_add(ourGlobals.canvas);
416 evas_object_image_file_set(sh, buf, NULL);
417 evas_object_resize(sh, 64, 64);
418 evas_object_show(sh);
419 evas_object_data_set(ourGlobals.bg, names[(i * 2) + 1], sh);
420 }
421
422 snprintf(buf, sizeof(buf), "%s/media/bubble.png", PACKAGE_DATA_DIR);
423 for (i = 0; i < (sizeof(names) / sizeof(char *) / 2); i++)
424 {
425 bub = evas_object_image_filled_add(ourGlobals.canvas);
426 evas_object_image_file_set(bub, buf, NULL);
427 evas_object_resize(bub, 64, 64);
428 evas_object_show(bub);
429 evas_object_data_set(ourGlobals.bg, names[(i * 2)], bub);
430 }
431 ani = ecore_animator_add(anim, &ourGlobals);
432 evas_object_data_set(ourGlobals.bg, "animator", ani);
433
434 // Setup our callbacks.
435 ecore_evas_callback_delete_request_set(ourGlobals.ee, _on_delete);
436 edje_object_signal_callback_add(ourGlobals.edje, "*", "game_*", _edje_signal_cb, &ourGlobals);
437 }
438
439 ecore_main_loop_begin();
440 if (ourGlobals.ui)
441 {
442 ecore_animator_del(ani);
443 ecore_evas_free(ourGlobals.ee);
444 }
445 }
446
447 edje_shutdown();
448 }
449 else
450 PCm("Failed to init edje!");
451 ecore_evas_shutdown();
452 }
453 else
454 PCm("Failed to init ecore_evas!");
455 }
456 else
457 PCm("Failed to connect to server!");
458 ecore_con_shutdown();
459 }
460 else
461 PCm("Failed to init ecore_con!");
462 }
463 else
464 fprintf(stderr, "Failed to init eina!");
465
466 return result;
467}
diff --git a/src/LuaSL/LuaSL_threads.c b/src/LuaSL/LuaSL_threads.c
new file mode 100644
index 0000000..234a7c5
--- /dev/null
+++ b/src/LuaSL/LuaSL_threads.c
@@ -0,0 +1,496 @@
1/* This code is heavily based on luaproc.
2 *
3 * The luaproc copyright notice and license is -
4
5 ***************************************************
6
7Copyright 2008 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27 ****************************************************
28 *
29 * Additions and changes Copyright 2012 by David Seikel, using the above license.
30 */
31
32
33/* This is a redesign of luaproc. The design goals and notes -
34 *
35 * In general use EFL where it is useful.
36 * Probably one fixed unique message channel per object, which each script in the object shares.
37 * But might be better to handle that C side anyway.
38 * Better integration with LuaSL.
39 * Use ecore threads instead of raw pthreads.
40 * Ecore threads pretty much wraps pthreads on posix, but has Windows support to.
41 * Merge in the edje Lua code, and keep an eye on that, coz we might want to actually add this to edje Lua in the future.
42 * Use my coding standards, or EFL ones. Pffft.
43 *
44 */
45
46#include "LuaSL.h"
47
48
49/* ready process queue insertion status */
50#define LUAPROC_SCHED_QUEUE_PROC_OK 0
51//#define LUAPROC_SCHED_QUEUE_PROC_ERR -1
52
53/* process is idle */
54#define LUAPROC_STAT_IDLE 0
55/* process is ready to run */
56#define LUAPROC_STAT_READY 1
57/* process is blocked on send */
58#define LUAPROC_STAT_BLOCKED_SEND 2
59/* process is blocked on receive */
60#define LUAPROC_STAT_BLOCKED_RECV 3
61
62
63typedef struct
64{
65 Eina_Clist node;
66 lua_State *L;
67} recycled;
68
69/*********
70* globals
71*********/
72
73/* ready process list */
74Eina_Clist lpready;
75
76/* ready process queue access mutex */
77pthread_mutex_t mutex_queue_access = PTHREAD_MUTEX_INITIALIZER;
78
79/* wake worker up conditional variable */
80pthread_cond_t cond_wakeup_worker = PTHREAD_COND_INITIALIZER;
81
82/* active luaproc count access mutex */
83pthread_mutex_t mutex_lp_count = PTHREAD_MUTEX_INITIALIZER;
84
85/* no active luaproc conditional variable */
86pthread_cond_t cond_no_active_lp = PTHREAD_COND_INITIALIZER;
87
88/* number of active luaprocs */
89int lpcount = 0;
90
91/* no more lua processes flag */
92int no_more_processes = FALSE;
93
94/* channel operations mutex */
95pthread_mutex_t mutex_channel = PTHREAD_MUTEX_INITIALIZER;
96
97/* recycle list mutex */
98pthread_mutex_t mutex_recycle_list = PTHREAD_MUTEX_INITIALIZER;
99
100/* recycled lua process list */
101Eina_Clist recyclelp;
102
103
104/******************************
105* library functions prototypes
106******************************/
107/* send a message to a lua process */
108static int luaproc_send( lua_State *L );
109/* receive a message from a lua process */
110static int luaproc_receive( lua_State *L );
111/* send a message back to the main loop */
112static int luaproc_send_back( lua_State *L );
113
114/* luaproc function registration array - main (parent) functions */
115static const struct luaL_reg luaproc_funcs_parent[] = {
116 { "sendback", luaproc_send_back },
117 { NULL, NULL }
118};
119
120/* luaproc function registration array - newproc (child) functions */
121static const struct luaL_reg luaproc_funcs_child[] = {
122 { "send", luaproc_send },
123 { "receive", luaproc_receive },
124 { "sendback", luaproc_send_back },
125 { NULL, NULL }
126};
127
128
129/* increase active lua process count */
130static void sched_lpcount_inc(void)
131{
132 pthread_mutex_lock(&mutex_lp_count);
133 lpcount++;
134 pthread_mutex_unlock(&mutex_lp_count);
135}
136
137/* decrease active lua process count */
138static void sched_lpcount_dec(void)
139{
140 pthread_mutex_lock(&mutex_lp_count);
141 lpcount--;
142 /* if count reaches zero, signal there are no more active processes */
143 if (lpcount == 0)
144 pthread_cond_signal(&cond_no_active_lp);
145 pthread_mutex_unlock(&mutex_lp_count);
146}
147
148/* worker thread main function */
149static void *workermain( void *args ) {
150
151 script *lp;
152 int procstat;
153
154 /* detach thread so resources are freed as soon as thread exits (no further joining) */
155 pthread_detach( pthread_self( ));
156
157 /* main worker loop */
158 while ( 1 ) {
159
160 /* get exclusive access to the ready process queue */
161 pthread_mutex_lock( &mutex_queue_access );
162
163 /* wait until instructed to wake up (because there's work to do or because its time to finish) */
164 while (( eina_clist_count( &lpready ) == 0 ) && ( no_more_processes == FALSE )) {
165 pthread_cond_wait( &cond_wakeup_worker, &mutex_queue_access );
166 }
167
168 /* pop the first node from the ready process queue */
169 if ((lp = (script *) eina_clist_head(&lpready)))
170 eina_clist_remove(&(lp->node));
171 else {
172 /* free access to the process ready queue */
173 pthread_mutex_unlock( &mutex_queue_access );
174 /* finished thread */
175 pthread_exit( NULL );
176 }
177
178 /* free access to the process ready queue */
179 pthread_mutex_unlock( &mutex_queue_access );
180
181 /* execute the lua code specified in the lua process struct */
182 procstat = lua_resume(lp->L, lp->args);
183 /* reset the process argument count */
184 lp->args = 0;
185
186 /* check if process finished its whole execution, then recycle it */
187 if (procstat == 0)
188 {
189 recycled *trash = malloc(sizeof(recycled));
190
191 if (trash)
192 {
193 trash->L = lp->L;
194 pthread_mutex_lock(&mutex_recycle_list);
195 eina_clist_add_tail(&recyclelp, &(trash->node));
196 pthread_mutex_unlock(&mutex_recycle_list);
197 sched_lpcount_dec();
198 }
199 lua_close(lp->L);
200 if (lp->timer)
201 ecore_timer_del(lp->timer);
202 free(lp);
203 }
204
205 /* check if process yielded */
206 else if ( procstat == LUA_YIELD ) {
207
208 /* if so, further check if yield originated from an unmatched send/recv operation */
209 if (lp->status == LUAPROC_STAT_BLOCKED_SEND)
210 {
211 }
212 else if (lp->status == LUAPROC_STAT_BLOCKED_RECV)
213 {
214 }
215 /* or if yield resulted from an explicit call to coroutine.yield in the lua code being executed */
216 else
217 {
218 /* get exclusive access to the ready process queue */
219 pthread_mutex_lock( &mutex_queue_access );
220 /* re-insert the job at the end of the ready process queue */
221 eina_clist_add_tail(&lpready, &(lp->node));
222 /* free access to the process ready queue */
223 pthread_mutex_unlock( &mutex_queue_access );
224 }
225 }
226 /* check if there was any execution error (LUA_ERRRUN, LUA_ERRSYNTAX, LUA_ERRMEM or LUA_ERRERR) */
227 else
228 {
229 /* print error message */
230 fprintf( stderr, "close lua_State (error: %s)\n", luaL_checkstring(lp->L, -1 ));
231 /* close lua state */
232 lua_close(lp->L);
233 /* decrease active lua process count */
234 sched_lpcount_dec();
235 }
236 }
237}
238
239/* move process to ready queue (ie, schedule process) */
240static int sched_queue_proc( script *lp ) {
241
242 /* get exclusive access to the ready process queue */
243 pthread_mutex_lock( &mutex_queue_access );
244
245 /* add process to ready queue */
246 eina_clist_add_tail(&lpready, &(lp->node));
247
248 lp->status = LUAPROC_STAT_READY;
249
250 /* wake worker up */
251 pthread_cond_signal( &cond_wakeup_worker );
252 /* free access to the process ready queue */
253 pthread_mutex_unlock( &mutex_queue_access );
254
255 return LUAPROC_SCHED_QUEUE_PROC_OK;
256}
257
258/* synchronize worker threads */
259void sched_join_workerthreads( void ) {
260
261 pthread_mutex_lock( &mutex_lp_count );
262
263 /* wait until there is no more active lua processes */
264 while( lpcount != 0 ) {
265 pthread_cond_wait( &cond_no_active_lp, &mutex_lp_count );
266 }
267 /* get exclusive access to the ready process queue */
268 pthread_mutex_lock( &mutex_queue_access );
269 /* set the no more active lua processes flag to true */
270 no_more_processes = TRUE;
271 /* wake ALL workers up */
272 pthread_cond_broadcast( &cond_wakeup_worker );
273 /* free access to the process ready queue */
274 pthread_mutex_unlock( &mutex_queue_access );
275
276// We don't need this, as we only get here during shutdown. Linking this to EFL results in a hang otherwise anyway.
277 /* wait for (join) worker threads */
278// pthread_exit( NULL );
279
280 pthread_mutex_unlock( &mutex_lp_count );
281
282}
283
284/* create a new worker pthread */
285int sched_create_worker(void)
286{
287 pthread_t worker;
288
289 /* create a new pthread */
290 if (pthread_create( &worker, NULL, workermain, NULL ) != 0)
291 return LUAPROC_SCHED_PTHREAD_ERROR;
292 return LUAPROC_SCHED_OK;
293}
294
295void newProc(const char *code, int file, script *lp)
296{
297 int ret;
298 recycled *trash;
299
300 // Try to recycle a Lua state, otherwise create one from scratch.
301 pthread_mutex_lock(&mutex_recycle_list);
302 /* pop list head */
303 if ((trash = (recycled *) eina_clist_head(&recyclelp)))
304 {
305 eina_clist_remove(&(trash->node));
306 lp->L = trash->L;
307 free(trash);
308 }
309 pthread_mutex_unlock(&mutex_recycle_list);
310
311 if (NULL == lp->L)
312 {
313 lp->L = luaL_newstate();
314
315 /* store the script struct in its own Lua state */
316 lua_pushlightuserdata(lp->L, lp);
317 lua_setfield(lp->L, LUA_REGISTRYINDEX, "_SELF");
318 luaL_openlibs(lp->L);
319 luaL_register(lp->L, "luaproc", luaproc_funcs_child);
320 }
321
322 lp->status = LUAPROC_STAT_IDLE;
323 lp->args = 0;
324 eina_clist_element_init(&(lp->node));
325 eina_clist_init(&(lp->messages));
326
327 /* load process' code */
328 if (file)
329 ret = luaL_loadfile(lp->L, code);
330 else
331 ret = luaL_loadstring(lp->L, code);
332
333 /* in case of errors, destroy Lua process */
334 if (ret != 0)
335 {
336 lua_close(lp->L);
337 lp->L = NULL;
338 }
339
340 if (lp->L)
341 {
342 sched_lpcount_inc();
343
344 /* schedule luaproc */
345 if (sched_queue_proc(lp) != LUAPROC_SCHED_QUEUE_PROC_OK)
346 {
347 printf( "[luaproc] error queueing Lua process\n" );
348 sched_lpcount_dec();
349 lua_close(lp->L);
350 }
351 }
352}
353
354/* return the lua process associated with a given lua state */
355static script *luaproc_getself(lua_State *L)
356{
357 script *lp;
358
359 lua_getfield(L, LUA_REGISTRYINDEX, "_SELF");
360 lp = (script *) lua_touserdata(L, -1);
361 lua_pop(L, 1);
362 return lp;
363}
364
365/* send a message to the client process */
366static int luaproc_send_back(lua_State *L)
367{
368 script *self = luaproc_getself(L);
369 const char *message = luaL_checkstring(L, 1);
370
371 if (self)
372 {
373 scriptMessage *sm = calloc(1, sizeof(scriptMessage));
374
375 if (sm)
376 {
377 eina_clist_element_init(&(sm->node));
378 sm->script = self;
379 strcpy((char *) sm->message, message);
380 ecore_main_loop_thread_safe_call_async(scriptSendBack, sm);
381 }
382 }
383
384 return 0;
385}
386
387/* error messages for the sendToChannel function */
388const char *sendToChannelErrors[] =
389{
390 "non-existent channel",
391 "error scheduling process"
392};
393
394/* send a message to a lua process */
395const char *sendToChannel(gameGlobals *ourGlobals, const char *SID, const char *message)
396{
397 const char *result = NULL;
398 script *dstlp;
399
400 /* get exclusive access to operate on channels */
401 pthread_mutex_lock(&mutex_channel);
402
403 // Add the message to the queue.
404 if ((dstlp = eina_hash_find(ourGlobals->scripts, SID)))
405 {
406 scriptMessage *sm = NULL;
407
408 if ((sm = malloc(sizeof(scriptMessage))))
409 {
410 sm->script = dstlp;
411 strcpy((char *) sm->message, message);
412 eina_clist_add_tail(&(dstlp->messages), &(sm->node));
413 }
414
415 /* if it's already waiting, send the next message to it and (queue) wake it */
416 if (dstlp->status == LUAPROC_STAT_BLOCKED_RECV)
417 {
418 scriptMessage *msg = (scriptMessage *) eina_clist_head(&(dstlp->messages));
419
420 // See if there's a message on the queue. Note, this may not be the same as the incoming message, if there was already a queue.
421 if (msg)
422 {
423 eina_clist_remove(&(msg->node));
424 message = msg->message;
425 }
426 /* push the message onto the receivers stack */
427 lua_pushstring(dstlp->L, message);
428 dstlp->args = lua_gettop(dstlp->L) - 1;
429 if (msg)
430 free(msg);
431
432 if (sched_queue_proc(dstlp) != LUAPROC_SCHED_QUEUE_PROC_OK)
433 {
434 sched_lpcount_dec();
435 lua_close(dstlp->L);
436 result = sendToChannelErrors[1];
437 }
438 }
439 }
440
441 pthread_mutex_unlock(&mutex_channel);
442
443 return result;
444}
445
446/* send a message to a lua process */
447static int luaproc_send(lua_State *L)
448{
449 script *self = luaproc_getself(L);
450 const char *result = sendToChannel(self->game, luaL_checkstring(L, 1), luaL_checkstring(L, 2));
451
452 if (result)
453 {
454 lua_pushnil(L);
455 lua_pushstring(L, result);
456 return 2;
457 }
458
459 lua_pushboolean(L, TRUE);
460 return 1;
461}
462
463/* receive a message from a lua process */
464static int luaproc_receive(lua_State *L)
465{
466 script *self;
467 const char *chname = luaL_checkstring(L, 1);
468 scriptMessage *msg;
469
470 // First check if there are queued messages, and grab one.
471 self = luaproc_getself(L);
472 if ((msg = (scriptMessage *) eina_clist_head(&(self->messages))))
473 {
474 eina_clist_remove(&(msg->node));
475 lua_pushstring(L, msg->message);
476 free(msg);
477 return lua_gettop(L) - 1;
478 }
479
480 /* if trying an asynchronous receive, return an error */
481 if ( lua_toboolean( L, 2 ))
482 {
483 lua_pushnil(L);
484 lua_pushfstring(L, "no senders waiting on channel %s", chname);
485 return 2;
486 }
487 /* otherwise (synchronous receive) simply block process */
488 self->status = LUAPROC_STAT_BLOCKED_RECV;
489 return lua_yield(L, lua_gettop(L));
490}
491
492void luaprocInit(void)
493{
494 eina_clist_init(&recyclelp);
495 eina_clist_init(&lpready);
496}
diff --git a/src/LuaSL/LuaSL_threads.h b/src/LuaSL/LuaSL_threads.h
new file mode 100644
index 0000000..9a11b5c
--- /dev/null
+++ b/src/LuaSL/LuaSL_threads.h
@@ -0,0 +1,54 @@
1/* This code is heavily based on luaproc.
2 *
3 * The luaproc copyright notice and license is -
4
5 ***************************************************
6
7Copyright 2008 Alexandre Skyrme, Noemi Rodriguez, Roberto Ierusalimschy
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27 ****************************************************
28 *
29 * Additions and changes Copyright 2012 by David Seikel, using the above license.
30 */
31
32#ifndef __LUASL_THREADS_H__
33#define __LUASL_THREADS_H__
34
35/* scheduler function return constants */
36#define LUAPROC_SCHED_OK 0
37#define LUAPROC_SCHED_SOCKET_ERROR -1
38#define LUAPROC_SCHED_SETSOCKOPT_ERROR -2
39#define LUAPROC_SCHED_BIND_ERROR -3
40#define LUAPROC_SCHED_LISTEN_ERROR -4
41#define LUAPROC_SCHED_FORK_ERROR -5
42#define LUAPROC_SCHED_PTHREAD_ERROR -6
43#define LUAPROC_SCHED_INIT_ERROR -7
44
45
46void luaprocInit(void);
47int sched_create_worker(void);
48void newProc(const char *code, int file, script *lp);
49const char *sendToChannel(gameGlobals *ourGlobals, const char *SID, const char *message);
50
51/* join all worker threads and exit */
52void sched_join_workerthreads(void);
53
54#endif
diff --git a/src/LuaSL/LuaSL_utilities.c b/src/LuaSL/LuaSL_utilities.c
new file mode 100644
index 0000000..40263df
--- /dev/null
+++ b/src/LuaSL/LuaSL_utilities.c
@@ -0,0 +1,60 @@
1#include "LuaSL.h"
2
3
4void sendBack(gameGlobals *ourGlobals, Ecore_Con_Client *client, const char *SID, const char *message, ...)
5{
6 va_list args;
7 char buf[PATH_MAX];
8 int length = strlen(SID);
9
10 strncpy(buf, SID, length);
11 buf[length++] = '.';
12 va_start(args, message);
13 length += vsprintf(&buf[length], message, args);
14 va_end(args);
15 buf[length++] = '\n';
16 buf[length++] = '\0';
17 ecore_con_client_send(client, buf, strlen(buf));
18 ecore_con_client_flush(client);
19}
20
21void sendForth(gameGlobals *ourGlobals, const char *SID, const char *message, ...)
22{
23 va_list args;
24 char buf[PATH_MAX];
25 int length = strlen(SID);
26
27 strncpy(buf, SID, length);
28 buf[length++] = '.';
29 va_start(args, message);
30 length += vsprintf(&buf[length], message, args);
31 va_end(args);
32 buf[length++] = '\n';
33 buf[length++] = '\0';
34 ecore_con_server_send(ourGlobals->server, buf, strlen(buf));
35 ecore_con_server_flush(ourGlobals->server);
36}
37
38float timeDiff(struct timeval *now, struct timeval *then)
39{
40 if (0 == gettimeofday(now, 0))
41 {
42 struct timeval thisTime = { 0, 0 };
43 double result = 0.0;
44
45 thisTime.tv_sec = now->tv_sec;
46 thisTime.tv_usec = now->tv_usec;
47 if (thisTime.tv_usec < then->tv_usec)
48 {
49 thisTime.tv_sec--;
50 thisTime.tv_usec += 1000000;
51 }
52 thisTime.tv_usec -= then->tv_usec;
53 thisTime.tv_sec -= then->tv_sec;
54 result = ((double) thisTime.tv_usec) / ((double) 1000000.0);
55 result += thisTime.tv_sec;
56 return result;
57 }
58 else
59 return 0.0;
60}
diff --git a/src/LuaSL/build.lua b/src/LuaSL/build.lua
new file mode 100755
index 0000000..f86715c
--- /dev/null
+++ b/src/LuaSL/build.lua
@@ -0,0 +1,23 @@
1#!/usr/bin/env lua
2
3local dir = ...
4
5if 'nil' == type(dir) then
6 local build, err = loadfile('../../build.lua')
7 if build then
8 setfenv(build, getfenv(2))
9 build(2)
10 else
11 print("ERROR - " .. err)
12 end
13 dir = workingDir
14end
15
16removeFiles(dir, {'../../LuaSL', '*.o', '*.output', '*.backup', '../../media/LuaSL.edj', 'LuaSL_lexer.h', 'LuaSL_lexer.c', 'LuaSL_lemon_yaccer.h', 'LuaSL_lemon_yaccer.c', 'LuaSL_lemon_yaccer.out'})
17
18-- Run lemon first, flex depends on it to define the symbol values.
19runCommand('lemon', dir, '../../libraries/lemon/lemon -s -T../../libraries/lemon/lempar.c LuaSL_lemon_yaccer.y')
20runCommand('flex', dir, 'flex -C --outfile=LuaSL_lexer.c --header-file=LuaSL_lexer.h LuaSL_lexer.l')
21runCommand('edje_cc', dir, 'edje_cc ' .. EDJE_FLAGS .. ' LuaSL.edc ../../media/LuaSL.edj')
22compileFiles('../../LuaSL', dir, {'LuaSL_main', 'LuaSL_compile', 'LuaSL_threads', 'LuaSL_utilities', 'LuaSL_lexer', 'LuaSL_lemon_yaccer'})
23compileFiles('LuaSL_test', dir, {'LuaSL_test', 'LuaSL_utilities'})
diff --git a/src/LuaSL/test.sh b/src/LuaSL/test.sh
new file mode 100755
index 0000000..1c26ade
--- /dev/null
+++ b/src/LuaSL/test.sh
@@ -0,0 +1,27 @@
1#! /bin/bash
2
3wd=$(pwd)
4
5# Kill any left overs.
6killall -KILL LuaSL
7export LUA_PATH="$wd/../../libraries/?.lua"
8
9case $@ in
10
11 ddd)
12 ddd ../../LuaSL
13 ;;
14
15 gdb)
16 gdb ../../LuaSL
17 ;;
18
19 *)
20 echo "_______________ STARTING LuaSL _______________"
21 ../../LuaSL &
22 sleep 1
23 echo "_______________ STARTING LuaSL_test _______________"
24 ./LuaSL_test
25 ;;
26
27esac
diff --git a/src/extantz/CDemo.cpp b/src/extantz/CDemo.cpp
new file mode 100644
index 0000000..0ca40f1
--- /dev/null
+++ b/src/extantz/CDemo.cpp
@@ -0,0 +1,510 @@
1// This is a Demo of the Irrlicht Engine (c) 2005-2009 by N.Gebhardt.
2// This file is not documented.
3
4#include <irrlicht.h>
5#include "extantz.h"
6#include "extantzCamera.h"
7#include "CDemo.h"
8
9
10CDemo::CDemo(GLData *gld, bool a)
11: additive(a),
12 device(gld->device),
13 currentScene(0),
14 quakeLevelMesh(0), quakeLevelNode(0), skyboxNode(0), model1(0), model2(0),
15 campFire(0), metaSelector(0), mapSelector(0), sceneStartTime(0),
16 timeForThisScene(0)
17{
18}
19
20
21CDemo::~CDemo()
22{
23 if (mapSelector)
24 mapSelector->drop();
25
26 if (metaSelector)
27 metaSelector->drop();
28}
29
30
31void CDemo::setup(GLData *gld)
32{
33 device = gld->device;
34 IrrlichtDevice *device = gld->device;
35// IVideoDriver *driver = gld->driver;
36// ISceneManager *smgr = gld->smgr;
37
38 if (device->getFileSystem()->existFile("irrlicht.dat"))
39 device->getFileSystem()->addFileArchive("irrlicht.dat");
40 else
41 device->getFileSystem()->addFileArchive("media/Irrlicht/irrlicht.dat");
42 if (device->getFileSystem()->existFile("map-20kdm2.pk3"))
43 device->getFileSystem()->addFileArchive("map-20kdm2.pk3");
44 else
45 device->getFileSystem()->addFileArchive("media/Irrlicht/map-20kdm2.pk3");
46
47 sceneStartTime = device->getTimer()->getTime();
48 timeForThisScene = 0;
49 loadSceneData();
50}
51
52
53void CDemo::preDraw(GLData *gld, u32 now)
54{
55 if (((now - sceneStartTime) > timeForThisScene) && (timeForThisScene != -1))
56 switchToNextScene(gld);
57
58 createParticleImpacts();
59}
60
61
62bool CDemo::OnEvent(const SEvent& event)
63{
64 if (!device)
65 return false;
66
67 if (( ((event.EventType == EET_KEY_INPUT_EVENT) && (event.KeyInput.Key == KEY_SPACE) && (event.KeyInput.PressedDown == false)) ||
68 ((event.EventType == EET_MOUSE_INPUT_EVENT) && (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)) ) && (currentScene == 3))
69 {
70 shoot();
71 }
72 else
73 if (device->getSceneManager()->getActiveCamera())
74 {
75 device->getSceneManager()->getActiveCamera()->OnEvent(event);
76 return true;
77 }
78
79 return false;
80}
81
82
83void CDemo::switchToNextScene(GLData *gld)
84{
85 currentScene++;
86 if (currentScene > 3)
87 currentScene = 1;
88
89 scene::ISceneManager* sm = device->getSceneManager();
90 scene::ISceneNodeAnimator* sa = 0;
91 scene::ICameraSceneNode* camera = 0;
92
93 camera = sm->getActiveCamera();
94
95 switch(currentScene)
96 {
97 case 1: // panorama camera
98 {
99 core::array<core::vector3df> points, points2;
100
101 points.push_back(core::vector3df(-931.473755f, 900.0f, 2000.0f)); // -49873
102 points.push_back(core::vector3df(-931.473755f, 900.0f, 2000.0f)); // -49873
103 points.push_back(core::vector3df(-931.473755f, 700.0f, 1750.0f)); // -49873
104 points.push_back(core::vector3df(-931.473755f, 500.0f, 1500.0f)); // -49873
105 points.push_back(core::vector3df(-931.473755f, 300.0f, 1250.0f)); // -49873
106 points.push_back(core::vector3df(-931.473755f, 200.0f, 1000.0f)); // -49873
107 points.push_back(core::vector3df(-931.473755f, 138.300003f, 987.279114f)); // -49873
108 points.push_back(core::vector3df(-847.902222f, 136.757553f, 915.792725f)); // -50559
109 points.push_back(core::vector3df(-748.680420f, 152.254501f, 826.418945f)); // -51964
110 points.push_back(core::vector3df(-708.428406f, 213.569580f, 784.466675f)); // -53251
111 points.push_back(core::vector3df(-686.217651f, 288.141174f, 762.965576f)); // -54015
112 points.push_back(core::vector3df(-679.685059f, 365.095612f, 756.551453f)); // -54733
113 points.push_back(core::vector3df(-671.317871f, 447.360107f, 749.394592f)); // -55588
114 points.push_back(core::vector3df(-669.468445f, 583.335632f, 747.711853f)); // -56178
115 points.push_back(core::vector3df(-667.611267f, 727.313232f, 746.018250f)); // -56757
116 points.push_back(core::vector3df(-665.853210f, 862.791931f, 744.436096f)); // -57859
117 points.push_back(core::vector3df(-642.649597f, 1026.047607f, 724.259827f)); // -59705
118 points.push_back(core::vector3df(-517.793884f, 838.396790f, 490.326050f)); // -60983
119 points.push_back(core::vector3df(-474.387299f, 715.691467f, 344.639984f)); // -61629
120 points.push_back(core::vector3df(-444.600250f, 601.155701f, 180.938095f)); // -62319
121 points.push_back(core::vector3df(-414.808899f, 479.691406f, 4.866660f)); // -63048
122 points.push_back(core::vector3df(-410.418945f, 429.642242f, -134.332687f)); // -63757
123 points.push_back(core::vector3df(-399.837585f, 411.498383f, -349.350983f)); // -64418
124 points.push_back(core::vector3df(-390.756653f, 403.970093f, -524.454407f)); // -65005
125 points.push_back(core::vector3df(-334.864227f, 350.065491f, -732.397400f)); // -65701
126 points.push_back(core::vector3df(-195.253387f, 349.577209f, -812.475891f)); // -66335
127 points.push_back(core::vector3df(16.255573f, 363.743134f, -833.800415f)); // -67170
128 points.push_back(core::vector3df(234.940964f, 352.957825f, -820.150696f)); // -67939
129 points.push_back(core::vector3df(436.797668f, 349.236450f, -816.914185f)); // -68596
130 points.push_back(core::vector3df(575.236206f, 356.244812f, -719.788513f)); // -69166
131 points.push_back(core::vector3df(594.131042f, 387.173828f, -609.675598f)); // -69744
132 points.push_back(core::vector3df(617.615234f, 412.002899f, -326.174072f)); // -70640
133 points.push_back(core::vector3df(606.456848f, 403.221954f, -104.179291f)); // -71390
134 points.push_back(core::vector3df(610.958252f, 407.037750f, 117.209778f)); // -72085
135 points.push_back(core::vector3df(597.956909f, 395.167877f, 345.942200f)); // -72817
136 points.push_back(core::vector3df(587.383118f, 391.444519f, 566.098633f)); // -73477
137 points.push_back(core::vector3df(559.572449f, 371.991333f, 777.689453f)); // -74124
138 points.push_back(core::vector3df(423.753204f, 329.990051f, 925.859741f)); // -74941
139 points.push_back(core::vector3df(247.520050f, 252.818954f, 935.311829f)); // -75651
140 points.push_back(core::vector3df(114.756012f, 199.799759f, 805.014160f));
141 points.push_back(core::vector3df(96.783348f, 181.639481f, 648.188110f));
142 points.push_back(core::vector3df(97.865623f, 138.905975f, 484.812561f));
143 points.push_back(core::vector3df(99.612457f, 102.463669f, 347.603210f));
144 points.push_back(core::vector3df(99.0f, 95.0f, 347.0f));
145 points.push_back(core::vector3df(99.0f, 90.0f, 347.0f));
146 points.push_back(core::vector3df(99.0f, 85.0f, 347.0f));
147 points.push_back(core::vector3df(99.0f, 80.0f, 347.0f));
148 points.push_back(core::vector3df(99.0f, 75.0f, 347.0f));
149 points.push_back(core::vector3df(99.0f, 75.0f, 347.0f));
150 points.push_back(core::vector3df(99.0f, 75.0f, 347.0f));
151 timeForThisScene = (points.size() - 2) * 1000;
152 camera = sm->addCameraSceneNode(0, points[0], core::vector3df(0, 400, 0));
153 sa = sm->createFollowSplineAnimator(device->getTimer()->getTime(), points, 1.0f, 0.6f, false, false);
154 camera->addAnimator(sa);
155 sa->drop();
156 }
157 break;
158
159 case 2: // panorama camera
160 {
161 core::array<core::vector3df> points;
162
163 camera->setTarget(core::vector3df(100, 145, -80));
164
165 points.push_back(core::vector3df(99.0f, 75.0f, 347.0f));
166 points.push_back(core::vector3df(100.0f, 75.0f, 347.0f));
167 points.push_back(core::vector3df(105.0f, 75.0f, 347.0f));
168 points.push_back(core::vector3df(110.0f, 70.0f, 347.0f));
169 points.push_back(core::vector3df(115.0f, 70.0f, -160.0f));
170 points.push_back(core::vector3df(120.0f, 70.0f, -160.0f));
171 points.push_back(core::vector3df(125.0f, 65.0f, -160.0f));
172 points.push_back(core::vector3df(130.0f, 65.0f, -160.0f));
173 points.push_back(core::vector3df(135.0f, 65.0f, -160.0f));
174 points.push_back(core::vector3df(150.0f, 170.0f, -160.0f));
175 points.push_back(core::vector3df(150.0f, 170.0f, -160.0f));
176 points.push_back(core::vector3df(150.0f, 170.0f, -160.0f));
177 timeForThisScene = (points.size() - 2) * 1000;
178 sa = sm->createFollowSplineAnimator(device->getTimer()->getTime(), points, 1.0f, 0.6f, false, false);
179 camera->addAnimator(sa);
180 sa->drop();
181 }
182 break;
183
184 case 3: // interactive, go around
185 {
186 if (camera)
187 {
188 sm->setActiveCamera(0);
189 camera->remove();
190 camera = 0;
191 }
192 timeForThisScene = -1;
193
194 gld->camera = addExtantzCamera(sm, NULL, -1);
195 camera = gld->camera;
196 camera->setPosition(core::vector3df(108, 140, -140));
197 camera->setFarValue(5000.0f);
198 gld->move = getCameraMove(gld->camera);
199
200 scene::ISceneNodeAnimatorCollisionResponse* collider =
201 sm->createCollisionResponseAnimator(metaSelector, camera, core::vector3df(25, 50, 25), core::vector3df(0, quakeLevelMesh ? -10.f : 0.0f, 0), core::vector3df(0, 45, 0), 0.005f);
202 camera->addAnimator(collider);
203 collider->drop();
204 }
205 break;
206 }
207
208 sceneStartTime = device->getTimer()->getTime();
209}
210
211
212void CDemo::loadSceneData()
213{
214 // load quake level
215
216 video::IVideoDriver* driver = device->getVideoDriver();
217 scene::ISceneManager* sm = device->getSceneManager();
218
219 // Quake3 Shader controls Z-Writing
220 sm->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
221
222 quakeLevelMesh = (scene::IQ3LevelMesh*) sm->getMesh("maps/20kdm2.bsp");
223
224 if (quakeLevelMesh)
225 {
226 u32 i;
227
228 //move all quake level meshes (non-realtime)
229 core::matrix4 m;
230 m.setTranslation(core::vector3df(-1300,-70,-1249));
231
232 for (i = 0; i != scene::quake3::E_Q3_MESH_SIZE; ++i)
233 sm->getMeshManipulator()->transform(quakeLevelMesh->getMesh(i), m);
234
235 quakeLevelNode = sm->addOctreeSceneNode(quakeLevelMesh->getMesh( scene::quake3::E_Q3_MESH_GEOMETRY));
236 if (quakeLevelNode)
237 {
238 //quakeLevelNode->setPosition(core::vector3df(-1300, -70, -1249));
239 quakeLevelNode->setVisible(true);
240
241 // create map triangle selector
242 mapSelector = sm->createOctreeTriangleSelector(quakeLevelMesh->getMesh(0), quakeLevelNode, 128);
243
244 // if not using shader and no gamma it's better to use more lighting, because
245 // quake3 level are usually dark
246 quakeLevelNode->setMaterialType(video::EMT_LIGHTMAP_M4);
247
248 // set additive blending if wanted
249 if (additive)
250 quakeLevelNode->setMaterialType(video::EMT_LIGHTMAP_ADD);
251 }
252
253 // the additional mesh can be quite huge and is unoptimized
254 scene::IMesh *additional_mesh = quakeLevelMesh->getMesh(scene::quake3::E_Q3_MESH_ITEMS);
255
256 for (i = 0; i != additional_mesh->getMeshBufferCount(); ++i)
257 {
258 scene::IMeshBuffer *meshBuffer = additional_mesh->getMeshBuffer(i);
259 const video::SMaterial &material = meshBuffer->getMaterial();
260
261 //! The ShaderIndex is stored in the material parameter
262 s32 shaderIndex = (s32) material.MaterialTypeParam2;
263
264 // the meshbuffer can be rendered without additional support, or it has no shader
265 const scene::quake3::IShader *shader = quakeLevelMesh->getShader(shaderIndex);
266 if (0 == shader)
267 {
268 continue;
269 }
270 // Now add the MeshBuffer(s) with the current Shader to the Manager
271 sm->addQuake3SceneNode(meshBuffer, shader);
272 }
273 }
274
275 // load sydney model and create 2 instances
276
277 scene::IAnimatedMesh *mesh = 0;
278 mesh = sm->getMesh("media/Irrlicht/sydney.md2");
279 if (mesh)
280 {
281 model1 = sm->addAnimatedMeshSceneNode(mesh);
282 if (model1)
283 {
284 model1->setMaterialTexture(0, driver->getTexture("media/Irrlicht/sydney.bmp"));
285 model1->setPosition(core::vector3df(100, 40, -80));
286 model1->setScale(core::vector3df(2, 2, 2));
287 model1->setMD2Animation(scene::EMAT_STAND);
288 model1->setMaterialFlag(video::EMF_LIGHTING, true);
289 model1->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
290 model1->addShadowVolumeSceneNode();
291 }
292
293 model2 = sm->addAnimatedMeshSceneNode(mesh);
294 if (model2)
295 {
296 model2->setMaterialTexture(0, driver->getTexture("media/Irrlicht/spheremap.jpg"));
297 model2->setPosition(core::vector3df(180, 15, -60));
298 model2->setScale(core::vector3df(2, 2, 2));
299 model2->setMD2Animation(scene::EMAT_RUN);
300 model2->setMaterialFlag(video::EMF_LIGHTING, false);
301 model2->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
302 model2->setMaterialType(video::EMT_SPHERE_MAP);
303 model2->addShadowVolumeSceneNode();
304 }
305 }
306
307 scene::ISceneNodeAnimator *anim = 0;
308
309 // create sky box
310 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
311 skyboxNode = sm->addSkyBoxSceneNode(
312 driver->getTexture("media/Irrlicht/irrlicht2_up.jpg"),
313 driver->getTexture("media/Irrlicht/irrlicht2_dn.jpg"),
314 driver->getTexture("media/Irrlicht/irrlicht2_lf.jpg"),
315 driver->getTexture("media/Irrlicht/irrlicht2_rt.jpg"),
316 driver->getTexture("media/Irrlicht/irrlicht2_ft.jpg"),
317 driver->getTexture("media/Irrlicht/irrlicht2_bk.jpg"));
318 driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
319
320 // create walk-between-portals animation
321 core::vector3df waypoint[2];
322 waypoint[0].set(-150, 40, 100);
323 waypoint[1].set(350, 40, 100);
324
325 if (model2)
326 {
327 anim = device->getSceneManager()->createFlyStraightAnimator(waypoint[0], waypoint[1], 2000, true);
328 model2->addAnimator(anim);
329 anim->drop();
330 }
331
332 // create animation for portals;
333 core::array<video::ITexture*> textures;
334 for (s32 g=1; g<8; ++g)
335 {
336 core::stringc tmp("media/Irrlicht/portal");
337 tmp += g;
338 tmp += ".bmp";
339 video::ITexture* t = driver->getTexture(tmp);
340 textures.push_back(t);
341 }
342
343 anim = sm->createTextureAnimator(textures, 100);
344
345 // create portals
346 scene::IBillboardSceneNode* bill = 0;
347 for (int r = 0; r < 2; ++r)
348 {
349 bill = sm->addBillboardSceneNode(0, core::dimension2d<f32>(100, 100), waypoint[r]+ core::vector3df(0, 20, 0));
350 bill->setMaterialFlag(video::EMF_LIGHTING, false);
351 bill->setMaterialTexture(0, driver->getTexture("media/Irrlicht/portal1.bmp"));
352 bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
353 bill->addAnimator(anim);
354 }
355
356 anim->drop();
357
358 // create circle flying dynamic light with transparent billboard attached
359 scene::ILightSceneNode *light = 0;
360
361 light = sm->addLightSceneNode(0, core::vector3df(0, 0, 0), video::SColorf(1.0f, 1.0f, 1.f, 1.0f), 500.f);
362 anim = sm->createFlyCircleAnimator(core::vector3df(100, 150, 80), 80.0f, 0.0005f);
363
364 light->addAnimator(anim);
365 anim->drop();
366
367 bill = device->getSceneManager()->addBillboardSceneNode(light, core::dimension2d<f32>(40, 40));
368 bill->setMaterialFlag(video::EMF_LIGHTING, false);
369 bill->setMaterialTexture(0, driver->getTexture("media/Irrlicht/particlewhite.bmp"));
370 bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
371
372 // create meta triangle selector with all triangles selectors in it.
373 metaSelector = sm->createMetaTriangleSelector();
374 metaSelector->addTriangleSelector(mapSelector);
375
376 // create camp fire
377 campFire = sm->addParticleSystemSceneNode(false);
378 campFire->setPosition(core::vector3df(100, 120, 600));
379 campFire->setScale(core::vector3df(2, 2, 2));
380
381 scene::IParticleEmitter *em = campFire->createBoxEmitter(core::aabbox3d<f32>(-7, 0, -7, 7, 1, 7), core::vector3df(0.0f, 0.06f, 0.0f), 80, 100, video::SColor(1, 255, 255, 255), video::SColor(1, 255, 255, 255), 800, 2000);
382 em->setMinStartSize(core::dimension2d<f32>(20.0f, 10.0f));
383 em->setMaxStartSize(core::dimension2d<f32>(20.0f, 10.0f));
384 campFire->setEmitter(em);
385 em->drop();
386
387 scene::IParticleAffector *paf = campFire->createFadeOutParticleAffector();
388 campFire->addAffector(paf);
389 paf->drop();
390
391 campFire->setMaterialFlag(video::EMF_LIGHTING, false);
392 campFire->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
393 campFire->setMaterialTexture(0, driver->getTexture("media/Irrlicht/fireball.bmp"));
394 campFire->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
395}
396
397
398void CDemo::shoot()
399{
400 scene::ISceneManager *sm = device->getSceneManager();
401 scene::ICameraSceneNode *camera = sm->getActiveCamera();
402
403 if ((!camera) || (!mapSelector))
404 return;
405
406 SParticleImpact imp;
407 imp.when = 0;
408
409 // get line of camera
410 core::vector3df start = camera->getPosition();
411 core::vector3df end = (camera->getTarget() - start);
412 end.normalize();
413 start += end * 8.0f;
414 end = start + (end * camera->getFarValue());
415
416 core::triangle3df triangle;
417
418 core::line3d<f32> line(start, end);
419
420 // get intersection point with map
421 scene::ISceneNode* hitNode;
422 if (sm->getSceneCollisionManager()->getCollisionPoint(line, mapSelector, end, triangle, hitNode))
423 {
424 // collides with wall
425 core::vector3df out = triangle.getNormal();
426 out.setLength(0.03f);
427
428 imp.when = 1;
429 imp.outVector = out;
430 imp.pos = end;
431 }
432 else
433 {
434 // doesnt collide with wall
435 core::vector3df start = camera->getPosition();
436 core::vector3df end = (camera->getTarget() - start);
437 end.normalize();
438 start += end * 8.0f;
439 end = start + (end * camera->getFarValue());
440 }
441
442 // create fire ball
443 scene::ISceneNode *node = 0;
444 node = sm->addBillboardSceneNode(0, core::dimension2d<f32>(25, 25), start);
445
446 node->setMaterialFlag(video::EMF_LIGHTING, false);
447 node->setMaterialTexture(0, device->getVideoDriver()->getTexture("media/Irrlicht/fireball.bmp"));
448 node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
449
450 f32 length = (f32)(end - start).getLength();
451 const f32 speed = 0.6f;
452 u32 time = (u32) (length / speed);
453
454 scene::ISceneNodeAnimator *anim = 0;
455
456 // set flight line
457 anim = sm->createFlyStraightAnimator(start, end, time);
458 node->addAnimator(anim);
459 anim->drop();
460
461 anim = sm->createDeleteAnimator(time);
462 node->addAnimator(anim);
463 anim->drop();
464
465 if (imp.when)
466 {
467 // create impact note
468 imp.when = device->getTimer()->getTime() + (time - 100);
469 Impacts.push_back(imp);
470 }
471}
472
473
474void CDemo::createParticleImpacts()
475{
476 u32 now = device->getTimer()->getTime();
477 scene::ISceneManager *sm = device->getSceneManager();
478
479 for (s32 i = 0; i < (s32) Impacts.size(); ++i)
480 if (now > Impacts[i].when)
481 {
482 // create smoke particle system
483 scene::IParticleSystemSceneNode *pas = 0;
484
485 pas = sm->addParticleSystemSceneNode(false, 0, -1, Impacts[i].pos);
486
487 pas->setParticleSize(core::dimension2d<f32>(10.0f, 10.0f));
488
489 scene::IParticleEmitter* em = pas->createBoxEmitter(core::aabbox3d<f32>(-5, -5, -5, 5, 5, 5), Impacts[i].outVector, 20, 40, video::SColor(50, 255, 255, 255), video::SColor(50, 255, 255, 255), 1200, 1600, 20);
490 pas->setEmitter(em);
491 em->drop();
492
493 scene::IParticleAffector *paf = campFire->createFadeOutParticleAffector();
494 pas->addAffector(paf);
495 paf->drop();
496
497 pas->setMaterialFlag(video::EMF_LIGHTING, false);
498 pas->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
499 pas->setMaterialTexture(0, device->getVideoDriver()->getTexture("media/Irrlicht/smoke.bmp"));
500 pas->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
501
502 scene::ISceneNodeAnimator *anim = sm->createDeleteAnimator(2000);
503 pas->addAnimator(anim);
504 anim->drop();
505
506 // delete entry
507 Impacts.erase(i);
508 i--;
509 }
510}
diff --git a/src/extantz/CDemo.h b/src/extantz/CDemo.h
new file mode 100644
index 0000000..035c01f
--- /dev/null
+++ b/src/extantz/CDemo.h
@@ -0,0 +1,63 @@
1// This is a Demo of the Irrlicht Engine (c) 2006 by N.Gebhardt.
2// This file is not documented.
3
4#ifndef __C_DEMO_H_INCLUDED__
5#define __C_DEMO_H_INCLUDED__
6
7#ifdef _IRR_WINDOWS_
8#include <windows.h>
9#endif
10
11const int CAMERA_COUNT = 7;
12
13class CDemo : public IEventReceiver
14{
15public:
16
17 CDemo(GLData *gld, bool additive);
18
19 ~CDemo();
20
21 void setup(GLData *gld);
22 void preDraw(GLData *gld, u32 now);
23
24 virtual bool OnEvent(const SEvent& event);
25
26private:
27
28 void createLoadingScreen();
29 void loadSceneData();
30 void switchToNextScene(GLData *gld);
31 void shoot();
32 void createParticleImpacts();
33
34 bool additive;
35 IrrlichtDevice *device;
36
37 struct SParticleImpact
38 {
39 u32 when;
40 core::vector3df pos;
41 core::vector3df outVector;
42 };
43
44 int currentScene;
45
46 scene::IQ3LevelMesh* quakeLevelMesh;
47 scene::ISceneNode* quakeLevelNode;
48 scene::ISceneNode* skyboxNode;
49 scene::IAnimatedMeshSceneNode* model1;
50 scene::IAnimatedMeshSceneNode* model2;
51 scene::IParticleSystemSceneNode* campFire;
52
53 scene::IMetaTriangleSelector* metaSelector;
54 scene::ITriangleSelector* mapSelector;
55
56 s32 sceneStartTime;
57 s32 timeForThisScene;
58
59 core::array<SParticleImpact> Impacts;
60};
61
62#endif
63
diff --git a/src/extantz/build.lua b/src/extantz/build.lua
new file mode 100755
index 0000000..0bb2a0b
--- /dev/null
+++ b/src/extantz/build.lua
@@ -0,0 +1,26 @@
1#!/usr/bin/env lua
2
3local dir = ...
4
5if 'nil' == type(dir) then
6 local build, err = loadfile('../../build.lua')
7 if build then
8 setfenv(build, getfenv(2))
9 build(2)
10 else
11 print("ERROR - " .. err)
12 end
13 dir = workingDir
14end
15
16CFLAGS = CFLAGS .. ' -I../../libraries/irrlicht-1.8.1/include -I/usr/X11R6/include'
17LDFLAGS = LDFLAGS .. ' -L../../libraries/irrlicht-1.8.1/lib/Linux'
18libs = libs .. ' -lIrrlicht -lGL -lbz2'
19
20removeFiles(dir, {'../../extantz', 'crappisspuke.o', 'CDemo.o', 'extantzCamera.o', '../../media/extantz.edj'})
21
22runCommand('edje_cc', dir, 'edje_cc ' .. EDJE_FLAGS .. ' extantz.edc ../../media/extantz.edj')
23runCommand('Irrlicht files', dir, 'g++ ' .. CFLAGS .. ' -O3 -ffast-math -c crappisspuke.cpp -o crappisspuke.o ' .. LDFLAGS)
24runCommand(nil, dir, 'g++ ' .. CFLAGS .. ' -O3 -ffast-math -c CDemo.cpp -o CDemo.o ' .. LDFLAGS)
25runCommand('extantz', dir, 'g++ ' .. CFLAGS .. ' -O3 -ffast-math -c extantzCamera.cpp -o extantzCamera.o ' .. LDFLAGS)
26runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' extantz.c crappisspuke.o CDemo.o extantzCamera.o -o ../../extantz ' .. LDFLAGS .. ' ' .. libs)
diff --git a/src/extantz/crappisspuke.cpp b/src/extantz/crappisspuke.cpp
new file mode 100644
index 0000000..2af9dde
--- /dev/null
+++ b/src/extantz/crappisspuke.cpp
@@ -0,0 +1,294 @@
1
2#include <irrlicht.h>
3#include "extantz.h"
4#include "CDemo.h"
5
6
7SExposedVideoData videoData;
8
9IAnimatedMeshSceneNode *node;
10CDemo *myDemo;
11// This is the movement speed in units per second.
12const f32 MOVEMENT_SPEED = 5.f;
13// In order to do framerate independent movement, we have to know
14// how long it was since the last frame
15u32 then;
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21EAPI int startIrr(GLData *gld)
22{
23 SIrrlichtCreationParameters params;
24 IrrlichtDevice *device;
25 IVideoDriver *driver;
26 ISceneManager *smgr;
27 bool additive = true;
28
29 if (!gld->useIrr)
30 return 1; // Return 1 so that the caller stops asking on each frame.
31
32#if USE_IRR
33 void *display = NULL;
34 unsigned long sfc = 0;
35 void *ctx = NULL;
36// Evas_GL_API *gl = gld->glApi;
37
38#if USE_DEMO
39 myDemo = new CDemo(gld, additive);
40#endif
41
42/* Raster says -
434. evas exposes an opengl-es2 api. any existing engine needs to be adapted to
44use this. that's pretty much the end of that. if the engine doesn't have a
45gles2 port.. it will need one. once it has one, then it is a simple matter of
46replacing all the gl calls as follows:
47
48glDrawArrays() -> api->glDrawArrays()
49glBindBuffer() -> api->glBindBuffer()
50
51you could make the port switchable with a macro:
52
53#ifdef EVASGL
54#define EG() my_evas_gl_api->
55#else
56#define EG()
57#endif
58
59then fix up all the gl calls to be
60
61EG()glDrawArrays()
62EG()glBindBuffer()
63
64etc.
65
66doing the above allows evas to decide how to share context. it may allocate a
67separate context or share its own. either way as far as the evasgl api user is
68concerned.. they get their own private context to play with. if it does NOT do
69the above (use the api exposed by evas gl) then wrapping can't context switches
70can't work. all gl calls HAVE to go through the wrapped api to work right. this
71is because we can't REPLACE the internals of the gl driver which otherwise
72would be managing context and state all internally and we have zero access to
73that - especially with closed drivers. we'd end up writing a proxy gl library
74which conflicts with real gl symbol-wise (thus taking over and replacing
75normal gl calls) and i know i have no interest in maintaining a separate
76libGLwhatever.so that is an exact copy of gl and it's api's just to wrap it
77when we expose that wrapper without symbol complications via evas gl.
78
795. the engine will need to be adapted so the draw function is callable - eg by
80elm_glview. then it's easy to switch where rendering happens. evas offers a fast
81path to avoid buffer copies and make the gl view draw part of the evas
82rendering path directly. this would offer almost zero overhead vs doing it
83directly with egl/gles etc. to your backbuffer yourself, BUT gets you the bonus
84of having your 3d view as part of a larger scenegraph. combine 2 or 3 of them
85in a single window. overlay with evas objects or elm widgets for hud etc. all
86for free. this also implies the engine has to integrate to the efl mainloop
87etc. of course.
88*/
89
90
91 sfc = ecore_evas_window_get(gld->ee);
92 // This is the way Raster wants me to do things, but these functions are not actually available. Pffft
93// ctx = gl->glGetCurrentContext();
94// display = gl->glGetCurrentDisplay();
95 ctx = glXGetCurrentContext();
96 display = glXGetCurrentDisplay();
97 /* For using a pre existing X11 window (with optional OpenGL). */
98 videoData = SExposedVideoData();
99 videoData.OpenGLLinux.X11Display = display; // void * - Connection to the X server.
100 videoData.OpenGLLinux.X11Window = sfc; // unsigned long - Specifies a GLX drawable. Must be either an X window ID or a GLX pixmap ID.
101 videoData.OpenGLLinux.X11Context = ctx; // void * - Specifies a GLX rendering context that is to be attached to drawable.
102
103 /*
104 The most important function of the engine is the createDevice()
105 function. The IrrlichtDevice is created by it, which is the root
106 object for doing anything with the engine. createDevice() has 7
107 parameters:
108
109 - deviceType: Type of the device. This can currently be the Null-device,
110 one of the two software renderers, D3D8, D3D9, or OpenGL. In this
111 example we use EDT_SOFTWARE, but to try out, you might want to
112 change it to EDT_BURNINGSVIDEO, EDT_NULL, EDT_DIRECT3D8,
113 EDT_DIRECT3D9, or EDT_OPENGL.
114
115 - windowSize: Size of the Window or screen in FullScreenMode to be
116 created. In this example we use 640x480.
117
118 - bits: Amount of color bits per pixel. This should be 16 or 32. The
119 parameter is often ignored when running in windowed mode.
120
121 - fullscreen: Specifies if we want the device to run in fullscreen mode
122 or not.
123
124 - stencilbuffer: Specifies if we want to use the stencil buffer (for
125 drawing shadows).
126
127 - vsync: Specifies if we want to have vsync enabled, this is only useful
128 in fullscreen mode.
129
130 - eventReceiver: An object to receive events. We do not want to use this
131 parameter here, and set it to 0.
132
133 Always check the return value to cope with unsupported drivers,
134 dimensions, etc.
135 */
136
137 params.DeviceType = EIDT_X11; // EIDT_BEST might be preferable.
138 if (ctx)
139 params.DriverType = video::EDT_OPENGL;
140 else
141 params.DriverType = video::EDT_BURNINGSVIDEO;
142 params.WindowSize = dimension2d<u32>(gld->sfc_w, gld->sfc_h);
143 params.Bits = 32; // Ignored in windowed mode?
144 params.ZBufferBits = 16; // Default 16.
145 params.Fullscreen = false; // The default anyway.
146 params.Stencilbuffer = false; // For shadows.
147 params.Vsync = false;
148 params.AntiAlias=true;
149 params.WithAlphaChannel = true;
150 params.IgnoreInput = true;
151 params.EventReceiver = myDemo; // Probably useless, EFL might not let Irrlicht grab the input.
152 params.WindowId = (void *) videoData.OpenGLLinux.X11Window;
153 params.VideoData = &videoData;
154
155 device = createDeviceEx(params);
156
157 if (!device)
158 return 0;
159 gld->device = device;
160
161 /*
162 Get a pointer to the VideoDriver and the SceneManager so that we do not always have to write
163 device->getVideoDriver() or device->getSceneManager().
164 */
165 driver = device->getVideoDriver(); gld->driver = driver;
166 smgr = device->getSceneManager(); gld->smgr = smgr;
167
168 // FIXME - this is what makes the window vanish in EFL 1.8, but worked fine in 1.7 I think.
169// device->setResizable(true);
170 driver->OnResize(dimension2d<u32>(gld->img_w, gld->img_h));
171 // Just gives me a blank screen. grrrr
172// driver->setViewPort(rect<s32>(0, 0, gld->img_w, gld->img_h));
173
174 // set ambient light
175 smgr->setAmbientLight (video::SColorf(0x00c0c0c0));
176
177#if USE_DEMO
178 myDemo->setup(gld);
179#else
180 /*
181 To show something interesting, we load a Quake 2 model and display it.
182 We only have to get the Mesh from the Scene Manager with getMesh() and add
183 a SceneNode to display the mesh with addAnimatedMeshSceneNode(). We
184 check the return value of getMesh() to become aware of loading problems
185 and other errors.
186
187 Instead of writing the filename sydney.md2, it would also be possible
188 to load a Maya object file (.obj), a complete Quake3 map (.bsp) or any
189 other supported file format. By the way, that cool Quake 2 model
190 called sydney was modelled by Brian Collins.
191 */
192 IAnimatedMesh* mesh = smgr->getMesh("media/Irrlicht/sydney.md2");
193 if (!mesh)
194 {
195 device->drop();
196 return 0;
197 }
198 node = smgr->addAnimatedMeshSceneNode(mesh);
199
200 /*
201 To let the mesh look a little bit nicer, we change its material. We
202 disable lighting because we do not have a dynamic light in here, and
203 the mesh would be totally black otherwise. Then we set the frame loop,
204 such that the predefined STAND animation is used. And last, we apply a
205 texture to the mesh. Without it the mesh would be drawn using only a
206 color.
207 */
208 if (node)
209 {
210// node->setMaterialFlag(EMF_LIGHTING, false);
211 node->setMD2Animation(scene::EMAT_STAND);
212 node->setMaterialTexture(0, driver->getTexture("media/Irrlicht/sydney.bmp"));
213 }
214
215 /*
216 To look at the mesh, we place a camera into 3d space at the position
217 (0, 30, -40). The camera looks from there to (0,5,0), which is
218 approximately the place where our md2 model is.
219 */
220 smgr->addCameraSceneNode(0, vector3df(50, 70, -65), vector3df(0, 50, 0));
221#endif
222
223 then = device->getTimer()->getTime();
224#endif
225 return 1;
226}
227
228EAPI void drawIrr_start(GLData *gld)
229{
230 if (gld->useIrr)
231 {
232 IrrlichtDevice *device = gld->device;
233 IVideoDriver *driver = gld->driver;
234 ISceneManager *smgr = gld->smgr;
235
236 // Increase virtual timer time, instead of device->run() if doing our own input processing.
237 device->getTimer()->tick();
238
239 // Work out a frame delta time.
240 const u32 now = device->getTimer()->getTime();
241// const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
242 then = now;
243
244
245#if USE_DEMO
246 myDemo->preDraw(gld, now);
247#else
248 core::vector3df nodePosition = node->getPosition();
249// nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
250 node->setPosition(nodePosition);
251#endif
252
253 /*
254 Anything can be drawn between a beginScene() and an endScene()
255 call. The beginScene() call clears the screen with a color and
256 the depth buffer, if desired. Then we let the Scene Manager and
257 the GUI Environment draw their content. With the endScene()
258 call everything is presented on the screen.
259 */
260 driver->beginScene(true, true, SColor(255, 255, 255, 255), videoData, NULL); // This does the context change, then clearBuffers()
261
262 smgr->drawAll();
263 }
264}
265
266EAPI void drawIrr_end(GLData *gld)
267{
268 IVideoDriver *driver = gld->driver;
269
270 if (gld->useIrr)
271 driver->endScene();
272}
273
274EAPI void finishIrr(GLData *gld)
275{
276 IrrlichtDevice *device = gld->device;
277
278 /*
279 After we are done with the render loop, we have to delete the Irrlicht
280 Device created before with createDevice(). In the Irrlicht Engine, you
281 have to delete all objects you created with a method or function which
282 starts with 'create'. The object is simply deleted by calling ->drop().
283 See the documentation at irr::IReferenceCounted::drop() for more
284 information.
285 */
286 if (gld->useIrr)
287 device->drop();
288}
289
290
291#ifdef __cplusplus
292}
293#endif
294
diff --git a/src/extantz/extantz.c b/src/extantz/extantz.c
new file mode 100644
index 0000000..09b5196
--- /dev/null
+++ b/src/extantz/extantz.c
@@ -0,0 +1,1588 @@
1#include "extantz.h"
2
3
4int _log_domain = -1;
5
6Eina_Hash *grids;
7Eina_Hash *viewers;
8
9static char *gridTest[][3] =
10{
11 {"3rd Rock Grid", "http://grid.3rdrockgrid.com:8002/", "http://grid.3rdrockgrid.com/3rg_login"},
12 {"Infinite Grid", "http://grid.infinitegrid.org:8002/", "http://www.infinitegrid.org/loginscreen.php"},
13 {"Second Life Grid", "https://login.agni.lindenlab.com/cgi-bin/login.cgi", "http://secondlife.com/"},
14 {NULL, NULL, NULL}
15};
16
17static char *accountTest[][3] =
18{
19 {"3rd Rock Grid", "onefang rejected", "password"},
20 {"Infinite Grid", "infinite onefang", "MyB1GSecrit"},
21 {"Infinite Grid", "onefang rejected", "MySecrit"},
22 {NULL, NULL, NULL}
23};
24
25
26static char *viewerTest[][3] =
27{
28 {"Imprudence", "1.4.0 beta 3", ""},
29 {"Kokua", "3.4.4.25633", ""},
30 {"meta-impy", "1.4.0 beta 1.5", ""},
31 {"SL", "v3", ""},
32 {NULL, NULL, NULL}
33};
34
35
36static Elm_Genlist_Item_Class *grid_gic = NULL;
37static Elm_Genlist_Item_Class *account_gic = NULL;
38static Elm_Genlist_Item_Class *viewer_gic = NULL;
39
40//static const char *img1 = PACKAGE_DATA_DIR "/media/plant_01.jpg";
41//static const char *img2 = PACKAGE_DATA_DIR "/media/sky_01.jpg";
42static const char *img3 = PACKAGE_DATA_DIR "/media/rock_01.jpg";
43
44
45#define EPHYSICS_TEST_THEME "extantz"
46
47
48#if DO_GEARS
49//--------------------------------//
50// Gear Stuff.
51
52static GLfloat *vert(GLfloat *p, GLfloat x, GLfloat y, GLfloat z, GLfloat *n)
53{
54 p[0] = x;
55 p[1] = y;
56 p[2] = z;
57 p[3] = n[0];
58 p[4] = n[1];
59 p[5] = n[2];
60
61 return p + 6;
62}
63
64/* Draw a gear wheel. You'll probably want to call this function when
65 * building a display list since we do a lot of trig here.
66 *
67 * Input: inner_radius - radius of hole at center
68 * outer_radius - radius at center of teeth
69 * width - width of gear
70 * teeth - number of teeth
71 * tooth_depth - depth of tooth
72 */
73static Gear *make_gear(GLData *gld, GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth)
74{
75 GLint i;
76 GLfloat r0, r1, r2;
77 GLfloat da;
78 GLfloat *v;
79 Gear *gear;
80 double s[5], c[5];
81 GLfloat normal[3];
82 const int tris_per_tooth = 20;
83 Evas_GL_API *gl = gld->glApi;
84
85 gear = (Gear*)malloc(sizeof(Gear));
86 if (gear == NULL)
87 return NULL;
88
89 r0 = inner_radius;
90 r1 = outer_radius - tooth_depth / 2.0;
91 r2 = outer_radius + tooth_depth / 2.0;
92
93 da = 2.0 * M_PI / teeth / 4.0;
94
95 gear->vertices = calloc(teeth * tris_per_tooth * 3 * 6, sizeof *gear->vertices);
96 s[4] = 0;
97 c[4] = 1;
98 v = gear->vertices;
99 for (i = 0; i < teeth; i++)
100 {
101 s[0] = s[4];
102 c[0] = c[4];
103 s[1] = sin(i * 2.0 * M_PI / teeth + da);
104 c[1] = cos(i * 2.0 * M_PI / teeth + da);
105 s[2] = sin(i * 2.0 * M_PI / teeth + da * 2);
106 c[2] = cos(i * 2.0 * M_PI / teeth + da * 2);
107 s[3] = sin(i * 2.0 * M_PI / teeth + da * 3);
108 c[3] = cos(i * 2.0 * M_PI / teeth + da * 3);
109 s[4] = sin(i * 2.0 * M_PI / teeth + da * 4);
110 c[4] = cos(i * 2.0 * M_PI / teeth + da * 4);
111
112 normal[0] = 0.0;
113 normal[1] = 0.0;
114 normal[2] = 1.0;
115
116 v = vert(v, r2 * c[1], r2 * s[1], width * 0.5, normal);
117
118 v = vert(v, r2 * c[1], r2 * s[1], width * 0.5, normal);
119 v = vert(v, r2 * c[2], r2 * s[2], width * 0.5, normal);
120 v = vert(v, r1 * c[0], r1 * s[0], width * 0.5, normal);
121 v = vert(v, r1 * c[3], r1 * s[3], width * 0.5, normal);
122 v = vert(v, r0 * c[0], r0 * s[0], width * 0.5, normal);
123 v = vert(v, r1 * c[4], r1 * s[4], width * 0.5, normal);
124 v = vert(v, r0 * c[4], r0 * s[4], width * 0.5, normal);
125
126 v = vert(v, r0 * c[4], r0 * s[4], width * 0.5, normal);
127 v = vert(v, r0 * c[0], r0 * s[0], width * 0.5, normal);
128 v = vert(v, r0 * c[4], r0 * s[4], -width * 0.5, normal);
129 v = vert(v, r0 * c[0], r0 * s[0], -width * 0.5, normal);
130
131 normal[0] = 0.0;
132 normal[1] = 0.0;
133 normal[2] = -1.0;
134
135 v = vert(v, r0 * c[4], r0 * s[4], -width * 0.5, normal);
136
137 v = vert(v, r0 * c[4], r0 * s[4], -width * 0.5, normal);
138 v = vert(v, r1 * c[4], r1 * s[4], -width * 0.5, normal);
139 v = vert(v, r0 * c[0], r0 * s[0], -width * 0.5, normal);
140 v = vert(v, r1 * c[3], r1 * s[3], -width * 0.5, normal);
141 v = vert(v, r1 * c[0], r1 * s[0], -width * 0.5, normal);
142 v = vert(v, r2 * c[2], r2 * s[2], -width * 0.5, normal);
143 v = vert(v, r2 * c[1], r2 * s[1], -width * 0.5, normal);
144
145 v = vert(v, r1 * c[0], r1 * s[0], width * 0.5, normal);
146
147 v = vert(v, r1 * c[0], r1 * s[0], width * 0.5, normal);
148 v = vert(v, r1 * c[0], r1 * s[0], -width * 0.5, normal);
149 v = vert(v, r2 * c[1], r2 * s[1], width * 0.5, normal);
150 v = vert(v, r2 * c[1], r2 * s[1], -width * 0.5, normal);
151 v = vert(v, r2 * c[2], r2 * s[2], width * 0.5, normal);
152 v = vert(v, r2 * c[2], r2 * s[2], -width * 0.5, normal);
153 v = vert(v, r1 * c[3], r1 * s[3], width * 0.5, normal);
154 v = vert(v, r1 * c[3], r1 * s[3], -width * 0.5, normal);
155 v = vert(v, r1 * c[4], r1 * s[4], width * 0.5, normal);
156 v = vert(v, r1 * c[4], r1 * s[4], -width * 0.5, normal);
157
158 v = vert(v, r1 * c[4], r1 * s[4], -width * 0.5, normal);
159 }
160
161 gear->count = (v - gear->vertices) / 6;
162
163 gl->glGenBuffers(1, &gear->vbo);
164 gl->glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
165 gl->glBufferData(GL_ARRAY_BUFFER, gear->count * 6 * 4, gear->vertices, GL_STATIC_DRAW);
166
167
168 return gear;
169}
170
171static void free_gear(Gear *gear)
172{
173 free(gear->vertices);
174 free(gear);
175 gear = NULL;
176}
177
178static void multiply(GLfloat *m, const GLfloat *n)
179{
180 GLfloat tmp[16];
181 const GLfloat *row, *column;
182 div_t d;
183 int i, j;
184
185 for (i = 0; i < 16; i++)
186 {
187 tmp[i] = 0;
188 d = div(i, 4);
189 row = n + d.quot * 4;
190 column = m + d.rem;
191 for (j = 0; j < 4; j++)
192 tmp[i] += row[j] * column[j * 4];
193 }
194 memcpy(m, &tmp, sizeof tmp);
195}
196
197static void rotate(GLfloat *m, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
198{
199 double s, c;
200
201 s = sin(angle);
202 c = cos(angle);
203 GLfloat r[16] =
204 {
205 x * x * (1 - c) + c, y * x * (1 - c) + z * s, x * z * (1 - c) - y * s, 0,
206 x * y * (1 - c) - z * s, y * y * (1 - c) + c, y * z * (1 - c) + x * s, 0,
207 x * z * (1 - c) + y * s, y * z * (1 - c) - x * s, z * z * (1 - c) + c, 0,
208 0, 0, 0, 1
209 };
210
211 multiply(m, r);
212}
213
214static void translate(GLfloat *m, GLfloat x, GLfloat y, GLfloat z)
215{
216 GLfloat t[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 };
217
218 multiply(m, t);
219}
220
221static void draw_gear(GLData *gld, Gear *gear, GLfloat *m, GLfloat x, GLfloat y, GLfloat angle, const GLfloat *color)
222{
223 Evas_GL_API *gl = gld->glApi;
224 GLfloat tmp[16];
225
226 memcpy(tmp, m, sizeof tmp);
227 translate(tmp, x, y, 0);
228 rotate(tmp, 2 * M_PI * angle / 360.0, 0, 0, 1);
229 gl->glUniformMatrix4fv(gld->proj_location, 1, GL_FALSE, tmp);
230 gl->glUniform3fv(gld->light_location, 1, gld->light);
231 gl->glUniform4fv(gld->color_location, 1, color);
232
233 gl->glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
234
235 gl->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), NULL);
236 gl->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLfloat *) 0 + 3);
237 gl->glEnableVertexAttribArray(0);
238 gl->glEnableVertexAttribArray(1);
239 gl->glDrawArrays(GL_TRIANGLE_STRIP, 0, gear->count);
240}
241#endif
242
243
244static void gldata_init(GLData *gld)
245{
246 gld->useEGL = USE_EGL;
247 gld->useIrr = USE_IRR;
248
249 gld->view_rotx = -20.0;
250 gld->view_roty = -30.0;
251 gld->view_rotz = 0.0;
252 gld->angle = 0.0;
253
254 gld->light[0] = 1.0;
255 gld->light[1] = 1.0;
256 gld->light[2] = -5.0;
257}
258
259//-------------------------//
260
261
262#if DO_GEARS
263static const char vertex_shader[] =
264 "uniform mat4 proj;\n"
265 "attribute vec4 position;\n"
266 "attribute vec4 normal;\n"
267 "varying vec3 rotated_normal;\n"
268 "varying vec3 rotated_position;\n"
269 "vec4 tmp;\n"
270 "void main()\n"
271 "{\n"
272 " gl_Position = proj * position;\n"
273 " rotated_position = gl_Position.xyz;\n"
274 " tmp = proj * normal;\n"
275 " rotated_normal = tmp.xyz;\n"
276 "}\n";
277
278 static const char fragment_shader[] =
279 "#ifdef GL_ES\n"
280 "precision mediump float;\n"
281 "#endif\n"
282 "uniform vec4 color;\n"
283 "uniform vec3 light;\n"
284 "varying vec3 rotated_normal;\n"
285 "varying vec3 rotated_position;\n"
286 "vec3 light_direction;\n"
287 "vec4 white = vec4(0.5, 0.5, 0.5, 1.0);\n"
288 "void main()\n"
289 "{\n"
290 " light_direction = normalize(light - rotated_position);\n"
291 " gl_FragColor = color + white * dot(light_direction, rotated_normal);\n"
292 "}\n";
293
294static GLuint load_shader(GLData *gld, GLenum type, const char *shader_src)
295{
296 Evas_GL_API *gl = gld->glApi;
297 GLuint shader;
298 GLint compiled = 0;
299
300 // Create the shader object
301 if (!(shader = gl->glCreateShader(type))) return 0;
302 gl->glShaderSource(shader, 1, &shader_src, NULL);
303 // Compile the shader
304 gl->glCompileShader(shader);
305 gl->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
306
307 if (!compiled)
308 {
309 GLint len = 0;
310
311 gl->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
312 if (len > 1)
313 {
314 char *info = malloc(sizeof(char) * len);
315
316 if (info)
317 {
318 gl->glGetShaderInfoLog(shader, len, NULL, info);
319 printf("Error compiling shader:\n"
320 "%s\n", info);
321 free(info);
322 }
323 }
324 gl->glDeleteShader(shader);
325 return 0;
326 }
327 return shader;
328}
329
330static void gears_init(GLData *gld)
331{
332 Evas_GL_API *gl = gld->glApi;
333 GLint linked = 0;
334
335// char msg[512];
336
337 gl->glEnable(GL_CULL_FACE);
338 gl->glEnable(GL_DEPTH_TEST);
339 gl->glEnable(GL_BLEND);
340
341 // Load the vertex/fragment shaders
342 gld->vtx_shader = load_shader(gld, GL_VERTEX_SHADER, vertex_shader);
343 gld->fgmt_shader = load_shader(gld, GL_FRAGMENT_SHADER, fragment_shader);
344
345 // Create the program object
346 if (!(gld->program = gl->glCreateProgram()))
347 return;
348
349 gl->glAttachShader(gld->program, gld->vtx_shader);
350 gl->glAttachShader(gld->program, gld->fgmt_shader);
351
352 // Bind shader attributes.
353 gl->glBindAttribLocation(gld->program, 0, "position");
354 gl->glBindAttribLocation(gld->program, 1, "normal");
355
356 // Link the program
357 gl->glLinkProgram(gld->program);
358 gld->glApi->glGetProgramiv(gld->program, GL_LINK_STATUS, &linked);
359
360 if (!linked)
361 {
362 GLint len = 0;
363
364 gld->glApi->glGetProgramiv(gld->program, GL_INFO_LOG_LENGTH, &len);
365 if (len > 1)
366 {
367 char *info = malloc(sizeof(char) * len);
368
369 if (info)
370 {
371 gld->glApi->glGetProgramInfoLog(gld->program, len, NULL, info);
372 printf("Error linking program:\n%s\n", info);
373 free(info);
374 }
375 }
376 gld->glApi->glDeleteProgram(gld->program);
377 }
378
379 gl->glUseProgram(gld->program);
380 gld->proj_location = gl->glGetUniformLocation(gld->program, "proj");
381 gld->light_location = gl->glGetUniformLocation(gld->program, "light");
382 gld->color_location = gl->glGetUniformLocation(gld->program, "color");
383
384 /* make the gears */
385 gld->gear1 = make_gear(gld, 1.0, 4.0, 1.0, 20, 0.7);
386 gld->gear2 = make_gear(gld, 0.5, 2.0, 2.0, 10, 0.7);
387 gld->gear3 = make_gear(gld, 1.3, 2.0, 0.5, 10, 0.7);
388
389 gld->gearsInited = EINA_TRUE;
390}
391#endif
392
393static void _on_camera_input_down(void *data, Evas *evas, Evas_Object *obj, void *event_info)
394{
395 GLData *gld = data;
396 Evas_Event_Key_Down *ev = event_info;
397
398 if (gld->move)
399 {
400 // TODO - Careful, gld->move MIGHT be read at the other end by another thread. MIGHT, coz I really don't know at what point the camera animate routine is actually called.
401
402 // Yes, we are dealing with the horrid Evas keyboard handling FUCKING STRING COMPARES! Soooo ...
403 // TODO - make this a hash lookup dammit.
404 if (0 == strcmp(ev->key, "Escape"))
405 {
406 }
407 else if (0 == strcmp(ev->key, "Left"))
408 gld->move->r = 2.0;
409 else if (0 == strcmp(ev->key, "Right"))
410 gld->move->r = -2.0;
411 else if (0 == strcmp(ev->key, "Up"))
412 gld->move->x = 2.0;
413 else if (0 == strcmp(ev->key, "Down"))
414 gld->move->x = -2.0;
415// else if (0 == strcmp(ev->key, "Prior"))
416// ;
417// else if (0 == strcmp(ev->key, "Next"))
418// ;
419// else if (0 == strcmp(ev->key, "Home"))
420// ;
421// else if (0 == strcmp(ev->key, "End"))
422// ;
423 else if (0 == strcmp(ev->key, "space"))
424 gld->move->jump = 1.0;
425 else
426 printf("Unexpected down keystroke - %s\n", ev->key);
427 }
428 else
429 printf("Camera input not ready\n");
430}
431
432/* SL / OS camera controls
433 up / down / w / s moves avatar forward / backward
434 shifted version does the same
435 double tap triggers run mode / or fast fly mode
436 Running backwards turns your avatar to suit, walking does not.
437 left / right / a / d rotates avatar left / right, strafes in mouselook
438 shifted version turns the avatar to walk sideways, so not really a strafe.
439 So not sure if the "strafe" in mouse look turns the avatar as well?
440 PgDn / c crouch while it is held down move up in flight mode
441 PgUp jump move down in flight mode
442 Home toggle flying
443 End Nothing?
444 Esc return to third person view
445 m toggle mouse look
446 mouse wheel move view closer / further away from current focused object or avatar
447 Alt left click focus on some other object
448 Ins ???
449 Del ???
450 BS ???
451 Tab ???
452
453 Mouse look is just first person view, moving mouse looks left / right / up / down.
454 Not sure if the avatar rotates with left / right, but that's likely.
455
456 mouse moves With the left mouse button held down -
457 left / right up / down
458 ---------------------------------
459 for avatar swings avatar around zoom in and out of avatar
460 for object nothing
461 alt orbit left / right zoom in and out
462 alt ctrl orbit left / right orbit up / down
463 alt shift orbit left / right zoom in and out
464 alt ctrl shift shift view left / right / up / down
465 ctrl Nothing?
466 shift Nothing?
467 ctrl shift Nothing?
468
469 Need to also consider when looking at a moving object / avatar.
470
471 I think there are other letter keys that duplicate arrow keys and such. I'll look for them later, but I don't use them.
472 No idea what the function keys are mapped to, but think it's various non camera stuff.
473 I'm damn well leaving the Win/Command and Menu keys for the OS / window manager. lol
474 Keypad keys? Not interested, I don't have them.
475 Print Screen / SysRq, Pause / Break, other oddball keys, also not interested.
476 NOTE - gonna have an easily programmable "bind key to command" thingy, like E17s, so that can deal with other keys.
477 Should even let them be saveable so people can swap them with other people easily.
478
479 TODO - implement things like space mouse, sixaxis, phone as controller, joysticks, data gloves, etc.
480*/
481
482/* A moveRotate array of floats.
483 * X, Y, Z, and whatever the usual letters are for rotations. lol
484 * Each one means "move or rotate this much in this direction".
485 * Where 1.0 means "what ever the standard move is if that key is held down".
486 * So a keyboard move would just change it's part to 1.0 or -1.0 on key down,
487 * and back to 0.0 on key up. Or 2.0 / -2.0 if in run mode.
488 * Which would even work in fly mode.
489 * A joystick could be set to range over -2.0 to 2.0, and just set it's part directly.
490 * A mouse look rotate, well will come to that when we need to. B-)
491 * Setting the x or y to be the DIFFERENCE in window position of the mouse (-1.0 to 1.0) since the last frame.
492 *
493 * TODO - In the Elm_glview version, 2.0 seems to be correct speed for walking, but I thought 1.0 was in Evas_GL.
494 */
495
496static void _on_camera_input_up(void *data, Evas *evas, Evas_Object *obj, void *event_info)
497{
498 GLData *gld = data;
499 Evas_Event_Key_Up *ev = event_info;
500
501 if (gld->move)
502 {
503 // TODO - Careful, gld->move MIGHT be read at the other end by another thread. MIGHT, coz I really don't know at what point the camera animate routine is actually called.
504
505 // Yes, we are dealing with the horrid Evas keyboard handling FUCKING STRING COMPARES! Soooo ...
506 // TODO - make this a hash lookup dammit.
507 if (0 == strcmp(ev->key, "Escape"))
508 {
509 }
510 else if (0 == strcmp(ev->key, "Left"))
511 gld->move->r = 0.0;
512 else if (0 == strcmp(ev->key, "Right"))
513 gld->move->r = 0.0;
514 else if (0 == strcmp(ev->key, "Up"))
515 gld->move->x = 0.0;
516 else if (0 == strcmp(ev->key, "Down"))
517 gld->move->x = 0.0;
518// else if (0 == strcmp(ev->key, "Prior"))
519// ;
520// else if (0 == strcmp(ev->key, "Next"))
521// ;
522// else if (0 == strcmp(ev->key, "Home"))
523// ;
524// else if (0 == strcmp(ev->key, "End"))
525// ;
526 else if (0 == strcmp(ev->key, "space"))
527 gld->move->jump = 0.0;
528 else
529 printf("Unexpected up keystroke - %s\n", ev->key);
530 }
531 else
532 printf("Camera input not ready\n");
533}
534
535// Elm style event callback.
536static Eina_Bool _cb_event_GL(void *data, Evas_Object *obj, Evas_Object *src, Evas_Callback_Type type, void *event_info)
537{
538 GLData *gld = data;
539 Eina_Bool processed = EINA_FALSE;
540
541 switch (type)
542 {
543 case EVAS_CALLBACK_KEY_DOWN :
544 {
545 _on_camera_input_down(gld, evas_object_evas_get(obj), obj, event_info);
546 processed = EINA_TRUE;
547 break;
548 }
549
550 case EVAS_CALLBACK_KEY_UP :
551 {
552 _on_camera_input_up(gld, evas_object_evas_get(obj), obj, event_info);
553 processed = EINA_TRUE;
554 break;
555 }
556
557 default :
558 printf("Unknown GL input event.\n");
559 }
560
561 return processed;
562}
563
564// Elm inlined image windows needs this to change focus on mouse click.
565// Evas style event callback.
566static void _cb_mouse_down_elm(void *data, Evas *evas, Evas_Object *obj, void *event_info)
567{
568// GLData *gld = data;
569 Evas_Event_Mouse_Down *ev = event_info;
570
571 if (1 == ev->button)
572 elm_object_focus_set(obj, EINA_TRUE);
573}
574
575static void _resize_winwin(GLData *gld)
576{
577 Evas_Coord x, y, w, h;
578
579 evas_object_geometry_get(gld->elmGl, &x, &y, &w, &h);
580 evas_object_move(elm_win_inlined_image_object_get (gld->winwin), x, y);
581 evas_object_resize(elm_win_inlined_image_object_get(gld->winwin), w, h);
582}
583
584// Called from on_pixels (), or the Elm_gliew resize callback.
585static void _resize(GLData *gld)
586{
587 Evas_GL_API *gl = gld->glApi;
588
589 _resize_winwin(gld);
590
591#if DO_GEARS
592 GLfloat ar, m[16] = {
593 1.0, 0.0, 0.0, 0.0,
594 0.0, 1.0, 0.0, 0.0,
595 0.0, 0.0, 0.1, 0.0,
596 0.0, 0.0, 0.0, 1.0
597 };
598
599 // GL Viewport stuff. you can avoid doing this if viewport is all the
600 // same as last frame if you want
601 if (gld->img_w < gld->img_h)
602 ar = gld->img_w;
603 else
604 ar = gld->img_h;
605
606 m[0] = 0.1 * ar / gld->img_w;
607 m[5] = 0.1 * ar / gld->img_h;
608 memcpy(gld->proj, m, sizeof gld->proj);
609#endif
610
611 gl->glViewport(0, 0, (GLint) gld->img_w, (GLint) gld->img_h);
612}
613
614static void _resize_gl(Evas_Object *obj)
615{
616 int w, h;
617 GLData *gld = evas_object_data_get(obj, "gld");
618
619 elm_glview_size_get(obj, &w, &h);
620
621 gld->img_w = w;
622 gld->img_h = h;
623 _resize(gld);
624}
625
626static void on_pixels(void *data, Evas_Object *obj)
627{
628 GLData *gld = data;
629 Evas_GL_API *gl = gld->glApi;
630
631 // get the image size in case it changed with evas_object_image_size_set()
632 if (gld->r1)
633 {
634 Evas_Coord w, h;
635
636 // Poor mans resize check. coz Elm wont do it easily.
637 evas_object_image_size_get(gld->r1, &w, &h);
638 if ((gld->img_w != w) || (gld->img_h != h))
639 {
640 // No idea where this crap came from.
641 //float new_w = ((float) gld->scr_w / ((float) gld->scr_w * (float) w));
642 //float new_h = ((float) gld->scr_h / ((float) gld->scr_h * (float) h));
643
644 //gld->sfc_w = new_w;
645 //gld->sfc_h = new_h;
646 //evas_object_image_fill_set(gld->r1, 0, 0, gld->sfc_w, gld->sfc_h);
647 gld->img_w = w;
648 gld->img_h = h;
649 gld->resized = 1;
650 }
651 }
652
653 if (gld->useEGL)
654 {
655 // Yes, we DO need to do our own make current, coz aparently the Irrlicht one is useless.
656 // Hopefully Elm_GL has done this for us by now.
657 // Evas_GL needs it to.
658 if (gld->ctx)
659 evas_gl_make_current(gld->evasGl, gld->sfc, gld->ctx);
660 }
661
662 if (!gld->doneIrr)
663 gld->doneIrr = startIrr(gld); // Needs to be after gld->win is shown, and needs to be done in the render thread.
664#if DO_GEARS
665 if (!gld->gearsInited)
666 gears_init(gld);
667#endif
668
669 if (gld->resized)
670 _resize(gld);
671
672 drawIrr_start(gld);
673
674#if DO_GEARS
675 if (gld->useEGL)
676 {
677 static const GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
678 static const GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
679 static const GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
680 GLfloat m[16];
681
682 // Draw the gears.
683 if (!gld->useIrr)
684 {
685 gl->glClearColor(0.7, 0.0, 1.0, 1.0);
686 gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
687 }
688
689 memcpy(m, gld->proj, sizeof m);
690 rotate(m, 2 * M_PI * gld->view_rotx / 360.0, 1, 0, 0);
691 rotate(m, 2 * M_PI * gld->view_roty / 360.0, 0, 1, 0);
692 rotate(m, 2 * M_PI * gld->view_rotz / 360.0, 0, 0, 1);
693
694 draw_gear(gld, gld->gear1, m, -3.0, -2.0, gld->angle, red);
695 draw_gear(gld, gld->gear2, m, 3.1, -2.0, -2 * gld->angle - 9.0, green);
696 draw_gear(gld, gld->gear3, m, -3.1, 4.2, -2 * gld->angle - 25.0, blue);
697 gld->angle += 2.0;
698 }
699#endif
700
701 drawIrr_end(gld);
702
703#if USE_IR
704#else
705 // This might get done deep within drawIrr_end, but only if we are using Irrlicht.
706
707 // Optional - Flush the GL pipeline
708 gl->glFlush();
709// gl->glFinish();
710#endif
711
712 gld->resized = 0;
713}
714
715static void _draw_gl(Evas_Object *obj)
716{
717// Evas_GL_API *gl = elm_glview_gl_api_get(obj);
718 GLData *gld = evas_object_data_get(obj, "gld");
719 if (!gld) return;
720
721 on_pixels(gld, obj);
722}
723
724// Callback from Evas, also used as the general callback for deleting the GL stuff.
725static void _clean_gl(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
726{
727 GLData *gld = data;
728 Evas_GL_API *gl = gld->glApi;
729
730 ecore_animator_del(gld->animator);
731
732 if (gld->useEGL)
733 {
734 // Do a make_current before deleting all the GL stuff.
735 evas_gl_make_current(gld->evasGl, gld->sfc, gld->ctx);
736
737 }
738
739 gl->glDeleteShader(gld->vtx_shader);
740 gl->glDeleteShader(gld->fgmt_shader);
741 gl->glDeleteProgram(gld->program);
742
743 if (gld->evasGl)
744 {
745 // Irrlicht wants to destroy the context and surface, so only do this if Irrlicht wont.
746 if (!gld->doneIrr)
747 {
748 evas_gl_surface_destroy(gld->evasGl, gld->sfc);
749 evas_gl_context_destroy(gld->evasGl, gld->ctx);
750 }
751 // TODO - hope this is OK, considering the context and surface might get dealt with by Irrlicht.
752 // Might be better to teach Irrlicht to not destroy shit it did not create.
753 evas_gl_config_free(gld->cfg);
754 evas_gl_free(gld->evasGl);
755 }
756
757 // TODO - Since this is created on the render thread, better hope this is being deleted on the render thread.
758 finishIrr(gld);
759
760#if DO_GEARS
761 gl->glDeleteBuffers(1, &gld->gear1->vbo);
762 gl->glDeleteBuffers(1, &gld->gear2->vbo);
763 gl->glDeleteBuffers(1, &gld->gear3->vbo);
764
765 free_gear(gld->gear1);
766 free_gear(gld->gear2);
767 free_gear(gld->gear3);
768#endif
769}
770
771// Callback from Elm, coz they do shit different.
772static void _del_gl(Evas_Object *obj)
773{
774 GLData *gld = evas_object_data_get(obj, "gld");
775 if (!gld)
776 {
777 printf("Unable to get GLData. \n");
778 return;
779 }
780
781 _clean_gl(gld, NULL, NULL, NULL);
782
783 evas_object_data_del((Evas_Object*)obj, "gld");
784}
785
786// Callback for when the app quits.
787static void _on_done(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
788{
789 GLData *gld = data;
790
791 evas_object_del(gld->win);
792 free(gld);
793 elm_exit();
794}
795
796// Callback from the animator.
797static Eina_Bool doFrame(void *data)
798{
799 GLData *gld = data;
800
801 // Mark the pixels as dirty, so they get rerendered each frame, then Irrlicht can draw it's stuff each frame.
802 // This causes on_pixel to be triggered by Evas_GL, and _draw_gl for Elm_glview.
803 if (gld->r1)
804 evas_object_image_pixels_dirty_set(gld->r1, EINA_TRUE);
805 if (gld->elmGl)
806 elm_glview_changed_set(gld->elmGl);
807
808 // If not using Evas_GL, we need to call on_pixel() manually.
809 if (!gld->useEGL)
810 on_pixels(gld, gld->r1);
811
812 return EINA_TRUE; // Keep calling us.
813}
814
815static void init_evas_gl(GLData *gld)
816{
817 if (!gld->useEGL)
818 return;
819
820 gld->sfc_w = gld->win_w;
821 gld->sfc_h = gld->win_h;
822
823 // Get the Evas / canvas from the elm window (that the Evas_Object "lives on"), which is itself an Evas_Object created by Elm, so not sure if it was created internally with Ecore_Evas.
824 gld->canvas = evas_object_evas_get(gld->win);
825 // An Ecore_Evas holds an Evas.
826 // Get the Ecore_Evas that wraps an Evas.
827 gld->ee = ecore_evas_ecore_evas_get(gld->canvas); // Only use this on Evas that was created with Ecore_Evas.
828
829#if USE_ELM_GL
830 // Add a GLView
831 gld->elmGl = elm_glview_add(gld->win);
832 evas_object_size_hint_align_set(gld->elmGl, EVAS_HINT_FILL, EVAS_HINT_FILL);
833 evas_object_size_hint_weight_set(gld->elmGl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
834 elm_glview_mode_set(gld->elmGl, 0 | ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH | ELM_GLVIEW_DIRECT);
835 elm_glview_resize_policy_set(gld->elmGl, ELM_GLVIEW_RESIZE_POLICY_RECREATE); // Destroy the current surface on a resize and create a new one.
836 elm_glview_render_policy_set(gld->elmGl, ELM_GLVIEW_RENDER_POLICY_ON_DEMAND);
837// elm_glview_render_policy_set(gld->elmGl, ELM_GLVIEW_RENDER_POLICY_ALWAYS);
838 // These get called in the render thread I think.
839 // None let me pass data, so this is why we are adding "gld" data to the object below.
840 // Maybe we can use elm_object_signal_callback_add or elm_object_item_signal_callback_add (edje signals)?
841 //elm_glview_init_func_set(gld->elmGl, _init_gl); // Not actually needed, it gets done in on_pixels.
842 elm_glview_del_func_set(gld->elmGl, _del_gl);
843 elm_glview_resize_func_set(gld->elmGl, _resize_gl);
844 elm_glview_render_func_set(gld->elmGl, (Elm_GLView_Func_Cb) _draw_gl);
845
846 // Not needed, the resize callback above deals with that.
847 //elm_win_resize_object_add(gld->win, gld->elmGl);
848 gld->glApi = elm_glview_gl_api_get(gld->elmGl);
849 evas_object_data_set(gld->elmGl, "gld", gld);
850 evas_object_show(gld->elmGl);
851 elm_box_pack_end(gld->bx, gld->elmGl);
852#else
853 // get the evas gl handle for doing gl things
854 gld->evasGl = evas_gl_new(gld->canvas);
855 gld->glApi = evas_gl_api_get(gld->evasGl);
856
857 // Set a surface config
858 gld->cfg = evas_gl_config_new();
859 gld->cfg->color_format = EVAS_GL_RGBA_8888;
860 gld->cfg->depth_bits = EVAS_GL_DEPTH_BIT_32;
861 gld->cfg->stencil_bits = EVAS_GL_STENCIL_NONE;
862 gld->cfg->options_bits = EVAS_GL_OPTIONS_DIRECT;
863
864 // create a surface and context
865 gld->sfc = evas_gl_surface_create(gld->evasGl, gld->cfg, gld->sfc_w, gld->sfc_h);
866 gld->ctx = evas_gl_context_create(gld->evasGl, NULL); // The second NULL is for sharing contexts I think, which might be what we want to do with Irrlicht. It's not documented.
867
868 // Set up the image object, a filled one by default.
869 gld->r1 = evas_object_image_filled_add(gld->canvas);
870
871 // attach important data we need to the object using key names. This just
872 // avoids some global variables and means we can do nice cleanup. You can
873 // avoid this if you are lazy
874 // Not actually needed, with evas we can pass data pointers to stuff.
875 //evas_object_data_set(gld->r1, "gld", gld);
876
877 // when the object is deleted - call the on_del callback. like the above,
878 // this is just being clean
879 evas_object_event_callback_add(gld->r1, EVAS_CALLBACK_DEL, _clean_gl, gld);
880
881 // set up an actual pixel size for the buffer data. it may be different
882 // to the output size. any windowing system has something like this, just
883 // evas has 2 sizes, a pixel size and the output object size
884 evas_object_image_size_set(gld->r1, gld->sfc_w, gld->sfc_h);
885 // Not actualy needed, as we create the image already filled.
886 //evas_object_image_fill_set(gld->r1, 0, 0, gld->sfc_w, gld->sfc_h);
887
888 // These two are not in the original example, but I get black r1 when I leave them out.
889 evas_object_size_hint_align_set(gld->r1, EVAS_HINT_FILL, EVAS_HINT_FILL);
890 evas_object_size_hint_weight_set(gld->r1, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
891
892 // set up the native surface info to use the context and surface created
893 // above
894 evas_gl_native_surface_get(gld->evasGl, gld->sfc, &(gld->ns));
895 evas_object_image_native_surface_set(gld->r1, &(gld->ns));
896 evas_object_image_pixels_get_callback_set(gld->r1, on_pixels, gld);
897
898 // move the image object somewhere, resize it and show it. any windowing
899 // system would need this kind of thing - place a child "window"
900 // Hmm, no need to resize it anyway, it's sized above.
901 evas_object_move(gld->r1, 0, 0);
902 //evas_object_resize(gld->r1, gld->sfc_w, gld->sfc_h);
903 elm_win_resize_object_add(gld->win, gld->r1);
904 evas_object_show(gld->r1);
905 elm_box_pack_end(gld->bx, gld->r1);
906
907 evas_object_event_callback_add(gld->r1, EVAS_CALLBACK_MOUSE_DOWN, _cb_mouse_down_GL, gld);
908// evas_object_event_callback_add(gld->r1, EVAS_CALLBACK_KEY_DOWN, _on_camera_input_down, gld);
909// evas_object_event_callback_add(gld->r1, EVAS_CALLBACK_KEY_UP, _on_camera_input_up, gld);
910#endif
911
912 // NOTE: if you delete r1, this animator will keep running trying to access
913 // r1 so you'd better delete this animator with ecore_animator_del() or
914 // structure how you do animation differently. you can also attach it like
915 // evasGl, sfc, etc. etc. if this animator is specific to this object
916 // only and delete it in the del handler for the obj.
917 //
918 // TODO - apparently the proper way to deal with the new async rendering is to have this animator do the dirty thing, and call the Irrlicht rendering stuff in the on_pixel call set above.
919 // That still leaves the problem of the Irrlicht setup being in the main thread. Which also should be done in on_pixel, as that's done in the correct thread.
920
921 // Jiggling this seems to produce a trade off between flickering and frame rate. Nothing else changed the flickering.
922 ecore_animator_frametime_set(0.04); // Default is 1/30, or 0.033333
923 gld->animator = ecore_animator_add(doFrame, gld); // This animator will be called every frame tick, which defaults to 1/30 seconds.
924
925 return;
926}
927
928
929//-------------------------//
930
931
932static Evas_Object *_content_image_new(Evas_Object *parent, const char *img)
933{
934 Evas_Object *ic;
935
936 ic = elm_icon_add(parent);
937 elm_image_file_set(ic, img, NULL);
938 return ic;
939}
940
941static void _promote(void *data, Evas_Object *obj , void *event_info )
942{
943 elm_naviframe_item_promote(data);
944}
945
946static char *_grid_label_get(void *data, Evas_Object *obj, const char *part)
947{
948 ezGrid *thisGrid = data;
949 char buf[256];
950
951 if (!strcmp(part, "elm.text"))
952 {
953 int count = eina_clist_count(&(thisGrid->accounts));
954
955 if (0 == count)
956 snprintf(buf, sizeof(buf), "%s (no accounts)", thisGrid->name);
957 else if (1 == count)
958 snprintf(buf, sizeof(buf), "%s (%d account)", thisGrid->name, count);
959 else
960 snprintf(buf, sizeof(buf), "%s (%d accounts)", thisGrid->name, count);
961 }
962 else
963 snprintf(buf, sizeof(buf), "%s", thisGrid->loginURI);
964 return strdup(buf);
965}
966
967static Evas_Object *_grid_content_get(void *data, Evas_Object *obj, const char *part)
968{
969 ezGrid *thisGrid = data;
970 Evas_Object *ic = elm_icon_add(obj);
971
972 if (!strcmp(part, "elm.swallow.icon"))
973 elm_icon_standard_set(ic, thisGrid->icon);
974
975 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
976 return ic;
977}
978
979static char * _account_label_get(void *data, Evas_Object *obj, const char *part)
980{
981 ezAccount *thisAccount = data;
982 char buf[256];
983
984 buf[0] = '\0';
985 if (!strcmp(part, "elm.text"))
986 snprintf(buf, sizeof(buf), "%s", thisAccount->name);
987
988 return strdup(buf);
989}
990
991static Evas_Object *_account_content_get(void *data, Evas_Object *obj, const char *part)
992{
993 ezAccount *thisAccount = data;
994 Evas_Object *ic = elm_icon_add(obj);
995
996 if (!strcmp(part, "elm.swallow.icon"))
997 elm_icon_standard_set(ic, thisAccount->icon);
998
999 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
1000 return ic;
1001}
1002
1003static char *_viewer_label_get(void *data, Evas_Object *obj, const char *part)
1004{
1005 ezViewer *thisViewer = data;
1006 char buf[256];
1007
1008 if (!strcmp(part, "elm.text"))
1009 snprintf(buf, sizeof(buf), "%s", thisViewer->name);
1010 else
1011 snprintf(buf, sizeof(buf), "%s", thisViewer->version);
1012 return strdup(buf);
1013}
1014
1015static Evas_Object *_viewer_content_get(void *data, Evas_Object *obj, const char *part)
1016{
1017 ezViewer *thisViewer = data;
1018 Evas_Object *ic = elm_icon_add(obj);
1019
1020 if (!strcmp(part, "elm.swallow.icon"))
1021 elm_icon_standard_set(ic, thisViewer->icon);
1022
1023 evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1);
1024 return ic;
1025}
1026
1027
1028static void _grid_sel_cb(void *data, Evas_Object *obj, void *event_info)
1029{
1030 ezGrid *thisGrid = data;
1031 GLData *gld = thisGrid->gld;
1032 char buf[PATH_MAX];
1033
1034// sprintf(buf, "dillo -f -g '%dx%d+%d+%d' %s &", gld->win_w - (gld->win_w / 5), gld->win_h - 30, gld->win_w / 5, gld->win_y, thisGrid->splashPage);
1035 sprintf(buf, "uzbl -g '%dx%d+%d+%d' -u %s &", gld->win_w - (gld->win_w / 5), gld->win_h - 30, gld->win_w / 5, gld->win_y, thisGrid->splashPage);
1036 printf("%s ### genlist obj [%p], item pointer [%p]\n", buf, obj, event_info);
1037// comment this out for now, busy dealing with input stuff, don't want to trigger this multiple times.
1038// system(buf);
1039}
1040
1041static void cb_mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info)
1042{
1043 Evas_Event_Mouse_Move *ev = event_info;
1044 Evas_Object *orig = data;
1045 Evas_Coord x, y;
1046 Evas_Map *p;
1047 int i, w, h;
1048
1049 if (!ev->buttons) return;
1050 evas_object_geometry_get(obj, &x, &y, NULL, NULL);
1051 evas_object_move(obj,
1052 x + (ev->cur.canvas.x - ev->prev.output.x),
1053 y + (ev->cur.canvas.y - ev->prev.output.y));
1054 evas_object_image_size_get(orig, &w, &h);
1055 p = evas_map_new(4);
1056 evas_object_map_enable_set(orig, EINA_TRUE);
1057// evas_object_raise(orig);
1058 for (i = 0; i < 4; i++)
1059 {
1060 Evas_Object *hand;
1061 char key[32];
1062
1063 snprintf(key, sizeof(key), "h-%i\n", i);
1064 hand = evas_object_data_get(orig, key);
1065 evas_object_raise(hand);
1066 evas_object_geometry_get(hand, &x, &y, NULL, NULL);
1067 x += 15;
1068 y += 15;
1069 evas_map_point_coord_set(p, i, x, y, 0);
1070 if (i == 0) evas_map_point_image_uv_set(p, i, 0, 0);
1071 else if (i == 1) evas_map_point_image_uv_set(p, i, w, 0);
1072 else if (i == 2) evas_map_point_image_uv_set(p, i, w, h);
1073 else if (i == 3) evas_map_point_image_uv_set(p, i, 0, h);
1074 }
1075 evas_object_map_set(orig, p);
1076 evas_map_free(p);
1077}
1078
1079static void create_handles(Evas_Object *obj)
1080{
1081 int i;
1082 Evas_Coord x, y, w, h;
1083
1084 evas_object_geometry_get(obj, &x, &y, &w, &h);
1085 for (i = 0; i < 4; i++)
1086 {
1087 Evas_Object *hand;
1088 char buf[PATH_MAX];
1089 char key[32];
1090
1091 hand = evas_object_image_filled_add(evas_object_evas_get(obj));
1092 evas_object_resize(hand, 31, 31);
1093 snprintf(buf, sizeof(buf), "%s/media/pt.png", elm_app_data_dir_get());
1094 evas_object_image_file_set(hand, buf, NULL);
1095 if (i == 0) evas_object_move(hand, x - 15, y - 15);
1096 else if (i == 1) evas_object_move(hand, x + w - 15, y - 15);
1097 else if (i == 2) evas_object_move(hand, x + w - 15, y + h - 15);
1098 else if (i == 3) evas_object_move(hand, x - 15, y + h - 15);
1099 evas_object_event_callback_add(hand, EVAS_CALLBACK_MOUSE_MOVE, cb_mouse_move, obj);
1100 evas_object_show(hand);
1101 snprintf(key, sizeof(key), "h-%i\n", i);
1102 evas_object_data_set(obj, key, hand);
1103 }
1104}
1105
1106static Evas_Object *_toolbar_menu_add(Evas_Object *win, Evas_Object *tb, char *label)
1107{
1108 Evas_Object *menu= NULL;
1109 Elm_Object_Item *tb_it;
1110//, *menu_it;
1111
1112 tb_it = elm_toolbar_item_append(tb, NULL, label, NULL, NULL);
1113 elm_toolbar_item_menu_set(tb_it, EINA_TRUE);
1114 // Priority is for when toolbar items are set to hide or menu when there are too many of them. They get hidden or put on the menu based on priority.
1115 elm_toolbar_item_priority_set(tb_it, 9999);
1116 elm_toolbar_menu_parent_set(tb, win);
1117 menu = elm_toolbar_item_menu_get(tb_it);
1118
1119 return menu;
1120}
1121
1122static Evas_Object *fang_win_add(GLData *gld)
1123{
1124 Evas_Object *win, *bg;
1125
1126 // In theory this should create an EWS window, in practice, I'm not seeing any difference.
1127 // Guess I'll have to implement my own internal window manager. I don't think a basic one will be that hard. Famous last words.
1128// elm_config_engine_set("ews");
1129 win = elm_win_add(gld->win, "inlined", ELM_WIN_INLINED_IMAGE);
1130 // On mouse down we try to shift focus to the backing image, this seems to be the correct thing to force focus onto it's widgets.
1131 // According to the Elm inlined image window example, this is what's needed to.
1132 evas_object_event_callback_add(elm_win_inlined_image_object_get(win), EVAS_CALLBACK_MOUSE_DOWN, _cb_mouse_down_elm, gld);
1133 elm_win_alpha_set(win, EINA_TRUE);
1134
1135 // Apparently transparent is not good enough for ELM backgrounds, so make it a rectangle.
1136 // Apparently coz ELM prefers stuff to have edjes. A bit over the top if all I want is a transparent rectangle.
1137 bg = evas_object_rectangle_add(evas_object_evas_get(win));
1138 evas_object_color_set(bg, 50, 0, 100, 100);
1139 evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1140 elm_win_resize_object_add(win, bg);
1141 evas_object_show(bg);
1142
1143 return win;
1144}
1145
1146static void fang_win_complete(GLData *gld, Evas_Object *win, int x, int y, int w, int h)
1147{
1148 // image object for win is unlinked to its pos/size - so manual control
1149 // this allows also for using map and other things with it.
1150 evas_object_move(elm_win_inlined_image_object_get(win), x, y);
1151 // Odd, it needs to be resized twice. WTF?
1152 evas_object_resize(win, w, h);
1153 evas_object_resize(elm_win_inlined_image_object_get(win), w, h);
1154 evas_object_show(win);
1155 create_handles(elm_win_inlined_image_object_get(win));
1156}
1157
1158static void overlay_add(GLData *gld)
1159{
1160 Evas_Object *bg;
1161//, *bx, *tb, *menu;
1162// Elm_Object_Item *tb_it, *menu_it;
1163
1164 // There many are reasons for this window.
1165 // The first is to cover the GL and provide something to click on to change focus.
1166 // The second is to provide something to click on for all the GL type clicking stuff that needs to be done. In other words, no click through,we catch the clicks here.
1167 // So we can probably avoid the following issue -
1168 // How to do click through? evas_object_pass_events_set(rectangle, EINA_TRUE), and maybe need to do that to the underlaying window to?
1169 // Though if the rectangle is entirely transparent, or even hidden, events might pass through anyway.
1170 // Gotta have click through on the parts where there's no other window.
1171 // The third is to have the other windows live here.
1172 // This idea doesn't work, as it breaks the damn focus again.
1173 // Don't think it's needed anyway.
1174 // While on the subject of layers, need a HUD layer of some sort, but Irrlicht might support that itself.
1175
1176 gld->winwin = elm_win_add(gld->win, "inlined", ELM_WIN_INLINED_IMAGE);
1177 // On mouse down we try to shift focus to the backing image, this seems to be the correct thing to force focus onto it's widgets.
1178 // According to the Elm inlined image window example, this is what's needed to.
1179 evas_object_event_callback_add(elm_win_inlined_image_object_get(gld->winwin), EVAS_CALLBACK_MOUSE_DOWN, _cb_mouse_down_elm, gld);
1180 // In this code, we are making our own camera, so grab it's input when we are focused.
1181 evas_object_event_callback_add(gld->winwin, EVAS_CALLBACK_KEY_DOWN, _on_camera_input_down, gld);
1182 evas_object_event_callback_add(gld->winwin, EVAS_CALLBACK_KEY_UP, _on_camera_input_up, gld);
1183 elm_object_event_callback_add(gld->winwin, _cb_event_GL, gld);
1184
1185 elm_win_alpha_set(gld->winwin, EINA_TRUE);
1186 // Apparently transparent is not good enough for ELM backgrounds, so make it a rectangle.
1187 // Apparently coz ELM prefers stuff to have edjes. A bit over the top if all I want is a transparent rectangle.
1188 bg = evas_object_rectangle_add(evas_object_evas_get(gld->winwin));
1189 evas_object_color_set(bg, 0, 0, 0, 0);
1190 evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1191 elm_win_resize_object_add(gld->winwin, bg);
1192 evas_object_show(bg);
1193
1194 // image object for win is unlinked to its pos/size - so manual control
1195 // this allows also for using map and other things with it.
1196 evas_object_move(elm_win_inlined_image_object_get(gld->winwin), 0, 0);
1197 // Odd, it needs to be resized twice. WTF?
1198 evas_object_resize(gld->winwin, gld->win_w, gld->win_h);
1199 evas_object_resize(elm_win_inlined_image_object_get(gld->winwin), gld->win_w, gld->win_h);
1200 evas_object_show(gld->winwin);
1201}
1202
1203static void chat_add(GLData *gld)
1204{
1205 Evas_Object *win, *bx, *en;
1206
1207 win = fang_win_add(gld);
1208
1209 bx = elm_box_add(win);
1210 elm_win_resize_object_add(win, bx);
1211 evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1212 evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
1213
1214 en = elm_entry_add(win);
1215 elm_entry_scrollable_set(en, EINA_TRUE);
1216 evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1217 evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
1218 elm_object_text_set(en, "History is shown here");
1219 elm_entry_editable_set(en, EINA_FALSE);
1220 evas_object_show(en);
1221 elm_box_pack_end(bx, en);
1222
1223 en = elm_entry_add(win);
1224 elm_entry_scrollable_set(en, EINA_TRUE);
1225 evas_object_size_hint_weight_set(en, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1226 evas_object_size_hint_align_set(en, EVAS_HINT_FILL, EVAS_HINT_FILL);
1227 elm_object_text_set(en, "");
1228 elm_entry_editable_set(en, EINA_TRUE);
1229 evas_object_show(en);
1230 elm_box_pack_end(bx, en);
1231
1232 evas_object_show(bx);
1233
1234 fang_win_complete(gld, win, 30, 500, gld->win_w / 3, gld->win_h / 3);
1235}
1236
1237
1238static void woMan_add(GLData *gld)
1239{
1240// Evas_Object *win, *bg, *bx, *ic, *bb, *av, *en, *bt, *nf, *tab, *tb, *gridList, *viewerList, *menu;
1241 Evas_Object *win, *bx, *bt, *nf, *tab, *tb, *gridList, *viewerList, *menu;
1242 Elm_Object_Item *tb_it, *menu_it, *tab_it;
1243// char buf[PATH_MAX];
1244 int i;
1245
1246 win = fang_win_add(gld);
1247
1248 bx = elm_box_add(win);
1249 elm_win_resize_object_add(win, bx);
1250 evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1251 evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
1252
1253 // A tab thingy.
1254 tb = elm_toolbar_add(win);
1255 evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, 0.0);
1256 evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL);
1257 elm_toolbar_shrink_mode_set(tb, ELM_TOOLBAR_SHRINK_SCROLL);
1258
1259 // Menu.
1260 tb_it = elm_toolbar_item_append(tb, NULL, "Menu", NULL, NULL);
1261 elm_toolbar_item_menu_set(tb_it, EINA_TRUE);
1262 // Priority is for when toolbar items are set to hide or menu when there are too many of them. They get hidden or put on the menu based on priority.
1263 elm_toolbar_item_priority_set(tb_it, 9999);
1264 elm_toolbar_menu_parent_set(tb, win);
1265 menu = elm_toolbar_item_menu_get(tb_it);
1266
1267 menu_it = elm_menu_item_add(menu, NULL, NULL, "edit", NULL, NULL);
1268 elm_menu_item_add(menu, menu_it, NULL, "preferences", NULL, NULL);
1269 menu_it = elm_menu_item_add(menu, NULL, NULL, "help", NULL, NULL);
1270 elm_menu_item_add(menu, menu_it, NULL, "about woMan", NULL, NULL);
1271 elm_menu_item_separator_add(menu, NULL);
1272 menu_it = elm_menu_item_add(menu, NULL, NULL, "advanced", NULL, NULL);
1273 elm_menu_item_add(menu, menu_it, NULL, "debug settings", NULL, NULL);
1274
1275 // The toolbar needs to be packed into the box AFTER the menus are added.
1276 elm_box_pack_end(bx, tb);
1277 evas_object_show(tb);
1278
1279 gridList = elm_genlist_add(win);
1280 grids = eina_hash_stringshared_new(free);
1281
1282 grid_gic = elm_genlist_item_class_new();
1283 grid_gic->item_style = "double_label";
1284 grid_gic->func.text_get = _grid_label_get;
1285 grid_gic->func.content_get = _grid_content_get;
1286 grid_gic->func.state_get = NULL;
1287 grid_gic->func.del = NULL;
1288 for (i = 0; NULL != gridTest[i][0]; i++)
1289 {
1290 ezGrid *thisGrid = calloc(1, sizeof(ezGrid));
1291
1292 if (thisGrid)
1293 {
1294 eina_clist_init(&(thisGrid->accounts));
1295 eina_clist_init(&(thisGrid->landmarks));
1296 thisGrid->name = gridTest[i][0];
1297 thisGrid->loginURI = gridTest[i][1];
1298 thisGrid->splashPage = gridTest[i][2];
1299 thisGrid->icon = "folder";
1300 thisGrid->gld = gld;
1301 thisGrid->item = elm_genlist_item_append(gridList, grid_gic, thisGrid, NULL, ELM_GENLIST_ITEM_TREE, _grid_sel_cb, thisGrid);
1302 eina_hash_add(grids, thisGrid->name, thisGrid);
1303 }
1304 }
1305
1306 account_gic = elm_genlist_item_class_new();
1307 account_gic->item_style = "default";
1308 account_gic->func.text_get = _account_label_get;
1309 account_gic->func.content_get = _account_content_get;
1310 account_gic->func.state_get = NULL;
1311 account_gic->func.del = NULL;
1312 for (i = 0; NULL != accountTest[i][0]; i++)
1313 {
1314 ezAccount *thisAccount = calloc(1, sizeof(ezAccount));
1315 ezGrid *grid = eina_hash_find(grids, accountTest[i][0]);
1316
1317 if (thisAccount && grid)
1318 {
1319 thisAccount->name = accountTest[i][1];
1320 thisAccount->password = accountTest[i][2];
1321 thisAccount->icon = "file";
1322 elm_genlist_item_append(gridList, account_gic, thisAccount, grid->item, ELM_GENLIST_ITEM_NONE, NULL, NULL);
1323 eina_clist_add_tail(&(grid->accounts), &(thisAccount->grid));
1324 }
1325 }
1326
1327 // Viewers stuff
1328 viewerList = elm_genlist_add(win);
1329 viewer_gic = elm_genlist_item_class_new();
1330 viewer_gic->item_style = "double_label";
1331 viewer_gic->func.text_get = _viewer_label_get;
1332 viewer_gic->func.content_get = _viewer_content_get;
1333 viewer_gic->func.state_get = NULL;
1334 viewer_gic->func.del = NULL;
1335 for (i = 0; NULL != viewerTest[i][0]; i++)
1336 {
1337 ezViewer *thisViewer = calloc(1, sizeof(ezViewer));
1338
1339 if (thisViewer)
1340 {
1341 thisViewer->name = viewerTest[i][0];
1342 thisViewer->version = viewerTest[i][1];
1343 thisViewer->path = viewerTest[i][2];
1344 thisViewer->icon = "file";
1345 thisViewer->item = elm_genlist_item_append(viewerList, viewer_gic, thisViewer, NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
1346 }
1347 }
1348
1349 // Toolbar pages
1350 nf = elm_naviframe_add(win);
1351 evas_object_size_hint_weight_set(nf, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1352 evas_object_size_hint_align_set(nf, EVAS_HINT_FILL, EVAS_HINT_FILL);
1353 evas_object_show(nf);
1354
1355 tab = viewerList; tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Viewers", _promote, tab_it);
1356 tab = _content_image_new(win, img3); tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Landmarks", _promote, tab_it);
1357 tab = gridList; tab_it = elm_naviframe_item_push(nf, NULL, NULL, NULL, tab, NULL); elm_naviframe_item_title_enabled_set(tab_it, EINA_FALSE, EINA_TRUE); elm_toolbar_item_append(tb, NULL, "Grids", _promote, tab_it);
1358 elm_box_pack_end(bx, nf);
1359
1360#if USE_EO
1361 // Not ready for prime time yet, or I'm missing a step. Causes it to hang after closing the window.
1362 // Slightly better now, it bitches instead of hanging.
1363 bt = eo_add(ELM_OBJ_BUTTON_CLASS, win);
1364 elm_object_text_set(bt, "Login"); // No eo interface for this that I can find.
1365 eo_do(bt,
1366// evas_obj_text_set("Login"),
1367 evas_obj_size_hint_align_set(EVAS_HINT_FILL, EVAS_HINT_FILL),
1368 evas_obj_size_hint_weight_set(EVAS_HINT_EXPAND, 0.0),
1369 evas_obj_visibility_set(EINA_TRUE)
1370 );
1371#else
1372 bt = elm_button_add(win);
1373 elm_object_text_set(bt, "Login");
1374 evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL);
1375 evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0);
1376 evas_object_show(bt);
1377#endif
1378// evas_object_smart_callback_add(bt, "clicked", NULL, NULL);
1379 elm_box_pack_end(bx, bt);
1380 evas_object_show(bx);
1381
1382 fang_win_complete(gld, win, 30, 30, gld->win_w / 3, gld->win_h / 3);
1383}
1384
1385EAPI_MAIN int elm_main(int argc, char **argv)
1386{
1387// Evas_Object *bg, *menu, *bt, *tb;
1388 Evas_Object *menu, *tb;
1389 Elm_Object_Item *tb_it;
1390//, *menu_it;
1391 EPhysics_Body *boundary;
1392 EPhysics_World *world;
1393 EPhysics_Body *box_body1, *box_body2;
1394 Evas_Object *box1, *box2;
1395 GLData *gld = NULL;
1396// char buf[PATH_MAX];
1397// int i;
1398// Eina_Bool gotWebKit = elm_need_web(); // Initialise ewebkit if it exists, or return EINA_FALSE if it don't.
1399
1400 _log_domain = eina_log_domain_register("extantz", NULL);
1401 // Don't do this, we need to clean up other stuff to, so set a clean up function below.
1402 //elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
1403
1404 // If you want efl to handle finding your bin/lib/data dirs, you must do this below.
1405 elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
1406 elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
1407 elm_app_info_set(elm_main, "datadir", "media/sky_03.jpg");
1408 fprintf(stdout, "prefix was set to: %s\n", elm_app_prefix_dir_get());
1409 fprintf(stdout, "data directory is: %s\n", elm_app_data_dir_get());
1410 fprintf(stdout, "library directory is: %s\n", elm_app_lib_dir_get());
1411 fprintf(stdout, "locale directory is: %s\n", elm_app_locale_dir_get());
1412
1413 // These are set via the elementary_config tool, which is hard to find.
1414 elm_config_finger_size_set(0);
1415 elm_config_scale_set(1.0);
1416
1417 // alloc a data struct to hold our relevant gl info in
1418 if (!(gld = calloc(1, sizeof(GLData)))) return 1;
1419 gldata_init(gld);
1420
1421 // Set the engine to opengl_x11, then open our window.
1422 if (gld->useEGL)
1423 elm_config_preferred_engine_set("opengl_x11");
1424 gld->win = elm_win_add(NULL, "extantz", ELM_WIN_BASIC);
1425 // Set preferred engine back to default from config
1426 elm_config_preferred_engine_set(NULL);
1427
1428#if USE_PHYSICS
1429 if (!ephysics_init())
1430 return 1;
1431#endif
1432
1433 elm_win_title_set(gld->win, "extantz virtual world manager");
1434 evas_object_smart_callback_add(gld->win, "delete,request", _on_done, gld);
1435
1436 // Get the screen size.
1437 elm_win_screen_size_get(gld->win, &gld->win_x, &gld->win_y, &gld->scr_w, &gld->scr_h);
1438 gld->win_x = gld->win_x + (gld->scr_w / 3);
1439 gld->win_w = gld->scr_w / 2;
1440 gld->win_h = gld->scr_h - 30;
1441
1442 // Note, we don't need an Elm_bg, the entire thing gets covered with the GL rendering surface anyway.
1443#if 0
1444 bg = elm_bg_add(gld->win);
1445 elm_bg_load_size_set(bg, gld->win_w, gld->win_h);
1446 elm_bg_option_set(bg, ELM_BG_OPTION_CENTER);
1447 snprintf(buf, sizeof(buf), "%s/media/sky_03.jpg", elm_app_data_dir_get());
1448 elm_bg_file_set(bg, buf, NULL);
1449 evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1450 elm_win_resize_object_add(gld->win, bg);
1451 evas_object_show(bg);
1452#endif
1453
1454 gld->bx = elm_box_add(gld->win);
1455 evas_object_size_hint_weight_set(gld->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1456 evas_object_size_hint_align_set(gld->bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
1457 elm_win_resize_object_add(gld->win, gld->bx);
1458 evas_object_show(gld->bx);
1459
1460 overlay_add(gld);
1461 woMan_add(gld);
1462 chat_add(gld);
1463
1464 // Gotta do this after adding the windows, otherwise the menu renders under the window.
1465 // This sucks, gotta redefine this menu each time we create a new window?
1466 // Also, GL focus gets lost when any menu is used. sigh
1467
1468 // A toolbar thingy.
1469 tb = elm_toolbar_add(gld->win);
1470 evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, 0.0);
1471 evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL);
1472 elm_toolbar_shrink_mode_set(tb, ELM_TOOLBAR_SHRINK_SCROLL);
1473 elm_toolbar_align_set(tb, 0.0);
1474
1475 // Menus.
1476 menu = _toolbar_menu_add(gld->win, tb, "file");
1477 elm_menu_item_add(menu, NULL, NULL, "quit", _on_done, gld);
1478
1479 menu = _toolbar_menu_add(gld->win, tb, "edit");
1480 elm_menu_item_add(menu, NULL, NULL, "preferences", NULL, NULL);
1481
1482 menu = _toolbar_menu_add(gld->win, tb, "view");
1483 menu = _toolbar_menu_add(gld->win, tb, "world");
1484 menu = _toolbar_menu_add(gld->win, tb, "tools");
1485
1486 menu = _toolbar_menu_add(gld->win, tb, "help");
1487 elm_menu_item_add(menu, NULL, NULL, "grid help", NULL, NULL);
1488 elm_menu_item_separator_add(menu, NULL);
1489 elm_menu_item_add(menu, NULL, NULL, "extantz blogs", NULL, NULL);
1490 elm_menu_item_add(menu, NULL, NULL, "extantz forum", NULL, NULL);
1491 elm_menu_item_separator_add(menu, NULL);
1492 elm_menu_item_add(menu, NULL, NULL, "about extantz", NULL, NULL);
1493
1494 menu = _toolbar_menu_add(gld->win, tb, "advanced");
1495 elm_menu_item_add(menu, NULL, NULL, "debug settings", NULL, NULL);
1496
1497 menu = _toolbar_menu_add(gld->win, tb, "god");
1498
1499 // Other stuff in the toolbar.
1500 tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
1501 elm_toolbar_item_separator_set(tb_it, EINA_TRUE);
1502 tb_it = elm_toolbar_item_append(tb, NULL, "restriction icons", NULL, NULL);
1503 tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
1504 elm_toolbar_item_separator_set(tb_it, EINA_TRUE);
1505 tb_it = elm_toolbar_item_append(tb, NULL, "hop://localhost/Anarchadia 152, 155, 51 - Lost plot (Adult)", NULL, NULL);
1506 tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
1507 elm_toolbar_item_separator_set(tb_it, EINA_TRUE);
1508 tb_it = elm_toolbar_item_append(tb, NULL, "date time:o'clock", NULL, NULL);
1509
1510 // The toolbar needs to be packed into the box AFTER the menus are added.
1511 evas_object_show(tb);
1512 elm_box_pack_start(gld->bx, tb);
1513
1514 // This does elm_box_pack_end(), so needs to be after the others.
1515 init_evas_gl(gld);
1516
1517 evas_object_show(gld->bx);
1518
1519#if USE_PHYSICS
1520 // ePhysics stuff.
1521 world = ephysics_world_new();
1522 ephysics_world_render_geometry_set(world, 0, 0, -50, gld->win_w, gld->win_h, 100);
1523
1524 boundary = ephysics_body_bottom_boundary_add(world);
1525 ephysics_body_restitution_set(boundary, 1);
1526 ephysics_body_friction_set(boundary, 0);
1527
1528 boundary = ephysics_body_top_boundary_add(world);
1529 ephysics_body_restitution_set(boundary, 1);
1530 ephysics_body_friction_set(boundary, 0);
1531
1532 boundary = ephysics_body_left_boundary_add(world);
1533 ephysics_body_restitution_set(boundary, 1);
1534 ephysics_body_friction_set(boundary, 0);
1535
1536 boundary = ephysics_body_right_boundary_add(world);
1537 ephysics_body_restitution_set(boundary, 1);
1538 ephysics_body_friction_set(boundary, 0);
1539
1540 box1 = elm_image_add(gld->win);
1541 elm_image_file_set(box1, PACKAGE_DATA_DIR "/media/" EPHYSICS_TEST_THEME ".edj", "blue-cube");
1542 evas_object_move(box1, gld->win_w / 2 - 80, gld->win_h - 200);
1543 evas_object_resize(box1, 70, 70);
1544 evas_object_show(box1);
1545
1546 box_body1 = ephysics_body_box_add(world);
1547 ephysics_body_evas_object_set(box_body1, box1, EINA_TRUE);
1548 ephysics_body_restitution_set(box_body1, 0.7);
1549 ephysics_body_friction_set(box_body1, 0);
1550 ephysics_body_linear_velocity_set(box_body1, -150, 200, 0);
1551 ephysics_body_angular_velocity_set(box_body1, 0, 0, 36);
1552 ephysics_body_sleeping_threshold_set(box_body1, 0.1, 0.1);
1553
1554 box2 = elm_image_add(gld->win);
1555 elm_image_file_set(box2, PACKAGE_DATA_DIR "/media/" EPHYSICS_TEST_THEME ".edj", "purple-cube");
1556 evas_object_move(box2, gld->win_w / 2 + 10, gld->win_h - 200);
1557 evas_object_resize(box2, 70, 70);
1558 evas_object_show(box2);
1559
1560 box_body2 = ephysics_body_box_add(world);
1561 ephysics_body_evas_object_set(box_body2, box2, EINA_TRUE);
1562 ephysics_body_restitution_set(box_body2, 0.7);
1563 ephysics_body_friction_set(box_body2, 0);
1564 ephysics_body_linear_velocity_set(box_body2, 80, -60, 0);
1565 ephysics_body_angular_velocity_set(box_body2, 0, 0, 360);
1566 ephysics_body_sleeping_threshold_set(box_body2, 0.1, 0.1);
1567
1568 ephysics_world_gravity_set(world, 0, 0, 0);
1569#endif
1570
1571 evas_object_move(gld->win, gld->win_x, gld->win_y);
1572 evas_object_resize(gld->win, gld->win_w, gld->win_h);
1573 evas_object_show(gld->win);
1574
1575 _resize_winwin(gld);
1576
1577 elm_run();
1578
1579#if USE_PHYSICS
1580 ephysics_world_del(world);
1581 ephysics_shutdown();
1582#endif
1583
1584 elm_shutdown();
1585
1586 return 0;
1587}
1588ELM_MAIN()
diff --git a/src/extantz/extantz.edc b/src/extantz/extantz.edc
new file mode 100644
index 0000000..b4ed0b3
--- /dev/null
+++ b/src/extantz/extantz.edc
@@ -0,0 +1,30 @@
1externals {
2 external: "elm";
3}
4
5collections {
6#define ADD_CUBE(_group, _file) \
7 images { \
8 image: #_file##".png" COMP; \
9 } \
10 group { \
11 name: #_group; \
12 parts { \
13 part { \
14 name: "cube"; \
15 type: IMAGE; \
16 mouse_events: 1; \
17 repeat_events: 0; \
18 description { \
19 state: "default" 0.0; \
20 image.normal: #_file##".png"; \
21 } \
22 } \
23 } \
24 }
25
26ADD_CUBE(blue-cube, cube-blue);
27ADD_CUBE(purple-cube, cube-purple);
28
29#undef ADD_CUBE
30}
diff --git a/src/extantz/extantz.h b/src/extantz/extantz.h
new file mode 100644
index 0000000..afb94af
--- /dev/null
+++ b/src/extantz/extantz.h
@@ -0,0 +1,215 @@
1#define USE_EO 0
2#define USE_PHYSICS 1
3#define USE_EGL 1 // If using Evas_GL, though it might be via Elm.
4#define USE_ELM_GL 1
5#define USE_IRR 1
6#define USE_DEMO 1
7#define DO_GEARS 0
8
9#if USE_EO
10 /* Enable access to unstable EFL API that are still in beta */
11 #define EFL_BETA_API_SUPPORT 1
12 /* Enable access to unstable EFL EO API. */
13 #define EFL_EO_API_SUPPORT 1
14#endif
15
16#include <Elementary.h>
17#include <elm_widget_glview.h>
18#include <Evas_GL.h>
19#include <EPhysics.h>
20#include "extantzCamera.h"
21
22
23#ifdef GL_GLES
24#include <EGL/egl.h>
25#include <EGL/eglext.h>
26#else
27# include <GL/glext.h>
28# include <GL/glx.h>
29#endif
30
31
32#ifdef __cplusplus
33/*
34In the Irrlicht Engine, everything can be found in the namespace 'irr'. So if
35you want to use a class of the engine, you have to write irr:: before the name
36of the class. For example to use the IrrlichtDevice write: irr::IrrlichtDevice.
37To get rid of the irr:: in front of the name of every class, we tell the
38compiler that we use that namespace from now on, and we will not have to write
39irr:: anymore.
40*/
41using namespace irr;
42
43/*
44There are 5 sub namespaces in the Irrlicht Engine. Take a look at them, you can
45read a detailed description of them in the documentation by clicking on the top
46menu item 'Namespace List' or by using this link:
47http://irrlicht.sourceforge.net/docu/namespaces.html
48Like the irr namespace, we do not want these 5 sub namespaces now, to keep this
49example simple. Hence, we tell the compiler again that we do not want always to
50write their names.
51*/
52using namespace core;
53using namespace scene;
54using namespace video;
55
56extern "C"{
57#else
58
59// Irrlicht stuff. It's C++, so we gotta use incomplete types.
60typedef struct IrrlichtDevice IrrlichtDevice;
61typedef struct IVideoDriver IVideoDriver;
62typedef struct ISceneManager ISceneManager;
63typedef struct ICameraSceneNode ICameraSceneNode;
64
65#endif
66
67
68#define CRI(...) EINA_LOG_DOM_CRIT(_log_domain, _VA_ARGS__)
69#define ERR(...) EINA_LOG_DOM_ERR(_log_domain, __VA_ARGS__)
70#define WRN(...) EINA_LOG_DOM_WARN(_log_domain, __VA_ARGS__)
71#define INF(...) EINA_LOG_DOM_INFO(_log_domain, __VA_ARGS__)
72#define DBG(...) EINA_LOG_DOM_DBG(_log_domain, __VA_ARGS__)
73
74extern int _log_domain;
75
76
77typedef struct _Gear Gear;
78typedef struct _GLData GLData;
79
80typedef enum
81{
82 EZP_NONE,
83 EZP_AURORA,
84 EZP_OPENSIM,
85 EZP_SECOND_LIFE,
86 EZP_SLEDJHAMR,
87 EZP_TRITIUM
88} ezPlatform;
89
90typedef struct
91{
92 char *name;
93 char *version; // Version string.
94 char *path; // OS filesystem path to the viewer install.
95 char *icon;
96 uint16_t tag; // The UUID of the texture used in the avatar bake hack.
97 uint8_t r, g, b; // Colour used for the in world tag.
98 Elm_Object_Item *item;
99} ezViewer;
100
101typedef struct
102{
103 Eina_Clist accounts;
104 Eina_Clist landmarks;
105 char *name;
106 char *loginURI;
107 char *splashPage;
108 char *helperURI;
109 char *website;
110 char *supportPage;
111 char *registerPage;
112 char *passwordPage;
113 char *icon;
114 ezPlatform platform;
115 ezViewer *viewer;
116 Elm_Object_Item *item;
117GLData *gld; // Just a temporary evil hack to pass gld to _grid_sel_cb().
118} ezGrid;
119
120typedef struct
121{
122 Eina_Clist grid;
123 char *name;
124 char *password; // Think we need to pass unencrypted passwords to the viewer. B-(
125 char *icon;
126 ezViewer *viewer;
127} ezAccount;
128
129typedef struct
130{
131 Eina_Clist grid;
132 char *name;
133 char *sim;
134 char *screenshot;
135 short x, y, z;
136} ezLandmark;
137
138
139
140struct _Gear
141{
142 GLfloat *vertices;
143 GLuint vbo;
144 int count;
145};
146
147// GL related data here.
148struct _GLData
149{
150 Evas_Object *win, *winwin;
151
152 Ecore_Evas *ee;
153 Evas *canvas;
154 Evas_Native_Surface ns;
155
156 Evas_GL_Context *ctx;
157 Evas_GL_Surface *sfc;
158 Evas_GL_Config *cfg;
159 Evas_GL *evasGl; // The Evas way.
160 Evas_Object *elmGl; // The Elm way.
161 Evas_GL_API *glApi;
162
163 GLuint program;
164 GLuint vtx_shader;
165 GLuint fgmt_shader;
166 int scr_w, scr_h; // The size of the screen.
167 int win_w, win_h; // The size of the window.
168 int win_x, win_y; // The position of the window.
169 int sfc_w, sfc_h; // This is what Irrlicht is using, size of the GL image surface / glview.
170 int img_w, img_h; // Size of the viewport. DON'T reuse sfc_* here. Despite the fach that sfc_* is only used in the init when Irricht is disabled? WTF?
171 int useEGL : 1;
172 int useIrr : 1;
173 int doneIrr : 1;
174 int gearsInited : 1;
175 int resized : 1;
176
177 Evas_Object *bx, *r1;
178 Ecore_Animator *animator;
179
180 IrrlichtDevice *device;
181 IVideoDriver *driver;
182 ISceneManager *smgr;
183 ICameraSceneNode *camera;
184
185 cameraMove *move;
186
187 // Gear Stuff
188 GLfloat view_rotx;
189 GLfloat view_roty;
190 GLfloat view_rotz;
191
192 Gear *gear1;
193 Gear *gear2;
194 Gear *gear3;
195
196 GLfloat angle;
197
198 GLuint proj_location;
199 GLuint light_location;
200 GLuint color_location;
201
202 GLfloat proj[16];
203 GLfloat light[3];
204};
205
206
207EAPI int startIrr(GLData *gld);
208EAPI void drawIrr_start(GLData *gld);
209EAPI void drawIrr_end(GLData *gld);
210EAPI void finishIrr(GLData *gld);
211
212#ifdef __cplusplus
213}
214#endif
215
diff --git a/src/extantz/extantzCamera.cpp b/src/extantz/extantzCamera.cpp
new file mode 100644
index 0000000..6a7d36a
--- /dev/null
+++ b/src/extantz/extantzCamera.cpp
@@ -0,0 +1,282 @@
1// Copyright (C) 2002-2012 Nikolaus Gebhardt
2// This file is part of the "Irrlicht Engine".
3// For conditions of distribution and use, see copyright notice in irrlicht.h
4
5// The above is the copyright notice for CSceneNodeAnimatorCameraFPS.cpp,
6// According to the Irrlicht docs, that's just a demo and you are supposed to use it as an example for writing your own FPS style camera.
7// I'll be writing my own camera code, that includes first person, third person, and free camera styles.
8// I'll start with CSceneNodeAnimatorCameraFPS.cpp and morph it until it suits me.
9// As such, I expect lots of Nikolaus Gebhardt's code to go away.
10// To be replaced by my code, which will be copyright and licensed under the same license as the rest of extantz.
11
12// Initally I'll make it SecondLife like, coz that's what my muscle memory is used to.
13// It will get extended and made generic though.
14
15#include "extantzCamera.h"
16#include "IVideoDriver.h"
17#include "ISceneManager.h"
18#include "Keycodes.h"
19#include "ICursorControl.h"
20#include "ICameraSceneNode.h"
21#include "ISceneNodeAnimatorCollisionResponse.h"
22
23namespace irr
24{
25namespace scene
26{
27
28// Irrlicht hard codes a reference to the original FPS camera code inside it's scene manager. This is that code extracted so we can be more flexible.
29// TODO - Hmmm, Where's CursorControl come from? Ah, passed to the scene manager constructor, it's a GUI thing that we need to replace with an EFL thing. But only for mouselook mode.
30ICameraSceneNode *addExtantzCamera(ISceneManager* sm, ISceneNode* parent, s32 id)
31{
32 ICameraSceneNode* node = sm->addCameraSceneNode(parent, core::vector3df(), core::vector3df(0, 0, 100), id, true);
33 if (node)
34 {
35// ISceneNodeAnimator* anm = new extantzCamera(CursorControl);
36 ISceneNodeAnimator* anm = new extantzCamera();
37
38 // Bind the node's rotation to its target. This is consistent with 1.4.2 and below.
39 node->bindTargetAndRotation(true);
40 node->addAnimator(anm);
41 anm->drop();
42 }
43
44 return node;
45}
46
47
48//! constructor
49//extantzCamera::extantzCamera(gui::ICursorControl* cursorControl)
50// : CursorControl(cursorControl), MaxVerticalAngle(88.0f), MoveSpeed(0.4f), RotateSpeed(100.0f), JumpSpeed(3.0f),
51extantzCamera::extantzCamera()
52 : MaxVerticalAngle(88.0f), MouseYDirection(1.0f), LastAnimationTime(0), NoVerticalMovement(false)
53{
54 #ifdef _DEBUG
55 setDebugName("extantzCamera");
56 #endif
57
58 move.jump = 0.0; // Coz otherwise we start in jumping mode.
59 move.MoveSpeed = 0.1f;
60 move.RotateSpeed = 1.0f;
61 move.JumpSpeed = 3.0f;
62
63// if (CursorControl)
64// CursorControl->grab();
65}
66
67
68//! destructor
69extantzCamera::~extantzCamera()
70{
71// if (CursorControl)
72// CursorControl->drop();
73}
74
75
76void extantzCamera::animateNode(ISceneNode* node, u32 timeMs)
77{
78 if (!node || node->getType() != ESNT_CAMERA)
79 return;
80
81 ICameraSceneNode* camera = static_cast<ICameraSceneNode*>(node);
82
83 if (0 == LastAnimationTime)
84 {
85 camera->updateAbsolutePosition();
86// if (CursorControl )
87// {
88// CursorControl->setPosition(0.5f, 0.5f);
89// CursorPos = CenterCursor = CursorControl->getRelativePosition();
90// }
91
92 LastAnimationTime = timeMs;
93 }
94
95 // If the camera isn't the active camera, and receiving input, then don't process it.
96 // TODO - it never is, coz we are bypassing that, but can we replace this with something else?
97 if(!camera->isInputReceiverEnabled())
98 {
99// return;
100 }
101
102 scene::ISceneManager * smgr = camera->getSceneManager();
103 if(smgr && smgr->getActiveCamera() != camera)
104 return;
105
106 // get time
107 f32 timeDiff = (f32) (timeMs - LastAnimationTime);
108 LastAnimationTime = timeMs;
109
110 // update position
111 core::vector3df pos = camera->getPosition();
112
113 // Update rotation
114 core::vector3df target = (camera->getTarget() - camera->getAbsolutePosition());
115 core::vector3df relativeRotation = target.getHorizontalAngle();
116
117#if 0
118 if (CursorControl)
119 {
120 if (CursorPos != CenterCursor)
121 {
122 relativeRotation.Y -= (0.5f - CursorPos.X) * move.RotateSpeed;
123 relativeRotation.X -= (0.5f - CursorPos.Y) * move.RotateSpeed * MouseYDirection;
124
125 // X < MaxVerticalAngle or X > 360-MaxVerticalAngle
126
127 if (relativeRotation.X > MaxVerticalAngle*2 &&
128 relativeRotation.X < 360.0f-MaxVerticalAngle)
129 {
130 relativeRotation.X = 360.0f-MaxVerticalAngle;
131 }
132 else
133 if (relativeRotation.X > MaxVerticalAngle &&
134 relativeRotation.X < 360.0f-MaxVerticalAngle)
135 {
136 relativeRotation.X = MaxVerticalAngle;
137 }
138
139 // Do the fix as normal, special case below
140 // reset cursor position to the centre of the window.
141 CursorControl->setPosition(0.5f, 0.5f);
142 CenterCursor = CursorControl->getRelativePosition();
143
144 // needed to avoid problems when the event receiver is disabled
145 CursorPos = CenterCursor;
146 }
147
148 // Special case, mouse is whipped outside of window before it can update.
149 video::IVideoDriver* driver = smgr->getVideoDriver();
150 core::vector2d<u32> mousepos(u32(CursorControl->getPosition().X), u32(CursorControl->getPosition().Y));
151 core::rect<u32> screenRect(0, 0, driver->getScreenSize().Width, driver->getScreenSize().Height);
152
153 // Only if we are moving outside quickly.
154 bool reset = !screenRect.isPointInside(mousepos);
155
156 if(reset)
157 {
158 // Force a reset.
159 CursorControl->setPosition(0.5f, 0.5f);
160 CenterCursor = CursorControl->getRelativePosition();
161 CursorPos = CenterCursor;
162 }
163 }
164#else
165 relativeRotation.Y -= move.r * move.RotateSpeed;
166 relativeRotation.X -= move.s * move.RotateSpeed * MouseYDirection;
167
168 // X < MaxVerticalAngle or X > 360-MaxVerticalAngle
169
170 if ((relativeRotation.X > (MaxVerticalAngle * 2)) && (relativeRotation.X < (360.0f - MaxVerticalAngle)))
171 relativeRotation.X = 360.0f - MaxVerticalAngle;
172 else if ((relativeRotation.X > MaxVerticalAngle) && (relativeRotation.X < (360.0f - MaxVerticalAngle)))
173 relativeRotation.X = MaxVerticalAngle;
174#endif
175
176 // set target
177
178 target.set(0,0, core::max_(1.f, pos.getLength()));
179 core::vector3df movedir = target;
180
181 core::matrix4 mat;
182 mat.setRotationDegrees(core::vector3df(relativeRotation.X, relativeRotation.Y, 0));
183 mat.transformVect(target);
184
185 if (NoVerticalMovement)
186 {
187 mat.setRotationDegrees(core::vector3df(0, relativeRotation.Y, 0));
188 mat.transformVect(movedir);
189 }
190 else
191 {
192 movedir = target;
193 }
194
195 movedir.normalize();
196
197 pos += movedir * timeDiff * move.MoveSpeed * move.x;
198
199 // strafing
200 core::vector3df strafevect = target;
201 strafevect = strafevect.crossProduct(camera->getUpVector());
202
203 if (NoVerticalMovement)
204 strafevect.Y = 0.0f;
205
206 strafevect.normalize();
207
208 pos += strafevect * timeDiff * move.MoveSpeed * move.y;
209
210 // For jumping, we find the collision response animator attached to our camera
211 // and if it's not falling, we tell it to jump.
212 if (0.0 < move.jump)
213 {
214 const ISceneNodeAnimatorList& animators = camera->getAnimators();
215 ISceneNodeAnimatorList::ConstIterator it = animators.begin();
216 while(it != animators.end())
217 {
218 if(ESNAT_COLLISION_RESPONSE == (*it)->getType())
219 {
220 ISceneNodeAnimatorCollisionResponse * collisionResponse =
221 static_cast<ISceneNodeAnimatorCollisionResponse *>(*it);
222
223 if(!collisionResponse->isFalling())
224 collisionResponse->jump(move.JumpSpeed);
225 }
226
227 it++;
228 }
229 }
230
231 // write translation
232 camera->setPosition(pos);
233
234 // write right target
235 target += pos;
236 camera->setTarget(target);
237}
238
239
240ISceneNodeAnimator* extantzCamera::createClone(ISceneNode* node, ISceneManager* newManager)
241{
242// extantzCamera *newAnimator = new extantzCamera(CursorControl);
243 extantzCamera *newAnimator = new extantzCamera();
244 return newAnimator;
245}
246
247#ifdef __cplusplus
248extern "C" {
249#endif
250
251cameraMove *getCameraMove(ICameraSceneNode *camera)
252{
253 cameraMove *cm = NULL;
254
255 if (camera)
256 {
257 const ISceneNodeAnimatorList &animators = camera->getAnimators();
258 ISceneNodeAnimatorList::ConstIterator it = animators.begin();
259 while(it != animators.end())
260 {
261 // TODO - We assume FPS == extantzCamera, coz Irrlicht hard codes the camera types in an enum, which is a pain to add to from outside.
262 if(ESNAT_CAMERA_FPS == (*it)->getType())
263 {
264 extantzCamera *ec = static_cast<extantzCamera *>(*it);
265
266 cm = &(ec->move);
267 }
268
269 it++;
270 }
271 }
272 return cm;
273}
274
275#ifdef __cplusplus
276}
277#endif
278
279
280} // namespace scene
281} // namespace irr
282
diff --git a/src/extantz/extantzCamera.h b/src/extantz/extantzCamera.h
new file mode 100644
index 0000000..9d74236
--- /dev/null
+++ b/src/extantz/extantzCamera.h
@@ -0,0 +1,101 @@
1// Copyright (C) 2002-2012 Nikolaus Gebhardt
2// This file is part of the "Irrlicht Engine".
3// For conditions of distribution and use, see copyright notice in irrlicht.h
4
5#ifndef __EXTANTZ_CAMERA_H_INCLUDED__
6#define __EXTANTZ_CAMERA_H_INCLUDED__
7
8
9#ifdef __cplusplus
10#include <ISceneNodeAnimator.h>
11#include <vector2d.h>
12#include <position2d.h>
13#include <SKeyMap.h>
14#include <irrArray.h>
15#include <ICameraSceneNode.h>
16
17using namespace irr;
18using namespace scene;
19
20extern "C"{
21#else
22typedef struct extantzCamera extantzCamera;
23typedef struct ICameraSceneNode ICameraSceneNode;
24#endif
25
26typedef struct
27{
28 float x, y, z;
29 float r, s, t;
30 float jump;
31 float JumpSpeed, RotateSpeed, MoveSpeed;
32} cameraMove;
33
34cameraMove *getCameraMove(ICameraSceneNode *camera);
35
36#ifdef __cplusplus
37}
38
39
40//namespace irr::gui
41//{
42// class ICursorControl;
43//}
44
45
46namespace irr
47{
48namespace scene
49{
50 ICameraSceneNode *addExtantzCamera(ISceneManager* sm, ISceneNode* parent, s32 id);
51
52 class extantzCamera : public ISceneNodeAnimator
53 {
54 public:
55
56 //! Constructor
57// extantzCamera(gui::ICursorControl* cursorControl);
58 extantzCamera();
59
60 //! Destructor
61 virtual ~extantzCamera();
62
63 //! Animates the scene node, currently only works on cameras
64 virtual void animateNode(ISceneNode* node, u32 timeMs);
65
66 //! This animator will receive events when attached to the active camera
67 virtual bool isEventReceiverEnabled() const
68 {
69 return false;
70 }
71
72 //! Returns the type of this animator
73 virtual ESCENE_NODE_ANIMATOR_TYPE getType() const
74 {
75 return ESNAT_CAMERA_FPS;
76 }
77
78 //! Creates a clone of this animator.
79 /** Please note that you will have to drop
80 (IReferenceCounted::drop()) the returned pointer once you're
81 done with it. */
82 virtual ISceneNodeAnimator* createClone(ISceneNode* node, ISceneManager* newManager=0);
83
84 bool NoVerticalMovement;
85 // -1.0f for inverted mouse, defaults to 1.0f
86 f32 MouseYDirection;
87
88 cameraMove move;
89
90 private:
91 f32 MaxVerticalAngle;
92 s32 LastAnimationTime;
93// core::position2d<f32> CenterCursor, CursorPos;
94 };
95};
96};
97#endif
98
99
100#endif // __EXTANTZ_CAMERA_H_INCLUDED__
101