aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries
diff options
context:
space:
mode:
Diffstat (limited to 'libraries')
-rw-r--r--libraries/LSL.lua870
-rw-r--r--libraries/LumbrJack.c85
-rw-r--r--libraries/LumbrJack.h38
-rw-r--r--libraries/Runnr.c318
-rw-r--r--libraries/Runnr.h15
-rwxr-xr-xlibraries/build.lua24
-rw-r--r--libraries/skang.lua1686
7 files changed, 0 insertions, 3036 deletions
diff --git a/libraries/LSL.lua b/libraries/LSL.lua
deleted file mode 100644
index 662c880..0000000
--- a/libraries/LSL.lua
+++ /dev/null
@@ -1,870 +0,0 @@
1-- A module of LSL stuffs.
2
3-- Using a module means it gets compiled each time? Maybe not if I can use bytecode files. Perhaps LuaJIT caches these?
4-- Does not seem to be slowing it down noticably, but that might change once the stubs are filled out.
5
6-- Use it like this -
7-- local _LSL = require 'LSL'
8
9--[[ From http://lua-users.org/wiki/LuaModuleFunctionCritiqued
10A related note on C code: The luaL_register [9] function in C is
11somewhat analogous to the module function in Lua, so luaL_register
12shares similar problems, at least when a non-NULL libname is used.
13Furthermore, the luaL_newmetatable/luaL_getmetatable/luaL_checkudata
14functions use a C string as a key into the global registry. This poses
15some potential for name conflicts--either because the modules were
16written by different people or because they are different versions of
17the same module loaded simultaneously. To address this, one may instead
18use a lightuserdata (pointer to variable of static linkage to ensure
19global uniqueness) for this key or store the metatable as an
20upvalue--either way is a bit more efficient and less error prone.
21]]
22
23-- http://www.lua.org/pil/15.4.html looks useful.
24-- http://www.lua.org/pil/15.5.html the last part about autoloading functions might be useful.
25
26
27local LSL = {};
28local SID = "";
29local scriptName = "";
30local running = true
31local paused = false
32local currentState = {}
33local detectedGroups = {}
34local detectedGrabs = {}
35local detectedKeys = {}
36local detectedLinkNumbers = {}
37local detectedNames = {}
38local detectedOwners = {}
39local detectedPoss = {}
40local detectedRots = {}
41local detectedTouchBinormals = {}
42local detectedTouchFaces = {}
43local detectedTouchNormals = {}
44local detectedTouchPoss = {}
45local detectedTouchSTs = {}
46local detectedTouchUVs = {}
47local detectedTypes = {}
48local detectedVels = {}
49local waitAndProcess
50
51
52-- Debugging aids
53
54-- Functions to print tables.
55local print_table, print_table_start
56
57function print_table_start(table, space, name)
58 print(space .. name .. ": ");
59 print(space .. "{");
60 print_table(table, space .. " ");
61 print(space .. "}");
62end
63
64function print_table(table, space)
65 for k, v in pairs(table) do
66 if type(v) == "table" then
67 print_table_start(v, space, k);
68 elseif type(v) == "string" then
69 print(space .. k .. ': "' .. v .. '";')
70 else
71 print(space .. k .. ": " .. v .. ";")
72 end
73 end
74end
75
76function msg(...)
77 print(SID, ...) -- The comma adds a tab, fancy that. B-)
78end
79
80
81-- Stuff called from the wire protocol has to be global, but I think this means just global to this file.
82
83events = {}
84
85function start() paused = false end
86function stop() paused = true end
87function quit() running = false end
88
89function events.detectedGroups(list) detectedGroups = list end
90function events.detectedGrabs(list) detectedGrabs = list end
91function events.detectedKeys(list) detectedKeys = list end
92function events.detectedLinkNumbers(list) detectedLinkNumbers = list end
93function events.detectedNames(list) detectedNames = list end
94function events.detectedOwners(list) detectedOwners = list end
95function events.detectedPoss(list) detectedPoss = list end
96function events.detectedRots(list) detectedRots = list end
97function events.detectedTouchBinormals(list) detectedTouchBinormals = list end
98function events.detectedTouchFaces(list) detectedTouchFaces = list end
99function events.detectedTouchNormals(list) detectedTouchNormals = list end
100function events.detectedTouchPoss(list) detectedTouchPoss = list end
101function events.detectedTouchSTs(list) detectedTouchSTs = list end
102function events.detectedTouchUVs(list) detectedTouchUVs = list end
103function events.detectedTypes(list) detectedTypes = list end
104function events.detectedVels(list) detectedVels = list end
105
106function events.detectsClear()
107 detectedGroups = {}
108 detectedGrabs = {}
109 detectedKeys = {}
110 detectedLinkNumbers = {}
111 detectedNames = {}
112 detectedOwners = {}
113 detectedPoss = {}
114 detectedRots = {}
115 detectedTouchBinormals = {}
116 detectedTouchFaces = {}
117 detectedTouchNormals = {}
118 detectedTouchPoss = {}
119 detectedTouchSTs = {}
120 detectedTouchUVs = {}
121 detectedTypes = {}
122 detectedVels = {}
123end
124
125function events.at_rot_target(tnum, targetrot, ourrot) if nil ~= currentState.at_rot_target then currentState.at_rot_target(tnum, targetrot, ourrot) end events.detectsClear() end
126function events.at_target(tnum, targetpos, ourpos) if nil ~= currentState.at_target then currentState.at_target(tnum, targetpos, ourpos) end events.detectsClear() end
127function events.attach(id) if nil ~= currentState.attach then currentState.attach(id) end events.detectsClear() end
128function events.changed(change) if nil ~= currentState.changed then currentState.changed(change) end events.detectsClear() end
129function events.collision_start(num_detected) if nil ~= currentState.collision_start then currentState.collision_start(num_detected) end events.detectsClear() end
130function events.collision(num_detected) if nil ~= currentState.collision then currentState.collision(num_detected) end events.detectsClear() end
131function events.collision_end(num_detected) if nil ~= currentState.collision_end then currentState.collision_end(num_detected) end events.detectsClear() end
132function events.control(id, held, changed) if nil ~= currentState.control then currentState.control(id, held, changed) end events.detectsClear() end
133function events.dataserver(queryid, data) if nil ~= currentState.dataserver then currentState.dataserver(queryid, data) end events.detectsClear() end
134function events.email(Time, address, subj, message, num_left) if nil ~= currentState.email then currentState.email(Time, address, subj, message, num_left) end events.detectsClear() end
135function events.http_request(request_id, status, metadata, body) if nil ~= currentState.http_request then currentState.http_request(request_id, status, metadata, body) end events.detectsClear() end
136function events.http_response(request_id, status, metadata, body) if nil ~= currentState.http_response then currentState.http_response(request_id, status, metadata, body) end events.detectsClear() end
137function events.land_collision_start(pos) if nil ~= currentState.land_collision_start then currentState.land_collision_start(pos) end events.detectsClear() end
138function events.land_collision(pos) if nil ~= currentState.land_collision then currentState.land_collision(pos) end events.detectsClear() end
139function events.land_collision_end(pos) if nil ~= currentState.land_collision_end then currentState.land_collision_end(pos) end events.detectsClear() end
140function events.link_message(sender_num, num, str, id) if nil ~= currentState.link_message then currentState.link_message(sender_num, num, str, id) end events.detectsClear() end
141function events.listen(channel, name, id, message) if nil ~= currentState.listen then currentState.listen(channel, name, id, message) end events.detectsClear() end
142function events.money(id, amount) if nil ~= currentState.money then currentState.money(id, amount) end events.detectsClear() end
143function events.moving_start() if nil ~= currentState.moving_start then currentState.moving_start() end events.detectsClear() end
144function events.moving_end() if nil ~= currentState.moving_end then currentState.moving_end() end events.detectsClear() end
145function events.no_sensor() if nil ~= currentState.no_sensor then currentState.no_sensor() end events.detectsClear() end
146function events.not_at_rot_target() if nil ~= currentState.not_at_rot_target then currentState.not_at_rot_target() end events.detectsClear() end
147function events.not_at_target() if nil ~= currentState.not_at_target then currentState.not_at_target() end events.detectsClear() end
148function events.object_rez(id) if nil ~= currentState.object_rez then currentState.object_rez() end events.detectsClear() end
149function events.on_rez(start_param) if nil ~= currentState.on_rezz then currentState.on_rez(start_param) end events.detectsClear() end
150function events.remote_data(event_type, channel, message_id, sender, idata, sdata) if nil ~= currentState.remote_data then currentState.remote_data(event_type, channel, message_id, sender, idata, sdata) end events.detectsClear() end
151function events.run_time_permissions(perm) if nil ~= currentState.run_time_permisions then currentState.run_time_permissions(perm) end events.detectsClear() end
152function events.sensor(num_detected) if nil ~= currentState.sensor then currentState.sensor(num_detected) end events.detectsClear() end
153function events.state_entry() if nil ~= currentState.state_entry then currentState.state_entry() end events.detectsClear() end
154function events.state_exit() if nil ~= currentState.state_exit then currentState.state_exit() end events.detectsClear() end
155function events.timer() if nil ~= currentState.timer then currentState.timer() end events.detectsClear() end
156function events.touch_start(num_detected) if nil ~= currentState.touch_start then currentState.touch_start(num_detected) end events.detectsClear() end
157function events.touch(num_detected) if nil ~= currentState.touch then currentState.touch(num_detected) end events.detectsClear() end
158function events.touch_end(num_detected) if nil ~= currentState.touch_end then currentState.touch_end(num_detected) end events.detectsClear() end
159function events.transaction_result(id, success, data) if nil ~= currentState.transaction_result then currentState.transaction_result(id, success, data) end events.detectsClear() end
160
161
162-- LSL function and constant creation stuff.
163
164local args2string -- Pre declare this.
165local functions = {}
166local mt = {}
167
168local function value2string(value, Type)
169 local temp = ""
170
171 if "float" == Type then temp = temp .. value
172 elseif "integer" == Type then temp = temp .. value
173 elseif "key" == Type then temp = "\"" .. value .. "\""
174 elseif "list" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]"
175 elseif "table" == Type then temp = "[" .. args2string(true, unpack(value)) .. "]"
176 elseif "string" == Type then temp = "\"" .. value .. "\""
177 elseif "rotation" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ", " .. value.s .. ">"
178 elseif "vector" == Type then temp = "<" .. value.x .. ", " .. value.y .. ", " .. value.z .. ">"
179 else
180 temp = temp .. value
181 end
182 return temp
183end
184
185function args2string(doType, ...)
186 local temp = ""
187 local first = true
188
189 for j,w in ipairs( {...} ) do
190 if first then first = false else temp = temp .. ", " end
191 if doType then
192 temp = temp .. value2string(w, type(w))
193 else
194 temp = temp .. w
195 end
196 end
197 return temp
198end
199
200function mt.callAndReturn(name, ...)
201 luaproc.sendback(name .. "(" .. args2string(true, ...) .. ")")
202end
203
204function mt.callAndWait(name, ...)
205 mt.callAndReturn(name, ...);
206 -- Eventually a sendForth() is called, which should end up passing through SendToChannel().
207 -- Wait for the result, which should be a Lua value as a string.
208 return waitAndProcess(true)
209end
210
211local function newConst(Type, name, value)
212 LSL[name] = value
213 return { Type = Type, name = name }
214end
215
216local function newFunc(Type, name, ... )
217 functions[name] = { Type = Type, args = {...} }
218 if "" == Type then
219 LSL[name] = function(...) mt.callAndReturn(name, ... ) end
220 else
221 LSL[name] = function(...) return mt.callAndWait(name, ... ) end
222 end
223end
224
225
226-- LSL constants.
227
228local constants =
229{
230 newConst("float", "PI", 3.14159265358979323846264338327950),
231 newConst("float", "PI_BY_TWO", LSL.PI / 2), -- 1.57079632679489661923132169163975
232 newConst("float", "TWO_PI", LSL.PI * 2), -- 6.28318530717958647692528676655900
233 newConst("float", "DEG_TO_RAD", LSL.PI / 180.0), -- 0.01745329252
234 newConst("float", "RAD_TO_DEG", 180.0 / LSL.PI), -- 57.2957795131
235 newConst("float", "SQRT2", 1.4142135623730950488016887242097),
236
237 newConst("integer", "CHANGED_INVENTORY", 0x001),
238 newConst("integer", "CHANGED_COLOR", 0x002),
239 newConst("integer", "CHANGED_SHAPE", 0x004),
240 newConst("integer", "CHANGED_SCALE", 0x008),
241 newConst("integer", "CHANGED_TEXTURE", 0x010),
242 newConst("integer", "CHANGED_LINK", 0x020),
243 newConst("integer", "CHANGED_ALLOWED_DROP", 0x040),
244 newConst("integer", "CHANGED_OWNER", 0x080),
245 newConst("integer", "CHANGED_REGION", 0x100),
246 newConst("integer", "CHANGED_TELEPORT", 0x200),
247 newConst("integer", "CHANGED_REGION_START", 0x400),
248 newConst("integer", "CHANGED_MEDIA", 0x800),
249
250 newConst("integer", "DEBUG_CHANNEL", 2147483647),
251 newConst("integer", "PUBLIC_CHANNEL", 0),
252
253 newConst("integer", "INVENTORY_ALL", -1),
254 newConst("integer", "INVENTORY_NONE", -1),
255 newConst("integer", "INVENTORY_TEXTURE", 0),
256 newConst("integer", "INVENTORY_SOUND", 1),
257 newConst("integer", "INVENTORY_LANDMARK", 3),
258 newConst("integer", "INVENTORY_CLOTHING", 5),
259 newConst("integer", "INVENTORY_OBJECT", 6),
260 newConst("integer", "INVENTORY_NOTECARD", 7),
261 newConst("integer", "INVENTORY_SCRIPT", 10),
262 newConst("integer", "INVENTORY_BODYPART", 13),
263 newConst("integer", "INVENTORY_ANIMATION", 20),
264 newConst("integer", "INVENTORY_GESTURE", 21),
265
266 newConst("integer", "ALL_SIDES", -1),
267 newConst("integer", "LINK_SET", -1),
268 newConst("integer", "LINK_ROOT", 1),
269 newConst("integer", "LINK_ALL_OTHERS", -2),
270 newConst("integer", "LINK_ALL_CHILDREN", -3),
271 newConst("integer", "LINK_THIS", -4),
272
273 newConst("integer", "PERM_ALL", 0x7FFFFFFF),
274 newConst("integer", "PERM_COPY", 0x00008000),
275 newConst("integer", "PERM_MODIFY", 0x00004000),
276 newConst("integer", "PERM_MOVE", 0x00080000),
277 newConst("integer", "PERM_TRANSFER", 0x00002000),
278 newConst("integer", "MASK_BASE", 0),
279 newConst("integer", "MASK_OWNER", 1),
280 newConst("integer", "MASK_GROUP", 2),
281 newConst("integer", "MASK_EVERYONE", 3),
282 newConst("integer", "MASK_NEXT", 4),
283 newConst("integer", "PERMISSION_DEBIT", 0x0002),
284 newConst("integer", "PERMISSION_TAKE_CONTROLS", 0x0004),
285 newConst("integer", "PERMISSION_TRIGGER_ANIMATION", 0x0010),
286 newConst("integer", "PERMISSION_ATTACH", 0x0020),
287 newConst("integer", "PERMISSION_CHANGE_LINKS", 0x0080),
288 newConst("integer", "PERMISSION_TRACK_CAMERA", 0x0400),
289 newConst("integer", "PERMISSION_CONTRAL_CAMERA", 0x0800),
290
291 newConst("integer", "AGENT", 0x01),
292 newConst("integer", "ACTIVE", 0x02),
293 newConst("integer", "PASSIVE", 0x04),
294 newConst("integer", "SCRIPTED", 0x08),
295
296 newConst("integer", "OBJECT_UNKNOWN_DETAIL", -1),
297
298 newConst("integer", "PRIM_BUMP_SHINY", 19),
299 newConst("integer", "PRIM_COLOR", 18),
300 newConst("integer", "PRIM_FLEXIBLE", 21),
301 newConst("integer", "PRIM_FULLBRIGHT", 20),
302 newConst("integer", "PRIM_GLOW", 25),
303 newConst("integer", "PRIM_MATERIAL", 2),
304 newConst("integer", "PRIM_PHANTOM", 5),
305 newConst("integer", "PRIM_PHYSICS", 3),
306 newConst("integer", "PRIM_POINT_LIGHT", 23),
307 newConst("integer", "PRIM_POSITION", 6),
308 newConst("integer", "PRIM_ROTATION", 8),
309 newConst("integer", "PRIM_SIZE", 7),
310 newConst("integer", "PRIM_TEMP_ON_REZ", 4),
311 newConst("integer", "PRIM_TYPE", 9),
312 newConst("integer", "PRIM_TYPE_OLD", 1),
313 newConst("integer", "PRIM_TEXGEN", 22),
314 newConst("integer", "PRIM_TEXTURE", 17),
315 newConst("integer", "PRIM_TEXT", 26),
316
317 newConst("integer", "PRIM_BUMP_NONE", 0),
318 newConst("integer", "PRIM_BUMP_BRIGHT", 1),
319 newConst("integer", "PRIM_BUMP_DARK", 2),
320 newConst("integer", "PRIM_BUMP_WOOD", 3),
321 newConst("integer", "PRIM_BUMP_BARK", 4),
322 newConst("integer", "PRIM_BUMP_BRICKS", 5),
323 newConst("integer", "PRIM_BUMP_CHECKER", 6),
324 newConst("integer", "PRIM_BUMP_CONCRETE", 7),
325 newConst("integer", "PRIM_BUMP_TILE", 8),
326 newConst("integer", "PRIM_BUMP_STONE", 9),
327 newConst("integer", "PRIM_BUMP_DISKS", 10),
328 newConst("integer", "PRIM_BUMP_GRAVEL", 11),
329 newConst("integer", "PRIM_BUMP_BLOBS", 12),
330 newConst("integer", "PRIM_BUMP_SIDING", 13),
331 newConst("integer", "PRIM_BUMP_LARGETILE", 14),
332 newConst("integer", "PRIM_BUMP_STUCCO", 15),
333 newConst("integer", "PRIM_BUMP_SUCTION", 16),
334 newConst("integer", "PRIM_BUMP_WEAVE", 17),
335
336 newConst("integer", "PRIM_HOLE_DEFAULT", 0),
337 newConst("integer", "PRIM_HOLE_CIRCLE", 16),
338 newConst("integer", "PRIM_HOLE_SQUARE", 32),
339 newConst("integer", "PRIM_HOLE_TRIANGLE", 48),
340
341 newConst("integer", "PRIM_MATERIAL_STONE", 0),
342 newConst("integer", "PRIM_MATERIAL_METAL", 1),
343 newConst("integer", "PRIM_MATERIAL_GLASS", 2),
344 newConst("integer", "PRIM_MATERIAL_WOOD", 3),
345 newConst("integer", "PRIM_MATERIAL_FLESH", 4),
346 newConst("integer", "PRIM_MATERIAL_PLASTIC", 5),
347 newConst("integer", "PRIM_MATERIAL_RUBBER", 6),
348 newConst("integer", "PRIM_MATERIAL_LIGHT", 7),
349
350 newConst("integer", "PRIM_SCULPT_TYPE_SPHERE", 1),
351 newConst("integer", "PRIM_SCULPT_TYPE_TORUS", 2),
352 newConst("integer", "PRIM_SCULPT_TYPE_PLANE", 3),
353 newConst("integer", "PRIM_SCULPT_TYPE_CYLINDER", 4),
354 newConst("integer", "PRIM_SCULPT_TYPE_MESH", 5),
355 newConst("integer", "PRIM_SCULPT_TYPE_MIMESH", 6),
356
357 newConst("integer", "PRIM_SHINY_NONE", 0),
358 newConst("integer", "PRIM_SHINY_LOW", 1),
359 newConst("integer", "PRIM_SHINY_MEDIUM", 2),
360 newConst("integer", "PRIM_SHINY_HIGH", 3),
361
362 newConst("integer", "PRIM_TYPE_BOX", 0),
363 newConst("integer", "PRIM_TYPE_CYLINDER", 1),
364 newConst("integer", "PRIM_TYPE_PRISM", 2),
365 newConst("integer", "PRIM_TYPE_SPHERE", 3),
366 newConst("integer", "PRIM_TYPE_TORUS", 4),
367 newConst("integer", "PRIM_TYPE_TUBE", 5),
368 newConst("integer", "PRIM_TYPE_RING", 6),
369 newConst("integer", "PRIM_TYPE_SCULPT", 7),
370
371 newConst("integer", "STRING_TRIM", 3),
372 newConst("integer", "STRING_TRIM_HEAD", 1),
373 newConst("integer", "STRING_TRIM_TAIL", 2),
374
375 newConst("integer", "TRUE", 1),
376 newConst("integer", "FALSE", 0),
377
378 newConst("integer", "TOUCH_INVALID_FACE", 0x7FFFFFFF),
379 newConst("vector", "TOUCH_INVALID_TEXCOORD", {x=-1.0, y=-1.0, z=0.0}),
380 newConst("vector", "TOUCH_INVALID_VECTOR", {x=0.0, y=0.0, z=0.0}),
381
382 newConst("integer", "TYPE_INTEGER", 1),
383 newConst("integer", "TYPE_FLOAT", 2),
384 newConst("integer", "TYPE_STRING", 3),
385 newConst("integer", "TYPE_KEY", 4),
386 newConst("integer", "TYPE_VECTOR", 5),
387 newConst("integer", "TYPE_ROTATION", 6),
388 newConst("integer", "TYPE_INVALID", 0),
389
390 newConst("string", "NULL_KEY", "00000000-0000-0000-0000-000000000000"),
391 newConst("string", "EOF", "\\n\\n\\n"), -- Corner case, dealt with later.
392
393 newConst("rotation", "ZERO_ROTATION", {x=0.0, y=0.0, z=0.0, s=1.0}),
394 newConst("vector", "ZERO_VECTOR", {x=0.0, y=0.0, z=0.0}),
395
396-- TODO - Temporary dummy variables to get vector and rotation thingies to work for now.
397
398 newConst("float", "s", 1.0),
399 newConst("float", "x", 0.0),
400 newConst("float", "y", 0.0),
401 newConst("float", "z", 0.0),
402}
403
404-- ll*() function definitions
405
406-- LSL avatar functions
407newFunc("key", "llAvatarOnSitTarget")
408newFunc("list", "llGetAnimationList", "key id")
409newFunc("integer", "llGetPermissions")
410newFunc("key", "llGetPermissionsKey")
411newFunc("string", "llKey2Name", "key avatar")
412newFunc("", "llRequestPermissions", "key avatar", "integer perms")
413newFunc("integer", "llSameGroup", "key avatar")
414newFunc("", "llStartAnimation", "string anim")
415newFunc("", "llStopAnimation", "string anim")
416newFunc("", "llUnSit", "key avatar")
417
418-- LSL collision / detect / sensor functions
419newFunc("integer", "llDetectedGroup", "integer index")
420newFunc("vector", "llDetectedGrab", "integer index")
421newFunc("key", "llDetectedKey", "integer index")
422newFunc("integer", "llDetectedLinkNumber", "integer index")
423newFunc("string", "llDetectedName", "integer index")
424newFunc("key", "llDetectedOwner", "integer index")
425newFunc("vector", "llDetectedPos", "integer index")
426newFunc("rotation", "llDetectedRot", "integer index")
427newFunc("vector", "llDetectedTouchBinormal", "integer index")
428newFunc("integer", "llDetectedTouchFace", "integer index")
429newFunc("vector", "llDetectedTouchNormal", "integer index")
430newFunc("vector", "llDetectedTouchPos", "integer index")
431newFunc("vector", "llDetectedTouchST", "integer index")
432newFunc("vector", "llDetectedTouchUV", "integer index")
433newFunc("integer", "llDetectedType", "integer index")
434newFunc("vector", "llDetectedVel", "integer index")
435
436
437-- LSL communications functions
438newFunc("", "llDialog", "key avatar", "string caption", "list arseBackwardsMenu", "integer channel")
439newFunc("integer", "llListen", "integer channel", "string name", "key id", "string msg")
440newFunc("", "llListenRemove", "integer handle")
441newFunc("", "llOwnerSay", "string text")
442newFunc("", "llSay", "integer channel", "string text")
443newFunc("", "llShout", "integer channel", "string text")
444newFunc("", "llWhisper", "integer channel", "string text")
445newFunc("", "llMessageLinked", "integer link", "integer num", "string text", "key aKey")
446
447-- LSL inventory functions.
448newFunc("string", "llGetInventoryName", "integer Type", "integer index")
449newFunc("integer", "llGetInventoryNumber", "integer Type")
450newFunc("integer", "llGetInventoryType", "string name")
451newFunc("key", "llGetNotecardLine", "string name", "integer index")
452newFunc("", "llRezAtRoot", "string name", "vector position", "vector velocity", "rotation rot", "integer channel")
453newFunc("", "llRezObject", "string name", "vector position", "vector velocity", "rotation rot", "integer channel")
454
455-- LSL list functions.
456newFunc("list", "llCSV2List", "string text")
457newFunc("list", "llDeleteSubList", "list l", "integer start", "integer End")
458newFunc("string", "llDumpList2String", "list l", "string separator")
459newFunc("integer", "llGetListLength", "list l")
460newFunc("string", "llList2CSV", "list l")
461newFunc("float", "llList2Float", "list l", "integer index")
462newFunc("integer", "llList2Integer", "list l", "integer index")
463newFunc("key", "llList2Key", "list l", "integer index")
464newFunc("list", "llList2List", "list l", "integer start", "integer End")
465newFunc("string", "llList2String", "list l", "integer index")
466newFunc("rotation", "llList2Rotation", "list l", "integer index")
467newFunc("vector", "llList2Vector", "list l", "integer index")
468newFunc("integer", "llListFindList", "list l", "list l1")
469newFunc("list", "llListInsertList", "list l", "list l1", "integer index")
470newFunc("list", "llListReplaceList", "list l", "list part", "integer start", "integer End")
471newFunc("list", "llListSort", "list l", "integer stride", "integer ascending")
472newFunc("list", "llParseString2List", "string In", "list l", "list l1")
473newFunc("list", "llParseStringKeepNulls", "string In", "list l", "list l1")
474
475-- LSL math functions
476newFunc("rotation", "llEuler2Rot", "vector vec")
477newFunc("float", "llFrand", "float max")
478newFunc("float", "llPow", "float number", "float places")
479newFunc("vector", "llRot2Euler", "rotation rot")
480newFunc("integer", "llRound", "float number")
481
482-- LSL media functions
483newFunc("", "llPlaySound", "string name", "float volume")
484
485-- LSL object / prim functions
486newFunc("", "llDie")
487newFunc("key", "llGetKey")
488newFunc("integer", "llGetLinkNumber")
489newFunc("string", "llGetObjectDesc")
490newFunc("string", "llGetObjectName")
491newFunc("key", "llGetOwner")
492newFunc("", "llSetObjectDesc", "string text")
493newFunc("", "llSetObjectName", "string text")
494newFunc("", "llSetPrimitiveParams", "list params")
495newFunc("", "llSetSitText", "string text")
496newFunc("", "llSetText", "string text", "vector colour", "float alpha")
497newFunc("", "llSitTarget", "vector pos", "rotation rot")
498
499-- LSL rotation / scaling / translation functions
500newFunc("vector", "llGetPos")
501newFunc("rotation", "llGetRot")
502newFunc("", "llSetPos", "vector pos")
503newFunc("", "llSetRot", "rotation rot")
504newFunc("", "llSetScale", "vector scale")
505
506-- LSL script functions
507newFunc("integer", "llGetFreeMemory")
508newFunc("string", "llGetScriptName")
509newFunc("", "llResetOtherScript", "string name")
510newFunc("", "llResetScript")
511newFunc("", "llSetScriptState", "string name", "integer running")
512
513-- LSL string functions
514newFunc("string", "llGetSubString", "string text", "integer start", "integer End")
515newFunc("integer", "llStringLength", "string text")
516newFunc("string", "llStringTrim", "string text", "integer type")
517newFunc("integer", "llSubStringIndex", "string text", "string sub")
518
519-- LSL texture functions
520newFunc("float", "llGetAlpha", "integer side")
521newFunc("", "llSetAlpha", "float alpha", "integer side")
522newFunc("", "llSetColor", "vector colour", "integer side")
523
524-- LSL time functions
525newFunc("float", "llGetTime")
526newFunc("", "llResetTime")
527newFunc("", "llSetTimerEvent", "float seconds")
528newFunc("float", "llSleep", "float seconds") -- Faked return type, it actually does not return anything. This forces it to wait. Actually fully implements llSleep(). B-)
529
530
531-- TODO - fake this for now.
532function --[[integer]] LSL.llGetInventoryType(--[[string]] name) return LSL.INVENTORY_SCRIPT end;
533
534
535-- LSL collision / detect / sensor functions
536function --[[integer]] LSL.llDetectedGroup( --[[integer]] index) return detectedGroups [index + 1] or false end
537function --[[vector]] LSL.llDetectedGrab( --[[integer]] index) return detectedGrabs [index + 1] or LSL.ZERO_VECTOR end
538function --[[key]] LSL.llDetectedKey( --[[integer]] index) return detectedKeys [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY
539function --[[integer]] LSL.llDetectedLinkNumber( --[[integer]] index) return detectedLinkNumbers [index + 1] or 0 end
540function --[[string]] LSL.llDetectedName( --[[integer]] index) return detectedNames [index + 1] or "" end -- LL says it returns NULL_KEY, LSL Wiki says an empty string. Apparently there used to be an exploit for creating multi kb names, normally they are 255 characters.
541function --[[key]] LSL.llDetectedOwner( --[[integer]] index) return detectedOwners [index + 1] or LSL.NULL_KEY end -- LL says "returns empty key" which is "", but LSL Wiki says NULL_KEY
542function --[[vector]] LSL.llDetectedPos( --[[integer]] index) return detectedPoss [index + 1] or LSL.ZERO_VECTOR end
543function --[[rotation]] LSL.llDetectedRot( --[[integer]] index) return detectedRots [index + 1] or LSL.ZERO_ROTATION end
544function --[[vector]] LSL.llDetectedTouchBinormal( --[[integer]] index) return detectedTouchBinormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end
545function --[[integer]] LSL.llDetectedTouchFace( --[[integer]] index) return detectedTouchFaces [index + 1] or LSL.TOUCH_INVALID_FACE end
546function --[[vector]] LSL.llDetectedTouchNormal( --[[integer]] index) return detectedTouchNormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end
547function --[[vector]] LSL.llDetectedTouchPos( --[[integer]] index) return detectedTouchPoss [index + 1] or LSL.TOUCH_INVALID_VECTOR end
548function --[[vector]] LSL.llDetectedTouchST( --[[integer]] index) return detectedTouchSTs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end
549function --[[vector]] LSL.llDetectedTouchUV( --[[integer]] index) return detectedTouchUVs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end
550function --[[integer]] LSL.llDetectedType( --[[integer]] index) return detectedTypes [index + 1] or 0 end
551function --[[vector]] LSL.llDetectedVel( --[[integer]] index) return detectedVels [index + 1] or LSL.ZERO_VECTOR end
552
553
554-- LSL list functions.
555
556--function --[[list]] LSL.llCSV2List(--[[string]] text) return {} end;
557function --[[list]] LSL.llDeleteSubList(--[[list]] l,--[[integer]] start,--[[integer]] eNd)
558 local result = {}
559 local x = 1
560
561 -- Deal with the impedance mismatch.
562 start = start + 1
563 eNd = eNd + 1
564 for i = 1,#l do
565 if i < start then result[x] = l[i]; x = x + 1
566 elseif i > eNd then result[x] = l[i]; x = x + 1
567 end
568 end
569
570 return result
571end
572
573function --[[string]] LSL.llDumpList2String(--[[list]] l, --[[string]] separator)
574 local result = ""
575 for i = 1,#l do
576 if "" ~= result then result = result .. separator end
577 result = result .. l[i]
578 end
579 return result
580end
581
582function --[[integer]] LSL.llGetListLength(--[[list]] l)
583 return #l
584end
585
586function --[[string]] LSL.llList2CSV(--[[list]] l)
587 return LSL.llDumpList2String(l, ",")
588end
589
590function --[[float]] LSL.llList2Float(--[[list]] l,--[[integer]] index)
591 local result = tonumber(l[index])
592 if nil == result then result = 0.0 end
593 return result
594end
595
596function --[[integer]] LSL.llList2Integer(--[[list]] l,--[[integer]] index)
597 local result = tonumber(l[index+1])
598 if nil == result then result = 0 end
599 return result
600end
601
602function --[[key]] LSL.llList2Key(--[[list]] l,--[[integer]] index)
603 local result = l[index+1]
604 if result then return "" .. result else return LSL.NULL_KEY end
605end
606
607function --[[list]] LSL.llList2List(--[[list]] l,--[[integer]] start,--[[integer]] eNd)
608 local result = {}
609 local x = 1
610
611 --[[ TODO -
612Using negative numbers for start and/or end causes the index to count backwards from the length of the list, so 0, -1 would capture the entire list.
613If start is larger than end the list returned is the exclusion of the entries, so 6, 4 would give the entire list except for the 5th entry.
614 ]]
615
616 -- Deal with the impedance mismatch.
617 start = start + 1
618 eNd = eNd + 1
619 for i = 1,#l do
620 if i >= start then result[x] = l[i]; x = x + 1
621 elseif i <= eNd then result[x] = l[i]; x = x + 1
622 end
623 end
624
625 return result
626end
627
628function --[[string]] LSL.llList2String(--[[list]] l,--[[integer]] index)
629 local result = l[index+1]
630 if result then return "" .. result else return "" end
631end
632
633function --[[rotation]] LSL.llList2Rotation(--[[list]] l,--[[integer]] index)
634 local result = l[index+1]
635 if nil == result then result = LSL.ZERO_ROTATION end
636 -- TODO - check if it's not an actual rotation, then return LSS.ZERO_ROTATION
637 return result
638end
639
640function --[[vector]] LSL.llList2Vector(--[[list]] l,--[[integer]] index)
641 local result = l[index+1]
642 if nil == result then result = LSL.ZERO_VECTOR end
643 -- TODO - check if it's not an actual rotation, then return LSS.ZERO_VECTOR
644 return result
645end
646
647--function --[[integer]] LSL.llListFindList(--[[list]] l, --[[list]] l1) return 0 end;
648function --[[list]] LSL.llListInsertList(--[[list]] l, --[[list]] l1,--[[integer]] index)
649 local result = {}
650 local x = 1
651 local y
652 for i = 1,index do
653 result[x] = l[i]
654 x = x + 1
655 end
656 y = x
657 for i = 1,#ll do
658 result[x] = ll[i]
659 x = x + 1
660 end
661 for i = y,#l do
662 result[x] = l[i]
663 x = x + 1
664 end
665 return result
666end
667
668function --[[list]] LSL.llListReplaceList(--[[list]] l, --[[list]] part,--[[integer]] start,--[[integer]] eNd)
669 local result = {}
670 local x = 1
671 local y
672 for i = 1,index do
673 result[x] = l[i]
674 x = x + 1
675 end
676 for i = 1,#part do
677 result[x] = part[i]
678 x = x + 1
679 end
680 for i = index,#l do
681 result[x] = l[i]
682 x = x + 1
683 end
684 return result
685end
686
687function --[[list]] LSL.llListSort(--[[list]] l,--[[integer]] stride,--[[integer]] ascending)
688 local result = {}
689
690 -- TODO - Deal with stride and ascending.
691 for i = 1,#l do
692 result[x] = l[i]; x = x + 1
693 end
694 table.sort(result)
695
696 return result
697end
698
699--function --[[list]] LSL.llParseString2List(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end;
700--function --[[list]] LSL.llParseStringKeepNulls(--[[string]] In, --[[list]] l, --[[list]] l1) return {} end;
701
702
703-- LSL script functions
704
705function --[[string]] LSL.llGetScriptName()
706 return scriptName
707end
708
709
710-- LSL string functions
711
712function --[[string]] LSL.llGetSubString(--[[string]] text, --[[integer]] start, --[[integer]] End)
713 -- Deal with the impedance mismatch.
714 if 0 <= start then start = start + 1 end
715 if 0 <= End then End = End + 1 end
716-- TODO - If start is larger than end the substring is the exclusion of the entries, so 6,4 would give the entire string except for the 5th character.
717 return string.sub(text, start, End)
718end
719
720function --[[integer]] LSL.llSubStringIndex(--[[string]] text, --[[string]] sub)
721 local start, End = string.find(text, sub, 1, true)
722
723 if nil == start then return -1 else return start - 1 end
724end
725
726
727-- Crements stuff.
728
729function LSL.preDecrement(name) _G[name] = _G[name] - 1; return _G[name]; end;
730function LSL.preIncrement(name) _G[name] = _G[name] + 1; return _G[name]; end;
731function LSL.postDecrement(name) local temp = _G[name]; _G[name] = _G[name] - 1; return temp; end;
732function LSL.postIncrement(name) local temp = _G[name]; _G[name] = _G[name] + 1; return temp; end;
733
734
735-- State stuff
736
737function LSL.stateChange(x)
738 if currentState ~= x then -- Changing to the same state is a NOP.
739 -- TODO - Should clear out pending events, except timer()
740 -- Also forget about any event setup that needs setting up via some ll*() function, except timer().
741 if nil ~= currentState.state_exit then
742 currentState.state_exit();
743 end
744 currentState = x;
745 --[[ Never return to the current states event handler. In theory. lol
746 Notably, it's not actually legal to do a state change from a function, only from handlers.
747 There is a hack though, but it's unsupported, so I don't have to worry about it so much.
748
749 Write out "state new;" as "return _LSL.stateChange(newState);", with stateChange() returning new.state_entry()) which will force two tail calls.
750
751 The caller of stateChange() might be a function rather than an event handler.
752 Which will return to the event handler (possibly via other nested function calls) as if the state never changed.
753 http://lslwiki.net/lslwiki/wakka.php?wakka=FunctionStateChangeHack seems to imply that this is exactly what LSL does.
754 Soooo, this might actually work perfectly.
755 Except for one minor quirk, as that page shows - only the top level function's state sticks, unless the handler does one to, then the handlers one overrides things.
756 Which I can probably ignore anyway, as this entire calling states within functions thing is an unsupported hack.
757 ]]
758 if nil ~= currentState.state_entry then
759 return currentState.state_entry();
760 end
761 end
762end;
763
764function LSL.mainLoop(sid, name, x)
765 local status, errorMsg
766 local result
767
768 SID = sid
769 scriptName = name
770 LSL.EOF = "\n\n\n" -- Fix this up now.
771
772 LSL.stateChange(x);
773 waitAndProcess(false)
774 msg("Script quitting.")
775end
776
777function waitAndProcess(returnWanted)
778 local Type = "event"
779
780 if returnWanted then Type = "result" end
781 while running do
782 local message = luaproc.receive(SID)
783 if message then
784 -- TODO - should we be discarding return values while paused? I don't think so, so we need to process those,
785 if paused then
786 if "start()" == message then paused = false end
787 else
788 result, errorMsg = loadstring(message) -- "The environment of the returned function is the global environment." Though normally, a function inherits it's environment from the function creating it. Which is what we want. lol
789 if nil == result then
790 msg("Not a valid " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg)
791 else
792 -- Set the functions environment to ours, for the protection of the script, coz loadstring sets it to the global environment instead.
793 -- TODO - On the other hand, we will need the global environment when we call event handlers. So we should probably stash it around here somewhere.
794 -- Meh, seems to be working fine as it is.
795 setfenv(result, getfenv(1))
796 status, result = pcall(result)
797 if not status then
798 msg("Error from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. result)
799 elseif result then
800 -- Check if we are waiting for a return, and got it.
801 if returnWanted and string.match(message, "^return ") then
802 return result
803 end
804 -- Otherwise, just run it and keep looping.
805 status, errorMsg = luaproc.send(sid, result)
806 if not status then
807 msg("Error sending results from " .. Type .. ": " .. message .. " ERROR MESSAGE: " .. errorMsg)
808 end
809 end
810 end
811 end
812 end
813 end
814end
815
816-- Typecasting stuff.
817
818function LSL.floatTypecast(x)
819 local temp = tonumber(x)
820 if nil == temp then temp = 0 end
821 return temp;
822end
823
824function LSL.integerTypecast(x)
825 local temp = tonumber(x)
826 if nil == temp then temp = 0 end
827 return temp;
828end
829
830function LSL.keyTypecast(x)
831 return "" .. x;
832end
833
834function LSL.listTypecast(x)
835 return {x};
836end
837
838function LSL.rotationTypecast(x)
839 return x;
840end
841
842function LSL.stringTypecast(x)
843 return "" .. x;
844end
845
846function LSL.vectorTypecast(x)
847 return x;
848end
849
850
851-- Called at compiler set up time, to produce the constants.lsl file.
852function LSL.gimmeLSL()
853 for i,v in ipairs(constants) do
854 local value = LSL[v.name]
855
856 print(v.Type .. " " .. v.name .. " = " .. value2string(value, v.Type) .. ";")
857 end
858
859 for k in pairs(functions) do
860 local v = functions[k]
861 if nil == v.args then
862 print(v.Type .. " " .. k .. "(){}")
863 else
864 print(v.Type .. " " .. k .. "(" .. args2string(false, unpack(v.args)) .. "){}")
865 end
866 end
867end
868
869
870return LSL;
diff --git a/libraries/LumbrJack.c b/libraries/LumbrJack.c
deleted file mode 100644
index 084d916..0000000
--- a/libraries/LumbrJack.c
+++ /dev/null
@@ -1,85 +0,0 @@
1/* LumbrJack - a logging library that wraps Eina logging.
2
3*/
4
5
6#include "LumbrJack.h"
7
8
9static char dateTime[DATE_TIME_LEN];
10
11static void _ggg_log_print_cb(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args)
12{
13 FILE *f = data;
14 char dt[DATE_TIME_LEN + 1];
15 char fileTab[256], funcTab[256];
16
17 getDateTime(NULL, dt, NULL);
18 dt[19] = '\0';
19 if (12 > strlen(file))
20 snprintf(fileTab, sizeof(fileTab), "%s\t\t", file);
21 else
22 snprintf(fileTab, sizeof(fileTab), "%s\t", file);
23 snprintf(funcTab, sizeof(funcTab), "\t%s", fnc);
24 fprintf(f, "%s ", dt);
25 if (f == stderr)
26 eina_log_print_cb_stderr(d, level, fileTab, funcTab, line, fmt, data, args);
27 else if (f == stdout)
28 eina_log_print_cb_stdout(d, level, fileTab, funcTab, line, fmt, data, args);
29 fflush(f);
30}
31
32int loggingStartup(char *name, int logDom)
33{
34 if (logDom < 0)
35 {
36 logDom = eina_log_domain_register(name, NULL);
37 if (logDom < 0)
38 {
39 EINA_LOG_CRIT("could not register log domain '%s'", name);
40 return logDom;
41 }
42 }
43 eina_log_level_set(EINA_LOG_LEVEL_DBG);
44 eina_log_domain_level_set(name, EINA_LOG_LEVEL_DBG);
45 eina_log_print_cb_set(_ggg_log_print_cb, stderr);
46
47 // Shut up the excess debugging shit from EFL.
48 eina_log_domain_level_set("eo", EINA_LOG_LEVEL_WARN);
49 eina_log_domain_level_set("eldbus", EINA_LOG_LEVEL_WARN);
50 eina_log_domain_level_set("eet", EINA_LOG_LEVEL_WARN);
51 eina_log_domain_level_set("ecore", EINA_LOG_LEVEL_WARN);
52 eina_log_domain_level_set("ecore_audio", EINA_LOG_LEVEL_WARN);
53 eina_log_domain_level_set("ecore_con", EINA_LOG_LEVEL_WARN);
54 eina_log_domain_level_set("ecore_evas", EINA_LOG_LEVEL_WARN);
55 eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN);
56 eina_log_domain_level_set("ecore_input_evas", EINA_LOG_LEVEL_WARN);
57 eina_log_domain_level_set("ecore_system_upower", EINA_LOG_LEVEL_WARN);
58 eina_log_domain_level_set("eio", EINA_LOG_LEVEL_WARN);
59 eina_log_domain_level_set("evas_main", EINA_LOG_LEVEL_WARN);
60
61 return logDom;
62}
63
64char *getDateTime(struct tm **nowOut, char *dateOut, time_t *timeOut)
65{
66 struct tm *newTime;
67 time_t szClock;
68 char *date = dateTime;
69
70 // Get time in seconds
71 time(&szClock);
72 // Convert time to struct tm form
73 newTime = localtime(&szClock);
74
75 if (nowOut)
76 *nowOut = newTime;
77 if (dateOut)
78 date = dateOut;
79 if (timeOut)
80 *timeOut = szClock;
81
82 // format
83 strftime(date, DATE_TIME_LEN, "%d/%m/%Y %H:%M:%S\r", newTime);
84 return (dateTime);
85}
diff --git a/libraries/LumbrJack.h b/libraries/LumbrJack.h
deleted file mode 100644
index 4a3290c..0000000
--- a/libraries/LumbrJack.h
+++ /dev/null
@@ -1,38 +0,0 @@
1
2#include <stdio.h>
3#include <ctype.h>
4
5#include <Eina.h>
6
7
8#define PC(...) EINA_LOG_DOM_CRIT(ourGlobals->logDom, __VA_ARGS__)
9#define PE(...) EINA_LOG_DOM_ERR(ourGlobals->logDom, __VA_ARGS__)
10#define PW(...) EINA_LOG_DOM_WARN(ourGlobals->logDom, __VA_ARGS__)
11#define PD(...) EINA_LOG_DOM_DBG(ourGlobals->logDom, __VA_ARGS__)
12#define PI(...) EINA_LOG_DOM_INFO(ourGlobals->logDom, __VA_ARGS__)
13
14#define PCm(...) EINA_LOG_DOM_CRIT(ourGlobals.logDom, __VA_ARGS__)
15#define PEm(...) EINA_LOG_DOM_ERR(ourGlobals.logDom, __VA_ARGS__)
16#define PWm(...) EINA_LOG_DOM_WARN(ourGlobals.logDom, __VA_ARGS__)
17#define PDm(...) EINA_LOG_DOM_DBG(ourGlobals.logDom, __VA_ARGS__)
18#define PIm(...) EINA_LOG_DOM_INFO(ourGlobals.logDom, __VA_ARGS__)
19
20#define D() PD("DEBUG")
21
22
23// "01:03:52 01-01-1973\n\0"
24#define DATE_TIME_LEN 21
25
26
27#ifndef FALSE
28// NEVER change this
29typedef enum
30{
31 FALSE = 0,
32 TRUE = 1
33} boolean;
34#endif
35
36
37int loggingStartup(char *name, int logDom);
38char *getDateTime(struct tm **nowOut, char *dateOut, time_t *tiemOut);
diff --git a/libraries/Runnr.c b/libraries/Runnr.c
deleted file mode 100644
index a24e7f5..0000000
--- a/libraries/Runnr.c
+++ /dev/null
@@ -1,318 +0,0 @@
1/* Runnr - a library that deals with running Lua scripts.
2
3*/
4
5
6#include "Runnr.h"
7
8
9void dumpStack(lua_State *L, int i)
10{
11 int type = lua_type(L, i);
12
13 switch (type)
14 {
15 case LUA_TNONE : printf("Stack %d is empty\n", i); break;
16 case LUA_TNIL : printf("Stack %d is a nil\n", i); break;
17 case LUA_TBOOLEAN : printf("Stack %d is a boolean - %d\n", i, lua_toboolean(L, i)); break;
18 case LUA_TNUMBER : printf("Stack %d is a number\n - %f", i, lua_tonumber(L, i)); break;
19 case LUA_TSTRING : printf("Stack %d is a string - %s\n", i, lua_tostring(L, i)); break;
20 case LUA_TFUNCTION : printf("Stack %d is a function\n", i); break;
21 case LUA_TTHREAD : printf("Stack %d is a thread\n", i); break;
22 case LUA_TTABLE :
23 {
24 int j;
25
26 printf("Stack %d is a table", i);
27 lua_getfield(L, i, "_NAME");
28 j = lua_gettop(L);
29 if (lua_isstring(L, j))
30 printf(" - %s", lua_tostring(L, j));
31 lua_pop(L, 1);
32 printf("\n");
33 break;
34 }
35 case LUA_TUSERDATA : printf("Stack %d is a userdata\n", i); break;
36 case LUA_TLIGHTUSERDATA : printf("Stack %d is a light userdata\n", i); break;
37 default : printf("Stack %d is unknown\n", i); break;
38 }
39}
40
41
42// These are what the various symbols are for each type -
43// int %
44// num #
45// str $
46// bool !
47// C func &
48// table.field @ Expects an integer and a string.
49// nil ~
50// table {} Starts and stops filling up a new table.
51// ( Just syntax sugar for call.
52// call ) Expects an integer, the number of results left after the call.
53// FIXME: Still to do, if we ever use them -
54// stack = Get a value from the stack, expects a stack index.
55// userdata +
56// lightuserdata *
57// thread ^
58
59static char *_push_name(lua_State *L, char *q, int *idx) // Stack usage [-0, +1, e or m]
60{
61 char *p = q;
62 char temp = '\0';
63
64 // A simplistic scan through an identifier, it's wrong, but it's quick,
65 // and we don't mind that it's wrong, coz this is only internal.
66 while (isalnum((int)*q))
67 q++;
68 temp = *q;
69 *q = '\0';
70 if (*idx > 0)
71 lua_getfield(L, *idx, p); // Stack usage [-0, +1, e]
72 else
73 {
74 if (p != q)
75 lua_pushstring(L, p); // Stack usage [-0, +1, m]
76 else
77 {
78 lua_pushnumber(L, (lua_Number) (0 - (*idx)));
79 (*idx)--;
80 }
81 }
82 *q = temp;
83
84 return q;
85}
86
87int pull_lua(lua_State *L, int i, char *params, ...) // Stack usage -
88 // if i is a table
89 // [-n, +n, e]
90 // else
91 // [-0, +0, -]
92{
93 va_list vl;
94 char *f = strdup(params);
95 char *p = f;
96 int n = 0, j = i, count = 0;
97 Eina_Bool table = EINA_FALSE;
98
99 if (!f) return -1;
100 va_start(vl, params);
101
102 if (lua_istable(L, i)) // Stack usage [-0, +0, -]
103 {
104 j = -1;
105 table = EINA_TRUE;
106 }
107
108 while (*p)
109 {
110 char *q;
111 Eina_Bool get = EINA_TRUE;
112
113 while (isspace((int)*p))
114 p++;
115 q = p + 1;
116 switch (*p)
117 {
118 case '%':
119 {
120 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e]
121 if (lua_isnumber(L, j)) // Stack usage [-0, +0, -]
122 {
123 int *v = va_arg(vl, int *);
124 *v = lua_tointeger(L, j); // Stack usage [-0, +0, -]
125 n++;
126 }
127 break;
128 }
129 case '#':
130 {
131 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e]
132 if (lua_isnumber(L, j)) // Stack usage [-0, +0, -]
133 {
134 double *v = va_arg(vl, double *);
135 *v = lua_tonumber(L, j); // Stack usage [-0, +0, -]
136 n++;
137 }
138 break;
139 }
140 case '$':
141 {
142 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e]
143 if (lua_isstring(L, j)) // Stack usage [-0, +0, -]
144 {
145 char **v = va_arg(vl, char **);
146 size_t len;
147 char *temp = (char *) lua_tolstring(L, j, &len); // Stack usage [-0, +0, m]
148
149 len++; // Cater for the null at the end.
150 *v = malloc(len);
151 if (*v)
152 {
153 memcpy(*v, temp, len);
154 n++;
155 }
156 }
157 break;
158 }
159 case '!':
160 {
161 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, e]
162 if (lua_isboolean(L, j)) // Stack usage [-0, +0, -]
163 {
164 int *v = va_arg(vl, int *);
165 *v = lua_toboolean(L, j); // Stack usage [-0, +0, -]
166 n++;
167 }
168 break;
169 }
170 default:
171 {
172 get = EINA_FALSE;
173 break;
174 }
175 }
176
177 if (get)
178 {
179 if (table)
180 {
181 // If this is a table, then we pushed a value on the stack, pop it off.
182 lua_pop(L, 1); // Stack usage [-n, +0, -]
183 }
184 else
185 j++;
186 count++;
187 }
188 p = q;
189 }
190
191 va_end(vl);
192 free(f);
193 if (count > n)
194 n = 0;
195 else if (table)
196 n = 1;
197 return n;
198}
199
200int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em]
201{
202 va_list vl;
203 char *f = strdup(params);
204 char *p = f;
205 int n = 0, table = 0, i = -1;
206
207 if (!f) return -1;
208
209 va_start(vl, params);
210
211 while (*p)
212 {
213 char *q;
214 Eina_Bool set = EINA_TRUE;
215
216 while (isspace((int)*p))
217 p++;
218 q = p + 1;
219 switch (*p)
220 {
221 case '%':
222 {
223 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
224 lua_pushinteger(L, va_arg(vl, int)); // Stack usage [-0, +1, -]
225 break;
226 }
227 case '#':
228 {
229 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
230 lua_pushnumber(L, va_arg(vl, double)); // Stack usage [-0, +1, -]
231 break;
232 }
233 case '$':
234 {
235 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
236 lua_pushstring(L, va_arg(vl, char *)); // Stack usage [-0, +1, m]
237 break;
238 }
239 case '!':
240 {
241 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
242 lua_pushboolean(L, va_arg(vl, int)); // Stack usage [-0, +1, -]
243 break;
244 }
245 case '=':
246 {
247 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
248 lua_pushvalue(L, va_arg(vl, int)); // Stack usage [-0, +1, -]
249 break;
250 }
251 case '@':
252 {
253 int tabl = va_arg(vl, int);
254 char *field = va_arg(vl, char *);
255
256 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
257 lua_getfield(L, tabl, field); // Stack usage [-0, +1, e]
258 break;
259 }
260 case '&':
261 {
262 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
263 lua_pushcfunction(L, va_arg(vl, void *)); // Stack usage [-0, +1, m]
264 break;
265 }
266 case '~':
267 {
268 if (table) q = _push_name(L, q, &i); // Stack usage [-0, +1, m]
269 lua_pushnil(L); // Stack usage [-0, +1, -]
270 break;
271 }
272 case '(': // Just syntax sugar.
273 {
274 set = EINA_FALSE;
275 break;
276 }
277 case ')':
278 {
279 lua_call(L, n - 1, va_arg(vl, int));
280 n = 0;
281 set = EINA_FALSE;
282 break;
283 }
284 case '{':
285 {
286 lua_newtable(L);
287 table++;
288 n++;
289 set = EINA_FALSE;
290 break;
291 }
292 case '}':
293 {
294 table--;
295 set = EINA_FALSE;
296 break;
297 }
298 default:
299 {
300 set = EINA_FALSE;
301 break;
302 }
303 }
304
305 if (set)
306 {
307 if (table > 0)
308 lua_settable(L, -3); // Stack usage [-2, +0, e]
309 else
310 n++;
311 }
312 p = q;
313 }
314
315 va_end(vl);
316 free(f);
317 return n;
318}
diff --git a/libraries/Runnr.h b/libraries/Runnr.h
deleted file mode 100644
index dc720ff..0000000
--- a/libraries/Runnr.h
+++ /dev/null
@@ -1,15 +0,0 @@
1
2#include <stdio.h>
3#include <ctype.h>
4
5#include <Eina.h>
6
7#include <lua.h>
8#include <luajit.h>
9#include <lualib.h>
10#include <lauxlib.h>
11
12
13void dumpStack(lua_State *L, int i);
14int pull_lua(lua_State *L, int i, char *params, ...);
15int push_lua(lua_State *L, char *params, ...);
diff --git a/libraries/build.lua b/libraries/build.lua
deleted file mode 100755
index 8b48b70..0000000
--- a/libraries/build.lua
+++ /dev/null
@@ -1,24 +0,0 @@
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('')
10 else
11 print("ERROR - " .. err)
12 end
13 dir = workingDir
14end
15
16LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS
17
18removeFiles(dir, {'LumbrJack.o', 'libLumbrJack.so', 'Runnr.o', 'libRunnr.so'})
19
20runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -fPIC -c LumbrJack.c')
21runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libLumbrJack.so -o libLumbrJack.so LumbrJack.o')
22
23runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c Runnr.c')
24runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libRunnr.so -o libRunnr.so Runnr.o')
diff --git a/libraries/skang.lua b/libraries/skang.lua
deleted file mode 100644
index 23549c3..0000000
--- a/libraries/skang.lua
+++ /dev/null
@@ -1,1686 +0,0 @@
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.