diff options
author | William Wilgus <me.theuser@yahoo.com> | 2019-07-26 01:30:00 -0500 |
---|---|---|
committer | William Wilgus <me.theuser@yahoo.com> | 2019-07-29 02:51:29 -0500 |
commit | 90118f14cf078358f9ebdee110450b976c9a9e11 (patch) | |
tree | 1cbde562e0c83719db2e16f23fc33cef690fd2c3 | |
parent | 60c5a29408f7ca05a88ce1a98a4858293925169f (diff) | |
download | rockbox-90118f14cf078358f9ebdee110450b976c9a9e11.tar.gz rockbox-90118f14cf078358f9ebdee110450b976c9a9e11.zip |
lua add demo scripts, atexit handler, gui_scrollbar_draw
Change-Id: Ie8794e8a487f73952dae43e036787b6972fdbbee
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/SUBDIRS | 1 | ||||
-rw-r--r-- | apps/plugins/SUBDIRS.app_build | 1 | ||||
-rw-r--r-- | apps/plugins/lua/include_lua/printtable.lua | 385 | ||||
-rw-r--r-- | apps/plugins/lua/loslib.c | 5 | ||||
-rw-r--r-- | apps/plugins/lua/lua.make | 2 | ||||
-rw-r--r-- | apps/plugins/lua/rocklib.c | 14 | ||||
-rw-r--r-- | apps/plugins/lua/rocklib_events.c | 10 | ||||
-rw-r--r-- | apps/plugins/lua/rocklib_img.c | 19 | ||||
-rw-r--r-- | apps/plugins/lua/rocklua.c | 68 | ||||
-rw-r--r-- | apps/plugins/lua_scripts.lua | 161 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/dbgettags.lua | 116 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/fade2sleep.lua | 107 | ||||
-rwxr-xr-x | apps/plugins/lua_scripts/filebrowse.lua | 190 | ||||
-rwxr-xr-x | apps/plugins/lua_scripts/fileview.lua | 79 | ||||
-rwxr-xr-x | apps/plugins/lua_scripts/fileviewers.lua | 465 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/lua_scripts.make | 24 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/print_lua_func.lua | 304 | ||||
-rwxr-xr-x | apps/plugins/lua_scripts/printmenu.lua | 83 | ||||
-rw-r--r-- | apps/plugins/lua_scripts/tagnav.lua | 344 | ||||
-rwxr-xr-x | tools/buildzip.pl | 16 |
22 files changed, 2374 insertions, 22 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index ad99f3959f..bb8688347d 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -49,6 +49,7 @@ lamp,apps | |||
49 | logo,demos | 49 | logo,demos |
50 | lrcplayer,apps | 50 | lrcplayer,apps |
51 | lua,viewers | 51 | lua,viewers |
52 | lua_scripts,demos | ||
52 | fractals,demos | 53 | fractals,demos |
53 | main_menu_config,apps | 54 | main_menu_config,apps |
54 | matrix,demos | 55 | matrix,demos |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index c3e56af254..6551df71a9 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -154,6 +154,7 @@ metronome.c | |||
154 | /* Lua needs at least 160 KB to work in */ | 154 | /* Lua needs at least 160 KB to work in */ |
155 | #if PLUGIN_BUFFER_SIZE >= 0x80000 | 155 | #if PLUGIN_BUFFER_SIZE >= 0x80000 |
156 | boomshine.lua | 156 | boomshine.lua |
157 | lua_scripts.lua | ||
157 | #ifdef HAVE_LCD_COLOR | 158 | #ifdef HAVE_LCD_COLOR |
158 | pixel-painter.lua | 159 | pixel-painter.lua |
159 | #endif /* HAVE_LCD_COLOR */ | 160 | #endif /* HAVE_LCD_COLOR */ |
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS index 93cf8297b5..cf2ccff771 100644 --- a/apps/plugins/SUBDIRS +++ b/apps/plugins/SUBDIRS | |||
@@ -101,4 +101,5 @@ mpegplayer | |||
101 | /* Lua needs at least 160 KB to work in */ | 101 | /* Lua needs at least 160 KB to work in */ |
102 | #if PLUGIN_BUFFER_SIZE >= 0x80000 | 102 | #if PLUGIN_BUFFER_SIZE >= 0x80000 |
103 | lua | 103 | lua |
104 | lua_scripts | ||
104 | #endif | 105 | #endif |
diff --git a/apps/plugins/SUBDIRS.app_build b/apps/plugins/SUBDIRS.app_build index 954044146a..48a2d36d87 100644 --- a/apps/plugins/SUBDIRS.app_build +++ b/apps/plugins/SUBDIRS.app_build | |||
@@ -7,6 +7,7 @@ | |||
7 | * In fact, most of the plugins aren't supposed to be used on a touch(mouse) device | 7 | * In fact, most of the plugins aren't supposed to be used on a touch(mouse) device |
8 | */ | 8 | */ |
9 | lua | 9 | lua |
10 | lua_scripts | ||
10 | #ifdef HAVE_LCD_BITMAP | 11 | #ifdef HAVE_LCD_BITMAP |
11 | 12 | ||
12 | #if CONFIG_CODEC == SWCODEC && PLUGIN_BUFFER_SIZE > 0x20000 | 13 | #if CONFIG_CODEC == SWCODEC && PLUGIN_BUFFER_SIZE > 0x20000 |
diff --git a/apps/plugins/lua/include_lua/printtable.lua b/apps/plugins/lua/include_lua/printtable.lua new file mode 100644 index 0000000000..24c4f73b0a --- /dev/null +++ b/apps/plugins/lua/include_lua/printtable.lua | |||
@@ -0,0 +1,385 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
24 | |||
25 | require("actions") -- Contains rb.actions & rb.contexts | ||
26 | |||
27 | local _clr = require("color") | ||
28 | local _print = require("print") | ||
29 | local _timer = require("timer") | ||
30 | |||
31 | -- Button definitions -- | ||
32 | local EXIT_BUTTON = rb.PLA_EXIT | ||
33 | local CANCEL_BUTTON = rb.actions.PLA_CANCEL | ||
34 | local DOWN_BUTTON = rb.actions.PLA_DOWN | ||
35 | local DOWNR_BUTTON = rb.actions.PLA_DOWN_REPEAT | ||
36 | local EXIT_BUTTON = rb.actions.PLA_EXIT | ||
37 | local LEFT_BUTTON = rb.actions.PLA_LEFT | ||
38 | local LEFTR_BUTTON = rb.actions.PLA_LEFT_REPEAT | ||
39 | local RIGHT_BUTTON = rb.actions.PLA_RIGHT | ||
40 | local RIGHTR_BUTTON = rb.actions.PLA_RIGHT_REPEAT | ||
41 | local SEL_BUTTON = rb.actions.PLA_SELECT | ||
42 | local SELREL_BUTTON = rb.actions.PLA_SELECT_REL | ||
43 | local SELR_BUTTON = rb.actions.PLA_SELECT_REPEAT | ||
44 | local UP_BUTTON = rb.actions.PLA_UP | ||
45 | local UPR_BUTTON = rb.actions.PLA_UP_REPEAT | ||
46 | |||
47 | -- clamps value to >= min and <= max | ||
48 | local function clamp(iVal, iMin, iMax) | ||
49 | if iMin > iMax then | ||
50 | local swap = iMin | ||
51 | iMin, iMax = iMax, swap | ||
52 | end | ||
53 | |||
54 | if iVal < iMin then | ||
55 | return iMin | ||
56 | elseif iVal < iMax then | ||
57 | return iVal | ||
58 | end | ||
59 | |||
60 | return iMax | ||
61 | end | ||
62 | |||
63 | -------------------------------------------------------------------------------- | ||
64 | --[[ cursor style button routine | ||
65 | -- left / right are x, xi is increment xir is increment when repeat | ||
66 | -- up / down are y, yi is increment yir is increment when repeat | ||
67 | -- cancel is returned as 0,1 | ||
68 | -- select as 0, 1, 2, 3 (none, pressed, repeat, relesed) | ||
69 | -- x_chg and y_chg are the amount x or y changed | ||
70 | -- timeout == nil or -1 loop waits indefinitely till button is pressed | ||
71 | -- time since last button press is returned in ticks.. | ||
72 | -- make xi, xir, yi, yir negative to flip direction... | ||
73 | ]] | ||
74 | |||
75 | local function dpad(x, xi, xir, y, yi, yir, timeout) | ||
76 | _timer("dpad") -- start a persistant timer; keeps time between button events | ||
77 | if timeout == nil then timeout = -1 end | ||
78 | local cancel, select = 0, 0 | ||
79 | local x_chg, y_chg = 0, 0 | ||
80 | local button | ||
81 | while true do | ||
82 | button = rb.get_plugin_action(timeout) | ||
83 | |||
84 | if button == CANCEL_BUTTON then | ||
85 | cancel = 1 | ||
86 | break; | ||
87 | elseif button == EXIT_BUTTON then | ||
88 | cancel = 1 | ||
89 | break; | ||
90 | elseif button == SEL_BUTTON then | ||
91 | select = 1 | ||
92 | timeout = timeout + 1 | ||
93 | elseif button == SELR_BUTTON then | ||
94 | select = 2 | ||
95 | timeout = timeout + 1 | ||
96 | elseif button == SELREL_BUTTON then | ||
97 | select = -1 | ||
98 | timeout = timeout + 1 | ||
99 | elseif button == LEFT_BUTTON then | ||
100 | x_chg = x_chg - xi | ||
101 | elseif button == LEFTR_BUTTON then | ||
102 | x_chg = x_chg - xir | ||
103 | elseif button == RIGHT_BUTTON then | ||
104 | x_chg = x_chg + xi | ||
105 | elseif button == RIGHTR_BUTTON then | ||
106 | x_chg = x_chg + xir | ||
107 | elseif button == UP_BUTTON then | ||
108 | y_chg = y_chg + yi | ||
109 | elseif button == UPR_BUTTON then | ||
110 | y_chg = y_chg + yir | ||
111 | elseif button == DOWN_BUTTON then | ||
112 | y_chg = y_chg - yi | ||
113 | elseif button == DOWNR_BUTTON then | ||
114 | y_chg = y_chg - yir | ||
115 | elseif timeout >= 0 then--and rb.button_queue_count() < 1 then | ||
116 | break; | ||
117 | end | ||
118 | |||
119 | if x_chg ~= 0 or y_chg ~= 0 then | ||
120 | timeout = timeout + 1 | ||
121 | end | ||
122 | end | ||
123 | |||
124 | x = x + x_chg | ||
125 | y = y + y_chg | ||
126 | |||
127 | return cancel, select, x_chg, x, y_chg, y, _timer.check("dpad", true) | ||
128 | end -- dpad | ||
129 | |||
130 | |||
131 | |||
132 | -------------------------------------------------------------------------------- | ||
133 | --[[ prints a scrollable table to the screen; | ||
134 | -- requires a contiguous table with only strings; | ||
135 | -- 1st item in table is the title if hasheader == true | ||
136 | -- returns select item indice if NOT m_sel.. | ||
137 | -- if m_sel == true a table of selected indices are returned ]] | ||
138 | -------------------------------------------------------------------------------- | ||
139 | -- SECOND MODE OF OPERATION -- if co_routine is defined... | ||
140 | -- prints values returned from a resumable factory in a coroutine this allows | ||
141 | -- very large files etc to be displayed.. the downside is it takes time | ||
142 | -- to load data when scrolling also NO multiple selection is allowed | ||
143 | -- table is passed along with the final count t_count | ||
144 | -------------------------------------------------------------------------------- | ||
145 | |||
146 | function print_table(t, t_count, settings) | ||
147 | -- (table, t_count, {hasheader, wrap, m_sel, start, curpos, justify, co_routine}) | ||
148 | |||
149 | if type(t) ~= "table" then | ||
150 | rb.splash(rb.HZ * 5, "table expected got ".. type(t)) | ||
151 | return | ||
152 | end | ||
153 | |||
154 | local wrap, justify, start, curpos, co_routine, hasheader, m_sel | ||
155 | local header_fgc, header_bgc, item_fgc, item_bgc, item_selc | ||
156 | do | ||
157 | local s = settings or _print.get_settings() | ||
158 | wrap, justify = s.wrap, s.justify | ||
159 | start, curpos = s.start, s.curpos | ||
160 | co_routine = s.co_routine | ||
161 | hasheader = s.hasheader | ||
162 | m_sel = false | ||
163 | if co_routine == nil then | ||
164 | --no multi select in incremental mode | ||
165 | m_sel = s.msel | ||
166 | end | ||
167 | header_fgc = s.hfgc or _clr.set( 0, 000, 000, 000) | ||
168 | header_bgc = s.hbgc or _clr.set(-1, 255, 255, 255) | ||
169 | item_fgc = s.ifgc or _clr.set(-1, 000, 255, 060) | ||
170 | item_bgc = s.ibgc or _clr.set( 0, 000, 000, 000) | ||
171 | item_selc = s.iselc or _clr.set( 1, 000, 200, 100) | ||
172 | end | ||
173 | |||
174 | local table_p, line, maxline | ||
175 | |||
176 | local function set_vsb() end -- forward declaration; initialized below | ||
177 | |||
178 | local function init_position(acc_ticks, acc_steps) | ||
179 | if not acc_ticks then acc_ticks = 15 end-- accelerate scroll every this many ticks | ||
180 | if not acc_steps then acc_steps = 5 end -- default steps for an accelerated scroll | ||
181 | |||
182 | return {row = 1, row_scrl= acc_steps, | ||
183 | col = 0, col_scrl = acc_steps, | ||
184 | vcursor = 1, vcursor_min = 1, | ||
185 | acc_ticks = acc_ticks, | ||
186 | acc_steps = acc_steps} | ||
187 | end | ||
188 | |||
189 | local function set_accel(time, scrl, t_p) | ||
190 | if time < t_p.acc_ticks then -- accelerate scroll | ||
191 | scrl = scrl + 1 | ||
192 | else | ||
193 | scrl = t_p.acc_steps | ||
194 | end | ||
195 | return scrl | ||
196 | end | ||
197 | |||
198 | --adds or removes \0 from end of table entry to mark selected items | ||
199 | local function select_item(item) | ||
200 | if item < 1 then item = 1 end | ||
201 | if not t[item] then return end | ||
202 | if t[item]:sub(-1) == "\0" then | ||
203 | t[item] = t[item]:sub(1, -2) -- de-select | ||
204 | else | ||
205 | t[item] = t[item] .. "\0" -- select | ||
206 | end | ||
207 | end | ||
208 | |||
209 | -- displays header text at top | ||
210 | local function disp_header(hstr) | ||
211 | local header = header or hstr | ||
212 | local opts = _print.opt.get() | ||
213 | _print.opt.overflow("none") -- don't scroll header; colors change | ||
214 | _print.opt.color(header_fgc, header_bgc) | ||
215 | _print.opt.line(1) | ||
216 | |||
217 | _print.f() | ||
218 | local line = _print.f(header) | ||
219 | |||
220 | _print.opt.set(opts) | ||
221 | _print.opt.line(2) | ||
222 | return 2 | ||
223 | end | ||
224 | |||
225 | -- gets user input to select items, quit, scroll | ||
226 | local function get_input(t_p) | ||
227 | set_vsb(t_p.row + t_p.vcursor - 1)--t_p.row) | ||
228 | rb.lcd_update() | ||
229 | |||
230 | local quit, select, x_chg, xi, y_chg, yi, timeb = | ||
231 | dpad(t_p.col, -1, -t_p.col_scrl, t_p.row, -1, -t_p.row_scrl) | ||
232 | |||
233 | t_p.vcursor = t_p.vcursor + y_chg | ||
234 | |||
235 | if t_p.vcursor > maxline or t_p.vcursor < t_p.vcursor_min then | ||
236 | t_p.row = yi | ||
237 | end | ||
238 | |||
239 | if wrap == true and (y_chg == 1 or y_chg == -1) then | ||
240 | |||
241 | -- wraps list, stops at end if accelerated | ||
242 | if t_p.row < t_p.vcursor_min - 1 then | ||
243 | t_p.row = t_count - maxline + 1 | ||
244 | t_p.vcursor = maxline | ||
245 | elseif t_p.row + maxline - 1 > t_count then | ||
246 | t_p.row, t_p.vcursor = t_p.vcursor_min - 1, t_p.vcursor_min - 1 | ||
247 | end | ||
248 | end | ||
249 | |||
250 | t_p.row = clamp(t_p.row, 1, math.max(t_count - maxline + 1, 1)) | ||
251 | t_p.vcursor = clamp(t_p.vcursor, t_p.vcursor_min, maxline) | ||
252 | |||
253 | if x_chg ~= 0 then | ||
254 | |||
255 | if x_chg ~= 1 and x_chg ~= -1 then --stop at the center if accelerated | ||
256 | if (t_p.col <= 0 and xi > 0) or (t_p.col >= 0 and xi < 0) then | ||
257 | xi = 0 | ||
258 | end | ||
259 | end | ||
260 | t_p.col = xi | ||
261 | |||
262 | t_p.col_scrl = set_accel(timeb, t_p.col_scrl, t_p) | ||
263 | |||
264 | elseif y_chg ~= 0 then | ||
265 | --t_p.col = 0 -- reset column to the beginning | ||
266 | _print.clear() | ||
267 | _print.opt.sel_line(t_p.vcursor) | ||
268 | |||
269 | t_p.row_scrl = set_accel(timeb, t_p.row_scrl, t_p) | ||
270 | |||
271 | end | ||
272 | |||
273 | if select > 0 and timeb > 15 then --select may be sent multiple times | ||
274 | if m_sel == true then | ||
275 | select_item(t_p.row + t_p.vcursor - 1) | ||
276 | else | ||
277 | return -1, 0, 0, (t_p.row + t_p.vcursor - 1) | ||
278 | end | ||
279 | end | ||
280 | if quit > 0 then return -2, 0, 0, 0 end | ||
281 | return t_p.row, x_chg, y_chg, 0 | ||
282 | end | ||
283 | |||
284 | -- displays the actual table | ||
285 | local function display_table(table_p, col_c, row_c, sel) | ||
286 | local i = table_p.row | ||
287 | while i >= 1 and i <= t_count do | ||
288 | |||
289 | -- only print if beginning or user scrolled up/down | ||
290 | if row_c ~= 0 then | ||
291 | |||
292 | if t[i] == nil and co_routine then | ||
293 | --value has been garbage collected or not created yet | ||
294 | coroutine.resume(co_routine, i) | ||
295 | end | ||
296 | |||
297 | if t[i] == nil then | ||
298 | rb.splash(1, string.format("ERROR %d is nil", i)) | ||
299 | t[i] = "???" | ||
300 | if rb.get_plugin_action(10) == CANCEL_BUTTON then return 0 end | ||
301 | end | ||
302 | |||
303 | if m_sel == true and t[i]:sub(-1) == "\0" then | ||
304 | _print.opt.sel_line(line) | ||
305 | end | ||
306 | |||
307 | if i == 1 and hasheader == true then | ||
308 | line = disp_header(t[1]) | ||
309 | else | ||
310 | line = _print.f("%s", tostring(t[i])) | ||
311 | end | ||
312 | |||
313 | end | ||
314 | |||
315 | i = i + 1 -- important! | ||
316 | |||
317 | if line == 1 or i > t_count or col_c ~= 0 then | ||
318 | _print.opt.column(table_p.col) | ||
319 | i, col_c, row_c, sel = get_input(table_p) | ||
320 | end | ||
321 | |||
322 | rb.button_clear_queue() -- keep the button queue from overflowing | ||
323 | end | ||
324 | return sel | ||
325 | end -- display_table | ||
326 | --============================================================================-- | ||
327 | |||
328 | _print.opt.defaults() | ||
329 | _print.opt.autoupdate(false) | ||
330 | _print.opt.color(item_fgc, item_bgc, item_selc) | ||
331 | |||
332 | table_p = init_position(15, 5) | ||
333 | line, maxline = _print.opt.area(5, 1, rb.LCD_WIDTH - 10, rb.LCD_HEIGHT - 2) | ||
334 | maxline = math.min(maxline, t_count) | ||
335 | |||
336 | -- allow user to start at a position other than the beginning | ||
337 | if start ~= nil then table_p.row = clamp(start, 1, t_count + 1) end | ||
338 | |||
339 | if hasheader == true then | ||
340 | table_p.vcursor_min = 2 -- lowest selectable item | ||
341 | table_p.vcursor = 2 | ||
342 | end | ||
343 | |||
344 | table_p.vcursor = curpos or table_p.vcursor_min | ||
345 | |||
346 | if table_p.vcursor < 1 or table_p.vcursor > maxline then | ||
347 | table_p.vcursor = table_p.vcursor_min | ||
348 | end | ||
349 | |||
350 | _print.opt.sel_line(table_p.vcursor) | ||
351 | _print.opt.overflow("manual") | ||
352 | _print.opt.justify(justify) | ||
353 | |||
354 | -- initialize vertical scrollbar | ||
355 | set_vsb(); do | ||
356 | local vsb =_print.opt.get() | ||
357 | if rb.LCD_DEPTH == 2 then -- invert 2-bit screens | ||
358 | vsb.fg_pattern = 3 - vsb.fg_pattern | ||
359 | vsb.bg_pattern = 3 - vsb.bg_pattern | ||
360 | end | ||
361 | |||
362 | set_vsb = function (item) | ||
363 | if t_count > (maxline or t_count) then | ||
364 | rb.set_viewport(vsb) | ||
365 | item = item or 0 | ||
366 | local m = maxline / 2 + 1 | ||
367 | rb.gui_scrollbar_draw(vsb.width - 5, vsb.y, 5, vsb.height, | ||
368 | t_count, math.max(0, item - m), | ||
369 | math.min(item + m, t_count), 0) | ||
370 | end | ||
371 | end | ||
372 | end -- set_vsb | ||
373 | local selected = display_table(table_p, 0, 1, 0) | ||
374 | |||
375 | _print.opt.defaults() | ||
376 | |||
377 | if m_sel == true then -- walk the table to get selected items | ||
378 | selected = {} | ||
379 | for i = 1, t_count do | ||
380 | if t[i]:sub(-1) == "\0" then table.insert(selected, i) end | ||
381 | end | ||
382 | end | ||
383 | --rb.splash(100, string.format("#1 %d, %d, %d", row, vcursor_pos, sel)) | ||
384 | return selected, table_p.row, table_p.vcursor | ||
385 | end --print_table | ||
diff --git a/apps/plugins/lua/loslib.c b/apps/plugins/lua/loslib.c index dce8811fbe..a0ea8e8c24 100644 --- a/apps/plugins/lua/loslib.c +++ b/apps/plugins/lua/loslib.c | |||
@@ -172,8 +172,11 @@ static int os_time (lua_State *L) { | |||
172 | 172 | ||
173 | 173 | ||
174 | static int os_exit (lua_State *L) { | 174 | static int os_exit (lua_State *L) { |
175 | lua_settop(L, 2); | ||
175 | int status = luaL_optint(L, 1, EXIT_SUCCESS); | 176 | int status = luaL_optint(L, 1, EXIT_SUCCESS); |
176 | lua_close(L); | 177 | if (status != EXIT_SUCCESS && lua_type (L, 2) != LUA_TSTRING) |
178 | lua_pushfstring(L, "exit (%d)", status); | ||
179 | lua_pushvalue(L, 1); /* put exit status on top of stack */ | ||
177 | exit(status); | 180 | exit(status); |
178 | return EXIT_SUCCESS; /* never reached, surpress warning */ | 181 | return EXIT_SUCCESS; /* never reached, surpress warning */ |
179 | } | 182 | } |
diff --git a/apps/plugins/lua/lua.make b/apps/plugins/lua/lua.make index 16e25c3028..d5fb85afbc 100644 --- a/apps/plugins/lua/lua.make +++ b/apps/plugins/lua/lua.make | |||
@@ -19,7 +19,7 @@ LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua | |||
19 | LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua \ | 19 | LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua \ |
20 | image.lua lcd.lua math_ex.lua print.lua \ | 20 | image.lua lcd.lua math_ex.lua print.lua \ |
21 | timer.lua playlist.lua pcm.lua sound.lua \ | 21 | timer.lua playlist.lua pcm.lua sound.lua \ |
22 | rbcompat.lua ) | 22 | rbcompat.lua printtable.lua) |
23 | 23 | ||
24 | 24 | ||
25 | ifndef APP_TYPE | 25 | ifndef APP_TYPE |
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c index 1e3bc207b7..8c662b7359 100644 --- a/apps/plugins/lua/rocklib.c +++ b/apps/plugins/lua/rocklib.c | |||
@@ -771,6 +771,17 @@ RB_WRAP(audio_current_track) | |||
771 | return mem_read_write(L, address, maxsize); | 771 | return mem_read_write(L, address, maxsize); |
772 | } | 772 | } |
773 | 773 | ||
774 | RB_WRAP(restart_lua) | ||
775 | { | ||
776 | /*close lua state, open a new lua state, load script @ filename */ | ||
777 | luaL_checktype (L, 1, LUA_TSTRING); | ||
778 | lua_settop(L, 1); | ||
779 | lua_pushlightuserdata(L, L); /* signal exit handler */ | ||
780 | exit(1); /* atexit in rocklua.c */ | ||
781 | return -1; | ||
782 | } | ||
783 | |||
784 | |||
774 | #define RB_FUNC(func) {#func, rock_##func} | 785 | #define RB_FUNC(func) {#func, rock_##func} |
775 | #define RB_ALIAS(name, func) {name, rock_##func} | 786 | #define RB_ALIAS(name, func) {name, rock_##func} |
776 | static const luaL_Reg rocklib[] = | 787 | static const luaL_Reg rocklib[] = |
@@ -843,6 +854,8 @@ static const luaL_Reg rocklib[] = | |||
843 | RB_FUNC(audio_next_track), | 854 | RB_FUNC(audio_next_track), |
844 | RB_FUNC(audio_current_track), | 855 | RB_FUNC(audio_current_track), |
845 | 856 | ||
857 | RB_FUNC(restart_lua), | ||
858 | |||
846 | {NULL, NULL} | 859 | {NULL, NULL} |
847 | }; | 860 | }; |
848 | #undef RB_FUNC | 861 | #undef RB_FUNC |
@@ -939,4 +952,3 @@ LUALIB_API int luaopen_rock(lua_State *L) | |||
939 | #endif | 952 | #endif |
940 | return 1; | 953 | return 1; |
941 | } | 954 | } |
942 | |||
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c index 9e363edbdd..270c4ab393 100644 --- a/apps/plugins/lua/rocklib_events.c +++ b/apps/plugins/lua/rocklib_events.c | |||
@@ -137,6 +137,7 @@ struct event_data { | |||
137 | int thread_state; | 137 | int thread_state; |
138 | long *event_stack; | 138 | long *event_stack; |
139 | long timer_ticks; | 139 | long timer_ticks; |
140 | short freq_input; | ||
140 | short next_input; | 141 | short next_input; |
141 | short next_event; | 142 | short next_event; |
142 | /* callbacks */ | 143 | /* callbacks */ |
@@ -171,8 +172,9 @@ static void init_event_data(lua_State *L, struct event_data *ev_data) | |||
171 | ev_data->thread_state = THREAD_YIELD; | 172 | ev_data->thread_state = THREAD_YIELD; |
172 | //ev_data->event_stack = NULL; | 173 | //ev_data->event_stack = NULL; |
173 | //ev_data->timer_ticks = 0; | 174 | //ev_data->timer_ticks = 0; |
174 | ev_data->next_input = EV_TICKS; | 175 | ev_data->freq_input = EV_INPUT; |
175 | ev_data->next_event = EV_INPUT; | 176 | ev_data->next_input = EV_INPUT; |
177 | ev_data->next_event = EV_TICKS; | ||
176 | /* callbacks */ | 178 | /* callbacks */ |
177 | for (int i= 0; i < EVENT_CT; i++) | 179 | for (int i= 0; i < EVENT_CT; i++) |
178 | ev_data->cb[i] = NULL; | 180 | ev_data->cb[i] = NULL; |
@@ -336,7 +338,7 @@ static void rev_timer_isr(void) | |||
336 | if (ev_data.next_input <=0) | 338 | if (ev_data.next_input <=0) |
337 | { | 339 | { |
338 | ev_data.thread_state |= ((ev_data.thread_state & THREAD_INPUTMASK) >> 16); | 340 | ev_data.thread_state |= ((ev_data.thread_state & THREAD_INPUTMASK) >> 16); |
339 | ev_data.next_input = EV_INPUT; | 341 | ev_data.next_input = ev_data.freq_input; |
340 | } | 342 | } |
341 | 343 | ||
342 | if (ev_data.cb[TIMEREVENT] != NULL && !is_suspend(TIMEREVENT)) | 344 | if (ev_data.cb[TIMEREVENT] != NULL && !is_suspend(TIMEREVENT)) |
@@ -535,6 +537,8 @@ static int rockev_register(lua_State *L) | |||
535 | case ACTEVENT: | 537 | case ACTEVENT: |
536 | /* fall through */ | 538 | /* fall through */ |
537 | case BUTEVENT: | 539 | case BUTEVENT: |
540 | ev_data.freq_input = luaL_optinteger(L, 3, EV_INPUT); | ||
541 | if (ev_data.freq_input < HZ / 20) ev_data.freq_input = HZ / 20; | ||
538 | ev_data.thread_state |= (ev_flag | (ev_flag << 16)); | 542 | ev_data.thread_state |= (ev_flag | (ev_flag << 16)); |
539 | break; | 543 | break; |
540 | case CUSTOMEVENT: | 544 | case CUSTOMEVENT: |
diff --git a/apps/plugins/lua/rocklib_img.c b/apps/plugins/lua/rocklib_img.c index aaecc4d64c..1150b511c3 100644 --- a/apps/plugins/lua/rocklib_img.c +++ b/apps/plugins/lua/rocklib_img.c | |||
@@ -1225,6 +1225,8 @@ static const struct luaL_reg rli_lib [] = | |||
1225 | #define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L) | 1225 | #define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L) |
1226 | 1226 | ||
1227 | #if defined NB_SCREENS && (NB_SCREENS > 1) | 1227 | #if defined NB_SCREENS && (NB_SCREENS > 1) |
1228 | #define RB_SCREEN_STRUCT(luastate, narg) \ | ||
1229 | rb->screens[get_screen(luastate, narg)] | ||
1228 | #define RB_SCREENS(luastate, narg, func, ...) \ | 1230 | #define RB_SCREENS(luastate, narg, func, ...) \ |
1229 | rb->screens[get_screen(luastate, narg)]->func(__VA_ARGS__) | 1231 | rb->screens[get_screen(luastate, narg)]->func(__VA_ARGS__) |
1230 | 1232 | ||
@@ -1240,6 +1242,8 @@ static int get_screen(lua_State *L, int narg) | |||
1240 | return screen; | 1242 | return screen; |
1241 | } | 1243 | } |
1242 | #else /* only SCREEN_MAIN exists */ | 1244 | #else /* only SCREEN_MAIN exists */ |
1245 | #define RB_SCREEN_STRUCT(luastate, narg) \ | ||
1246 | rb->screens[SCREEN_MAIN] | ||
1243 | #define RB_SCREENS(luastate, narg, func, ...) \ | 1247 | #define RB_SCREENS(luastate, narg, func, ...) \ |
1244 | rb->screens[SCREEN_MAIN]->func(__VA_ARGS__) | 1248 | rb->screens[SCREEN_MAIN]->func(__VA_ARGS__) |
1245 | #endif | 1249 | #endif |
@@ -1376,7 +1380,6 @@ RB_WRAP(font_getstringsize) | |||
1376 | } | 1380 | } |
1377 | 1381 | ||
1378 | #ifdef HAVE_LCD_BITMAP | 1382 | #ifdef HAVE_LCD_BITMAP |
1379 | |||
1380 | RB_WRAP(lcd_framebuffer) | 1383 | RB_WRAP(lcd_framebuffer) |
1381 | { | 1384 | { |
1382 | rli_wrap(L, rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT); | 1385 | rli_wrap(L, rb->lcd_framebuffer, LCD_WIDTH, LCD_HEIGHT); |
@@ -1399,6 +1402,19 @@ static void get_rect_bounds(lua_State *L, int narg, int *x, int *y, int *w, int* | |||
1399 | *h = luaL_checkint(L, narg + 3); | 1402 | *h = luaL_checkint(L, narg + 3); |
1400 | } | 1403 | } |
1401 | 1404 | ||
1405 | RB_WRAP(gui_scrollbar_draw) | ||
1406 | { | ||
1407 | int x, y, width, height; | ||
1408 | get_rect_bounds(L, 1, &x, &y, &width, &height); | ||
1409 | int items = luaL_checkint(L, 5); | ||
1410 | int min_shown = luaL_checkint(L, 6); | ||
1411 | int max_shown = luaL_checkint(L, 7); | ||
1412 | unsigned flags = (unsigned) luaL_checkint(L, 8); | ||
1413 | rb->gui_scrollbar_draw(RB_SCREEN_STRUCT(L, 9), x, y, width, height, | ||
1414 | items, min_shown, max_shown, flags); | ||
1415 | return 0; | ||
1416 | } | ||
1417 | |||
1402 | RB_WRAP(lcd_mono_bitmap_part) | 1418 | RB_WRAP(lcd_mono_bitmap_part) |
1403 | { | 1419 | { |
1404 | struct rocklua_image *src = rli_checktype(L, 1); | 1420 | struct rocklua_image *src = rli_checktype(L, 1); |
@@ -1644,6 +1660,7 @@ static const luaL_Reg rocklib_img[] = | |||
1644 | #ifdef HAVE_LCD_BITMAP | 1660 | #ifdef HAVE_LCD_BITMAP |
1645 | R(lcd_framebuffer), | 1661 | R(lcd_framebuffer), |
1646 | R(lcd_setfont), | 1662 | R(lcd_setfont), |
1663 | R(gui_scrollbar_draw), | ||
1647 | R(lcd_mono_bitmap_part), | 1664 | R(lcd_mono_bitmap_part), |
1648 | R(lcd_mono_bitmap), | 1665 | R(lcd_mono_bitmap), |
1649 | #if LCD_DEPTH > 1 | 1666 | #if LCD_DEPTH > 1 |
diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c index 0d0b1f63f7..eb48fa2799 100644 --- a/apps/plugins/lua/rocklua.c +++ b/apps/plugins/lua/rocklua.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include "luadir.h" | 28 | #include "luadir.h" |
29 | #include "rocklib_events.h" | 29 | #include "rocklib_events.h" |
30 | 30 | ||
31 | static lua_State *Ls = NULL; | ||
32 | static int lu_status = 0; | ||
31 | 33 | ||
32 | static const luaL_Reg lualibs[] = { | 34 | static const luaL_Reg lualibs[] = { |
33 | {"", luaopen_base}, | 35 | {"", luaopen_base}, |
@@ -142,41 +144,77 @@ static int docall (lua_State *L) { | |||
142 | return status; | 144 | return status; |
143 | } | 145 | } |
144 | 146 | ||
147 | static void lua_atexit(void); | ||
148 | static int loadfile_newstate(lua_State **L, const char *filename) | ||
149 | { | ||
150 | *L = luaL_newstate(); | ||
151 | rb_atexit(lua_atexit); | ||
152 | rocklua_openlibs(*L); | ||
153 | return luaL_loadfile(*L, filename); | ||
154 | } | ||
155 | |||
156 | static void lua_atexit(void) | ||
157 | { | ||
158 | char *filename; | ||
159 | |||
160 | if(Ls && lua_gettop(Ls) > 1) | ||
161 | { | ||
162 | if (Ls == lua_touserdata(Ls, -1)) /* signal from restart_lua */ | ||
163 | { | ||
164 | filename = (char *) malloc(MAX_PATH); | ||
165 | |||
166 | if (filename) /* out of memory? */ | ||
167 | rb->strlcpy(filename, lua_tostring(Ls, -2), MAX_PATH); | ||
168 | lua_close(Ls); /* close old state */ | ||
169 | |||
170 | lu_status = loadfile_newstate(&Ls, filename); | ||
171 | |||
172 | free(filename); | ||
173 | plugin_start(NULL); | ||
174 | } | ||
175 | else if (lua_tointeger(Ls, -1) != 0) /* os.exit */ | ||
176 | { | ||
177 | lu_status = LUA_ERRRUN; | ||
178 | lua_pop(Ls, 1); /* put exit string on top of stack */ | ||
179 | plugin_start(NULL); | ||
180 | } | ||
181 | } | ||
182 | _exit(0); /* don't call exit handler */ | ||
183 | } | ||
145 | 184 | ||
146 | /***************** Plugin Entry Point *****************/ | 185 | /***************** Plugin Entry Point *****************/ |
147 | enum plugin_status plugin_start(const void* parameter) | 186 | enum plugin_status plugin_start(const void* parameter) |
148 | { | 187 | { |
149 | const char* filename; | 188 | const char* filename; |
150 | int status; | ||
151 | 189 | ||
152 | if (parameter == NULL) | 190 | if (parameter == NULL) |
153 | { | 191 | { |
192 | if (!Ls) | ||
154 | rb->splash(HZ, "Play a .lua file!"); | 193 | rb->splash(HZ, "Play a .lua file!"); |
155 | return PLUGIN_ERROR; | ||
156 | } | 194 | } |
157 | else | 195 | else |
158 | { | 196 | { |
159 | filename = (char*) parameter; | 197 | filename = (char*) parameter; |
198 | lu_status = loadfile_newstate(&Ls, filename); | ||
199 | } | ||
160 | 200 | ||
161 | lua_State *L = luaL_newstate(); | 201 | if (Ls) |
162 | 202 | { | |
163 | rocklua_openlibs(L); | 203 | if (!lu_status) { |
164 | status = luaL_loadfile(L, filename); | ||
165 | if (!status) { | ||
166 | rb->lcd_scroll_stop(); /* rb doesn't like bg change while scroll */ | 204 | rb->lcd_scroll_stop(); /* rb doesn't like bg change while scroll */ |
167 | rb->lcd_clear_display(); | 205 | rb->lcd_clear_display(); |
168 | status = docall(L); | 206 | lu_status= docall(Ls); |
169 | } | 207 | } |
170 | 208 | ||
171 | if (status) { | 209 | if (lu_status) { |
172 | DEBUGF("%s\n", lua_tostring(L, -1)); | 210 | DEBUGF("%s\n", lua_tostring(Ls, -1)); |
173 | rb->splashf(5 * HZ, "%s", lua_tostring(L, -1)); | 211 | rb->splash(5 * HZ, lua_tostring(Ls, -1)); |
174 | lua_pop(L, 1); | 212 | /*lua_pop(Ls, 1);*/ |
175 | } | 213 | } |
176 | 214 | lua_close(Ls); | |
177 | lua_close(L); | ||
178 | } | 215 | } |
216 | else | ||
217 | return PLUGIN_ERROR; | ||
179 | 218 | ||
180 | return PLUGIN_OK; | 219 | return PLUGIN_OK; |
181 | } | 220 | } |
182 | |||
diff --git a/apps/plugins/lua_scripts.lua b/apps/plugins/lua_scripts.lua new file mode 100644 index 0000000000..02fe50b327 --- /dev/null +++ b/apps/plugins/lua_scripts.lua | |||
@@ -0,0 +1,161 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | |||
24 | local scrpath = rb.current_path() .. "/lua_scripts/" | ||
25 | |||
26 | package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path | ||
27 | require("printtable") | ||
28 | |||
29 | rb.actions = nil | ||
30 | package.loaded["actions"] = nil | ||
31 | |||
32 | local excludedsrc = ";filebrowse.lua;fileviewers.lua;printmenu.lua;dbgettags.lua;" | ||
33 | -------------------------------------------------------------------------------- | ||
34 | |||
35 | local function get_files(path, norecurse, finddir, findfile, f_t, d_t) | ||
36 | |||
37 | local quit = false | ||
38 | |||
39 | local files = f_t or {} | ||
40 | local dirs = d_t or {} | ||
41 | |||
42 | local function f_filedir(name) | ||
43 | --default find function | ||
44 | -- example: return name:find(".mp3", 1, true) ~= nil | ||
45 | if name:len() <= 2 and (name == "." or name == "..") then | ||
46 | return false | ||
47 | end | ||
48 | if string.find(excludedsrc, ";" .. name .. ";") then | ||
49 | return false | ||
50 | end | ||
51 | if string.sub(name, -4) == ".lua" then | ||
52 | return true | ||
53 | end | ||
54 | return false | ||
55 | end | ||
56 | local function d_filedir(name) | ||
57 | --default discard function | ||
58 | return false | ||
59 | end | ||
60 | |||
61 | if finddir == nil then | ||
62 | finddir = f_filedir | ||
63 | elseif type(finddir) ~= "function" then | ||
64 | finddir = d_filedir | ||
65 | end | ||
66 | |||
67 | if findfile == nil then | ||
68 | findfile = f_filedir | ||
69 | elseif type(findfile) ~= "function" then | ||
70 | findfile = d_filedir | ||
71 | end | ||
72 | |||
73 | local function _get_files(path, cancelbtn) | ||
74 | local sep = "" | ||
75 | if string.sub(path, - 1) ~= "/" then sep = "/" end | ||
76 | for fname, isdir in luadir.dir(path) do | ||
77 | |||
78 | if isdir and finddir(fname) then | ||
79 | table.insert(dirs, path .. sep ..fname) | ||
80 | elseif not isdir and findfile(fname) then | ||
81 | table.insert(files, path .. sep ..fname) | ||
82 | end | ||
83 | |||
84 | if rb.get_plugin_action(0) == cancelbtn then | ||
85 | return true | ||
86 | end | ||
87 | end | ||
88 | end | ||
89 | |||
90 | local function cmp_alphanum (op1, op2) | ||
91 | local type1= type(op1) | ||
92 | local type2 = type(op2) | ||
93 | |||
94 | if type1 ~= type2 then | ||
95 | return type1 < type2 | ||
96 | else | ||
97 | if type1 == "string" then | ||
98 | op1 = op1:upper() | ||
99 | op2 = op2:upper() | ||
100 | end | ||
101 | return op1 < op2 | ||
102 | end | ||
103 | end | ||
104 | |||
105 | table.insert(dirs, path) -- root | ||
106 | |||
107 | for key,value in pairs(dirs) do | ||
108 | --luadir.dir may error out so we need to do the call protected | ||
109 | _, quit = pcall(_get_files, value, CANCEL_BUTTON) | ||
110 | |||
111 | if quit == true or norecurse then | ||
112 | break; | ||
113 | end | ||
114 | end | ||
115 | |||
116 | table.sort(files, cmp_alphanum) | ||
117 | table.sort(dirs, cmp_alphanum) | ||
118 | |||
119 | return dirs, files | ||
120 | end -- get_files | ||
121 | -------------------------------------------------------------------------------- | ||
122 | |||
123 | -- uses print_table and get_files to display simple file browser | ||
124 | function script_choose(dir, title) | ||
125 | local dstr | ||
126 | local hstr = title | ||
127 | |||
128 | local norecurse = true | ||
129 | local f_finddir = false -- function to match directories; nil all, false none | ||
130 | local f_findfile = nil -- function to match files; nil all, false none | ||
131 | |||
132 | local p_settings = {wrap = true, hasheader = true} | ||
133 | local files = {} | ||
134 | local dirs = {} | ||
135 | local item = 1 | ||
136 | rb.lcd_clear_display() | ||
137 | |||
138 | while item > 0 do | ||
139 | dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files) | ||
140 | for i=1, #dirs do dirs[i] = nil end -- empty table for reuse | ||
141 | table.insert(dirs, 1, hstr) | ||
142 | for i = 1, #files do | ||
143 | table.insert(dirs, "\t" .. string.gsub(files[i], ".*/","")) | ||
144 | end | ||
145 | |||
146 | item = print_table(dirs, #dirs, p_settings) | ||
147 | |||
148 | -- If item was selected follow directory or return filename | ||
149 | if item > 0 then | ||
150 | dir = files[item - 1] | ||
151 | if not rb.dir_exists("/" .. dir) then | ||
152 | return dir | ||
153 | end | ||
154 | end | ||
155 | |||
156 | end | ||
157 | end -- file_choose | ||
158 | -------------------------------------------------------------------------------- | ||
159 | |||
160 | local script_path = script_choose(scrpath, "lua scripts") | ||
161 | if script_path then rb.restart_lua(script_path) end | ||
diff --git a/apps/plugins/lua_scripts/dbgettags.lua b/apps/plugins/lua_scripts/dbgettags.lua new file mode 100644 index 0000000000..06fa6e8830 --- /dev/null +++ b/apps/plugins/lua_scripts/dbgettags.lua | |||
@@ -0,0 +1,116 @@ | |||
1 | --dbgettags.lua Bilgus 2018 | ||
2 | --[[ | ||
3 | /*************************************************************************** | ||
4 | * __________ __ ___. | ||
5 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
6 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
7 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
8 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
9 | * \/ \/ \/ \/ \/ | ||
10 | * $Id$ | ||
11 | * | ||
12 | * Copyright (C) 2017 William Wilgus | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | ]] | ||
24 | |||
25 | require("actions") | ||
26 | local CANCEL_BUTTON = rb.actions.PLA_CANCEL | ||
27 | |||
28 | local sINVALIDDATABASE = "Invalid Database" | ||
29 | local sERROROPENING = "Error opening" | ||
30 | |||
31 | -- tag cache header | ||
32 | local sTCVERSION = string.char(0x0F) | ||
33 | local sTCHEADER = string.reverse("TCH" .. sTCVERSION) | ||
34 | local DATASZ = 4 -- int32_t | ||
35 | local TCHSIZE = 3 * DATASZ -- 3 x int32_t | ||
36 | |||
37 | local function bytesLE_n(str) | ||
38 | str = str or "" | ||
39 | local tbyte={str:byte(1, -1)} | ||
40 | local bpos = 1 | ||
41 | local num = 0 | ||
42 | for k = 1,#tbyte do -- (k = #t, 1, -1 for BE) | ||
43 | num = num + tbyte[k] * bpos | ||
44 | bpos = bpos * 256 | ||
45 | end | ||
46 | return num | ||
47 | end | ||
48 | |||
49 | -- uses database files to retrieve database tags | ||
50 | -- adds all unique tags into a lua table | ||
51 | function get_tags(filename, hstr) | ||
52 | |||
53 | if not filename then return end | ||
54 | |||
55 | hstr = hstr or filename | ||
56 | |||
57 | local file = io.open('/' .. filename or "", "r") --read | ||
58 | if not file then rb.splash(100, sERROROPENING .. " " .. filename) return end | ||
59 | |||
60 | local fsz = file:seek("end") | ||
61 | |||
62 | local posln = 0 | ||
63 | local tag_len = TCHSIZE | ||
64 | local idx | ||
65 | |||
66 | local function readchrs(count) | ||
67 | if posln >= fsz then return nil end | ||
68 | file:seek("set", posln) | ||
69 | posln = posln + count | ||
70 | return file:read(count) | ||
71 | end | ||
72 | |||
73 | local tagcache_header = readchrs(DATASZ) or "" | ||
74 | local tagcache_sz = readchrs(DATASZ) or "" | ||
75 | local tagcache_entries = readchrs(DATASZ) or "" | ||
76 | |||
77 | if tagcache_header ~= sTCHEADER or | ||
78 | bytesLE_n(tagcache_sz) ~= (fsz - TCHSIZE) then | ||
79 | rb.splash(100, sINVALIDDATABASE .. " " .. filename) | ||
80 | return | ||
81 | end | ||
82 | |||
83 | -- local tag_entries = bytesLE_n(tagcache_entries) | ||
84 | |||
85 | local ftable = {} | ||
86 | table.insert(ftable, 1, hstr) | ||
87 | |||
88 | local tline = #ftable + 1 | ||
89 | ftable[tline] = "" | ||
90 | |||
91 | local str = "" | ||
92 | |||
93 | while true do | ||
94 | tag_len = bytesLE_n(readchrs(DATASZ)) | ||
95 | readchrs(DATASZ) -- idx = bytesLE_n(readchrs(DATASZ)) | ||
96 | str = readchrs(tag_len) or "" | ||
97 | str = string.match(str, "(%Z+)%z") | ||
98 | |||
99 | if str then | ||
100 | if ftable[tline - 1] ~= str then -- Remove dupes | ||
101 | ftable[tline] = str | ||
102 | tline = tline + 1 | ||
103 | end | ||
104 | elseif posln >= fsz then | ||
105 | break | ||
106 | end | ||
107 | |||
108 | if rb.get_plugin_action(0) == CANCEL_BUTTON then | ||
109 | break | ||
110 | end | ||
111 | end | ||
112 | |||
113 | file:close() | ||
114 | |||
115 | return ftable | ||
116 | end -- get_tags | ||
diff --git a/apps/plugins/lua_scripts/fade2sleep.lua b/apps/plugins/lua_scripts/fade2sleep.lua new file mode 100644 index 0000000000..03a4533591 --- /dev/null +++ b/apps/plugins/lua_scripts/fade2sleep.lua | |||
@@ -0,0 +1,107 @@ | |||
1 | --Bilgus 12-2016 | ||
2 | --revisited 8-2019 | ||
3 | require "actions" | ||
4 | require "buttons" | ||
5 | require "sound" | ||
6 | require "audio" | ||
7 | TIMEOUT = 0 | ||
8 | |||
9 | local SOUND_VOLUME = rb.sound_settings.SOUND_VOLUME | ||
10 | rb.sound_settings = nil | ||
11 | package.loaded["sound_defines"] = nil | ||
12 | |||
13 | function say_msg(message, timeout) | ||
14 | rb.splash(1, message) | ||
15 | rb.sleep(timeout * rb.HZ) | ||
16 | end | ||
17 | |||
18 | function say_value(value,message,timeout) | ||
19 | local message = string.format(message .. "%d", value) | ||
20 | say_msg(message, timeout) | ||
21 | end | ||
22 | |||
23 | function ShowMainMenu() -- we invoke this function every time we want to display the main menu of the script | ||
24 | local s = 0 | ||
25 | local mult = 1 | ||
26 | local unit = " Minutes" | ||
27 | |||
28 | |||
29 | while s == 0 or s == 5 do -- don't exit of program until user selects Exit | ||
30 | if mult < 1 then | ||
31 | mult = 1 | ||
32 | s = 0 | ||
33 | end | ||
34 | mainmenu = {"More", mult * 1 .. unit, mult * 5 .. unit, mult * 10 .. unit, mult * 15 .. unit, "Less", "Exit"} -- define the items of the menu | ||
35 | s = rb.do_menu("Reduce volume + sleep over", mainmenu, s, false) -- actually tell Rockbox to draw the menu | ||
36 | |||
37 | -- In the line above: "Test" is the title of the menu, mainmenu is an array with the items | ||
38 | -- of the menu, nil is a null value that needs to be there, and the last parameter is | ||
39 | -- whether the theme should be drawn on the menu or not. | ||
40 | -- the variable s will hold the index of the selected item on the menu. | ||
41 | -- the index is zero based. This means that the first item is 0, the second one is 1, etc. | ||
42 | if s == 0 then mult = mult + 1 | ||
43 | elseif s == 1 then TIMEOUT = mult | ||
44 | elseif s == 2 then TIMEOUT = mult * 5 | ||
45 | elseif s == 3 then TIMEOUT = mult * 10 | ||
46 | elseif s == 4 then TIMEOUT = mult * 15 | ||
47 | elseif s == 5 then mult = mult - 1 -- User selected to exit | ||
48 | elseif s == 6 then os.exit() -- User selected to exit | ||
49 | elseif s == -2 then os.exit() -- -2 index is returned from do_menu() when user presses the key to exit the menu (on iPods, it's the left key). | ||
50 | -- In this case, user probably wants to exit (or go back to last menu). | ||
51 | else rb.splash(2 * rb.HZ, "Error! Selected index: " .. s) -- something strange happened. The program shows this message when | ||
52 | -- the selected item is not on the index from 0 to 3 (in this case), and displays | ||
53 | -- the selected index. Having this type of error handling is not | ||
54 | -- required, but it might be nice to have Especially while you're still | ||
55 | -- developing the plugin. | ||
56 | end | ||
57 | end | ||
58 | end | ||
59 | |||
60 | ShowMainMenu() | ||
61 | rb.set_sleeptimer_duration(TIMEOUT) | ||
62 | rb.lcd_clear_display() | ||
63 | rb.lcd_update() | ||
64 | |||
65 | local volume = rb.sound_current(SOUND_VOLUME) | ||
66 | local vol_min = rb.sound_min(SOUND_VOLUME) | ||
67 | local volsteps = -(vol_min - volume) | ||
68 | local seconds = (TIMEOUT * 60) / volsteps | ||
69 | local sec_left = (TIMEOUT * 60) | ||
70 | local hb = 0 | ||
71 | local action = rb.get_action(rb.contexts.CONTEXT_STD, 0) | ||
72 | if rb.audio_status() == 1 then | ||
73 | while ((volume > vol_min) and (action ~= rb.actions.ACTION_STD_CANCEL)) do | ||
74 | rb.lcd_clear_display() | ||
75 | say_value(volume,sec_left .. " Sec, Volume: ", 1) | ||
76 | local i = seconds * 2 | ||
77 | while ((i > 0) and (action ~= rb.actions.ACTION_STD_CANCEL)) do | ||
78 | i = i - 1 | ||
79 | rb.lcd_drawline(hb, 1, hb, 1) | ||
80 | rb.lcd_update() | ||
81 | if hb >= rb.LCD_WIDTH then | ||
82 | hb = 0 | ||
83 | rb.lcd_clear_display() | ||
84 | say_value(volume,sec_left .. " Sec, Volume: ", 1) | ||
85 | end | ||
86 | hb = hb + 1 | ||
87 | rb.sleep(rb.HZ / 2) | ||
88 | action = rb.get_action(rb.contexts.CONTEXT_STD, 0) | ||
89 | rb.yield() | ||
90 | end | ||
91 | volume = volume - 1 | ||
92 | rb.sound_set(SOUND_VOLUME, volume); | ||
93 | sec_left = sec_left - seconds | ||
94 | |||
95 | end | ||
96 | rb.audio_stop() | ||
97 | rb.lcd_clear_display() | ||
98 | rb.lcd_update() | ||
99 | |||
100 | os.exit(1, "Playback Stopped") | ||
101 | |||
102 | else | ||
103 | rb.lcd_clear_display() | ||
104 | rb.lcd_update() | ||
105 | |||
106 | os.exit(2, "Nothing is playing") | ||
107 | end | ||
diff --git a/apps/plugins/lua_scripts/filebrowse.lua b/apps/plugins/lua_scripts/filebrowse.lua new file mode 100755 index 0000000000..5e75365cf8 --- /dev/null +++ b/apps/plugins/lua_scripts/filebrowse.lua | |||
@@ -0,0 +1,190 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end | ||
24 | require("printtable") | ||
25 | local _lcd = require("lcd") | ||
26 | local _timer = require("timer") | ||
27 | |||
28 | -------------------------------------------------------------------------------- | ||
29 | --[[ returns a sorted tables of directories and (another) of files | ||
30 | -- path is the starting path; norecurse == true.. only that path will be searched | ||
31 | -- findfile & finddir are definable search functions | ||
32 | -- if not defined all files/dirs are returned if false is passed.. none | ||
33 | -- or you can provide your own function see below.. | ||
34 | -- f_t and d_t allow you to pass your own tables for re-use but isn't necessary | ||
35 | ]] | ||
36 | local function get_files(path, norecurse, finddir, findfile, f_t, d_t) | ||
37 | |||
38 | local quit = false | ||
39 | |||
40 | local files = f_t or {} | ||
41 | local dirs = d_t or {} | ||
42 | |||
43 | local function f_filedir(name) | ||
44 | --default find function | ||
45 | -- example: return name:find(".mp3", 1, true) ~= nil | ||
46 | if name:len() <= 2 and (name == "." or name == "..") then | ||
47 | return false | ||
48 | end | ||
49 | return true | ||
50 | end | ||
51 | local function d_filedir(name) | ||
52 | --default discard function | ||
53 | return false | ||
54 | end | ||
55 | |||
56 | if finddir == nil then | ||
57 | finddir = f_filedir | ||
58 | elseif type(finddir) ~= "function" then | ||
59 | finddir = d_filedir | ||
60 | end | ||
61 | |||
62 | if findfile == nil then | ||
63 | findfile = f_filedir | ||
64 | elseif type(findfile) ~= "function" then | ||
65 | findfile = d_filedir | ||
66 | end | ||
67 | |||
68 | local function _get_files(path, cancelbtn) | ||
69 | local sep = "" | ||
70 | if string.sub(path, - 1) ~= "/" then sep = "/" end | ||
71 | for fname, isdir in luadir.dir(path) do | ||
72 | |||
73 | if isdir and finddir(fname) then | ||
74 | table.insert(dirs, path .. sep ..fname) | ||
75 | elseif not isdir and findfile(fname) then | ||
76 | table.insert(files, path .. sep ..fname) | ||
77 | end | ||
78 | |||
79 | if rb.get_plugin_action(0) == cancelbtn then | ||
80 | return true | ||
81 | end | ||
82 | end | ||
83 | end | ||
84 | |||
85 | local function cmp_alphanum (op1, op2) | ||
86 | local type1= type(op1) | ||
87 | local type2 = type(op2) | ||
88 | |||
89 | if type1 ~= type2 then | ||
90 | return type1 < type2 | ||
91 | else | ||
92 | if type1 == "string" then | ||
93 | op1 = op1:upper() | ||
94 | op2 = op2:upper() | ||
95 | end | ||
96 | return op1 < op2 | ||
97 | end | ||
98 | end | ||
99 | |||
100 | _lcd:splashf(1, "Searching for Files") | ||
101 | |||
102 | table.insert(dirs, path) -- root | ||
103 | |||
104 | for key,value in pairs(dirs) do | ||
105 | --luadir.dir may error out so we need to do the call protected | ||
106 | _, quit = pcall(_get_files, value, CANCEL_BUTTON) | ||
107 | |||
108 | if quit == true or norecurse then | ||
109 | break; | ||
110 | end | ||
111 | end | ||
112 | |||
113 | table.sort(files, cmp_alphanum) | ||
114 | table.sort(dirs, cmp_alphanum) | ||
115 | |||
116 | return dirs, files | ||
117 | end -- get_files | ||
118 | -------------------------------------------------------------------------------- | ||
119 | |||
120 | -- uses print_table and get_files to display simple file browser | ||
121 | function file_choose(dir, title) | ||
122 | local dstr, hstr = "" | ||
123 | if not title then | ||
124 | dstr = "%d items found in %0d.%02d seconds" | ||
125 | else | ||
126 | hstr = title | ||
127 | end | ||
128 | |||
129 | -- returns whole seconds and remainder | ||
130 | local function tick2seconds(ticks) | ||
131 | local secs = (ticks / rb.HZ) | ||
132 | local csecs = (ticks - (secs * rb.HZ)) | ||
133 | return secs, csecs | ||
134 | end | ||
135 | |||
136 | local norecurse = true | ||
137 | local f_finddir = nil -- function to match directories; nil all, false none | ||
138 | local f_findfile = nil -- function to match files; nil all, false none | ||
139 | |||
140 | local p_settings = {wrap = true, hasheader = true} | ||
141 | |||
142 | local timer | ||
143 | local files = {} | ||
144 | local dirs = {} | ||
145 | local item = 1 | ||
146 | _lcd:clear() | ||
147 | |||
148 | while item > 0 do | ||
149 | if not title then | ||
150 | timer = _timer() | ||
151 | end | ||
152 | |||
153 | dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files) | ||
154 | |||
155 | local parentdir = dirs[1] | ||
156 | for i = 1, #dirs do | ||
157 | dirs[i] = "\t" .. dirs[i] | ||
158 | end | ||
159 | |||
160 | for i = 1, #files do | ||
161 | table.insert(dirs, "\t" .. files[i]) | ||
162 | end | ||
163 | |||
164 | for i=1, #files do files[i] = nil end -- empty table for reuse | ||
165 | |||
166 | if not title then | ||
167 | hstr = string.format(dstr, #dirs - 1, tick2seconds(timer:stop())) | ||
168 | end | ||
169 | |||
170 | table.insert(dirs, 1, hstr) | ||
171 | |||
172 | item = print_table(dirs, #dirs, p_settings) | ||
173 | |||
174 | -- If item was selected follow directory or return filename | ||
175 | if item > 0 then | ||
176 | dir = string.gsub(dirs[item], "%c+","") | ||
177 | if not rb.dir_exists("/" .. dir) then | ||
178 | return dir | ||
179 | end | ||
180 | end | ||
181 | |||
182 | if dir == parentdir then | ||
183 | dir = dir:sub(1, dir:match(".*()/") - 1) | ||
184 | if dir == "" then dir = "/" end | ||
185 | end | ||
186 | for i=1, #dirs do dirs[i] = nil end -- empty table for reuse | ||
187 | |||
188 | end | ||
189 | end -- file_choose | ||
190 | -------------------------------------------------------------------------------- | ||
diff --git a/apps/plugins/lua_scripts/fileview.lua b/apps/plugins/lua_scripts/fileview.lua new file mode 100755 index 0000000000..920281bbef --- /dev/null +++ b/apps/plugins/lua_scripts/fileview.lua | |||
@@ -0,0 +1,79 @@ | |||
1 | --[[ | ||
2 | __________ __ ___. | ||
3 | Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | \/ \/ \/ \/ \/ | ||
8 | $Id$ | ||
9 | Example Lua File Viewer script | ||
10 | Copyright (C) 2017 William Wilgus | ||
11 | This program is free software; you can redistribute it and/or | ||
12 | modify it under the terms of the GNU General Public License | ||
13 | as published by the Free Software Foundation; either version 2 | ||
14 | of the License, or (at your option) any later version. | ||
15 | This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | KIND, either express or implied. | ||
17 | ]]-- | ||
18 | |||
19 | require("actions") -- Contains rb.actions & rb.contexts | ||
20 | -- require("buttons") -- Contains rb.buttons -- not needed for this example | ||
21 | |||
22 | --local _timer = require("timer") | ||
23 | --local _clr = require("color") -- clrset, clrinc provides device independent colors | ||
24 | local _lcd = require("lcd") -- lcd helper functions | ||
25 | --local _print = require("print") -- advanced text printing | ||
26 | --local _img = require("image") -- image manipulation save, rotate, resize, tile, new, load | ||
27 | --local _blit = require("blit") -- handy list of blit operations | ||
28 | --local _draw = require("draw") -- draw all the things (primitives) | ||
29 | --local _math = require("math_ex") -- missing math sine cosine, sqrt, clamp functions | ||
30 | |||
31 | |||
32 | local scrpath = rb.current_path()--rb.PLUGIN_DIR .. "/demos/lua_scripts/" | ||
33 | |||
34 | package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path | ||
35 | |||
36 | require("printmenu") --menu | ||
37 | require("filebrowse") -- file browser | ||
38 | require("fileviewers") -- fileviewer, hexviewer | ||
39 | |||
40 | rb.actions = nil | ||
41 | package.loaded["actions"] = nil | ||
42 | |||
43 | -- uses print_table to display a menu | ||
44 | function main_menu() | ||
45 | |||
46 | local mt = { | ||
47 | [1] = "Rocklua File View Example", | ||
48 | [2] = "File View", | ||
49 | [3] = "File Hex View", | ||
50 | [4] = "Simple Browser", | ||
51 | [5] = "Exit" | ||
52 | } | ||
53 | |||
54 | local ft = { | ||
55 | [0] = exit_now, --if user cancels do this function | ||
56 | [1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot | ||
57 | [2] = function(VIEWF) -- view file | ||
58 | print_file_increment(file_choose("/", "Choose File")) | ||
59 | end, | ||
60 | [3] = function(VHEXF) -- view hex | ||
61 | print_file_hex(file_choose("/", "Choose File"), 8) | ||
62 | end, | ||
63 | [4] = function(BROWS) -- file browser | ||
64 | _lcd:splashf(rb.HZ, "%s", file_choose("/") or "None") | ||
65 | end, | ||
66 | [5] = function(EXIT_) return true end | ||
67 | } | ||
68 | |||
69 | print_menu(mt, ft) | ||
70 | |||
71 | end | ||
72 | |||
73 | function exit_now() | ||
74 | _lcd:update() | ||
75 | os.exit() | ||
76 | end -- exit_now | ||
77 | |||
78 | main_menu() | ||
79 | exit_now() | ||
diff --git a/apps/plugins/lua_scripts/fileviewers.lua b/apps/plugins/lua_scripts/fileviewers.lua new file mode 100755 index 0000000000..5a9c417d36 --- /dev/null +++ b/apps/plugins/lua_scripts/fileviewers.lua | |||
@@ -0,0 +1,465 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end | ||
24 | require("printtable") | ||
25 | local _clr = require("color") | ||
26 | local _lcd = require("lcd") | ||
27 | local _print = require("print") | ||
28 | local _timer = require("timer") | ||
29 | |||
30 | require("actions") | ||
31 | local CANCEL_BUTTON = rb.actions.PLA_CANCEL | ||
32 | -------------------------------------------------------------------------------- | ||
33 | -- builds an index of byte position of every line at each bufsz increment | ||
34 | -- in filename; bufsz == 1 would be every line; saves to filename.ext.idx_ext | ||
35 | -- lnbyte should be nil for text files and number of bytes per line for binary | ||
36 | local function build_file_index(filename, idx_ext, bufsz, lnbyte) | ||
37 | |||
38 | if not filename then return end | ||
39 | local file = io.open('/' .. filename, "r") --read | ||
40 | if not file then _lcd:splashf(100, "Can't open %s", filename) return end | ||
41 | local fsz = file:seek("end") | ||
42 | local fsz_kb = fsz / 1024 | ||
43 | local count | ||
44 | local ltable = {0} --first index is the beginning of the file | ||
45 | local timer = _timer() | ||
46 | local fread | ||
47 | _lcd:splashf(100, "Indexing file %d Kb", (fsz / 1024)) | ||
48 | |||
49 | if lnbyte then | ||
50 | fread = function(f) return f:read(lnbyte) end | ||
51 | else | ||
52 | lnbyte = -1 | ||
53 | fread = function(f) return f:read("*l") end | ||
54 | end | ||
55 | |||
56 | file:seek("set", 0) | ||
57 | for i = 1, fsz do | ||
58 | if i % bufsz == 0 then | ||
59 | local loc = file:seek() | ||
60 | ltable[#ltable + 1] = loc | ||
61 | _lcd:splashf(1, "Parsing %d of %d Kb", loc / 1024, fsz_kb) | ||
62 | end | ||
63 | if rb.get_plugin_action(0) == CANCEL_BUTTON then | ||
64 | return | ||
65 | end | ||
66 | if not fread(file) then | ||
67 | count = i | ||
68 | break | ||
69 | end | ||
70 | end | ||
71 | |||
72 | local fileidx = io.open('/' .. filename .. idx_ext, "w+") -- write/erase | ||
73 | if fileidx then | ||
74 | fileidx:write(fsz .. "\n") | ||
75 | fileidx:write(count .. "\n") | ||
76 | fileidx:write(bufsz .. "\n") | ||
77 | fileidx:write(lnbyte .. "\n") | ||
78 | fileidx:write(table.concat(ltable, "\n")) | ||
79 | fileidx:close() | ||
80 | _lcd:splashf(100, "Finished in %d seconds", timer.stop() / rb.HZ) | ||
81 | collectgarbage("collect") | ||
82 | else | ||
83 | error("unable to save index file") | ||
84 | end | ||
85 | end -- build_file_index | ||
86 | -------------------------------------------------------------------------------- | ||
87 | |||
88 | --- returns size of original file, total lines buffersize, and table filled | ||
89 | -- with line offsets in index file -> filename | ||
90 | local function load_index_file(filename) | ||
91 | local filesz, count, bufsz, lnbyte | ||
92 | local ltable | ||
93 | local fileidx = io.open('/' .. filename, "r") --read | ||
94 | if fileidx then | ||
95 | local idx = -3 | ||
96 | ltable = {} | ||
97 | fileidx:seek("set", 0) | ||
98 | for line in fileidx:lines() do | ||
99 | if idx == -3 then | ||
100 | filesz = tonumber(line) | ||
101 | elseif idx == -2 then | ||
102 | count = tonumber(line) | ||
103 | elseif idx == -1 then | ||
104 | bufsz = tonumber(line) | ||
105 | elseif idx == 0 then | ||
106 | lnbyte = tonumber(line) | ||
107 | else | ||
108 | ltable[idx] = tonumber(line) | ||
109 | end | ||
110 | idx = idx + 1 | ||
111 | end | ||
112 | fileidx:close() | ||
113 | end | ||
114 | return lnbyte, filesz, count, bufsz, ltable | ||
115 | end -- load_index_file | ||
116 | -------------------------------------------------------------------------------- | ||
117 | |||
118 | -- creates a fixed index with fixed line lengths, perfect for viewing hex files | ||
119 | -- not so great for reading text files but works as a fallback | ||
120 | local function load_fixed_index(bytesperline, filesz, bufsz) | ||
121 | local lnbyte = bytesperline | ||
122 | local count = (filesz + lnbyte - 1) / lnbyte + 1 | ||
123 | local idx_t = {} -- build index | ||
124 | for i = 0, filesz, bufsz do | ||
125 | idx_t[#idx_t + 1] = lnbyte * i | ||
126 | end | ||
127 | return lnbyte, filesz, count, bufsz, idx_t | ||
128 | end -- load_fixed_index | ||
129 | -------------------------------------------------------------------------------- | ||
130 | |||
131 | -- uses print_table to display a whole file | ||
132 | function print_file(filename, maxlinelen, settings) | ||
133 | |||
134 | if not filename then return end | ||
135 | local file = io.open('/' .. filename or "", "r") --read | ||
136 | if not file then _lcd:splashf(100, "Can't open %s", filename) return end | ||
137 | maxlinelen = 33 | ||
138 | local hstr = filename | ||
139 | local ftable = {} | ||
140 | table.insert(ftable, 1, hstr) | ||
141 | |||
142 | local tline = #ftable + 1 | ||
143 | local remln = maxlinelen | ||
144 | local posln = 1 | ||
145 | |||
146 | for line in file:lines() do | ||
147 | if line then | ||
148 | if maxlinelen then | ||
149 | if line == "" then | ||
150 | ftable[tline] = ftable[tline] or "" | ||
151 | tline = tline + 1 | ||
152 | remln = maxlinelen | ||
153 | else | ||
154 | line = line:match("%w.+") or "" | ||
155 | end | ||
156 | local linelen = line:len() | ||
157 | while linelen > 0 do | ||
158 | |||
159 | local fsp = line:find("%s", posln + remln - 5) or 0x0 | ||
160 | fsp = fsp - (posln + remln) | ||
161 | if fsp >= 0 then | ||
162 | local fspr = fsp | ||
163 | fsp = line:find("%s", posln + remln) or linelen | ||
164 | fsp = fsp - (posln + remln) | ||
165 | if math.abs(fspr) < fsp then fsp = fspr end | ||
166 | end | ||
167 | if fsp > 5 or fsp < -5 then fsp = 0 end | ||
168 | |||
169 | local str = line:sub(posln, posln + remln + fsp) | ||
170 | local slen = str:len() | ||
171 | ftable[tline] = ftable[tline] or "" | ||
172 | ftable[tline] = ftable[tline] .. str | ||
173 | linelen = linelen - slen | ||
174 | if linelen > 0 then | ||
175 | tline = tline + 1 | ||
176 | posln = posln + slen | ||
177 | remln = maxlinelen | ||
178 | --loop continues | ||
179 | else | ||
180 | ftable[tline] = ftable[tline] .. " " | ||
181 | remln = maxlinelen - slen | ||
182 | posln = 1 | ||
183 | --loop ends | ||
184 | end | ||
185 | |||
186 | end | ||
187 | else | ||
188 | ftable[#ftable + 1] = line | ||
189 | end | ||
190 | |||
191 | |||
192 | end | ||
193 | end | ||
194 | |||
195 | file:close() | ||
196 | |||
197 | _lcd:clear() | ||
198 | _print.clear() | ||
199 | |||
200 | if not settings then | ||
201 | settings = {} | ||
202 | settings.justify = "center" | ||
203 | settings.wrap = true | ||
204 | settings.msel = true | ||
205 | end | ||
206 | settings.hasheader = true | ||
207 | settings.co_routine = nil | ||
208 | |||
209 | local sel = | ||
210 | print_table(ftable, #ftable, settings) | ||
211 | |||
212 | _lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", ")) | ||
213 | ftable = nil | ||
214 | end -- print_file | ||
215 | -------------------------------------------------------------------------------- | ||
216 | |||
217 | -- uses print_table to display a portion of a file | ||
218 | function print_file_increment(filename, settings) | ||
219 | |||
220 | if not filename then return end | ||
221 | local file = io.open('/' .. filename, "r") --read | ||
222 | if not file then _lcd:splashf(100, "Can't open %s", filename) return end | ||
223 | local fsz = file:seek("end") | ||
224 | local bsz = 1023 | ||
225 | --if small file do it the easier way and load whole file to table | ||
226 | if fsz < 60 * 1024 then | ||
227 | file:close() | ||
228 | print_file(filename, settings) | ||
229 | return | ||
230 | end | ||
231 | |||
232 | local ext = ".idx" | ||
233 | local lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext) | ||
234 | |||
235 | if not idx_t or fsz ~= filesz then -- build file index | ||
236 | build_file_index(filename, ext, bsz) | ||
237 | lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext) | ||
238 | end | ||
239 | |||
240 | -- if invalid or user canceled creation fallback to a fixed index | ||
241 | if not idx_t or fsz ~= filesz or count <= 0 then | ||
242 | _lcd:splashf(rb.HZ * 5, "Unable to read file index %s", filename .. ext) | ||
243 | lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(32, fsz, bsz) | ||
244 | end | ||
245 | |||
246 | if not idx_t or fsz ~= filesz or count <= 0 then | ||
247 | _lcd:splashf(rb.HZ * 5, "Unable to load file %s", filename) | ||
248 | return | ||
249 | end | ||
250 | |||
251 | local hstr = filename | ||
252 | local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values | ||
253 | -- this allows them to be garbage collected as space is needed | ||
254 | -- rebuilds when needed | ||
255 | local ovf = 0 | ||
256 | local lpos = 1 | ||
257 | local timer = _timer() | ||
258 | file:seek("set", 0) | ||
259 | |||
260 | function print_co() | ||
261 | while true do | ||
262 | collectgarbage("step") | ||
263 | file_t[1] = hstr --position 1 is ALWAYS header/title | ||
264 | |||
265 | for i = 1, bufsz + ovf do | ||
266 | file_t[lpos + i] = file:read ("*l") | ||
267 | end | ||
268 | ovf = 0 | ||
269 | lpos = lpos + bufsz | ||
270 | |||
271 | local bpos = coroutine.yield() | ||
272 | |||
273 | if bpos <= lpos then -- roll over or scroll up | ||
274 | bpos = (bpos - bufsz) + bpos % bufsz | ||
275 | timer:check(true) | ||
276 | end | ||
277 | |||
278 | lpos = bpos - bpos % bufsz | ||
279 | |||
280 | if lpos < 1 then | ||
281 | lpos = 1 | ||
282 | elseif lpos > count - bufsz then -- partial fill | ||
283 | ovf = count - bufsz - lpos | ||
284 | end | ||
285 | --get position in file of the nearest indexed line | ||
286 | file:seek("set", idx_t[bpos / bufsz + 1]) | ||
287 | |||
288 | -- on really large files if it has been more than 10 minutes | ||
289 | -- since the user scrolled up the screen wipe out the prior | ||
290 | -- items to free memory | ||
291 | if lpos % 5000 == 0 and timer:check() > rb.HZ * 600 then | ||
292 | for i = 1, lpos - 100 do | ||
293 | file_t[i] = nil | ||
294 | end | ||
295 | end | ||
296 | |||
297 | end | ||
298 | end | ||
299 | |||
300 | co = coroutine.create(print_co) | ||
301 | _lcd:clear() | ||
302 | _print.clear() | ||
303 | |||
304 | if not settings then | ||
305 | settings = {} | ||
306 | settings.justify = "center" | ||
307 | settings.wrap = true | ||
308 | end | ||
309 | settings.hasheader = true | ||
310 | settings.co_routine = co | ||
311 | settings.msel = false | ||
312 | |||
313 | table.insert(file_t, 1, hstr) --position 1 is header/title | ||
314 | local sel = | ||
315 | print_table(file_t, count, settings) | ||
316 | file:close() | ||
317 | idx_t = nil | ||
318 | file_t = nil | ||
319 | return sel | ||
320 | end --print_file_increment | ||
321 | -------------------------------------------------------------------------------- | ||
322 | function print_file_hex(filename, bytesperline, settings) | ||
323 | |||
324 | if not filename then return end | ||
325 | local file = io.open('/' .. filename, "r") --read | ||
326 | if not file then _lcd:splashf(100, "Can't open %s", filename) return end | ||
327 | local hstr = filename | ||
328 | local bpl = bytesperline | ||
329 | local fsz = file:seek("end") | ||
330 | --[[ | ||
331 | local filesz = file:seek("end") | ||
332 | local bufsz = 1023 | ||
333 | local lnbyte = bytesperline | ||
334 | local count = (filesz + lnbyte - 1) / lnbyte + 1 | ||
335 | |||
336 | local idx_t = {} -- build index | ||
337 | for i = 0, filesz, bufsz do | ||
338 | idx_t[#idx_t + 1] = lnbyte * i | ||
339 | end]] | ||
340 | |||
341 | local lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(bpl, fsz, 1023) | ||
342 | |||
343 | local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values | ||
344 | -- this allows them to be garbage collected as space is needed | ||
345 | -- rebuilds when needed | ||
346 | local ovf = 0 | ||
347 | local lpos = 1 | ||
348 | local timer = _timer() | ||
349 | file:seek("set", 0) | ||
350 | |||
351 | function hex_co() | ||
352 | while true do | ||
353 | collectgarbage("step") | ||
354 | file_t[1] = hstr --position 1 is ALWAYS header/title | ||
355 | |||
356 | for i = 1, bufsz + ovf do | ||
357 | local pos = file:seek() | ||
358 | local s = file:read (lnbyte) | ||
359 | if not s then -- EOF | ||
360 | file_t[lpos + i] = "" | ||
361 | break; | ||
362 | end | ||
363 | local s_len = s:len() | ||
364 | |||
365 | if s_len > 0 then | ||
366 | local fmt = "0x%04X: " .. string.rep("%02X ", s_len) | ||
367 | local schrs = " " .. s:gsub("(%c)", " . ") | ||
368 | file_t[lpos + i] = string.format(fmt, pos, s:byte(1, s_len)) .. | ||
369 | schrs | ||
370 | else | ||
371 | file_t[lpos + i] = string.format("0x%04X: ", pos) | ||
372 | end | ||
373 | end | ||
374 | ovf = 0 | ||
375 | lpos = lpos + bufsz | ||
376 | |||
377 | local bpos = coroutine.yield() | ||
378 | |||
379 | if bpos < lpos then -- roll over or scroll up | ||
380 | bpos = (bpos - bufsz) + bpos % bufsz | ||
381 | timer:check(true) | ||
382 | end | ||
383 | |||
384 | lpos = bpos - bpos % bufsz | ||
385 | |||
386 | if lpos < 1 then | ||
387 | lpos = 1 | ||
388 | elseif lpos > count - bufsz then -- partial fill | ||
389 | ovf = count - bufsz - lpos | ||
390 | end | ||
391 | --get position in file of the nearest indexed line | ||
392 | file:seek("set", idx_t[bpos / bufsz + 1]) | ||
393 | |||
394 | -- on really large files if it has been more than 10 minutes | ||
395 | -- since the user scrolled up the screen wipe out the prior | ||
396 | -- items to free memory | ||
397 | if lpos % 10000 == 0 and timer:check() > rb.HZ * 600 then | ||
398 | for i = 1, lpos - 100 do | ||
399 | file_t[i] = nil | ||
400 | end | ||
401 | end | ||
402 | |||
403 | end | ||
404 | end | ||
405 | |||
406 | co = coroutine.create(hex_co) | ||
407 | |||
408 | local function repl(char) | ||
409 | local ret = "" | ||
410 | if char:sub(1,2) == "0x" then | ||
411 | return string.format("%dd:", tonumber(char:sub(3, -2), 16)) | ||
412 | else | ||
413 | return string.format("%03d ", tonumber(char, 16)) | ||
414 | end | ||
415 | end | ||
416 | |||
417 | |||
418 | _lcd:clear() | ||
419 | _print.clear() | ||
420 | |||
421 | local sel, start, vcur = 1 | ||
422 | table.insert(file_t, 1, hstr) --position 1 is header/title | ||
423 | |||
424 | if not settings then | ||
425 | settings = {} | ||
426 | settings.justify = "left" | ||
427 | settings.wrap = true | ||
428 | settings.msel = false | ||
429 | settings.hfgc = _clr.set( 0, 000, 000, 000) | ||
430 | settings.hbgc = _clr.set(-1, 255, 255, 255) | ||
431 | settings.ifgc = _clr.set(-1, 255, 255, 255) | ||
432 | settings.ibgc = _clr.set( 0, 000, 000, 000) | ||
433 | settings.iselc = _clr.set( 1, 000, 200, 100) | ||
434 | end | ||
435 | |||
436 | settings.hasheader = true | ||
437 | settings.co_routine = co | ||
438 | settings.start = start | ||
439 | settings.curpos = vcur | ||
440 | |||
441 | while sel > 0 do | ||
442 | settings.start = start | ||
443 | settings.curpos = vcur | ||
444 | |||
445 | sel, start, vcur = print_table(file_t, count, settings) | ||
446 | |||
447 | if sel > 1 and file_t[sel] then -- flips between hex and decimal | ||
448 | local s = file_t[sel] | ||
449 | if s:sub(-1) == "\b" then | ||
450 | file_t[sel] = nil | ||
451 | ovf = -(bufsz - 1) | ||
452 | coroutine.resume(co, sel) --rebuild this item | ||
453 | else | ||
454 | s = s:gsub("(0x%x+:)", repl) .. "\b" | ||
455 | file_t[sel] = s:gsub("(%x%x%s)", repl) .. "\b" | ||
456 | end | ||
457 | end | ||
458 | end | ||
459 | |||
460 | file:close() | ||
461 | idx_t = nil | ||
462 | file_t = nil | ||
463 | return sel | ||
464 | end -- print_file_hex | ||
465 | -------------------------------------------------------------------------------- | ||
diff --git a/apps/plugins/lua_scripts/lua_scripts.make b/apps/plugins/lua_scripts/lua_scripts.make new file mode 100644 index 0000000000..2f46f9d74a --- /dev/null +++ b/apps/plugins/lua_scripts/lua_scripts.make | |||
@@ -0,0 +1,24 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | LUASCR_SRCDIR := $(APPSDIR)/plugins/lua_scripts | ||
11 | LUASCR_BUILDDIR := $(BUILDDIR)/apps/plugins/lua_scripts | ||
12 | LUASCRS := $(wildcard $(LUASCR_SRCDIR)/*.lua) | ||
13 | |||
14 | #DUMMY := $(info [${LUASCRS}]) | ||
15 | |||
16 | DUMMY : all | ||
17 | |||
18 | all: $(subst $(LUASCR_SRCDIR)/,$(LUASCR_BUILDDIR)/,$(LUASCRS)) | ||
19 | |||
20 | $(LUASCR_BUILDDIR)/%.lua: $(LUASCR_SRCDIR)/%.lua | $(LUASCR_BUILDDIR) | ||
21 | $(call PRINTS,CP $(subst $(LUASCR_SRCDIR)/,,$<))cp $< $@ | ||
22 | |||
23 | $(LUASCR_BUILDDIR): | ||
24 | $(call PRINTS,MKDIR $@)mkdir -p $(LUASCR_BUILDDIR)/ | ||
diff --git a/apps/plugins/lua_scripts/print_lua_func.lua b/apps/plugins/lua_scripts/print_lua_func.lua new file mode 100644 index 0000000000..f2642a187b --- /dev/null +++ b/apps/plugins/lua_scripts/print_lua_func.lua | |||
@@ -0,0 +1,304 @@ | |||
1 | --RB LUA show all global variables; BILGUS | ||
2 | require "actions" | ||
3 | require "audio" | ||
4 | require "buttons" | ||
5 | require "color" | ||
6 | require "draw" | ||
7 | require "image" | ||
8 | require "lcd" | ||
9 | require "math_ex" | ||
10 | require "pcm" | ||
11 | require "playlist" | ||
12 | require "print" | ||
13 | --require "settings" --uses a lot of memory | ||
14 | require "sound" | ||
15 | collectgarbage("collect") | ||
16 | |||
17 | local sDumpFile = "/rb-lua_functions.txt" | ||
18 | local filehandle | ||
19 | |||
20 | local function a2m_m2a(addr_member) | ||
21 | --turns members into addresses; addresses back into members | ||
22 | return addr_member | ||
23 | end | ||
24 | |||
25 | local function dtTag(sType) | ||
26 | --convert named type; 'number'.. to short type '[n]...' | ||
27 | --if '?' supplied print out datatype key; number = [n]... | ||
28 | local retType = "?" | ||
29 | local typ = { | ||
30 | ["nil"] = "nil", | ||
31 | ["boolean"] = "b", | ||
32 | ["number"] = "n", | ||
33 | ["string"] = "s", | ||
34 | ["userdata"] = "u", | ||
35 | ["function"] = "f", | ||
36 | ["thread"] = "thr", | ||
37 | ["table"] = "t" | ||
38 | } | ||
39 | if sType == "?" then retType = "Datatypes: " end | ||
40 | for k,v in pairs(typ) do | ||
41 | if sType == k then | ||
42 | retType = v break | ||
43 | elseif (sType == "?") then | ||
44 | retType = retType .. " [" ..v.. "] = " .. k | ||
45 | end | ||
46 | end | ||
47 | return " [" ..retType.. "] " | ||
48 | end | ||
49 | |||
50 | local function tableByName(tName) | ||
51 | --find the longest match possible to an actual table | ||
52 | --Name comes in as (table) tName.var so we can pass back out the name found PITA | ||
53 | --returns the table found (key and value) | ||
54 | local ld = {} | ||
55 | local sMatch = "" | ||
56 | local kMatch = nil | ||
57 | local vMatch = nil | ||
58 | |||
59 | ----FUNCTIONS for tableByName ----------------------------------------------------- | ||
60 | local function search4Str(n, k, v) | ||
61 | local sKey = tostring(k) | ||
62 | if string.find (n, sKey,1,true) then | ||
63 | if sKey:len() > sMatch:len() then sMatch = sKey kMatch = k vMatch = v end | ||
64 | --find the longest match we can | ||
65 | end | ||
66 | end | ||
67 | ----END FUNCTIONS for tableByName ------------------------------------------------- | ||
68 | |||
69 | if tName.val ~= nil and tName.val ~= "" then | ||
70 | for k, v in pairs(_G) do | ||
71 | --_G check both since some tables are only in _G or package.loaded | ||
72 | search4Str(tName.val, k, v) | ||
73 | end | ||
74 | for k, v in pairs(package.loaded) do --package.loaded | ||
75 | search4Str(tName.val, k, v) | ||
76 | end | ||
77 | if not string.find (sMatch, "_G",1,true) then sMatch = "_G." .. sMatch end | ||
78 | -- put the root _G in if not exist | ||
79 | if kMatch and vMatch then ld[kMatch] = vMatch tName.val = sMatch return ld end | ||
80 | end | ||
81 | tName.val = "_G" | ||
82 | return package.loaded --Not Found return default | ||
83 | end | ||
84 | |||
85 | local function dump_Tables(tBase, sFunc, tSeen, tRet) | ||
86 | --Based on: http://www.lua.org/cgi-bin/demo?globals | ||
87 | --Recurse through tBase tables copying all found Tables | ||
88 | local sSep="" | ||
89 | local ld={} | ||
90 | local tNameBuf = {} | ||
91 | local sName | ||
92 | if sFunc ~= "" then sSep = "." end | ||
93 | |||
94 | for k, v in pairs(tBase) do | ||
95 | k = tostring(k) | ||
96 | tNameBuf[1] = sFunc | ||
97 | tNameBuf[2] = sSep | ||
98 | tNameBuf[3] = k | ||
99 | |||
100 | |||
101 | if k ~= "loaded" and type(v) == "table" and not tSeen[v] then | ||
102 | tSeen[v]=sFunc | ||
103 | sName = table.concat(tNameBuf) | ||
104 | tRet[sName] = a2m_m2a(v) --place all keys into ld[i]=value | ||
105 | dump_Tables(v, sName, tSeen, tRet) | ||
106 | elseif type(v) == "table" and not tSeen[v] then | ||
107 | tSeen[v]=sFunc | ||
108 | tRet[table.concat(tNameBuf)] = a2m_m2a(v) -- dump 'loaded' table | ||
109 | for k1, v1 in pairs(v) do | ||
110 | if not _G[k1] and type(v1) == "table" and not tSeen[v1] then | ||
111 | -- dump tables that are loaded but not global | ||
112 | tSeen[v1]=sFunc | ||
113 | tNameBuf[3] = k1 | ||
114 | sName = table.concat(tNameBuf) | ||
115 | tRet[sName] = a2m_m2a(v1) --place all keys into ld[i]=value | ||
116 | dump_Tables(v1, sName, tSeen, tRet) | ||
117 | end | ||
118 | end | ||
119 | end | ||
120 | end | ||
121 | end | ||
122 | |||
123 | local function dump_Functions(tBase) | ||
124 | --Based on: http://www.lua.org/cgi-bin/demo?globals | ||
125 | --We already recursed through tBase copying all found tables | ||
126 | --we look up the table by name and then (ab)use a2m_m2a() to load the address | ||
127 | --after finding the table by address in tBase we will | ||
128 | --put the table address of tFuncs in its place | ||
129 | local tFuncBuf = {} | ||
130 | for k,v in pairs(tBase) do | ||
131 | local tTable = a2m_m2a(v) | ||
132 | local tFuncs = {} | ||
133 | |||
134 | for key, val in pairs(tTable) do | ||
135 | if key ~= "loaded" then | ||
136 | tFuncBuf[1] = dtTag(type(val)) | ||
137 | tFuncBuf[2] = tostring(key) | ||
138 | tFuncs[table.concat(tFuncBuf)]= val | ||
139 | --put the name and value in our tFuncs table | ||
140 | end | ||
141 | end | ||
142 | tBase[k] = a2m_m2a(tFuncs) -- copy the address back to tBase | ||
143 | end | ||
144 | |||
145 | end | ||
146 | |||
147 | local function get_common_branches(t, tRet) | ||
148 | --load t 'names(values)' into keys | ||
149 | --strip off long paths then iterate value if it exists | ||
150 | --local tRet={} | ||
151 | local sBranch = "" | ||
152 | local tName = {} | ||
153 | for k in pairs(t) do | ||
154 | tName["val"]=k | ||
155 | tableByName(tName) | ||
156 | sBranch = tName.val | ||
157 | if tRet[sBranch] == nil then | ||
158 | tRet[sBranch] = 1 --first instance of this branch | ||
159 | else | ||
160 | tRet[sBranch] = tRet[sBranch] + 1 | ||
161 | end | ||
162 | end | ||
163 | end | ||
164 | |||
165 | local function pairsByPairs (t, tkSorted) | ||
166 | --tkSorted should be an already sorted (i)table with t[keys] in the values | ||
167 | --https://www.lua.org/pil/19.3.html | ||
168 | --!!Note: table sort default function does not like numbers as [KEY]!! | ||
169 | --see *sortbyKeys*cmp_alphanum* | ||
170 | |||
171 | local i = 0 -- iterator variable | ||
172 | local iter = function () -- iterator function | ||
173 | i = i + 1 | ||
174 | if tkSorted[i] == nil then return nil | ||
175 | else return tkSorted[i], t[tkSorted[i]] | ||
176 | end | ||
177 | end | ||
178 | return iter | ||
179 | end | ||
180 | |||
181 | local function sortbyKeys(t, tkSorted) | ||
182 | --loads keys of (t) into values of tkSorted | ||
183 | --and then sorts them | ||
184 | --tkSorted has integer keys (see ipairs) | ||
185 | ----FUNCTIONS for sortByKeys ------------- | ||
186 | local cmp_alphanum = function (op1, op2) | ||
187 | local type1= type(op1) | ||
188 | local type2 = type(op2) | ||
189 | if type1 ~= type2 then | ||
190 | return type1 < type2 | ||
191 | else | ||
192 | return op1 < op2 | ||
193 | end | ||
194 | end | ||
195 | ----END FUNCTIONS for sortByKeys --------- | ||
196 | for n in pairs(t) do table.insert(tkSorted, n) end | ||
197 | table.sort(tkSorted, cmp_alphanum)--table.sort(tkSorted) | ||
198 | end | ||
199 | |||
200 | local function funcprint(tBuf, strName, value) | ||
201 | local sType = type(value) | ||
202 | local sVal = "" | ||
203 | local sHex = "" | ||
204 | tBuf[#tBuf + 1] = "\t" | ||
205 | tBuf[#tBuf + 1] = strName | ||
206 | if nil ~= string.find (";string;number;userdata;boolean;", sType, 1, true) then | ||
207 | --If any of the above types print the contents of variable | ||
208 | sVal = tostring(value) | ||
209 | |||
210 | if type(value) == "number" then | ||
211 | sHex = " = 0x" .. string.format("%x", value) | ||
212 | else | ||
213 | sHex = "" | ||
214 | sVal = string.gsub(sVal, "\n", "\\n") --replace newline with \n | ||
215 | end | ||
216 | tBuf[#tBuf + 1] = " : " | ||
217 | tBuf[#tBuf + 1] = sVal | ||
218 | tBuf[#tBuf + 1] = sHex | ||
219 | end | ||
220 | tBuf[#tBuf + 1] = "\r\n" | ||
221 | end | ||
222 | |||
223 | local function errorHandler( err ) | ||
224 | filehandle:write(" ERROR:" .. err .. "\n") | ||
225 | end | ||
226 | |||
227 | |||
228 | ------------MAIN---------------------------------------------------------------- | ||
229 | local _NIL = nil | ||
230 | local tSeen= {} | ||
231 | local tcBase = {} | ||
232 | local tkSortCbase = {} | ||
233 | local tMods= {} | ||
234 | local tkSortMods = {} | ||
235 | local tWriteBuf = {} | ||
236 | local n = 0 -- count of how many items were found | ||
237 | |||
238 | filehandle = io.open(sDumpFile, "w+") --overwrite | ||
239 | tWriteBuf[#tWriteBuf + 1] = "*Loaded Modules* \n" | ||
240 | |||
241 | xpcall( function() | ||
242 | dump_Tables(tableByName({["val"] = "_G"}),"", tSeen, tMods) | ||
243 | --you can put a table name here if you just wanted to display | ||
244 | --only its items, ex. "os" or "rb" or "io" | ||
245 | --However, it has to be accessible directly from _G | ||
246 | --so "rb.actions" wouldn't return anything since its technically | ||
247 | --enumerated through _G.rb | ||
248 | end , errorHandler ) | ||
249 | tSeen = nil | ||
250 | |||
251 | xpcall( function()dump_Functions(tMods)end , errorHandler ) | ||
252 | |||
253 | get_common_branches(tMods, tcBase) | ||
254 | |||
255 | sortbyKeys(tcBase, tkSortCbase) | ||
256 | sortbyKeys(tMods, tkSortMods) | ||
257 | |||
258 | for k, v in pairsByPairs(tcBase, tkSortCbase ) do | ||
259 | n = n + 1 | ||
260 | if n ~= 1 then | ||
261 | tWriteBuf[#tWriteBuf + 1] = ", " | ||
262 | end | ||
263 | tWriteBuf[#tWriteBuf + 1] = tostring(k) | ||
264 | if n >= 3 then -- split loaded modules to multiple lines | ||
265 | n = 0 | ||
266 | tWriteBuf[#tWriteBuf + 1] = "\r\n" | ||
267 | end | ||
268 | if #tWriteBuf > 25 then | ||
269 | filehandle:write(table.concat(tWriteBuf)) | ||
270 | for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table | ||
271 | end | ||
272 | end | ||
273 | |||
274 | tcBase= nil tkSortCbase= nil | ||
275 | tWriteBuf[#tWriteBuf + 1] = "\r\n" | ||
276 | tWriteBuf[#tWriteBuf + 1] = dtTag("?") | ||
277 | tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n" | ||
278 | tWriteBuf[#tWriteBuf + 1] = "Functions: \r\n" | ||
279 | |||
280 | n = 0 | ||
281 | for key, val in pairsByPairs(tMods, tkSortMods) do | ||
282 | local tkSorted = {} | ||
283 | local tFuncs = a2m_m2a(val) | ||
284 | sortbyKeys(tFuncs, tkSorted) | ||
285 | tWriteBuf[#tWriteBuf + 1] = "\r\n" | ||
286 | tWriteBuf[#tWriteBuf + 1] = tostring(key) | ||
287 | tWriteBuf[#tWriteBuf + 1] = "\r\n" | ||
288 | for k, v in pairsByPairs(tFuncs, tkSorted) do | ||
289 | n = n + 1 | ||
290 | funcprint(tWriteBuf, k,v) | ||
291 | if #tWriteBuf > 25 then | ||
292 | filehandle:write(table.concat(tWriteBuf)) | ||
293 | for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table | ||
294 | end | ||
295 | end | ||
296 | end | ||
297 | tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n" | ||
298 | tWriteBuf[#tWriteBuf + 1] = n | ||
299 | tWriteBuf[#tWriteBuf + 1] = " Items Found \r\n" | ||
300 | filehandle:write(table.concat(tWriteBuf)) | ||
301 | for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- empty table | ||
302 | filehandle:close() | ||
303 | rb.splash(rb.HZ * 5, n .. " Items dumped to : " .. sDumpFile) | ||
304 | --rb.splash(500, collectgarbage("count")) | ||
diff --git a/apps/plugins/lua_scripts/printmenu.lua b/apps/plugins/lua_scripts/printmenu.lua new file mode 100755 index 0000000000..3cb17d8026 --- /dev/null +++ b/apps/plugins/lua_scripts/printmenu.lua | |||
@@ -0,0 +1,83 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2017 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
24 | |||
25 | require("printtable") | ||
26 | local _clr = require("color") | ||
27 | |||
28 | local _LCD = rb.lcd_framebuffer() | ||
29 | -------------------------------------------------------------------------------- | ||
30 | -- displays text in menu_t calls function in same indice of func_t when selected | ||
31 | function print_menu(menu_t, func_t, selected, settings, copy_screen) | ||
32 | |||
33 | local i, start, vcur, screen_img | ||
34 | |||
35 | if selected then vcur = selected + 1 end | ||
36 | if vcur and vcur <= 1 then vcur = 2 end | ||
37 | |||
38 | if not settings then | ||
39 | settings = {} | ||
40 | settings.justify = "center" | ||
41 | settings.wrap = true | ||
42 | settings.hfgc = _clr.set( 0, 000, 000, 000) | ||
43 | settings.hbgc = _clr.set(-1, 255, 255, 255) | ||
44 | settings.ifgc = _clr.set(-1, 000, 255, 060) | ||
45 | settings.ibgc = _clr.set( 0, 000, 000, 000) | ||
46 | settings.iselc = _clr.set( 1, 000, 200, 100) | ||
47 | settings.default = true | ||
48 | end | ||
49 | |||
50 | settings.hasheader = true | ||
51 | settings.co_routine = nil | ||
52 | settings.msel = false | ||
53 | settings.start = start | ||
54 | settings.curpos = vcur | ||
55 | |||
56 | while not i or i > 0 do | ||
57 | if copy_screen == true then | ||
58 | --make a copy of screen for restoration | ||
59 | screen_img = screen_img or rb.new_image() | ||
60 | screen_img:copy(_LCD) | ||
61 | else | ||
62 | screen_img = nil | ||
63 | end | ||
64 | |||
65 | _LCD:clear(settings.ibgc) | ||
66 | |||
67 | settings.start = start | ||
68 | settings.curpos = vcur | ||
69 | |||
70 | i, start, vcur = print_table(menu_t, #menu_t, settings) | ||
71 | --vcur = vcur + 1 | ||
72 | collectgarbage("collect") | ||
73 | if copy_screen == true then _LCD:copy(screen_img) end | ||
74 | |||
75 | if func_t and func_t[i] then | ||
76 | if func_t[i](i, menu_t) == true then break end | ||
77 | else | ||
78 | break | ||
79 | end | ||
80 | end | ||
81 | if settings.default == true then settings = nil end | ||
82 | return screen_img | ||
83 | end | ||
diff --git a/apps/plugins/lua_scripts/tagnav.lua b/apps/plugins/lua_scripts/tagnav.lua new file mode 100644 index 0000000000..35e691045d --- /dev/null +++ b/apps/plugins/lua_scripts/tagnav.lua | |||
@@ -0,0 +1,344 @@ | |||
1 | -- BILGUS 2018 | ||
2 | |||
3 | --local scrpath = rb.current_path()" | ||
4 | |||
5 | --package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path | ||
6 | require("printmenu") | ||
7 | require("printtable") | ||
8 | require("dbgettags") | ||
9 | |||
10 | local _print = require("print") | ||
11 | |||
12 | rb.actions = nil | ||
13 | package.loaded["actions"] = nil | ||
14 | |||
15 | local sERROROPENING = "Error opening" | ||
16 | local sERRORMENUENTRY = "Error finding menu entry" | ||
17 | |||
18 | local sBLANKLINE = "##sBLANKLINE##" | ||
19 | local sDEFAULTMENU = "customfilter" | ||
20 | |||
21 | local sFILEOUT = "/.rockbox/tagnavi_custom.config" | ||
22 | local sFILEHEADER = "#! rockbox/tagbrowser/2.0" | ||
23 | local sMENUSTART = "%menu_start \"custom\" \"Database\"" | ||
24 | local sMENUTITLE = "title = \"fmt_title\"" | ||
25 | |||
26 | local TAG_ARTIST, TAG_ALBARTIST, TAG_ALBUM, TAG_GENRE, TAG_COMPOSER = 1, 2, 3, 4, 5 | ||
27 | local ts_TAGTYPE = {"Artist", "AlbumArtist", "Album", "Genre", "Composer"} | ||
28 | local ts_DBPATH = {"database_0.tcd", "database_7.tcd", "database_1.tcd", "database_2.tcd", "database_5.tcd"} | ||
29 | |||
30 | local COND_OR, COND_AND, COND_NOR, COND_NAND = 1, 2, 3, 4 | ||
31 | local ts_CONDITIONALS = {"OR", "AND", "!, OR", "!, AND"} | ||
32 | local ts_CONDSYMBOLS = {"|", "&", "|", "&"} | ||
33 | |||
34 | local ts_YESNO = {"", "Yes", "No"} | ||
35 | local s_OVERWRITE = "Overwrite" | ||
36 | local s_EXISTS = "Exists" | ||
37 | |||
38 | |||
39 | local function question(tInquiry, start) | ||
40 | settings = {} | ||
41 | settings.justify = "center" | ||
42 | settings.wrap = true | ||
43 | settings.msel = false | ||
44 | settings.hasheader = true | ||
45 | settings.co_routine = nil | ||
46 | settings.curpos = start or 1 | ||
47 | local sel = print_table(tInquiry, #tInquiry, settings) | ||
48 | return sel | ||
49 | end | ||
50 | |||
51 | local function find_linepos(t_lines, search, startline) | ||
52 | startline = startline or 1 | ||
53 | |||
54 | for i = startline, #t_lines do | ||
55 | if string.match (t_lines[i], search) then | ||
56 | return i | ||
57 | end | ||
58 | end | ||
59 | |||
60 | return -1 | ||
61 | end | ||
62 | |||
63 | local function replacelines(t_lines, search, replace, startline) | ||
64 | startline = startline or 1 | ||
65 | repcount = 0 | ||
66 | for i = startline, #t_lines do | ||
67 | if string.match (t_lines[i], search) then | ||
68 | t_lines[i] = replace | ||
69 | repcount = repcount + 1 | ||
70 | end | ||
71 | end | ||
72 | return repcount | ||
73 | end | ||
74 | |||
75 | local function replaceemptylines(t_lines, replace, startline) | ||
76 | startline = startline or 1 | ||
77 | replace = replace or nil | ||
78 | repcount = 0 | ||
79 | for i = startline, #t_lines do | ||
80 | if t_lines[i] == "" then | ||
81 | t_lines[i] = replace | ||
82 | repcount = repcount + 1 | ||
83 | end | ||
84 | end | ||
85 | return repcount | ||
86 | end | ||
87 | |||
88 | local function checkexistingmenu(t_lines, menuname) | ||
89 | local pos = find_linepos(t_lines, "^\"" .. menuname .. "\"%s*%->.+") | ||
90 | local sel = 0 | ||
91 | if pos > 0 then | ||
92 | ts_YESNO[1] = menuname .. " " .. s_EXISTS .. ", " .. s_OVERWRITE .. "?" | ||
93 | sel = question(ts_YESNO, 3) | ||
94 | if sel == 3 then | ||
95 | pos = nil | ||
96 | elseif sel < 2 then | ||
97 | pos = 0 | ||
98 | end | ||
99 | else | ||
100 | pos = nil | ||
101 | end | ||
102 | return pos | ||
103 | end | ||
104 | |||
105 | local function savedata(filename, ts_tags, cond, menuname) | ||
106 | menuname = menuname or sDEFAULTMENU | ||
107 | |||
108 | local lines = {} | ||
109 | local curline = 0 | ||
110 | local function lines_next(str, pos) | ||
111 | pos = pos or #lines + 1 | ||
112 | lines[pos] = str or "" | ||
113 | curline = pos | ||
114 | end | ||
115 | |||
116 | local function lines_append(str, pos) | ||
117 | pos = pos or curline or #lines | ||
118 | lines[pos] = lines[pos] .. str or "" | ||
119 | end | ||
120 | |||
121 | if rb.file_exists(filename) ~= true then | ||
122 | lines_next(sFILEHEADER) | ||
123 | lines_next("#") | ||
124 | lines_next("# MAIN MENU") | ||
125 | lines_next(sMENUSTART) | ||
126 | else | ||
127 | local file = io.open(filename, "r") -- read | ||
128 | if not file then | ||
129 | rb.splash(rb.HZ, "Error opening" .. " " .. filename) | ||
130 | return | ||
131 | end | ||
132 | |||
133 | for line in file:lines() do | ||
134 | lines_next(line) | ||
135 | end | ||
136 | file:close() | ||
137 | end | ||
138 | |||
139 | local menupos = find_linepos(lines, sMENUSTART) | ||
140 | if menupos < 1 then | ||
141 | rb.splash(rb.HZ, sERRORMENUENTRY) | ||
142 | return | ||
143 | end | ||
144 | |||
145 | replaceemptylines(lines, sBLANKLINE, menupos) | ||
146 | |||
147 | local existmenupos = checkexistingmenu(lines, menuname) | ||
148 | if existmenupos and existmenupos < 1 then return end -- user canceled | ||
149 | |||
150 | local lastcond = "" | ||
151 | local n_cond = COND_OR | ||
152 | local tags, tagtype | ||
153 | |||
154 | local function buildtag(e_tagtype) | ||
155 | if ts_tags[e_tagtype] then | ||
156 | n_cond = (cond[e_tagtype] or COND_OR) | ||
157 | if e_tagtype > 1 then | ||
158 | lines_append(" " .. ts_CONDSYMBOLS[n_cond]) | ||
159 | end | ||
160 | tags = ts_tags[e_tagtype] | ||
161 | tagtype = string.lower(ts_TAGTYPE[e_tagtype]) | ||
162 | |||
163 | if n_cond <= COND_AND then | ||
164 | lines_append(" " .. tagtype) | ||
165 | lines_append(" @ \"".. table.concat(tags, "|") .. "\"") | ||
166 | else | ||
167 | for k = 1, #tags do | ||
168 | lines_append(" " .. tagtype) | ||
169 | lines_append(" !~ \"".. tags[k] .. "\"") | ||
170 | if k < #tags then lines_append(" &") end | ||
171 | end | ||
172 | end | ||
173 | end | ||
174 | end | ||
175 | |||
176 | if ts_tags[TAG_ARTIST] or ts_tags[TAG_ALBARTIST] or ts_tags[TAG_ALBUM] or | ||
177 | ts_tags[TAG_GENRE] or ts_tags[TAG_COMPOSER] then | ||
178 | |||
179 | lines_next("\"" .. menuname .. "\" -> " .. sMENUTITLE .. " ?", existmenupos) | ||
180 | |||
181 | buildtag(TAG_ARTIST) | ||
182 | buildtag(TAG_ALBARTIST) | ||
183 | buildtag(TAG_ALBUM) | ||
184 | buildtag(TAG_GENRE) | ||
185 | buildtag(TAG_COMPOSER) | ||
186 | |||
187 | lines_next("\n") | ||
188 | else | ||
189 | rb.splash(rb.HZ, "Nothing to save") | ||
190 | end | ||
191 | |||
192 | local file = io.open(filename, "w+") -- overwrite | ||
193 | if not file then | ||
194 | rb.splash(rb.HZ, "Error writing " .. filename) | ||
195 | return | ||
196 | end | ||
197 | |||
198 | for i = 1, #lines do | ||
199 | if lines[i] and lines[i] ~= sBLANKLINE then | ||
200 | file:write(lines[i], "\n") | ||
201 | end | ||
202 | end | ||
203 | |||
204 | file:close() | ||
205 | end | ||
206 | |||
207 | -- uses print_table to display database tags | ||
208 | local function print_tags(ftable, settings, t_selected) | ||
209 | if not s_cond then s_sep = "|" end | ||
210 | ftable = ftable or {} | ||
211 | |||
212 | if t_selected then | ||
213 | for k = 1, #t_selected do | ||
214 | ftable[t_selected[k]] = ftable[t_selected[k]] .. "\0" | ||
215 | end | ||
216 | end | ||
217 | rb.lcd_clear_display() | ||
218 | _print.clear() | ||
219 | |||
220 | if not settings then | ||
221 | settings = {} | ||
222 | settings.justify = "center" | ||
223 | settings.wrap = true | ||
224 | settings.msel = true | ||
225 | end | ||
226 | |||
227 | settings.hasheader = true | ||
228 | settings.co_routine = nil | ||
229 | |||
230 | local sel = print_table(ftable, #ftable, settings) | ||
231 | |||
232 | --_lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", ")) | ||
233 | local selected = {} | ||
234 | local str = "" | ||
235 | for k = 1,#sel do | ||
236 | str = ftable[sel[k]] or "" | ||
237 | selected[#selected + 1] = string.sub(str, 1, -2) -- REMOVE \0 | ||
238 | end | ||
239 | |||
240 | ftable = nil | ||
241 | |||
242 | if #sel == 0 then | ||
243 | return nil, nil | ||
244 | end | ||
245 | |||
246 | return sel, selected | ||
247 | end -- print_tags | ||
248 | |||
249 | -- uses print_table to display a menu | ||
250 | function main_menu() | ||
251 | local menuname = sDEFAULTMENU | ||
252 | local t_tags | ||
253 | local ts_tags = {} | ||
254 | local cond = {} | ||
255 | local sel = {} | ||
256 | local mt = { | ||
257 | [1] = "TagNav Customizer", | ||
258 | [2] = "", --ts_CONDITIONALS[cond[TAG_ARTIST] or COND_OR], | ||
259 | [3] = ts_TAGTYPE[TAG_ARTIST], | ||
260 | [4] = ts_CONDITIONALS[cond[TAG_ALBARTIST] or COND_OR], | ||
261 | [5] = ts_TAGTYPE[TAG_ALBARTIST], | ||
262 | [6] = ts_CONDITIONALS[cond[TAG_ALBUM] or COND_OR], | ||
263 | [7] = ts_TAGTYPE[TAG_ALBUM], | ||
264 | [8] = ts_CONDITIONALS[cond[TAG_GENRE] or COND_OR], | ||
265 | [9] = ts_TAGTYPE[TAG_GENRE], | ||
266 | [10] = ts_CONDITIONALS[cond[TAG_COMPOSER] or COND_OR], | ||
267 | [11] = ts_TAGTYPE[TAG_COMPOSER], | ||
268 | [12] = "Save to Tagnav", | ||
269 | [13] = "Exit" | ||
270 | } | ||
271 | |||
272 | local function sel_cond(item, item_mt) | ||
273 | cond[item] = cond[item] or 1 | ||
274 | cond[item] = cond[item] + 1 | ||
275 | if cond[item] > #ts_CONDITIONALS then cond[item] = 1 end | ||
276 | mt[item_mt] = ts_CONDITIONALS[cond[item]] | ||
277 | end | ||
278 | |||
279 | local function sel_tag(item, item_mt, t_tags) | ||
280 | t_tags = get_tags(rb.ROCKBOX_DIR .. "/" .. ts_DBPATH[item], ts_TAGTYPE[item]) | ||
281 | sel[item], ts_tags[item] = print_tags(t_tags, nil, sel[item]) | ||
282 | if ts_tags[item] then | ||
283 | mt[item_mt] = ts_TAGTYPE[item] .. " [" .. #sel[item] .. "]" | ||
284 | else | ||
285 | mt[item_mt] = ts_TAGTYPE[item] | ||
286 | end | ||
287 | end | ||
288 | |||
289 | local ft = { | ||
290 | [0] = exit_now, --if user cancels do this function | ||
291 | [1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot | ||
292 | [2] = function(ARTCOND) | ||
293 | sel_cond(TAG_ARTIST, ARTCOND) | ||
294 | end, | ||
295 | [3] = function(ART) | ||
296 | sel_tag(TAG_ARTIST, ART, t_tags) | ||
297 | end, | ||
298 | [4] = function(ALBARTCOND) | ||
299 | sel_cond(TAG_ALBARTIST, ALBARTCOND) | ||
300 | end, | ||
301 | [5] = function(ALBART) | ||
302 | sel_tag(TAG_ALBARTIST, ALBART, t_tags) | ||
303 | end, | ||
304 | [6] = function(ALBCOND) | ||
305 | sel_cond(TAG_ALBUM, ALBCOND) | ||
306 | end, | ||
307 | [7] = function(ALB) | ||
308 | sel_tag(TAG_ALBUM, ALB, t_tags) | ||
309 | end, | ||
310 | [8] = function(GENRECOND) | ||
311 | sel_cond(TAG_GENRE, GENRECOND) | ||
312 | end, | ||
313 | [9] = function(GENRE) | ||
314 | sel_tag(TAG_GENRE, GENRE, t_tags) | ||
315 | end, | ||
316 | [10] = function(COMPCOND) | ||
317 | sel_cond(TAG_COMPOSER, COMPCOND) | ||
318 | end, | ||
319 | [11] = function(COMP) | ||
320 | sel_tag(TAG_COMPOSER, COMP, t_tags) | ||
321 | end, | ||
322 | |||
323 | [12] = function(SAVET) | ||
324 | menuname = menuname or sDEFAULTMENU | ||
325 | menuname = rb.kbd_input(menuname) | ||
326 | menuname = string.match(menuname, "%w+") | ||
327 | if menuname == "" then menuname = nil end | ||
328 | menuname = menuname or sDEFAULTMENU | ||
329 | savedata(sFILEOUT, ts_tags, cond, menuname) | ||
330 | end, | ||
331 | [13] = function(EXIT_) return true end | ||
332 | } | ||
333 | |||
334 | print_menu(mt, ft, 2) --start at item 2 | ||
335 | |||
336 | end | ||
337 | |||
338 | function exit_now() | ||
339 | rb.lcd_update() | ||
340 | os.exit() | ||
341 | end -- exit_now | ||
342 | |||
343 | main_menu() | ||
344 | exit_now() | ||
diff --git a/tools/buildzip.pl b/tools/buildzip.pl index 1fd242e12c..6a67f7b51b 100755 --- a/tools/buildzip.pl +++ b/tools/buildzip.pl | |||
@@ -173,6 +173,16 @@ sub make_install { | |||
173 | } | 173 | } |
174 | glob_install("$src/rocks/viewers/lua/*", "$libdir/rocks/viewers/lua"); | 174 | glob_install("$src/rocks/viewers/lua/*", "$libdir/rocks/viewers/lua"); |
175 | 175 | ||
176 | #lua example scripts | ||
177 | if(-e "$ROOT/apps/plugins/lua_scripts") { | ||
178 | unless (glob_mkdir("$libdir/rocks/demos/lua_scripts")) { | ||
179 | return 0; | ||
180 | } | ||
181 | glob_install("$ROOT/apps/plugins/lua_scripts/*.lua", "$libdir/rocks/demos/lua_scripts"); | ||
182 | #glob_mkdir("$temp_dir/rocks/demos/lua_scripts"); | ||
183 | #glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/"); | ||
184 | } | ||
185 | |||
176 | # all the rest directories | 186 | # all the rest directories |
177 | foreach my $t (@userstuff) { | 187 | foreach my $t (@userstuff) { |
178 | unless (glob_mkdir("$userdir/$t")) { | 188 | unless (glob_mkdir("$userdir/$t")) { |
@@ -433,6 +443,12 @@ sub buildzip { | |||
433 | 443 | ||
434 | find(find_copyfile(qr/\.(rock|ovl|lua)/, abs_path("$temp_dir/rocks/")), 'apps/plugins'); | 444 | find(find_copyfile(qr/\.(rock|ovl|lua)/, abs_path("$temp_dir/rocks/")), 'apps/plugins'); |
435 | 445 | ||
446 | #lua example scripts | ||
447 | if(-e "$ROOT/apps/plugins/lua_scripts") { | ||
448 | glob_mkdir("$temp_dir/rocks/demos/lua_scripts"); | ||
449 | glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/"); | ||
450 | } | ||
451 | |||
436 | # exclude entries for the image file types not supported by the imageviewer for the target. | 452 | # exclude entries for the image file types not supported by the imageviewer for the target. |
437 | my $viewers = "$ROOT/apps/plugins/viewers.config"; | 453 | my $viewers = "$ROOT/apps/plugins/viewers.config"; |
438 | my $c="cat $viewers | gcc $cppdef -I. -I$firmdir/export -E -P -include config.h -"; | 454 | my $c="cat $viewers | gcc $cppdef -I. -I$firmdir/export -E -P -include config.h -"; |