diff options
Diffstat (limited to 'libraries')
-rw-r--r-- | libraries/LSL.lua | 870 | ||||
-rw-r--r-- | libraries/LumbrJack.c | 85 | ||||
-rw-r--r-- | libraries/LumbrJack.h | 38 | ||||
-rw-r--r-- | libraries/Runnr.c | 318 | ||||
-rw-r--r-- | libraries/Runnr.h | 15 | ||||
-rwxr-xr-x | libraries/build.lua | 24 | ||||
-rw-r--r-- | libraries/skang.lua | 1686 |
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 | ||
10 | A related note on C code: The luaL_register [9] function in C is | ||
11 | somewhat analogous to the module function in Lua, so luaL_register | ||
12 | shares similar problems, at least when a non-NULL libname is used. | ||
13 | Furthermore, the luaL_newmetatable/luaL_getmetatable/luaL_checkudata | ||
14 | functions use a C string as a key into the global registry. This poses | ||
15 | some potential for name conflicts--either because the modules were | ||
16 | written by different people or because they are different versions of | ||
17 | the same module loaded simultaneously. To address this, one may instead | ||
18 | use a lightuserdata (pointer to variable of static linkage to ensure | ||
19 | global uniqueness) for this key or store the metatable as an | ||
20 | upvalue--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 | |||
27 | local LSL = {}; | ||
28 | local SID = ""; | ||
29 | local scriptName = ""; | ||
30 | local running = true | ||
31 | local paused = false | ||
32 | local currentState = {} | ||
33 | local detectedGroups = {} | ||
34 | local detectedGrabs = {} | ||
35 | local detectedKeys = {} | ||
36 | local detectedLinkNumbers = {} | ||
37 | local detectedNames = {} | ||
38 | local detectedOwners = {} | ||
39 | local detectedPoss = {} | ||
40 | local detectedRots = {} | ||
41 | local detectedTouchBinormals = {} | ||
42 | local detectedTouchFaces = {} | ||
43 | local detectedTouchNormals = {} | ||
44 | local detectedTouchPoss = {} | ||
45 | local detectedTouchSTs = {} | ||
46 | local detectedTouchUVs = {} | ||
47 | local detectedTypes = {} | ||
48 | local detectedVels = {} | ||
49 | local waitAndProcess | ||
50 | |||
51 | |||
52 | -- Debugging aids | ||
53 | |||
54 | -- Functions to print tables. | ||
55 | local print_table, print_table_start | ||
56 | |||
57 | function print_table_start(table, space, name) | ||
58 | print(space .. name .. ": "); | ||
59 | print(space .. "{"); | ||
60 | print_table(table, space .. " "); | ||
61 | print(space .. "}"); | ||
62 | end | ||
63 | |||
64 | function 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 | ||
74 | end | ||
75 | |||
76 | function msg(...) | ||
77 | print(SID, ...) -- The comma adds a tab, fancy that. B-) | ||
78 | end | ||
79 | |||
80 | |||
81 | -- Stuff called from the wire protocol has to be global, but I think this means just global to this file. | ||
82 | |||
83 | events = {} | ||
84 | |||
85 | function start() paused = false end | ||
86 | function stop() paused = true end | ||
87 | function quit() running = false end | ||
88 | |||
89 | function events.detectedGroups(list) detectedGroups = list end | ||
90 | function events.detectedGrabs(list) detectedGrabs = list end | ||
91 | function events.detectedKeys(list) detectedKeys = list end | ||
92 | function events.detectedLinkNumbers(list) detectedLinkNumbers = list end | ||
93 | function events.detectedNames(list) detectedNames = list end | ||
94 | function events.detectedOwners(list) detectedOwners = list end | ||
95 | function events.detectedPoss(list) detectedPoss = list end | ||
96 | function events.detectedRots(list) detectedRots = list end | ||
97 | function events.detectedTouchBinormals(list) detectedTouchBinormals = list end | ||
98 | function events.detectedTouchFaces(list) detectedTouchFaces = list end | ||
99 | function events.detectedTouchNormals(list) detectedTouchNormals = list end | ||
100 | function events.detectedTouchPoss(list) detectedTouchPoss = list end | ||
101 | function events.detectedTouchSTs(list) detectedTouchSTs = list end | ||
102 | function events.detectedTouchUVs(list) detectedTouchUVs = list end | ||
103 | function events.detectedTypes(list) detectedTypes = list end | ||
104 | function events.detectedVels(list) detectedVels = list end | ||
105 | |||
106 | function 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 = {} | ||
123 | end | ||
124 | |||
125 | function 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 | ||
126 | function events.at_target(tnum, targetpos, ourpos) if nil ~= currentState.at_target then currentState.at_target(tnum, targetpos, ourpos) end events.detectsClear() end | ||
127 | function events.attach(id) if nil ~= currentState.attach then currentState.attach(id) end events.detectsClear() end | ||
128 | function events.changed(change) if nil ~= currentState.changed then currentState.changed(change) end events.detectsClear() end | ||
129 | function events.collision_start(num_detected) if nil ~= currentState.collision_start then currentState.collision_start(num_detected) end events.detectsClear() end | ||
130 | function events.collision(num_detected) if nil ~= currentState.collision then currentState.collision(num_detected) end events.detectsClear() end | ||
131 | function events.collision_end(num_detected) if nil ~= currentState.collision_end then currentState.collision_end(num_detected) end events.detectsClear() end | ||
132 | function events.control(id, held, changed) if nil ~= currentState.control then currentState.control(id, held, changed) end events.detectsClear() end | ||
133 | function events.dataserver(queryid, data) if nil ~= currentState.dataserver then currentState.dataserver(queryid, data) end events.detectsClear() end | ||
134 | function 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 | ||
135 | function 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 | ||
136 | function 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 | ||
137 | function events.land_collision_start(pos) if nil ~= currentState.land_collision_start then currentState.land_collision_start(pos) end events.detectsClear() end | ||
138 | function events.land_collision(pos) if nil ~= currentState.land_collision then currentState.land_collision(pos) end events.detectsClear() end | ||
139 | function events.land_collision_end(pos) if nil ~= currentState.land_collision_end then currentState.land_collision_end(pos) end events.detectsClear() end | ||
140 | function 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 | ||
141 | function events.listen(channel, name, id, message) if nil ~= currentState.listen then currentState.listen(channel, name, id, message) end events.detectsClear() end | ||
142 | function events.money(id, amount) if nil ~= currentState.money then currentState.money(id, amount) end events.detectsClear() end | ||
143 | function events.moving_start() if nil ~= currentState.moving_start then currentState.moving_start() end events.detectsClear() end | ||
144 | function events.moving_end() if nil ~= currentState.moving_end then currentState.moving_end() end events.detectsClear() end | ||
145 | function events.no_sensor() if nil ~= currentState.no_sensor then currentState.no_sensor() end events.detectsClear() end | ||
146 | function events.not_at_rot_target() if nil ~= currentState.not_at_rot_target then currentState.not_at_rot_target() end events.detectsClear() end | ||
147 | function events.not_at_target() if nil ~= currentState.not_at_target then currentState.not_at_target() end events.detectsClear() end | ||
148 | function events.object_rez(id) if nil ~= currentState.object_rez then currentState.object_rez() end events.detectsClear() end | ||
149 | function events.on_rez(start_param) if nil ~= currentState.on_rezz then currentState.on_rez(start_param) end events.detectsClear() end | ||
150 | function 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 | ||
151 | function events.run_time_permissions(perm) if nil ~= currentState.run_time_permisions then currentState.run_time_permissions(perm) end events.detectsClear() end | ||
152 | function events.sensor(num_detected) if nil ~= currentState.sensor then currentState.sensor(num_detected) end events.detectsClear() end | ||
153 | function events.state_entry() if nil ~= currentState.state_entry then currentState.state_entry() end events.detectsClear() end | ||
154 | function events.state_exit() if nil ~= currentState.state_exit then currentState.state_exit() end events.detectsClear() end | ||
155 | function events.timer() if nil ~= currentState.timer then currentState.timer() end events.detectsClear() end | ||
156 | function events.touch_start(num_detected) if nil ~= currentState.touch_start then currentState.touch_start(num_detected) end events.detectsClear() end | ||
157 | function events.touch(num_detected) if nil ~= currentState.touch then currentState.touch(num_detected) end events.detectsClear() end | ||
158 | function events.touch_end(num_detected) if nil ~= currentState.touch_end then currentState.touch_end(num_detected) end events.detectsClear() end | ||
159 | function 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 | |||
164 | local args2string -- Pre declare this. | ||
165 | local functions = {} | ||
166 | local mt = {} | ||
167 | |||
168 | local 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 | ||
183 | end | ||
184 | |||
185 | function 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 | ||
198 | end | ||
199 | |||
200 | function mt.callAndReturn(name, ...) | ||
201 | luaproc.sendback(name .. "(" .. args2string(true, ...) .. ")") | ||
202 | end | ||
203 | |||
204 | function 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) | ||
209 | end | ||
210 | |||
211 | local function newConst(Type, name, value) | ||
212 | LSL[name] = value | ||
213 | return { Type = Type, name = name } | ||
214 | end | ||
215 | |||
216 | local 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 | ||
223 | end | ||
224 | |||
225 | |||
226 | -- LSL constants. | ||
227 | |||
228 | local 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 | ||
407 | newFunc("key", "llAvatarOnSitTarget") | ||
408 | newFunc("list", "llGetAnimationList", "key id") | ||
409 | newFunc("integer", "llGetPermissions") | ||
410 | newFunc("key", "llGetPermissionsKey") | ||
411 | newFunc("string", "llKey2Name", "key avatar") | ||
412 | newFunc("", "llRequestPermissions", "key avatar", "integer perms") | ||
413 | newFunc("integer", "llSameGroup", "key avatar") | ||
414 | newFunc("", "llStartAnimation", "string anim") | ||
415 | newFunc("", "llStopAnimation", "string anim") | ||
416 | newFunc("", "llUnSit", "key avatar") | ||
417 | |||
418 | -- LSL collision / detect / sensor functions | ||
419 | newFunc("integer", "llDetectedGroup", "integer index") | ||
420 | newFunc("vector", "llDetectedGrab", "integer index") | ||
421 | newFunc("key", "llDetectedKey", "integer index") | ||
422 | newFunc("integer", "llDetectedLinkNumber", "integer index") | ||
423 | newFunc("string", "llDetectedName", "integer index") | ||
424 | newFunc("key", "llDetectedOwner", "integer index") | ||
425 | newFunc("vector", "llDetectedPos", "integer index") | ||
426 | newFunc("rotation", "llDetectedRot", "integer index") | ||
427 | newFunc("vector", "llDetectedTouchBinormal", "integer index") | ||
428 | newFunc("integer", "llDetectedTouchFace", "integer index") | ||
429 | newFunc("vector", "llDetectedTouchNormal", "integer index") | ||
430 | newFunc("vector", "llDetectedTouchPos", "integer index") | ||
431 | newFunc("vector", "llDetectedTouchST", "integer index") | ||
432 | newFunc("vector", "llDetectedTouchUV", "integer index") | ||
433 | newFunc("integer", "llDetectedType", "integer index") | ||
434 | newFunc("vector", "llDetectedVel", "integer index") | ||
435 | |||
436 | |||
437 | -- LSL communications functions | ||
438 | newFunc("", "llDialog", "key avatar", "string caption", "list arseBackwardsMenu", "integer channel") | ||
439 | newFunc("integer", "llListen", "integer channel", "string name", "key id", "string msg") | ||
440 | newFunc("", "llListenRemove", "integer handle") | ||
441 | newFunc("", "llOwnerSay", "string text") | ||
442 | newFunc("", "llSay", "integer channel", "string text") | ||
443 | newFunc("", "llShout", "integer channel", "string text") | ||
444 | newFunc("", "llWhisper", "integer channel", "string text") | ||
445 | newFunc("", "llMessageLinked", "integer link", "integer num", "string text", "key aKey") | ||
446 | |||
447 | -- LSL inventory functions. | ||
448 | newFunc("string", "llGetInventoryName", "integer Type", "integer index") | ||
449 | newFunc("integer", "llGetInventoryNumber", "integer Type") | ||
450 | newFunc("integer", "llGetInventoryType", "string name") | ||
451 | newFunc("key", "llGetNotecardLine", "string name", "integer index") | ||
452 | newFunc("", "llRezAtRoot", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") | ||
453 | newFunc("", "llRezObject", "string name", "vector position", "vector velocity", "rotation rot", "integer channel") | ||
454 | |||
455 | -- LSL list functions. | ||
456 | newFunc("list", "llCSV2List", "string text") | ||
457 | newFunc("list", "llDeleteSubList", "list l", "integer start", "integer End") | ||
458 | newFunc("string", "llDumpList2String", "list l", "string separator") | ||
459 | newFunc("integer", "llGetListLength", "list l") | ||
460 | newFunc("string", "llList2CSV", "list l") | ||
461 | newFunc("float", "llList2Float", "list l", "integer index") | ||
462 | newFunc("integer", "llList2Integer", "list l", "integer index") | ||
463 | newFunc("key", "llList2Key", "list l", "integer index") | ||
464 | newFunc("list", "llList2List", "list l", "integer start", "integer End") | ||
465 | newFunc("string", "llList2String", "list l", "integer index") | ||
466 | newFunc("rotation", "llList2Rotation", "list l", "integer index") | ||
467 | newFunc("vector", "llList2Vector", "list l", "integer index") | ||
468 | newFunc("integer", "llListFindList", "list l", "list l1") | ||
469 | newFunc("list", "llListInsertList", "list l", "list l1", "integer index") | ||
470 | newFunc("list", "llListReplaceList", "list l", "list part", "integer start", "integer End") | ||
471 | newFunc("list", "llListSort", "list l", "integer stride", "integer ascending") | ||
472 | newFunc("list", "llParseString2List", "string In", "list l", "list l1") | ||
473 | newFunc("list", "llParseStringKeepNulls", "string In", "list l", "list l1") | ||
474 | |||
475 | -- LSL math functions | ||
476 | newFunc("rotation", "llEuler2Rot", "vector vec") | ||
477 | newFunc("float", "llFrand", "float max") | ||
478 | newFunc("float", "llPow", "float number", "float places") | ||
479 | newFunc("vector", "llRot2Euler", "rotation rot") | ||
480 | newFunc("integer", "llRound", "float number") | ||
481 | |||
482 | -- LSL media functions | ||
483 | newFunc("", "llPlaySound", "string name", "float volume") | ||
484 | |||
485 | -- LSL object / prim functions | ||
486 | newFunc("", "llDie") | ||
487 | newFunc("key", "llGetKey") | ||
488 | newFunc("integer", "llGetLinkNumber") | ||
489 | newFunc("string", "llGetObjectDesc") | ||
490 | newFunc("string", "llGetObjectName") | ||
491 | newFunc("key", "llGetOwner") | ||
492 | newFunc("", "llSetObjectDesc", "string text") | ||
493 | newFunc("", "llSetObjectName", "string text") | ||
494 | newFunc("", "llSetPrimitiveParams", "list params") | ||
495 | newFunc("", "llSetSitText", "string text") | ||
496 | newFunc("", "llSetText", "string text", "vector colour", "float alpha") | ||
497 | newFunc("", "llSitTarget", "vector pos", "rotation rot") | ||
498 | |||
499 | -- LSL rotation / scaling / translation functions | ||
500 | newFunc("vector", "llGetPos") | ||
501 | newFunc("rotation", "llGetRot") | ||
502 | newFunc("", "llSetPos", "vector pos") | ||
503 | newFunc("", "llSetRot", "rotation rot") | ||
504 | newFunc("", "llSetScale", "vector scale") | ||
505 | |||
506 | -- LSL script functions | ||
507 | newFunc("integer", "llGetFreeMemory") | ||
508 | newFunc("string", "llGetScriptName") | ||
509 | newFunc("", "llResetOtherScript", "string name") | ||
510 | newFunc("", "llResetScript") | ||
511 | newFunc("", "llSetScriptState", "string name", "integer running") | ||
512 | |||
513 | -- LSL string functions | ||
514 | newFunc("string", "llGetSubString", "string text", "integer start", "integer End") | ||
515 | newFunc("integer", "llStringLength", "string text") | ||
516 | newFunc("string", "llStringTrim", "string text", "integer type") | ||
517 | newFunc("integer", "llSubStringIndex", "string text", "string sub") | ||
518 | |||
519 | -- LSL texture functions | ||
520 | newFunc("float", "llGetAlpha", "integer side") | ||
521 | newFunc("", "llSetAlpha", "float alpha", "integer side") | ||
522 | newFunc("", "llSetColor", "vector colour", "integer side") | ||
523 | |||
524 | -- LSL time functions | ||
525 | newFunc("float", "llGetTime") | ||
526 | newFunc("", "llResetTime") | ||
527 | newFunc("", "llSetTimerEvent", "float seconds") | ||
528 | newFunc("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. | ||
532 | function --[[integer]] LSL.llGetInventoryType(--[[string]] name) return LSL.INVENTORY_SCRIPT end; | ||
533 | |||
534 | |||
535 | -- LSL collision / detect / sensor functions | ||
536 | function --[[integer]] LSL.llDetectedGroup( --[[integer]] index) return detectedGroups [index + 1] or false end | ||
537 | function --[[vector]] LSL.llDetectedGrab( --[[integer]] index) return detectedGrabs [index + 1] or LSL.ZERO_VECTOR end | ||
538 | function --[[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 | ||
539 | function --[[integer]] LSL.llDetectedLinkNumber( --[[integer]] index) return detectedLinkNumbers [index + 1] or 0 end | ||
540 | function --[[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. | ||
541 | function --[[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 | ||
542 | function --[[vector]] LSL.llDetectedPos( --[[integer]] index) return detectedPoss [index + 1] or LSL.ZERO_VECTOR end | ||
543 | function --[[rotation]] LSL.llDetectedRot( --[[integer]] index) return detectedRots [index + 1] or LSL.ZERO_ROTATION end | ||
544 | function --[[vector]] LSL.llDetectedTouchBinormal( --[[integer]] index) return detectedTouchBinormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end | ||
545 | function --[[integer]] LSL.llDetectedTouchFace( --[[integer]] index) return detectedTouchFaces [index + 1] or LSL.TOUCH_INVALID_FACE end | ||
546 | function --[[vector]] LSL.llDetectedTouchNormal( --[[integer]] index) return detectedTouchNormals [index + 1] or LSL.TOUCH_INVALID_VECTOR end | ||
547 | function --[[vector]] LSL.llDetectedTouchPos( --[[integer]] index) return detectedTouchPoss [index + 1] or LSL.TOUCH_INVALID_VECTOR end | ||
548 | function --[[vector]] LSL.llDetectedTouchST( --[[integer]] index) return detectedTouchSTs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end | ||
549 | function --[[vector]] LSL.llDetectedTouchUV( --[[integer]] index) return detectedTouchUVs [index + 1] or LSL.TOUCH_INVALID_TEXCOORD end | ||
550 | function --[[integer]] LSL.llDetectedType( --[[integer]] index) return detectedTypes [index + 1] or 0 end | ||
551 | function --[[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; | ||
557 | function --[[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 | ||
571 | end | ||
572 | |||
573 | function --[[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 | ||
580 | end | ||
581 | |||
582 | function --[[integer]] LSL.llGetListLength(--[[list]] l) | ||
583 | return #l | ||
584 | end | ||
585 | |||
586 | function --[[string]] LSL.llList2CSV(--[[list]] l) | ||
587 | return LSL.llDumpList2String(l, ",") | ||
588 | end | ||
589 | |||
590 | function --[[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 | ||
594 | end | ||
595 | |||
596 | function --[[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 | ||
600 | end | ||
601 | |||
602 | function --[[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 | ||
605 | end | ||
606 | |||
607 | function --[[list]] LSL.llList2List(--[[list]] l,--[[integer]] start,--[[integer]] eNd) | ||
608 | local result = {} | ||
609 | local x = 1 | ||
610 | |||
611 | --[[ TODO - | ||
612 | Using 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. | ||
613 | If 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 | ||
626 | end | ||
627 | |||
628 | function --[[string]] LSL.llList2String(--[[list]] l,--[[integer]] index) | ||
629 | local result = l[index+1] | ||
630 | if result then return "" .. result else return "" end | ||
631 | end | ||
632 | |||
633 | function --[[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 | ||
638 | end | ||
639 | |||
640 | function --[[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 | ||
645 | end | ||
646 | |||
647 | --function --[[integer]] LSL.llListFindList(--[[list]] l, --[[list]] l1) return 0 end; | ||
648 | function --[[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 | ||
666 | end | ||
667 | |||
668 | function --[[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 | ||
685 | end | ||
686 | |||
687 | function --[[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 | ||
697 | end | ||
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 | |||
705 | function --[[string]] LSL.llGetScriptName() | ||
706 | return scriptName | ||
707 | end | ||
708 | |||
709 | |||
710 | -- LSL string functions | ||
711 | |||
712 | function --[[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) | ||
718 | end | ||
719 | |||
720 | function --[[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 | ||
724 | end | ||
725 | |||
726 | |||
727 | -- Crements stuff. | ||
728 | |||
729 | function LSL.preDecrement(name) _G[name] = _G[name] - 1; return _G[name]; end; | ||
730 | function LSL.preIncrement(name) _G[name] = _G[name] + 1; return _G[name]; end; | ||
731 | function LSL.postDecrement(name) local temp = _G[name]; _G[name] = _G[name] - 1; return temp; end; | ||
732 | function LSL.postIncrement(name) local temp = _G[name]; _G[name] = _G[name] + 1; return temp; end; | ||
733 | |||
734 | |||
735 | -- State stuff | ||
736 | |||
737 | function 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 | ||
762 | end; | ||
763 | |||
764 | function 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.") | ||
775 | end | ||
776 | |||
777 | function 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 | ||
814 | end | ||
815 | |||
816 | -- Typecasting stuff. | ||
817 | |||
818 | function LSL.floatTypecast(x) | ||
819 | local temp = tonumber(x) | ||
820 | if nil == temp then temp = 0 end | ||
821 | return temp; | ||
822 | end | ||
823 | |||
824 | function LSL.integerTypecast(x) | ||
825 | local temp = tonumber(x) | ||
826 | if nil == temp then temp = 0 end | ||
827 | return temp; | ||
828 | end | ||
829 | |||
830 | function LSL.keyTypecast(x) | ||
831 | return "" .. x; | ||
832 | end | ||
833 | |||
834 | function LSL.listTypecast(x) | ||
835 | return {x}; | ||
836 | end | ||
837 | |||
838 | function LSL.rotationTypecast(x) | ||
839 | return x; | ||
840 | end | ||
841 | |||
842 | function LSL.stringTypecast(x) | ||
843 | return "" .. x; | ||
844 | end | ||
845 | |||
846 | function LSL.vectorTypecast(x) | ||
847 | return x; | ||
848 | end | ||
849 | |||
850 | |||
851 | -- Called at compiler set up time, to produce the constants.lsl file. | ||
852 | function 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 | ||
867 | end | ||
868 | |||
869 | |||
870 | return 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 | |||
9 | static char dateTime[DATE_TIME_LEN]; | ||
10 | |||
11 | static 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 | |||
32 | int 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 | |||
64 | char *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 | ||
29 | typedef enum | ||
30 | { | ||
31 | FALSE = 0, | ||
32 | TRUE = 1 | ||
33 | } boolean; | ||
34 | #endif | ||
35 | |||
36 | |||
37 | int loggingStartup(char *name, int logDom); | ||
38 | char *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 | |||
9 | void 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 | |||
59 | static 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 | |||
87 | int 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 | |||
200 | int 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 | |||
13 | void dumpStack(lua_State *L, int i); | ||
14 | int pull_lua(lua_State *L, int i, char *params, ...); | ||
15 | int 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 | |||
3 | local dir = ... | ||
4 | |||
5 | if '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 | ||
14 | end | ||
15 | |||
16 | LDFLAGS = '-L ' .. dir .. ' ' .. LDFLAGS | ||
17 | |||
18 | removeFiles(dir, {'LumbrJack.o', 'libLumbrJack.so', 'Runnr.o', 'libRunnr.so'}) | ||
19 | |||
20 | runCommand('C libraries', dir, 'gcc ' .. CFLAGS .. ' -fPIC -c LumbrJack.c') | ||
21 | runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -shared -Wl,-soname,libLumbrJack.so -o libLumbrJack.so LumbrJack.o') | ||
22 | |||
23 | runCommand(nil, dir, 'gcc ' .. CFLAGS .. ' -fPIC -c Runnr.c') | ||
24 | runCommand(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 | |||
3 | C 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 | |||
17 | In here should live all the internals of matrix-RAD that don't | ||
18 | specifically relate to widgets. This would include the "window" though. | ||
19 | |||
20 | skang.module(Evas) | ||
21 | skang.module(Elementary) | ||
22 | skang.skang('some/skang/file.skang') | ||
23 | |||
24 | This package is also what "apps" that use the system should "inherit" | ||
25 | from, in the same way matrix-RAD apps did. Skang "apps" could be Lua | ||
26 | modules. They could also be C code, like the extantz modules are likely | ||
27 | to be. Skang "apps" would automatically be associated with skang files | ||
28 | of the same name. | ||
29 | |||
30 | For a .skang file, the skang command (written in C) would strip off the | ||
31 | first line, add the two implied lines, then run it as Lua. The | ||
32 | skang.load() command would do the same. So that skang C comand would | ||
33 | just pass the file name to skang.load() in this library. B-) | ||
34 | |||
35 | The 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". | ||
69 | do -- Only I'm not gonna indent this. | ||
70 | |||
71 | mainSkin = {} | ||
72 | |||
73 | -- TODO - This needs to be expanded a bit to cover things like 1.42 | ||
74 | local 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. | ||
88 | moduleBegin = 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 | ||
170 | end | ||
171 | |||
172 | |||
173 | --[[ Parse command line parameters. | ||
174 | |||
175 | This is done in two parts. Skang will do an initial scan and tokenise, | ||
176 | then each module gets a chance to pull it's own Things from the result. | ||
177 | |||
178 | Make the command line parameter getting MUCH more intelligent, try to support the common | ||
179 | command line interfaces - | ||
180 | |||
181 | arg value | ||
182 | a value | ||
183 | /arg value | ||
184 | /a value | ||
185 | --arg value | ||
186 | --a value | ||
187 | -a value | ||
188 | -ab ('a' and 'b' are both shortcuts.) | ||
189 | arg=value | ||
190 | a=value | ||
191 | arg1=value1&arg2=value2 | ||
192 | arg1=value1|arg2=value2 | ||
193 | a=value1&a=value2 | ||
194 | +arg/-arg (Can't support this generically.) | ||
195 | |||
196 | Ignore /,-,--,& except as arg introducers. Use = as value introducer. Expect | ||
197 | arg or a. If type is String, expect a value. If type is integer, and next token is | ||
198 | not an integer, increment current value, otherwise expect integer value. If type is | ||
199 | boolean, value beginning with T, t, F, f, etc is true, otherwise value is false, unless | ||
200 | next token starts with an introducer, then value is true. | ||
201 | |||
202 | TODO - 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 | |||
218 | ARGS = {} | ||
219 | lua = '' | ||
220 | command = '' | ||
221 | |||
222 | |||
223 | -- Do an initial scan and tokenise of the command line arguments. | ||
224 | scanArguments = 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 | ||
242 | end | ||
243 | |||
244 | parseType = 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 | ||
305 | end | ||
306 | |||
307 | pullArguments = 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 | ||
344 | end | ||
345 | |||
346 | -- Restore the environment, and grab paramateres from standard places. | ||
347 | moduleEnd = 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 | ||
372 | end | ||
373 | |||
374 | |||
375 | -- Call this now so that from now on, this is like any other module. | ||
376 | local _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. | ||
379 | if type(jit) == 'table' then | ||
380 | print('Skang is being run by ' .. jit.version .. ' under ' .. jit.os .. ' on a ' .. jit.arch) | ||
381 | else | ||
382 | print('Skang is being run by Lua version ' .. _VERSION) | ||
383 | end | ||
384 | |||
385 | scanArguments(arg) | ||
386 | |||
387 | |||
388 | function printTableStart(table, space, name) | ||
389 | print(space .. name .. ": ") | ||
390 | print(space .. "{") | ||
391 | printTable(table, space .. " ") | ||
392 | print(space .. "}") | ||
393 | if '' == space then print('') end | ||
394 | end | ||
395 | |||
396 | function 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 | ||
421 | end | ||
422 | |||
423 | |||
424 | csv2table = 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 | ||
433 | end | ||
434 | |||
435 | |||
436 | shiftLeft = function (tab) | ||
437 | local result = tab[1] | ||
438 | table.remove(tab, 1) | ||
439 | return result | ||
440 | end | ||
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' | ||
445 | local isTrue = 't1aopswy' | ||
446 | -- false 0 no nack nope zero negative nah 'no way' 'get real' 'uh uh' 'fuck off' 'bugger off' | ||
447 | local isFalse = 'f0bgnuz' | ||
448 | isBoolean = 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 | ||
467 | end | ||
468 | |||
469 | |||
470 | --[[ Thing Java package | ||
471 | |||
472 | matrix-RAD had Thing as the base class of everything. | ||
473 | |||
474 | Each "users session" (matrix-RAD term that came from Java | ||
475 | applets/servlets) has a ThingSpace, which is a tree that holds | ||
476 | everything else. It holds the class cache, commands, loaded modules, | ||
477 | variables and their values, widgets and their states. In matrix-RAD I | ||
478 | built BonsiaTree and LeafLike, for the old FDO system I built dumbtrees. | ||
479 | |||
480 | Other 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 | |||
496 | In matrix-RAD Stuff took care of multi value Things, like database rows. | ||
497 | |||
498 | Stuff is an abstract class that gets extended by other classes, like | ||
499 | SquealStuff, which was the only thing extending it. It dealt with the | ||
500 | basic "collection of things" stuff. Each individual thing was called a | ||
501 | stufflet. A final fooStuff would extend SquealStuff, and include an | ||
502 | array of strings called "stufflets" that at least named the stufflets, | ||
503 | but could also include metadata and links to other Stuffs. | ||
504 | |||
505 | There was various infrastructure for reading and writing Stuff, throwing | ||
506 | rows of Stuff into grids, having choices of Stuff, linking stufflets to | ||
507 | individual widgets, having default Stuffs for windows, validating | ||
508 | Stuffs, etc. | ||
509 | |||
510 | In Lua, setting up stuff has been folded into the general Thing stuff. | ||
511 | |||
512 | ]] | ||
513 | |||
514 | |||
515 | --[[ Thing structure - | ||
516 | |||
517 | In the following, meta(table) is short for getmetatable(table). | ||
518 | |||
519 | In Lua everything is supposed to be a first class value, and variables are just places to store values. | ||
520 | All variables are in fact stored in tables, even if it's just the local environment table. | ||
521 | Any variable that has a value of nil, doesn't actually exist. That's the definition. | ||
522 | While non table things can have metatables, Lua can only set metatables on tables, C has no such restriction. | ||
523 | meta(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. | ||
528 | Using both __index and __newindex, and keeping the actual values elsewhere, is called a proxy table. | ||
529 | meta(table).__call(table, ...) is called when trying to access table as a function - table(...). | ||
530 | |||
531 | It's worth repeating - | ||
532 | All variables in Lua are in some table somewhere, even if it's just the global environment table. | ||
533 | Metatables are only associated vith values, not variables. | ||
534 | Lua can only attach metatables to values that are tables, but C can attach metatables to any value. | ||
535 | |||
536 | |||
537 | A Thing is a managed variable stored in a parent proxy table, which is usually empty. | ||
538 | So 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 | |||
543 | Each 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. | ||
555 | All things that a description doesn't have should be inherited from the Thing table. | ||
556 | setmetatable(aStuff, {__index = Thing}) | ||
557 | Descriptions should be able to be easily shared by various Things. | ||
558 | |||
559 | |||
560 | A parent's metatable has __self, which is it's own description. | ||
561 | A parent is free to use it's own name space for anything it wants. | ||
562 | Only the variables it specifies as managed Things are dealt with here. | ||
563 | |||
564 | |||
565 | Things that are tables are treated differently, in two different ways even. | ||
566 | Ordinary table Things are basically treated recursively, the table is a parent, and it gets it's own Things. | ||
567 | There is also 'Keyed' type table Things, where the keys to this table are arbitrary, but we still want to store Things in it. | ||
568 | In 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 | |||
571 | TODO - | ||
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. | ||
584 | local 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 | |||
654 | getStuffed = 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 | ||
669 | end | ||
670 | |||
671 | local 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] | ||
686 | end, | ||
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. | ||
748 | end, | ||
749 | |||
750 | __call = function (func, ...) | ||
751 | return thingasm(func, ...) | ||
752 | end, | ||
753 | |||
754 | } | ||
755 | |||
756 | newMum = function () | ||
757 | local result = {} | ||
758 | --[[ From an email by Mike Pall - | ||
759 | "Important: create the metatable and its metamethods once and reuse | ||
760 | the _same_ metatable for _every_ instance." | ||
761 | |||
762 | This is for LuaJIT, he's the author, and concerns performance. | ||
763 | |||
764 | TODO - 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 | ||
772 | end | ||
773 | |||
774 | |||
775 | -- skang.thingasm() Creates a new Thing, or changes an existing one. | ||
776 | --[[ It can be called in many different ways - | ||
777 | |||
778 | It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss) | ||
779 | Or it can be called with a table - {names, help, pattern='.*', acl='rwx'} | ||
780 | |||
781 | The first argument can be another Thing (the parent), or a string list of names (see below). | ||
782 | |||
783 | It can be called by itself, with no parent specified - | ||
784 | thingasm('foo', 'help text) | ||
785 | In 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. | ||
787 | All others include the parent as the first argument, which would be a table. | ||
788 | |||
789 | It 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'} | ||
799 | thingasm = 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()) | ||
884 | print('\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 | ||
919 | end | ||
920 | |||
921 | |||
922 | fixNames = 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 | ||
934 | end | ||
935 | |||
936 | |||
937 | copy = 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 | ||
957 | end | ||
958 | |||
959 | module = function (name) | ||
960 | _G[name] = require(name) | ||
961 | return _G[name] | ||
962 | end | ||
963 | |||
964 | stuff = function (aThingy, aStuff) | ||
965 | return getmetatable(aThingy).__self.stuff[aStuff] | ||
966 | end | ||
967 | |||
968 | |||
969 | get = 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 | ||
981 | end | ||
982 | |||
983 | |||
984 | reset = 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 | ||
994 | end | ||
995 | |||
996 | |||
997 | set = 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 | ||
1008 | end | ||
1009 | |||
1010 | |||
1011 | -- Get our C functions installed into skang. | ||
1012 | -- This has to be after thingasm is defined. | ||
1013 | package.cpath = package.cpath .. ';../../libraries/lib?.so' | ||
1014 | local GuiLua = require 'GuiLua' | ||
1015 | |||
1016 | |||
1017 | thingasm('module,l', 'Load a module.', module, 'file') | ||
1018 | thingasm('get', 'Get the current value of an existing Thing or metadata.', get, 'thing,key,name') | ||
1019 | thingasm('reset', 'Reset the current value of an existing Thing or metadata.', reset, 'thing,key,name') | ||
1020 | thingasm('set', 'Set the current value of an existing Thing or metadata.', set, 'thing,key,name,data') | ||
1021 | |||
1022 | thingasm('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 | ||
1028 | local widgets = {} | ||
1029 | --thingasm{widgets, 'window', 'The window.', types='userdata'} | ||
1030 | thingasm{widgets, 'W', 'Holds all the widgets', types='Keyed'} | ||
1031 | widgets.W{'Cwidget', 'The widget.', types='userdata'} | ||
1032 | widgets.W{'action,a', 'The action for the widget.', 'nada', types='string'} | ||
1033 | local aIsValid = function (self, parent) | ||
1034 | local result = Thing.isValid(self, parent) | ||
1035 | |||
1036 | if result then | ||
1037 | local value = parent[self.names[1] ] | ||
1038 | print('NEW ACTION - ' .. self.names[1] .. ' = ' .. value .. ' ' .. type(parent.Cwidget)) | ||
1039 | action(parent.Cwidget, value) | ||
1040 | end | ||
1041 | return result | ||
1042 | end | ||
1043 | |||
1044 | widgets.W{'look,l', 'The look of the widget.', types='string'} | ||
1045 | --[[ | ||
1046 | widgets.W{'colour,color,c', 'The colours of the widget.', types='table'} | ||
1047 | widgets.W.c{'red,r', 'The red colour of the widget.', 255, types='number'} | ||
1048 | widgets.W.c{'green,g', 'The green colour of the widget.', 255, types='number'} | ||
1049 | widgets.W.c{'blue,b', 'The blue colour of the widget.', 255, types='number'} | ||
1050 | widgets.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. | ||
1053 | local wMum = getmetatable(widgets.W.c) | ||
1054 | wMum.__call = function (func, ...) | ||
1055 | return Colour(func, ...) | ||
1056 | end | ||
1057 | ]] | ||
1058 | |||
1059 | window = 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 | ||
1067 | end | ||
1068 | |||
1069 | thingasm{'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. | ||
1073 | skang = function (name) | ||
1074 | end | ||
1075 | |||
1076 | thingasm('skang', 'Parse the contents of a skang file or URL.', skang, 'URL') | ||
1077 | |||
1078 | |||
1079 | moduleEnd(_M) | ||
1080 | |||
1081 | end | ||
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 | |||
1186 | Java skang could run as a stand alone applicion, as an applet in a web | ||
1187 | page, or as a servlet on a web server. This was pretty much all | ||
1188 | transparent to the user. The security system reflected that. Lua skang | ||
1189 | wont run in web pages, but can still have client / server behaviour. | ||
1190 | The general idea was, and still is, that the GUI is the client side (in | ||
1191 | web 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 | ||
1193 | server for in world scripts). Client side can request that server side | ||
1194 | runs commands. Serevr side can send values and commands back to the | ||
1195 | client. Mostly it all happenes automatically through the ACLs. | ||
1196 | |||
1197 | Bouncer is the Java skang security manager, it extended the Java | ||
1198 | SecurityManager. Lua has no such thing, though C code running stuff in | ||
1199 | a sandbox does a similar job. Fascist is the Java security supervisor, | ||
1200 | again should go into the C sandbox. | ||
1201 | |||
1202 | Human is used for authenticating a human, Puter for authenticating a | ||
1203 | computer, Suits for corporate style authentication, and they all | ||
1204 | extended Who, the base authentication module. | ||
1205 | |||
1206 | For now, I have no idea how this all translates into Lua, but putting | ||
1207 | this here for a reminder to think about security during the design | ||
1208 | stage. | ||
1209 | |||
1210 | |||
1211 | This is the old Java ACL definition - | ||
1212 | acl - access control list. | ||
1213 | Owner is usually the person running the Thingspace. | ||
1214 | RWX~,---,Rwxgroup1,r--group2,r-xgroup3,rw-group4,--X~user1 | ||
1215 | rwx~ is for the owner. The second one is the default. The rest are per group or per user. | ||
1216 | Capital letters mean that they get access from the network to. | ||
1217 | --- No access at all. | ||
1218 | RWX Full access. | ||
1219 | R-- Read only access. | ||
1220 | r-x Read and execute, but only locally. | ||
1221 | rw- 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. | ||
1225 | Ri- read, but only set from init (ei. skinURL not set from properties or skang files). | ||
1226 | RI- 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). | ||
1230 | s-- Read only, but not even visible to applets. | ||
1231 | sss Only visible to servlets and applications. | ||
1232 | --S Send to servlet to execute if applet, otherwise execute normally. | ||
1233 | S-- Read only, but ignore local version and get it from server. | ||
1234 | ggg GUI Thing, only visible to Applets and applications. | ||
1235 | GGG GUI Thing, but servlets can access them across the net. | ||
1236 | |||
1237 | For servlet only modules from an applet, the applet only loads the skanglet class, using it for all | ||
1238 | access to the module. | ||
1239 | |||
1240 | |||
1241 | Lua 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 | ||
1246 | to sandbox it at the process level. Everything else has far too many | ||
1247 | loopholes." | ||
1248 | |||
1249 | http://lua-users.org/lists/lua-l/2011-02/msg01595.html | ||
1250 | http://lua-users.org/lists/lua-l/2011-02/msg01609.html | ||
1251 | http://lua-users.org/lists/lua-l/2011-02/msg01097.html | ||
1252 | http://lua-users.org/lists/lua-l/2011-02/msg01106.html | ||
1253 | |||
1254 | So that's processes, not threads like LuaProc does. B-( | ||
1255 | |||
1256 | However, security in depth is good, so still worthwhile looking at it from other levels as well. | ||
1257 | |||
1258 | General solution is to create a new environment, which we are doing | ||
1259 | anyway, then whitelist the safe stuff into it, instead of blacklisting | ||
1260 | unsafe stuff. Coz you never know if new unsafe stuff might exist. | ||
1261 | |||
1262 | Different between 5.1 (setfenv) and 5.2 (_ENV) | ||
1263 | |||
1264 | http://lua-users.org/wiki/SandBoxes - | ||
1265 | |||
1266 | ------------------------------------------------------ | ||
1267 | -- make environment | ||
1268 | local 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] | ||
1300 | local 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) | ||
1306 | end | ||
1307 | |||
1308 | -- run code under environment [Lua 5.2] | ||
1309 | local 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) | ||
1313 | end | ||
1314 | ------------------------------------------------------ | ||
1315 | |||
1316 | Also includes a table of safe / unsafe stuff. | ||
1317 | |||
1318 | |||
1319 | While whitelisting stuff, could also wrap unsafe stuff to make them more safe. | ||
1320 | |||
1321 | print() -> should reroute to our output widgets. | ||
1322 | rawget/set() -> don't bypass the metatables, but gets tricky and recursive. | ||
1323 | require -> don't allow bypassing the sandbox to get access to restricted modules | ||
1324 | package.loaded -> ditto | ||
1325 | |||
1326 | |||
1327 | Other things to do - | ||
1328 | |||
1329 | debug.sethook() can be used to call a hook every X lines, which can help with endless loops and such. | ||
1330 | Have a custom memory allocater, like edje_lua2 does. | ||
1331 | |||
1332 | ------------------------------------------------------ | ||
1333 | ------------------------------------------------------ | ||
1334 | |||
1335 | The 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 | --[[ | ||
1418 | The 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 | |||
1458 | What 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 | |||
1482 | Alternate 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 | |||
1650 | public 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. | ||