1
0
Fork 0
forked from len0rd/rockbox

lua add demo scripts, atexit handler, gui_scrollbar_draw

Change-Id: Ie8794e8a487f73952dae43e036787b6972fdbbee
This commit is contained in:
William Wilgus 2019-07-26 01:30:00 -05:00
parent 60c5a29408
commit 90118f14cf
22 changed files with 2374 additions and 22 deletions

View file

@ -49,6 +49,7 @@ lamp,apps
logo,demos
lrcplayer,apps
lua,viewers
lua_scripts,demos
fractals,demos
main_menu_config,apps
matrix,demos

View file

@ -154,6 +154,7 @@ metronome.c
/* Lua needs at least 160 KB to work in */
#if PLUGIN_BUFFER_SIZE >= 0x80000
boomshine.lua
lua_scripts.lua
#ifdef HAVE_LCD_COLOR
pixel-painter.lua
#endif /* HAVE_LCD_COLOR */

View file

@ -101,4 +101,5 @@ mpegplayer
/* Lua needs at least 160 KB to work in */
#if PLUGIN_BUFFER_SIZE >= 0x80000
lua
lua_scripts
#endif

View file

@ -7,6 +7,7 @@
* In fact, most of the plugins aren't supposed to be used on a touch(mouse) device
*/
lua
lua_scripts
#ifdef HAVE_LCD_BITMAP
#if CONFIG_CODEC == SWCODEC && PLUGIN_BUFFER_SIZE > 0x20000

View file

@ -0,0 +1,385 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
require("actions") -- Contains rb.actions & rb.contexts
local _clr = require("color")
local _print = require("print")
local _timer = require("timer")
-- Button definitions --
local EXIT_BUTTON = rb.PLA_EXIT
local CANCEL_BUTTON = rb.actions.PLA_CANCEL
local DOWN_BUTTON = rb.actions.PLA_DOWN
local DOWNR_BUTTON = rb.actions.PLA_DOWN_REPEAT
local EXIT_BUTTON = rb.actions.PLA_EXIT
local LEFT_BUTTON = rb.actions.PLA_LEFT
local LEFTR_BUTTON = rb.actions.PLA_LEFT_REPEAT
local RIGHT_BUTTON = rb.actions.PLA_RIGHT
local RIGHTR_BUTTON = rb.actions.PLA_RIGHT_REPEAT
local SEL_BUTTON = rb.actions.PLA_SELECT
local SELREL_BUTTON = rb.actions.PLA_SELECT_REL
local SELR_BUTTON = rb.actions.PLA_SELECT_REPEAT
local UP_BUTTON = rb.actions.PLA_UP
local UPR_BUTTON = rb.actions.PLA_UP_REPEAT
-- clamps value to >= min and <= max
local function clamp(iVal, iMin, iMax)
if iMin > iMax then
local swap = iMin
iMin, iMax = iMax, swap
end
if iVal < iMin then
return iMin
elseif iVal < iMax then
return iVal
end
return iMax
end
--------------------------------------------------------------------------------
--[[ cursor style button routine
-- left / right are x, xi is increment xir is increment when repeat
-- up / down are y, yi is increment yir is increment when repeat
-- cancel is returned as 0,1
-- select as 0, 1, 2, 3 (none, pressed, repeat, relesed)
-- x_chg and y_chg are the amount x or y changed
-- timeout == nil or -1 loop waits indefinitely till button is pressed
-- time since last button press is returned in ticks..
-- make xi, xir, yi, yir negative to flip direction...
]]
local function dpad(x, xi, xir, y, yi, yir, timeout)
_timer("dpad") -- start a persistant timer; keeps time between button events
if timeout == nil then timeout = -1 end
local cancel, select = 0, 0
local x_chg, y_chg = 0, 0
local button
while true do
button = rb.get_plugin_action(timeout)
if button == CANCEL_BUTTON then
cancel = 1
break;
elseif button == EXIT_BUTTON then
cancel = 1
break;
elseif button == SEL_BUTTON then
select = 1
timeout = timeout + 1
elseif button == SELR_BUTTON then
select = 2
timeout = timeout + 1
elseif button == SELREL_BUTTON then
select = -1
timeout = timeout + 1
elseif button == LEFT_BUTTON then
x_chg = x_chg - xi
elseif button == LEFTR_BUTTON then
x_chg = x_chg - xir
elseif button == RIGHT_BUTTON then
x_chg = x_chg + xi
elseif button == RIGHTR_BUTTON then
x_chg = x_chg + xir
elseif button == UP_BUTTON then
y_chg = y_chg + yi
elseif button == UPR_BUTTON then
y_chg = y_chg + yir
elseif button == DOWN_BUTTON then
y_chg = y_chg - yi
elseif button == DOWNR_BUTTON then
y_chg = y_chg - yir
elseif timeout >= 0 then--and rb.button_queue_count() < 1 then
break;
end
if x_chg ~= 0 or y_chg ~= 0 then
timeout = timeout + 1
end
end
x = x + x_chg
y = y + y_chg
return cancel, select, x_chg, x, y_chg, y, _timer.check("dpad", true)
end -- dpad
--------------------------------------------------------------------------------
--[[ prints a scrollable table to the screen;
-- requires a contiguous table with only strings;
-- 1st item in table is the title if hasheader == true
-- returns select item indice if NOT m_sel..
-- if m_sel == true a table of selected indices are returned ]]
--------------------------------------------------------------------------------
-- SECOND MODE OF OPERATION -- if co_routine is defined...
-- prints values returned from a resumable factory in a coroutine this allows
-- very large files etc to be displayed.. the downside is it takes time
-- to load data when scrolling also NO multiple selection is allowed
-- table is passed along with the final count t_count
--------------------------------------------------------------------------------
function print_table(t, t_count, settings)
-- (table, t_count, {hasheader, wrap, m_sel, start, curpos, justify, co_routine})
if type(t) ~= "table" then
rb.splash(rb.HZ * 5, "table expected got ".. type(t))
return
end
local wrap, justify, start, curpos, co_routine, hasheader, m_sel
local header_fgc, header_bgc, item_fgc, item_bgc, item_selc
do
local s = settings or _print.get_settings()
wrap, justify = s.wrap, s.justify
start, curpos = s.start, s.curpos
co_routine = s.co_routine
hasheader = s.hasheader
m_sel = false
if co_routine == nil then
--no multi select in incremental mode
m_sel = s.msel
end
header_fgc = s.hfgc or _clr.set( 0, 000, 000, 000)
header_bgc = s.hbgc or _clr.set(-1, 255, 255, 255)
item_fgc = s.ifgc or _clr.set(-1, 000, 255, 060)
item_bgc = s.ibgc or _clr.set( 0, 000, 000, 000)
item_selc = s.iselc or _clr.set( 1, 000, 200, 100)
end
local table_p, line, maxline
local function set_vsb() end -- forward declaration; initialized below
local function init_position(acc_ticks, acc_steps)
if not acc_ticks then acc_ticks = 15 end-- accelerate scroll every this many ticks
if not acc_steps then acc_steps = 5 end -- default steps for an accelerated scroll
return {row = 1, row_scrl= acc_steps,
col = 0, col_scrl = acc_steps,
vcursor = 1, vcursor_min = 1,
acc_ticks = acc_ticks,
acc_steps = acc_steps}
end
local function set_accel(time, scrl, t_p)
if time < t_p.acc_ticks then -- accelerate scroll
scrl = scrl + 1
else
scrl = t_p.acc_steps
end
return scrl
end
--adds or removes \0 from end of table entry to mark selected items
local function select_item(item)
if item < 1 then item = 1 end
if not t[item] then return end
if t[item]:sub(-1) == "\0" then
t[item] = t[item]:sub(1, -2) -- de-select
else
t[item] = t[item] .. "\0" -- select
end
end
-- displays header text at top
local function disp_header(hstr)
local header = header or hstr
local opts = _print.opt.get()
_print.opt.overflow("none") -- don't scroll header; colors change
_print.opt.color(header_fgc, header_bgc)
_print.opt.line(1)
_print.f()
local line = _print.f(header)
_print.opt.set(opts)
_print.opt.line(2)
return 2
end
-- gets user input to select items, quit, scroll
local function get_input(t_p)
set_vsb(t_p.row + t_p.vcursor - 1)--t_p.row)
rb.lcd_update()
local quit, select, x_chg, xi, y_chg, yi, timeb =
dpad(t_p.col, -1, -t_p.col_scrl, t_p.row, -1, -t_p.row_scrl)
t_p.vcursor = t_p.vcursor + y_chg
if t_p.vcursor > maxline or t_p.vcursor < t_p.vcursor_min then
t_p.row = yi
end
if wrap == true and (y_chg == 1 or y_chg == -1) then
-- wraps list, stops at end if accelerated
if t_p.row < t_p.vcursor_min - 1 then
t_p.row = t_count - maxline + 1
t_p.vcursor = maxline
elseif t_p.row + maxline - 1 > t_count then
t_p.row, t_p.vcursor = t_p.vcursor_min - 1, t_p.vcursor_min - 1
end
end
t_p.row = clamp(t_p.row, 1, math.max(t_count - maxline + 1, 1))
t_p.vcursor = clamp(t_p.vcursor, t_p.vcursor_min, maxline)
if x_chg ~= 0 then
if x_chg ~= 1 and x_chg ~= -1 then --stop at the center if accelerated
if (t_p.col <= 0 and xi > 0) or (t_p.col >= 0 and xi < 0) then
xi = 0
end
end
t_p.col = xi
t_p.col_scrl = set_accel(timeb, t_p.col_scrl, t_p)
elseif y_chg ~= 0 then
--t_p.col = 0 -- reset column to the beginning
_print.clear()
_print.opt.sel_line(t_p.vcursor)
t_p.row_scrl = set_accel(timeb, t_p.row_scrl, t_p)
end
if select > 0 and timeb > 15 then --select may be sent multiple times
if m_sel == true then
select_item(t_p.row + t_p.vcursor - 1)
else
return -1, 0, 0, (t_p.row + t_p.vcursor - 1)
end
end
if quit > 0 then return -2, 0, 0, 0 end
return t_p.row, x_chg, y_chg, 0
end
-- displays the actual table
local function display_table(table_p, col_c, row_c, sel)
local i = table_p.row
while i >= 1 and i <= t_count do
-- only print if beginning or user scrolled up/down
if row_c ~= 0 then
if t[i] == nil and co_routine then
--value has been garbage collected or not created yet
coroutine.resume(co_routine, i)
end
if t[i] == nil then
rb.splash(1, string.format("ERROR %d is nil", i))
t[i] = "???"
if rb.get_plugin_action(10) == CANCEL_BUTTON then return 0 end
end
if m_sel == true and t[i]:sub(-1) == "\0" then
_print.opt.sel_line(line)
end
if i == 1 and hasheader == true then
line = disp_header(t[1])
else
line = _print.f("%s", tostring(t[i]))
end
end
i = i + 1 -- important!
if line == 1 or i > t_count or col_c ~= 0 then
_print.opt.column(table_p.col)
i, col_c, row_c, sel = get_input(table_p)
end
rb.button_clear_queue() -- keep the button queue from overflowing
end
return sel
end -- display_table
--============================================================================--
_print.opt.defaults()
_print.opt.autoupdate(false)
_print.opt.color(item_fgc, item_bgc, item_selc)
table_p = init_position(15, 5)
line, maxline = _print.opt.area(5, 1, rb.LCD_WIDTH - 10, rb.LCD_HEIGHT - 2)
maxline = math.min(maxline, t_count)
-- allow user to start at a position other than the beginning
if start ~= nil then table_p.row = clamp(start, 1, t_count + 1) end
if hasheader == true then
table_p.vcursor_min = 2 -- lowest selectable item
table_p.vcursor = 2
end
table_p.vcursor = curpos or table_p.vcursor_min
if table_p.vcursor < 1 or table_p.vcursor > maxline then
table_p.vcursor = table_p.vcursor_min
end
_print.opt.sel_line(table_p.vcursor)
_print.opt.overflow("manual")
_print.opt.justify(justify)
-- initialize vertical scrollbar
set_vsb(); do
local vsb =_print.opt.get()
if rb.LCD_DEPTH == 2 then -- invert 2-bit screens
vsb.fg_pattern = 3 - vsb.fg_pattern
vsb.bg_pattern = 3 - vsb.bg_pattern
end
set_vsb = function (item)
if t_count > (maxline or t_count) then
rb.set_viewport(vsb)
item = item or 0
local m = maxline / 2 + 1
rb.gui_scrollbar_draw(vsb.width - 5, vsb.y, 5, vsb.height,
t_count, math.max(0, item - m),
math.min(item + m, t_count), 0)
end
end
end -- set_vsb
local selected = display_table(table_p, 0, 1, 0)
_print.opt.defaults()
if m_sel == true then -- walk the table to get selected items
selected = {}
for i = 1, t_count do
if t[i]:sub(-1) == "\0" then table.insert(selected, i) end
end
end
--rb.splash(100, string.format("#1 %d, %d, %d", row, vcursor_pos, sel))
return selected, table_p.row, table_p.vcursor
end --print_table

View file

@ -172,8 +172,11 @@ static int os_time (lua_State *L) {
static int os_exit (lua_State *L) {
lua_settop(L, 2);
int status = luaL_optint(L, 1, EXIT_SUCCESS);
lua_close(L);
if (status != EXIT_SUCCESS && lua_type (L, 2) != LUA_TSTRING)
lua_pushfstring(L, "exit (%d)", status);
lua_pushvalue(L, 1); /* put exit status on top of stack */
exit(status);
return EXIT_SUCCESS; /* never reached, surpress warning */
}

View file

@ -19,7 +19,7 @@ LUA_INCLUDEDIR := $(LUA_SRCDIR)/include_lua
LUA_INCLUDELIST := $(addprefix $(LUA_BUILDDIR)/,audio.lua blit.lua color.lua draw.lua \
image.lua lcd.lua math_ex.lua print.lua \
timer.lua playlist.lua pcm.lua sound.lua \
rbcompat.lua )
rbcompat.lua printtable.lua)
ifndef APP_TYPE

View file

@ -771,6 +771,17 @@ RB_WRAP(audio_current_track)
return mem_read_write(L, address, maxsize);
}
RB_WRAP(restart_lua)
{
/*close lua state, open a new lua state, load script @ filename */
luaL_checktype (L, 1, LUA_TSTRING);
lua_settop(L, 1);
lua_pushlightuserdata(L, L); /* signal exit handler */
exit(1); /* atexit in rocklua.c */
return -1;
}
#define RB_FUNC(func) {#func, rock_##func}
#define RB_ALIAS(name, func) {name, rock_##func}
static const luaL_Reg rocklib[] =
@ -843,6 +854,8 @@ static const luaL_Reg rocklib[] =
RB_FUNC(audio_next_track),
RB_FUNC(audio_current_track),
RB_FUNC(restart_lua),
{NULL, NULL}
};
#undef RB_FUNC
@ -939,4 +952,3 @@ LUALIB_API int luaopen_rock(lua_State *L)
#endif
return 1;
}

View file

@ -137,6 +137,7 @@ struct event_data {
int thread_state;
long *event_stack;
long timer_ticks;
short freq_input;
short next_input;
short next_event;
/* callbacks */
@ -171,8 +172,9 @@ static void init_event_data(lua_State *L, struct event_data *ev_data)
ev_data->thread_state = THREAD_YIELD;
//ev_data->event_stack = NULL;
//ev_data->timer_ticks = 0;
ev_data->next_input = EV_TICKS;
ev_data->next_event = EV_INPUT;
ev_data->freq_input = EV_INPUT;
ev_data->next_input = EV_INPUT;
ev_data->next_event = EV_TICKS;
/* callbacks */
for (int i= 0; i < EVENT_CT; i++)
ev_data->cb[i] = NULL;
@ -336,7 +338,7 @@ static void rev_timer_isr(void)
if (ev_data.next_input <=0)
{
ev_data.thread_state |= ((ev_data.thread_state & THREAD_INPUTMASK) >> 16);
ev_data.next_input = EV_INPUT;
ev_data.next_input = ev_data.freq_input;
}
if (ev_data.cb[TIMEREVENT] != NULL && !is_suspend(TIMEREVENT))
@ -535,6 +537,8 @@ static int rockev_register(lua_State *L)
case ACTEVENT:
/* fall through */
case BUTEVENT:
ev_data.freq_input = luaL_optinteger(L, 3, EV_INPUT);
if (ev_data.freq_input < HZ / 20) ev_data.freq_input = HZ / 20;
ev_data.thread_state |= (ev_flag | (ev_flag << 16));
break;
case CUSTOMEVENT:

View file

@ -1225,6 +1225,8 @@ static const struct luaL_reg rli_lib [] =
#define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L)
#if defined NB_SCREENS && (NB_SCREENS > 1)
#define RB_SCREEN_STRUCT(luastate, narg) \
rb->screens[get_screen(luastate, narg)]
#define RB_SCREENS(luastate, narg, func, ...) \
rb->screens[get_screen(luastate, narg)]->func(__VA_ARGS__)
@ -1240,6 +1242,8 @@ static int get_screen(lua_State *L, int narg)
return screen;
}
#else /* only SCREEN_MAIN exists */
#define RB_SCREEN_STRUCT(luastate, narg) \
rb->screens[SCREEN_MAIN]
#define RB_SCREENS(luastate, narg, func, ...) \
rb->screens[SCREEN_MAIN]->func(__VA_ARGS__)
#endif
@ -1376,7 +1380,6 @@ RB_WRAP(font_getstringsize)
}
#ifdef HAVE_LCD_BITMAP
RB_WRAP(lcd_framebuffer)
{
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*
*h = luaL_checkint(L, narg + 3);
}
RB_WRAP(gui_scrollbar_draw)
{
int x, y, width, height;
get_rect_bounds(L, 1, &x, &y, &width, &height);
int items = luaL_checkint(L, 5);
int min_shown = luaL_checkint(L, 6);
int max_shown = luaL_checkint(L, 7);
unsigned flags = (unsigned) luaL_checkint(L, 8);
rb->gui_scrollbar_draw(RB_SCREEN_STRUCT(L, 9), x, y, width, height,
items, min_shown, max_shown, flags);
return 0;
}
RB_WRAP(lcd_mono_bitmap_part)
{
struct rocklua_image *src = rli_checktype(L, 1);
@ -1644,6 +1660,7 @@ static const luaL_Reg rocklib_img[] =
#ifdef HAVE_LCD_BITMAP
R(lcd_framebuffer),
R(lcd_setfont),
R(gui_scrollbar_draw),
R(lcd_mono_bitmap_part),
R(lcd_mono_bitmap),
#if LCD_DEPTH > 1

View file

@ -28,6 +28,8 @@
#include "luadir.h"
#include "rocklib_events.h"
static lua_State *Ls = NULL;
static int lu_status = 0;
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
@ -142,41 +144,77 @@ static int docall (lua_State *L) {
return status;
}
static void lua_atexit(void);
static int loadfile_newstate(lua_State **L, const char *filename)
{
*L = luaL_newstate();
rb_atexit(lua_atexit);
rocklua_openlibs(*L);
return luaL_loadfile(*L, filename);
}
static void lua_atexit(void)
{
char *filename;
if(Ls && lua_gettop(Ls) > 1)
{
if (Ls == lua_touserdata(Ls, -1)) /* signal from restart_lua */
{
filename = (char *) malloc(MAX_PATH);
if (filename) /* out of memory? */
rb->strlcpy(filename, lua_tostring(Ls, -2), MAX_PATH);
lua_close(Ls); /* close old state */
lu_status = loadfile_newstate(&Ls, filename);
free(filename);
plugin_start(NULL);
}
else if (lua_tointeger(Ls, -1) != 0) /* os.exit */
{
lu_status = LUA_ERRRUN;
lua_pop(Ls, 1); /* put exit string on top of stack */
plugin_start(NULL);
}
}
_exit(0); /* don't call exit handler */
}
/***************** Plugin Entry Point *****************/
enum plugin_status plugin_start(const void* parameter)
{
const char* filename;
int status;
if (parameter == NULL)
{
if (!Ls)
rb->splash(HZ, "Play a .lua file!");
return PLUGIN_ERROR;
}
else
{
filename = (char*) parameter;
lu_status = loadfile_newstate(&Ls, filename);
}
lua_State *L = luaL_newstate();
rocklua_openlibs(L);
status = luaL_loadfile(L, filename);
if (!status) {
if (Ls)
{
if (!lu_status) {
rb->lcd_scroll_stop(); /* rb doesn't like bg change while scroll */
rb->lcd_clear_display();
status = docall(L);
lu_status= docall(Ls);
}
if (status) {
DEBUGF("%s\n", lua_tostring(L, -1));
rb->splashf(5 * HZ, "%s", lua_tostring(L, -1));
lua_pop(L, 1);
if (lu_status) {
DEBUGF("%s\n", lua_tostring(Ls, -1));
rb->splash(5 * HZ, lua_tostring(Ls, -1));
/*lua_pop(Ls, 1);*/
}
lua_close(L);
lua_close(Ls);
}
else
return PLUGIN_ERROR;
return PLUGIN_OK;
}

View file

@ -0,0 +1,161 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
local scrpath = rb.current_path() .. "/lua_scripts/"
package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path
require("printtable")
rb.actions = nil
package.loaded["actions"] = nil
local excludedsrc = ";filebrowse.lua;fileviewers.lua;printmenu.lua;dbgettags.lua;"
--------------------------------------------------------------------------------
local function get_files(path, norecurse, finddir, findfile, f_t, d_t)
local quit = false
local files = f_t or {}
local dirs = d_t or {}
local function f_filedir(name)
--default find function
-- example: return name:find(".mp3", 1, true) ~= nil
if name:len() <= 2 and (name == "." or name == "..") then
return false
end
if string.find(excludedsrc, ";" .. name .. ";") then
return false
end
if string.sub(name, -4) == ".lua" then
return true
end
return false
end
local function d_filedir(name)
--default discard function
return false
end
if finddir == nil then
finddir = f_filedir
elseif type(finddir) ~= "function" then
finddir = d_filedir
end
if findfile == nil then
findfile = f_filedir
elseif type(findfile) ~= "function" then
findfile = d_filedir
end
local function _get_files(path, cancelbtn)
local sep = ""
if string.sub(path, - 1) ~= "/" then sep = "/" end
for fname, isdir in luadir.dir(path) do
if isdir and finddir(fname) then
table.insert(dirs, path .. sep ..fname)
elseif not isdir and findfile(fname) then
table.insert(files, path .. sep ..fname)
end
if rb.get_plugin_action(0) == cancelbtn then
return true
end
end
end
local function cmp_alphanum (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
if type1 == "string" then
op1 = op1:upper()
op2 = op2:upper()
end
return op1 < op2
end
end
table.insert(dirs, path) -- root
for key,value in pairs(dirs) do
--luadir.dir may error out so we need to do the call protected
_, quit = pcall(_get_files, value, CANCEL_BUTTON)
if quit == true or norecurse then
break;
end
end
table.sort(files, cmp_alphanum)
table.sort(dirs, cmp_alphanum)
return dirs, files
end -- get_files
--------------------------------------------------------------------------------
-- uses print_table and get_files to display simple file browser
function script_choose(dir, title)
local dstr
local hstr = title
local norecurse = true
local f_finddir = false -- function to match directories; nil all, false none
local f_findfile = nil -- function to match files; nil all, false none
local p_settings = {wrap = true, hasheader = true}
local files = {}
local dirs = {}
local item = 1
rb.lcd_clear_display()
while item > 0 do
dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files)
for i=1, #dirs do dirs[i] = nil end -- empty table for reuse
table.insert(dirs, 1, hstr)
for i = 1, #files do
table.insert(dirs, "\t" .. string.gsub(files[i], ".*/",""))
end
item = print_table(dirs, #dirs, p_settings)
-- If item was selected follow directory or return filename
if item > 0 then
dir = files[item - 1]
if not rb.dir_exists("/" .. dir) then
return dir
end
end
end
end -- file_choose
--------------------------------------------------------------------------------
local script_path = script_choose(scrpath, "lua scripts")
if script_path then rb.restart_lua(script_path) end

View file

@ -0,0 +1,116 @@
--dbgettags.lua Bilgus 2018
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
require("actions")
local CANCEL_BUTTON = rb.actions.PLA_CANCEL
local sINVALIDDATABASE = "Invalid Database"
local sERROROPENING = "Error opening"
-- tag cache header
local sTCVERSION = string.char(0x0F)
local sTCHEADER = string.reverse("TCH" .. sTCVERSION)
local DATASZ = 4 -- int32_t
local TCHSIZE = 3 * DATASZ -- 3 x int32_t
local function bytesLE_n(str)
str = str or ""
local tbyte={str:byte(1, -1)}
local bpos = 1
local num = 0
for k = 1,#tbyte do -- (k = #t, 1, -1 for BE)
num = num + tbyte[k] * bpos
bpos = bpos * 256
end
return num
end
-- uses database files to retrieve database tags
-- adds all unique tags into a lua table
function get_tags(filename, hstr)
if not filename then return end
hstr = hstr or filename
local file = io.open('/' .. filename or "", "r") --read
if not file then rb.splash(100, sERROROPENING .. " " .. filename) return end
local fsz = file:seek("end")
local posln = 0
local tag_len = TCHSIZE
local idx
local function readchrs(count)
if posln >= fsz then return nil end
file:seek("set", posln)
posln = posln + count
return file:read(count)
end
local tagcache_header = readchrs(DATASZ) or ""
local tagcache_sz = readchrs(DATASZ) or ""
local tagcache_entries = readchrs(DATASZ) or ""
if tagcache_header ~= sTCHEADER or
bytesLE_n(tagcache_sz) ~= (fsz - TCHSIZE) then
rb.splash(100, sINVALIDDATABASE .. " " .. filename)
return
end
-- local tag_entries = bytesLE_n(tagcache_entries)
local ftable = {}
table.insert(ftable, 1, hstr)
local tline = #ftable + 1
ftable[tline] = ""
local str = ""
while true do
tag_len = bytesLE_n(readchrs(DATASZ))
readchrs(DATASZ) -- idx = bytesLE_n(readchrs(DATASZ))
str = readchrs(tag_len) or ""
str = string.match(str, "(%Z+)%z")
if str then
if ftable[tline - 1] ~= str then -- Remove dupes
ftable[tline] = str
tline = tline + 1
end
elseif posln >= fsz then
break
end
if rb.get_plugin_action(0) == CANCEL_BUTTON then
break
end
end
file:close()
return ftable
end -- get_tags

View file

@ -0,0 +1,107 @@
--Bilgus 12-2016
--revisited 8-2019
require "actions"
require "buttons"
require "sound"
require "audio"
TIMEOUT = 0
local SOUND_VOLUME = rb.sound_settings.SOUND_VOLUME
rb.sound_settings = nil
package.loaded["sound_defines"] = nil
function say_msg(message, timeout)
rb.splash(1, message)
rb.sleep(timeout * rb.HZ)
end
function say_value(value,message,timeout)
local message = string.format(message .. "%d", value)
say_msg(message, timeout)
end
function ShowMainMenu() -- we invoke this function every time we want to display the main menu of the script
local s = 0
local mult = 1
local unit = " Minutes"
while s == 0 or s == 5 do -- don't exit of program until user selects Exit
if mult < 1 then
mult = 1
s = 0
end
mainmenu = {"More", mult * 1 .. unit, mult * 5 .. unit, mult * 10 .. unit, mult * 15 .. unit, "Less", "Exit"} -- define the items of the menu
s = rb.do_menu("Reduce volume + sleep over", mainmenu, s, false) -- actually tell Rockbox to draw the menu
-- In the line above: "Test" is the title of the menu, mainmenu is an array with the items
-- of the menu, nil is a null value that needs to be there, and the last parameter is
-- whether the theme should be drawn on the menu or not.
-- the variable s will hold the index of the selected item on the menu.
-- the index is zero based. This means that the first item is 0, the second one is 1, etc.
if s == 0 then mult = mult + 1
elseif s == 1 then TIMEOUT = mult
elseif s == 2 then TIMEOUT = mult * 5
elseif s == 3 then TIMEOUT = mult * 10
elseif s == 4 then TIMEOUT = mult * 15
elseif s == 5 then mult = mult - 1 -- User selected to exit
elseif s == 6 then os.exit() -- User selected to exit
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).
-- In this case, user probably wants to exit (or go back to last menu).
else rb.splash(2 * rb.HZ, "Error! Selected index: " .. s) -- something strange happened. The program shows this message when
-- the selected item is not on the index from 0 to 3 (in this case), and displays
-- the selected index. Having this type of error handling is not
-- required, but it might be nice to have Especially while you're still
-- developing the plugin.
end
end
end
ShowMainMenu()
rb.set_sleeptimer_duration(TIMEOUT)
rb.lcd_clear_display()
rb.lcd_update()
local volume = rb.sound_current(SOUND_VOLUME)
local vol_min = rb.sound_min(SOUND_VOLUME)
local volsteps = -(vol_min - volume)
local seconds = (TIMEOUT * 60) / volsteps
local sec_left = (TIMEOUT * 60)
local hb = 0
local action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
if rb.audio_status() == 1 then
while ((volume > vol_min) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
local i = seconds * 2
while ((i > 0) and (action ~= rb.actions.ACTION_STD_CANCEL)) do
i = i - 1
rb.lcd_drawline(hb, 1, hb, 1)
rb.lcd_update()
if hb >= rb.LCD_WIDTH then
hb = 0
rb.lcd_clear_display()
say_value(volume,sec_left .. " Sec, Volume: ", 1)
end
hb = hb + 1
rb.sleep(rb.HZ / 2)
action = rb.get_action(rb.contexts.CONTEXT_STD, 0)
rb.yield()
end
volume = volume - 1
rb.sound_set(SOUND_VOLUME, volume);
sec_left = sec_left - seconds
end
rb.audio_stop()
rb.lcd_clear_display()
rb.lcd_update()
os.exit(1, "Playback Stopped")
else
rb.lcd_clear_display()
rb.lcd_update()
os.exit(2, "Nothing is playing")
end

View file

@ -0,0 +1,190 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end
require("printtable")
local _lcd = require("lcd")
local _timer = require("timer")
--------------------------------------------------------------------------------
--[[ returns a sorted tables of directories and (another) of files
-- path is the starting path; norecurse == true.. only that path will be searched
-- findfile & finddir are definable search functions
-- if not defined all files/dirs are returned if false is passed.. none
-- or you can provide your own function see below..
-- f_t and d_t allow you to pass your own tables for re-use but isn't necessary
]]
local function get_files(path, norecurse, finddir, findfile, f_t, d_t)
local quit = false
local files = f_t or {}
local dirs = d_t or {}
local function f_filedir(name)
--default find function
-- example: return name:find(".mp3", 1, true) ~= nil
if name:len() <= 2 and (name == "." or name == "..") then
return false
end
return true
end
local function d_filedir(name)
--default discard function
return false
end
if finddir == nil then
finddir = f_filedir
elseif type(finddir) ~= "function" then
finddir = d_filedir
end
if findfile == nil then
findfile = f_filedir
elseif type(findfile) ~= "function" then
findfile = d_filedir
end
local function _get_files(path, cancelbtn)
local sep = ""
if string.sub(path, - 1) ~= "/" then sep = "/" end
for fname, isdir in luadir.dir(path) do
if isdir and finddir(fname) then
table.insert(dirs, path .. sep ..fname)
elseif not isdir and findfile(fname) then
table.insert(files, path .. sep ..fname)
end
if rb.get_plugin_action(0) == cancelbtn then
return true
end
end
end
local function cmp_alphanum (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
if type1 == "string" then
op1 = op1:upper()
op2 = op2:upper()
end
return op1 < op2
end
end
_lcd:splashf(1, "Searching for Files")
table.insert(dirs, path) -- root
for key,value in pairs(dirs) do
--luadir.dir may error out so we need to do the call protected
_, quit = pcall(_get_files, value, CANCEL_BUTTON)
if quit == true or norecurse then
break;
end
end
table.sort(files, cmp_alphanum)
table.sort(dirs, cmp_alphanum)
return dirs, files
end -- get_files
--------------------------------------------------------------------------------
-- uses print_table and get_files to display simple file browser
function file_choose(dir, title)
local dstr, hstr = ""
if not title then
dstr = "%d items found in %0d.%02d seconds"
else
hstr = title
end
-- returns whole seconds and remainder
local function tick2seconds(ticks)
local secs = (ticks / rb.HZ)
local csecs = (ticks - (secs * rb.HZ))
return secs, csecs
end
local norecurse = true
local f_finddir = nil -- function to match directories; nil all, false none
local f_findfile = nil -- function to match files; nil all, false none
local p_settings = {wrap = true, hasheader = true}
local timer
local files = {}
local dirs = {}
local item = 1
_lcd:clear()
while item > 0 do
if not title then
timer = _timer()
end
dirs, files = get_files(dir, norecurse, f_finddir, f_findfile, dirs, files)
local parentdir = dirs[1]
for i = 1, #dirs do
dirs[i] = "\t" .. dirs[i]
end
for i = 1, #files do
table.insert(dirs, "\t" .. files[i])
end
for i=1, #files do files[i] = nil end -- empty table for reuse
if not title then
hstr = string.format(dstr, #dirs - 1, tick2seconds(timer:stop()))
end
table.insert(dirs, 1, hstr)
item = print_table(dirs, #dirs, p_settings)
-- If item was selected follow directory or return filename
if item > 0 then
dir = string.gsub(dirs[item], "%c+","")
if not rb.dir_exists("/" .. dir) then
return dir
end
end
if dir == parentdir then
dir = dir:sub(1, dir:match(".*()/") - 1)
if dir == "" then dir = "/" end
end
for i=1, #dirs do dirs[i] = nil end -- empty table for reuse
end
end -- file_choose
--------------------------------------------------------------------------------

View file

@ -0,0 +1,79 @@
--[[
__________ __ ___.
Open \______ \ ____ ____ | | _\_ |__ _______ ___
Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
\/ \/ \/ \/ \/
$Id$
Example Lua File Viewer script
Copyright (C) 2017 William Wilgus
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
KIND, either express or implied.
]]--
require("actions") -- Contains rb.actions & rb.contexts
-- require("buttons") -- Contains rb.buttons -- not needed for this example
--local _timer = require("timer")
--local _clr = require("color") -- clrset, clrinc provides device independent colors
local _lcd = require("lcd") -- lcd helper functions
--local _print = require("print") -- advanced text printing
--local _img = require("image") -- image manipulation save, rotate, resize, tile, new, load
--local _blit = require("blit") -- handy list of blit operations
--local _draw = require("draw") -- draw all the things (primitives)
--local _math = require("math_ex") -- missing math sine cosine, sqrt, clamp functions
local scrpath = rb.current_path()--rb.PLUGIN_DIR .. "/demos/lua_scripts/"
package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path
require("printmenu") --menu
require("filebrowse") -- file browser
require("fileviewers") -- fileviewer, hexviewer
rb.actions = nil
package.loaded["actions"] = nil
-- uses print_table to display a menu
function main_menu()
local mt = {
[1] = "Rocklua File View Example",
[2] = "File View",
[3] = "File Hex View",
[4] = "Simple Browser",
[5] = "Exit"
}
local ft = {
[0] = exit_now, --if user cancels do this function
[1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot
[2] = function(VIEWF) -- view file
print_file_increment(file_choose("/", "Choose File"))
end,
[3] = function(VHEXF) -- view hex
print_file_hex(file_choose("/", "Choose File"), 8)
end,
[4] = function(BROWS) -- file browser
_lcd:splashf(rb.HZ, "%s", file_choose("/") or "None")
end,
[5] = function(EXIT_) return true end
}
print_menu(mt, ft)
end
function exit_now()
_lcd:update()
os.exit()
end -- exit_now
main_menu()
exit_now()

View file

@ -0,0 +1,465 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
if ... == nil then rb.splash(rb.HZ * 3, "use 'require'") end
require("printtable")
local _clr = require("color")
local _lcd = require("lcd")
local _print = require("print")
local _timer = require("timer")
require("actions")
local CANCEL_BUTTON = rb.actions.PLA_CANCEL
--------------------------------------------------------------------------------
-- builds an index of byte position of every line at each bufsz increment
-- in filename; bufsz == 1 would be every line; saves to filename.ext.idx_ext
-- lnbyte should be nil for text files and number of bytes per line for binary
local function build_file_index(filename, idx_ext, bufsz, lnbyte)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local fsz = file:seek("end")
local fsz_kb = fsz / 1024
local count
local ltable = {0} --first index is the beginning of the file
local timer = _timer()
local fread
_lcd:splashf(100, "Indexing file %d Kb", (fsz / 1024))
if lnbyte then
fread = function(f) return f:read(lnbyte) end
else
lnbyte = -1
fread = function(f) return f:read("*l") end
end
file:seek("set", 0)
for i = 1, fsz do
if i % bufsz == 0 then
local loc = file:seek()
ltable[#ltable + 1] = loc
_lcd:splashf(1, "Parsing %d of %d Kb", loc / 1024, fsz_kb)
end
if rb.get_plugin_action(0) == CANCEL_BUTTON then
return
end
if not fread(file) then
count = i
break
end
end
local fileidx = io.open('/' .. filename .. idx_ext, "w+") -- write/erase
if fileidx then
fileidx:write(fsz .. "\n")
fileidx:write(count .. "\n")
fileidx:write(bufsz .. "\n")
fileidx:write(lnbyte .. "\n")
fileidx:write(table.concat(ltable, "\n"))
fileidx:close()
_lcd:splashf(100, "Finished in %d seconds", timer.stop() / rb.HZ)
collectgarbage("collect")
else
error("unable to save index file")
end
end -- build_file_index
--------------------------------------------------------------------------------
--- returns size of original file, total lines buffersize, and table filled
-- with line offsets in index file -> filename
local function load_index_file(filename)
local filesz, count, bufsz, lnbyte
local ltable
local fileidx = io.open('/' .. filename, "r") --read
if fileidx then
local idx = -3
ltable = {}
fileidx:seek("set", 0)
for line in fileidx:lines() do
if idx == -3 then
filesz = tonumber(line)
elseif idx == -2 then
count = tonumber(line)
elseif idx == -1 then
bufsz = tonumber(line)
elseif idx == 0 then
lnbyte = tonumber(line)
else
ltable[idx] = tonumber(line)
end
idx = idx + 1
end
fileidx:close()
end
return lnbyte, filesz, count, bufsz, ltable
end -- load_index_file
--------------------------------------------------------------------------------
-- creates a fixed index with fixed line lengths, perfect for viewing hex files
-- not so great for reading text files but works as a fallback
local function load_fixed_index(bytesperline, filesz, bufsz)
local lnbyte = bytesperline
local count = (filesz + lnbyte - 1) / lnbyte + 1
local idx_t = {} -- build index
for i = 0, filesz, bufsz do
idx_t[#idx_t + 1] = lnbyte * i
end
return lnbyte, filesz, count, bufsz, idx_t
end -- load_fixed_index
--------------------------------------------------------------------------------
-- uses print_table to display a whole file
function print_file(filename, maxlinelen, settings)
if not filename then return end
local file = io.open('/' .. filename or "", "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
maxlinelen = 33
local hstr = filename
local ftable = {}
table.insert(ftable, 1, hstr)
local tline = #ftable + 1
local remln = maxlinelen
local posln = 1
for line in file:lines() do
if line then
if maxlinelen then
if line == "" then
ftable[tline] = ftable[tline] or ""
tline = tline + 1
remln = maxlinelen
else
line = line:match("%w.+") or ""
end
local linelen = line:len()
while linelen > 0 do
local fsp = line:find("%s", posln + remln - 5) or 0x0
fsp = fsp - (posln + remln)
if fsp >= 0 then
local fspr = fsp
fsp = line:find("%s", posln + remln) or linelen
fsp = fsp - (posln + remln)
if math.abs(fspr) < fsp then fsp = fspr end
end
if fsp > 5 or fsp < -5 then fsp = 0 end
local str = line:sub(posln, posln + remln + fsp)
local slen = str:len()
ftable[tline] = ftable[tline] or ""
ftable[tline] = ftable[tline] .. str
linelen = linelen - slen
if linelen > 0 then
tline = tline + 1
posln = posln + slen
remln = maxlinelen
--loop continues
else
ftable[tline] = ftable[tline] .. " "
remln = maxlinelen - slen
posln = 1
--loop ends
end
end
else
ftable[#ftable + 1] = line
end
end
end
file:close()
_lcd:clear()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = true
end
settings.hasheader = true
settings.co_routine = nil
local sel =
print_table(ftable, #ftable, settings)
_lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", "))
ftable = nil
end -- print_file
--------------------------------------------------------------------------------
-- uses print_table to display a portion of a file
function print_file_increment(filename, settings)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local fsz = file:seek("end")
local bsz = 1023
--if small file do it the easier way and load whole file to table
if fsz < 60 * 1024 then
file:close()
print_file(filename, settings)
return
end
local ext = ".idx"
local lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext)
if not idx_t or fsz ~= filesz then -- build file index
build_file_index(filename, ext, bsz)
lnbyte, filesz, count, bufsz, idx_t = load_index_file(filename .. ext)
end
-- if invalid or user canceled creation fallback to a fixed index
if not idx_t or fsz ~= filesz or count <= 0 then
_lcd:splashf(rb.HZ * 5, "Unable to read file index %s", filename .. ext)
lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(32, fsz, bsz)
end
if not idx_t or fsz ~= filesz or count <= 0 then
_lcd:splashf(rb.HZ * 5, "Unable to load file %s", filename)
return
end
local hstr = filename
local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values
-- this allows them to be garbage collected as space is needed
-- rebuilds when needed
local ovf = 0
local lpos = 1
local timer = _timer()
file:seek("set", 0)
function print_co()
while true do
collectgarbage("step")
file_t[1] = hstr --position 1 is ALWAYS header/title
for i = 1, bufsz + ovf do
file_t[lpos + i] = file:read ("*l")
end
ovf = 0
lpos = lpos + bufsz
local bpos = coroutine.yield()
if bpos <= lpos then -- roll over or scroll up
bpos = (bpos - bufsz) + bpos % bufsz
timer:check(true)
end
lpos = bpos - bpos % bufsz
if lpos < 1 then
lpos = 1
elseif lpos > count - bufsz then -- partial fill
ovf = count - bufsz - lpos
end
--get position in file of the nearest indexed line
file:seek("set", idx_t[bpos / bufsz + 1])
-- on really large files if it has been more than 10 minutes
-- since the user scrolled up the screen wipe out the prior
-- items to free memory
if lpos % 5000 == 0 and timer:check() > rb.HZ * 600 then
for i = 1, lpos - 100 do
file_t[i] = nil
end
end
end
end
co = coroutine.create(print_co)
_lcd:clear()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
end
settings.hasheader = true
settings.co_routine = co
settings.msel = false
table.insert(file_t, 1, hstr) --position 1 is header/title
local sel =
print_table(file_t, count, settings)
file:close()
idx_t = nil
file_t = nil
return sel
end --print_file_increment
--------------------------------------------------------------------------------
function print_file_hex(filename, bytesperline, settings)
if not filename then return end
local file = io.open('/' .. filename, "r") --read
if not file then _lcd:splashf(100, "Can't open %s", filename) return end
local hstr = filename
local bpl = bytesperline
local fsz = file:seek("end")
--[[
local filesz = file:seek("end")
local bufsz = 1023
local lnbyte = bytesperline
local count = (filesz + lnbyte - 1) / lnbyte + 1
local idx_t = {} -- build index
for i = 0, filesz, bufsz do
idx_t[#idx_t + 1] = lnbyte * i
end]]
local lnbyte, filesz, count, bufsz, idx_t = load_fixed_index(bpl, fsz, 1023)
local file_t = setmetatable({},{__mode = "kv"}) --weak keys and values
-- this allows them to be garbage collected as space is needed
-- rebuilds when needed
local ovf = 0
local lpos = 1
local timer = _timer()
file:seek("set", 0)
function hex_co()
while true do
collectgarbage("step")
file_t[1] = hstr --position 1 is ALWAYS header/title
for i = 1, bufsz + ovf do
local pos = file:seek()
local s = file:read (lnbyte)
if not s then -- EOF
file_t[lpos + i] = ""
break;
end
local s_len = s:len()
if s_len > 0 then
local fmt = "0x%04X: " .. string.rep("%02X ", s_len)
local schrs = " " .. s:gsub("(%c)", " . ")
file_t[lpos + i] = string.format(fmt, pos, s:byte(1, s_len)) ..
schrs
else
file_t[lpos + i] = string.format("0x%04X: ", pos)
end
end
ovf = 0
lpos = lpos + bufsz
local bpos = coroutine.yield()
if bpos < lpos then -- roll over or scroll up
bpos = (bpos - bufsz) + bpos % bufsz
timer:check(true)
end
lpos = bpos - bpos % bufsz
if lpos < 1 then
lpos = 1
elseif lpos > count - bufsz then -- partial fill
ovf = count - bufsz - lpos
end
--get position in file of the nearest indexed line
file:seek("set", idx_t[bpos / bufsz + 1])
-- on really large files if it has been more than 10 minutes
-- since the user scrolled up the screen wipe out the prior
-- items to free memory
if lpos % 10000 == 0 and timer:check() > rb.HZ * 600 then
for i = 1, lpos - 100 do
file_t[i] = nil
end
end
end
end
co = coroutine.create(hex_co)
local function repl(char)
local ret = ""
if char:sub(1,2) == "0x" then
return string.format("%dd:", tonumber(char:sub(3, -2), 16))
else
return string.format("%03d ", tonumber(char, 16))
end
end
_lcd:clear()
_print.clear()
local sel, start, vcur = 1
table.insert(file_t, 1, hstr) --position 1 is header/title
if not settings then
settings = {}
settings.justify = "left"
settings.wrap = true
settings.msel = false
settings.hfgc = _clr.set( 0, 000, 000, 000)
settings.hbgc = _clr.set(-1, 255, 255, 255)
settings.ifgc = _clr.set(-1, 255, 255, 255)
settings.ibgc = _clr.set( 0, 000, 000, 000)
settings.iselc = _clr.set( 1, 000, 200, 100)
end
settings.hasheader = true
settings.co_routine = co
settings.start = start
settings.curpos = vcur
while sel > 0 do
settings.start = start
settings.curpos = vcur
sel, start, vcur = print_table(file_t, count, settings)
if sel > 1 and file_t[sel] then -- flips between hex and decimal
local s = file_t[sel]
if s:sub(-1) == "\b" then
file_t[sel] = nil
ovf = -(bufsz - 1)
coroutine.resume(co, sel) --rebuild this item
else
s = s:gsub("(0x%x+:)", repl) .. "\b"
file_t[sel] = s:gsub("(%x%x%s)", repl) .. "\b"
end
end
end
file:close()
idx_t = nil
file_t = nil
return sel
end -- print_file_hex
--------------------------------------------------------------------------------

View file

@ -0,0 +1,24 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id$
#
LUASCR_SRCDIR := $(APPSDIR)/plugins/lua_scripts
LUASCR_BUILDDIR := $(BUILDDIR)/apps/plugins/lua_scripts
LUASCRS := $(wildcard $(LUASCR_SRCDIR)/*.lua)
#DUMMY := $(info [${LUASCRS}])
DUMMY : all
all: $(subst $(LUASCR_SRCDIR)/,$(LUASCR_BUILDDIR)/,$(LUASCRS))
$(LUASCR_BUILDDIR)/%.lua: $(LUASCR_SRCDIR)/%.lua | $(LUASCR_BUILDDIR)
$(call PRINTS,CP $(subst $(LUASCR_SRCDIR)/,,$<))cp $< $@
$(LUASCR_BUILDDIR):
$(call PRINTS,MKDIR $@)mkdir -p $(LUASCR_BUILDDIR)/

View file

@ -0,0 +1,304 @@
--RB LUA show all global variables; BILGUS
require "actions"
require "audio"
require "buttons"
require "color"
require "draw"
require "image"
require "lcd"
require "math_ex"
require "pcm"
require "playlist"
require "print"
--require "settings" --uses a lot of memory
require "sound"
collectgarbage("collect")
local sDumpFile = "/rb-lua_functions.txt"
local filehandle
local function a2m_m2a(addr_member)
--turns members into addresses; addresses back into members
return addr_member
end
local function dtTag(sType)
--convert named type; 'number'.. to short type '[n]...'
--if '?' supplied print out datatype key; number = [n]...
local retType = "?"
local typ = {
["nil"] = "nil",
["boolean"] = "b",
["number"] = "n",
["string"] = "s",
["userdata"] = "u",
["function"] = "f",
["thread"] = "thr",
["table"] = "t"
}
if sType == "?" then retType = "Datatypes: " end
for k,v in pairs(typ) do
if sType == k then
retType = v break
elseif (sType == "?") then
retType = retType .. " [" ..v.. "] = " .. k
end
end
return " [" ..retType.. "] "
end
local function tableByName(tName)
--find the longest match possible to an actual table
--Name comes in as (table) tName.var so we can pass back out the name found PITA
--returns the table found (key and value)
local ld = {}
local sMatch = ""
local kMatch = nil
local vMatch = nil
----FUNCTIONS for tableByName -----------------------------------------------------
local function search4Str(n, k, v)
local sKey = tostring(k)
if string.find (n, sKey,1,true) then
if sKey:len() > sMatch:len() then sMatch = sKey kMatch = k vMatch = v end
--find the longest match we can
end
end
----END FUNCTIONS for tableByName -------------------------------------------------
if tName.val ~= nil and tName.val ~= "" then
for k, v in pairs(_G) do
--_G check both since some tables are only in _G or package.loaded
search4Str(tName.val, k, v)
end
for k, v in pairs(package.loaded) do --package.loaded
search4Str(tName.val, k, v)
end
if not string.find (sMatch, "_G",1,true) then sMatch = "_G." .. sMatch end
-- put the root _G in if not exist
if kMatch and vMatch then ld[kMatch] = vMatch tName.val = sMatch return ld end
end
tName.val = "_G"
return package.loaded --Not Found return default
end
local function dump_Tables(tBase, sFunc, tSeen, tRet)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--Recurse through tBase tables copying all found Tables
local sSep=""
local ld={}
local tNameBuf = {}
local sName
if sFunc ~= "" then sSep = "." end
for k, v in pairs(tBase) do
k = tostring(k)
tNameBuf[1] = sFunc
tNameBuf[2] = sSep
tNameBuf[3] = k
if k ~= "loaded" and type(v) == "table" and not tSeen[v] then
tSeen[v]=sFunc
sName = table.concat(tNameBuf)
tRet[sName] = a2m_m2a(v) --place all keys into ld[i]=value
dump_Tables(v, sName, tSeen, tRet)
elseif type(v) == "table" and not tSeen[v] then
tSeen[v]=sFunc
tRet[table.concat(tNameBuf)] = a2m_m2a(v) -- dump 'loaded' table
for k1, v1 in pairs(v) do
if not _G[k1] and type(v1) == "table" and not tSeen[v1] then
-- dump tables that are loaded but not global
tSeen[v1]=sFunc
tNameBuf[3] = k1
sName = table.concat(tNameBuf)
tRet[sName] = a2m_m2a(v1) --place all keys into ld[i]=value
dump_Tables(v1, sName, tSeen, tRet)
end
end
end
end
end
local function dump_Functions(tBase)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--We already recursed through tBase copying all found tables
--we look up the table by name and then (ab)use a2m_m2a() to load the address
--after finding the table by address in tBase we will
--put the table address of tFuncs in its place
local tFuncBuf = {}
for k,v in pairs(tBase) do
local tTable = a2m_m2a(v)
local tFuncs = {}
for key, val in pairs(tTable) do
if key ~= "loaded" then
tFuncBuf[1] = dtTag(type(val))
tFuncBuf[2] = tostring(key)
tFuncs[table.concat(tFuncBuf)]= val
--put the name and value in our tFuncs table
end
end
tBase[k] = a2m_m2a(tFuncs) -- copy the address back to tBase
end
end
local function get_common_branches(t, tRet)
--load t 'names(values)' into keys
--strip off long paths then iterate value if it exists
--local tRet={}
local sBranch = ""
local tName = {}
for k in pairs(t) do
tName["val"]=k
tableByName(tName)
sBranch = tName.val
if tRet[sBranch] == nil then
tRet[sBranch] = 1 --first instance of this branch
else
tRet[sBranch] = tRet[sBranch] + 1
end
end
end
local function pairsByPairs (t, tkSorted)
--tkSorted should be an already sorted (i)table with t[keys] in the values
--https://www.lua.org/pil/19.3.html
--!!Note: table sort default function does not like numbers as [KEY]!!
--see *sortbyKeys*cmp_alphanum*
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if tkSorted[i] == nil then return nil
else return tkSorted[i], t[tkSorted[i]]
end
end
return iter
end
local function sortbyKeys(t, tkSorted)
--loads keys of (t) into values of tkSorted
--and then sorts them
--tkSorted has integer keys (see ipairs)
----FUNCTIONS for sortByKeys -------------
local cmp_alphanum = function (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
return op1 < op2
end
end
----END FUNCTIONS for sortByKeys ---------
for n in pairs(t) do table.insert(tkSorted, n) end
table.sort(tkSorted, cmp_alphanum)--table.sort(tkSorted)
end
local function funcprint(tBuf, strName, value)
local sType = type(value)
local sVal = ""
local sHex = ""
tBuf[#tBuf + 1] = "\t"
tBuf[#tBuf + 1] = strName
if nil ~= string.find (";string;number;userdata;boolean;", sType, 1, true) then
--If any of the above types print the contents of variable
sVal = tostring(value)
if type(value) == "number" then
sHex = " = 0x" .. string.format("%x", value)
else
sHex = ""
sVal = string.gsub(sVal, "\n", "\\n") --replace newline with \n
end
tBuf[#tBuf + 1] = " : "
tBuf[#tBuf + 1] = sVal
tBuf[#tBuf + 1] = sHex
end
tBuf[#tBuf + 1] = "\r\n"
end
local function errorHandler( err )
filehandle:write(" ERROR:" .. err .. "\n")
end
------------MAIN----------------------------------------------------------------
local _NIL = nil
local tSeen= {}
local tcBase = {}
local tkSortCbase = {}
local tMods= {}
local tkSortMods = {}
local tWriteBuf = {}
local n = 0 -- count of how many items were found
filehandle = io.open(sDumpFile, "w+") --overwrite
tWriteBuf[#tWriteBuf + 1] = "*Loaded Modules* \n"
xpcall( function()
dump_Tables(tableByName({["val"] = "_G"}),"", tSeen, tMods)
--you can put a table name here if you just wanted to display
--only its items, ex. "os" or "rb" or "io"
--However, it has to be accessible directly from _G
--so "rb.actions" wouldn't return anything since its technically
--enumerated through _G.rb
end , errorHandler )
tSeen = nil
xpcall( function()dump_Functions(tMods)end , errorHandler )
get_common_branches(tMods, tcBase)
sortbyKeys(tcBase, tkSortCbase)
sortbyKeys(tMods, tkSortMods)
for k, v in pairsByPairs(tcBase, tkSortCbase ) do
n = n + 1
if n ~= 1 then
tWriteBuf[#tWriteBuf + 1] = ", "
end
tWriteBuf[#tWriteBuf + 1] = tostring(k)
if n >= 3 then -- split loaded modules to multiple lines
n = 0
tWriteBuf[#tWriteBuf + 1] = "\r\n"
end
if #tWriteBuf > 25 then
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
end
end
tcBase= nil tkSortCbase= nil
tWriteBuf[#tWriteBuf + 1] = "\r\n"
tWriteBuf[#tWriteBuf + 1] = dtTag("?")
tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
tWriteBuf[#tWriteBuf + 1] = "Functions: \r\n"
n = 0
for key, val in pairsByPairs(tMods, tkSortMods) do
local tkSorted = {}
local tFuncs = a2m_m2a(val)
sortbyKeys(tFuncs, tkSorted)
tWriteBuf[#tWriteBuf + 1] = "\r\n"
tWriteBuf[#tWriteBuf + 1] = tostring(key)
tWriteBuf[#tWriteBuf + 1] = "\r\n"
for k, v in pairsByPairs(tFuncs, tkSorted) do
n = n + 1
funcprint(tWriteBuf, k,v)
if #tWriteBuf > 25 then
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- reuse table
end
end
end
tWriteBuf[#tWriteBuf + 1] = "\r\n\r\n"
tWriteBuf[#tWriteBuf + 1] = n
tWriteBuf[#tWriteBuf + 1] = " Items Found \r\n"
filehandle:write(table.concat(tWriteBuf))
for i=1, #tWriteBuf do tWriteBuf[i] = _NIL end -- empty table
filehandle:close()
rb.splash(rb.HZ * 5, n .. " Items dumped to : " .. sDumpFile)
--rb.splash(500, collectgarbage("count"))

View file

@ -0,0 +1,83 @@
--[[
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2017 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
]]
if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
require("printtable")
local _clr = require("color")
local _LCD = rb.lcd_framebuffer()
--------------------------------------------------------------------------------
-- displays text in menu_t calls function in same indice of func_t when selected
function print_menu(menu_t, func_t, selected, settings, copy_screen)
local i, start, vcur, screen_img
if selected then vcur = selected + 1 end
if vcur and vcur <= 1 then vcur = 2 end
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.hfgc = _clr.set( 0, 000, 000, 000)
settings.hbgc = _clr.set(-1, 255, 255, 255)
settings.ifgc = _clr.set(-1, 000, 255, 060)
settings.ibgc = _clr.set( 0, 000, 000, 000)
settings.iselc = _clr.set( 1, 000, 200, 100)
settings.default = true
end
settings.hasheader = true
settings.co_routine = nil
settings.msel = false
settings.start = start
settings.curpos = vcur
while not i or i > 0 do
if copy_screen == true then
--make a copy of screen for restoration
screen_img = screen_img or rb.new_image()
screen_img:copy(_LCD)
else
screen_img = nil
end
_LCD:clear(settings.ibgc)
settings.start = start
settings.curpos = vcur
i, start, vcur = print_table(menu_t, #menu_t, settings)
--vcur = vcur + 1
collectgarbage("collect")
if copy_screen == true then _LCD:copy(screen_img) end
if func_t and func_t[i] then
if func_t[i](i, menu_t) == true then break end
else
break
end
end
if settings.default == true then settings = nil end
return screen_img
end

View file

@ -0,0 +1,344 @@
-- BILGUS 2018
--local scrpath = rb.current_path()"
--package.path = scrpath .. "/?.lua;" .. package.path --add lua_scripts directory to path
require("printmenu")
require("printtable")
require("dbgettags")
local _print = require("print")
rb.actions = nil
package.loaded["actions"] = nil
local sERROROPENING = "Error opening"
local sERRORMENUENTRY = "Error finding menu entry"
local sBLANKLINE = "##sBLANKLINE##"
local sDEFAULTMENU = "customfilter"
local sFILEOUT = "/.rockbox/tagnavi_custom.config"
local sFILEHEADER = "#! rockbox/tagbrowser/2.0"
local sMENUSTART = "%menu_start \"custom\" \"Database\""
local sMENUTITLE = "title = \"fmt_title\""
local TAG_ARTIST, TAG_ALBARTIST, TAG_ALBUM, TAG_GENRE, TAG_COMPOSER = 1, 2, 3, 4, 5
local ts_TAGTYPE = {"Artist", "AlbumArtist", "Album", "Genre", "Composer"}
local ts_DBPATH = {"database_0.tcd", "database_7.tcd", "database_1.tcd", "database_2.tcd", "database_5.tcd"}
local COND_OR, COND_AND, COND_NOR, COND_NAND = 1, 2, 3, 4
local ts_CONDITIONALS = {"OR", "AND", "!, OR", "!, AND"}
local ts_CONDSYMBOLS = {"|", "&", "|", "&"}
local ts_YESNO = {"", "Yes", "No"}
local s_OVERWRITE = "Overwrite"
local s_EXISTS = "Exists"
local function question(tInquiry, start)
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = false
settings.hasheader = true
settings.co_routine = nil
settings.curpos = start or 1
local sel = print_table(tInquiry, #tInquiry, settings)
return sel
end
local function find_linepos(t_lines, search, startline)
startline = startline or 1
for i = startline, #t_lines do
if string.match (t_lines[i], search) then
return i
end
end
return -1
end
local function replacelines(t_lines, search, replace, startline)
startline = startline or 1
repcount = 0
for i = startline, #t_lines do
if string.match (t_lines[i], search) then
t_lines[i] = replace
repcount = repcount + 1
end
end
return repcount
end
local function replaceemptylines(t_lines, replace, startline)
startline = startline or 1
replace = replace or nil
repcount = 0
for i = startline, #t_lines do
if t_lines[i] == "" then
t_lines[i] = replace
repcount = repcount + 1
end
end
return repcount
end
local function checkexistingmenu(t_lines, menuname)
local pos = find_linepos(t_lines, "^\"" .. menuname .. "\"%s*%->.+")
local sel = 0
if pos > 0 then
ts_YESNO[1] = menuname .. " " .. s_EXISTS .. ", " .. s_OVERWRITE .. "?"
sel = question(ts_YESNO, 3)
if sel == 3 then
pos = nil
elseif sel < 2 then
pos = 0
end
else
pos = nil
end
return pos
end
local function savedata(filename, ts_tags, cond, menuname)
menuname = menuname or sDEFAULTMENU
local lines = {}
local curline = 0
local function lines_next(str, pos)
pos = pos or #lines + 1
lines[pos] = str or ""
curline = pos
end
local function lines_append(str, pos)
pos = pos or curline or #lines
lines[pos] = lines[pos] .. str or ""
end
if rb.file_exists(filename) ~= true then
lines_next(sFILEHEADER)
lines_next("#")
lines_next("# MAIN MENU")
lines_next(sMENUSTART)
else
local file = io.open(filename, "r") -- read
if not file then
rb.splash(rb.HZ, "Error opening" .. " " .. filename)
return
end
for line in file:lines() do
lines_next(line)
end
file:close()
end
local menupos = find_linepos(lines, sMENUSTART)
if menupos < 1 then
rb.splash(rb.HZ, sERRORMENUENTRY)
return
end
replaceemptylines(lines, sBLANKLINE, menupos)
local existmenupos = checkexistingmenu(lines, menuname)
if existmenupos and existmenupos < 1 then return end -- user canceled
local lastcond = ""
local n_cond = COND_OR
local tags, tagtype
local function buildtag(e_tagtype)
if ts_tags[e_tagtype] then
n_cond = (cond[e_tagtype] or COND_OR)
if e_tagtype > 1 then
lines_append(" " .. ts_CONDSYMBOLS[n_cond])
end
tags = ts_tags[e_tagtype]
tagtype = string.lower(ts_TAGTYPE[e_tagtype])
if n_cond <= COND_AND then
lines_append(" " .. tagtype)
lines_append(" @ \"".. table.concat(tags, "|") .. "\"")
else
for k = 1, #tags do
lines_append(" " .. tagtype)
lines_append(" !~ \"".. tags[k] .. "\"")
if k < #tags then lines_append(" &") end
end
end
end
end
if ts_tags[TAG_ARTIST] or ts_tags[TAG_ALBARTIST] or ts_tags[TAG_ALBUM] or
ts_tags[TAG_GENRE] or ts_tags[TAG_COMPOSER] then
lines_next("\"" .. menuname .. "\" -> " .. sMENUTITLE .. " ?", existmenupos)
buildtag(TAG_ARTIST)
buildtag(TAG_ALBARTIST)
buildtag(TAG_ALBUM)
buildtag(TAG_GENRE)
buildtag(TAG_COMPOSER)
lines_next("\n")
else
rb.splash(rb.HZ, "Nothing to save")
end
local file = io.open(filename, "w+") -- overwrite
if not file then
rb.splash(rb.HZ, "Error writing " .. filename)
return
end
for i = 1, #lines do
if lines[i] and lines[i] ~= sBLANKLINE then
file:write(lines[i], "\n")
end
end
file:close()
end
-- uses print_table to display database tags
local function print_tags(ftable, settings, t_selected)
if not s_cond then s_sep = "|" end
ftable = ftable or {}
if t_selected then
for k = 1, #t_selected do
ftable[t_selected[k]] = ftable[t_selected[k]] .. "\0"
end
end
rb.lcd_clear_display()
_print.clear()
if not settings then
settings = {}
settings.justify = "center"
settings.wrap = true
settings.msel = true
end
settings.hasheader = true
settings.co_routine = nil
local sel = print_table(ftable, #ftable, settings)
--_lcd:splashf(rb.HZ * 2, "%d items {%s}", #sel, table.concat(sel, ", "))
local selected = {}
local str = ""
for k = 1,#sel do
str = ftable[sel[k]] or ""
selected[#selected + 1] = string.sub(str, 1, -2) -- REMOVE \0
end
ftable = nil
if #sel == 0 then
return nil, nil
end
return sel, selected
end -- print_tags
-- uses print_table to display a menu
function main_menu()
local menuname = sDEFAULTMENU
local t_tags
local ts_tags = {}
local cond = {}
local sel = {}
local mt = {
[1] = "TagNav Customizer",
[2] = "", --ts_CONDITIONALS[cond[TAG_ARTIST] or COND_OR],
[3] = ts_TAGTYPE[TAG_ARTIST],
[4] = ts_CONDITIONALS[cond[TAG_ALBARTIST] or COND_OR],
[5] = ts_TAGTYPE[TAG_ALBARTIST],
[6] = ts_CONDITIONALS[cond[TAG_ALBUM] or COND_OR],
[7] = ts_TAGTYPE[TAG_ALBUM],
[8] = ts_CONDITIONALS[cond[TAG_GENRE] or COND_OR],
[9] = ts_TAGTYPE[TAG_GENRE],
[10] = ts_CONDITIONALS[cond[TAG_COMPOSER] or COND_OR],
[11] = ts_TAGTYPE[TAG_COMPOSER],
[12] = "Save to Tagnav",
[13] = "Exit"
}
local function sel_cond(item, item_mt)
cond[item] = cond[item] or 1
cond[item] = cond[item] + 1
if cond[item] > #ts_CONDITIONALS then cond[item] = 1 end
mt[item_mt] = ts_CONDITIONALS[cond[item]]
end
local function sel_tag(item, item_mt, t_tags)
t_tags = get_tags(rb.ROCKBOX_DIR .. "/" .. ts_DBPATH[item], ts_TAGTYPE[item])
sel[item], ts_tags[item] = print_tags(t_tags, nil, sel[item])
if ts_tags[item] then
mt[item_mt] = ts_TAGTYPE[item] .. " [" .. #sel[item] .. "]"
else
mt[item_mt] = ts_TAGTYPE[item]
end
end
local ft = {
[0] = exit_now, --if user cancels do this function
[1] = function(TITLE) return true end, -- shouldn't happen title occupies this slot
[2] = function(ARTCOND)
sel_cond(TAG_ARTIST, ARTCOND)
end,
[3] = function(ART)
sel_tag(TAG_ARTIST, ART, t_tags)
end,
[4] = function(ALBARTCOND)
sel_cond(TAG_ALBARTIST, ALBARTCOND)
end,
[5] = function(ALBART)
sel_tag(TAG_ALBARTIST, ALBART, t_tags)
end,
[6] = function(ALBCOND)
sel_cond(TAG_ALBUM, ALBCOND)
end,
[7] = function(ALB)
sel_tag(TAG_ALBUM, ALB, t_tags)
end,
[8] = function(GENRECOND)
sel_cond(TAG_GENRE, GENRECOND)
end,
[9] = function(GENRE)
sel_tag(TAG_GENRE, GENRE, t_tags)
end,
[10] = function(COMPCOND)
sel_cond(TAG_COMPOSER, COMPCOND)
end,
[11] = function(COMP)
sel_tag(TAG_COMPOSER, COMP, t_tags)
end,
[12] = function(SAVET)
menuname = menuname or sDEFAULTMENU
menuname = rb.kbd_input(menuname)
menuname = string.match(menuname, "%w+")
if menuname == "" then menuname = nil end
menuname = menuname or sDEFAULTMENU
savedata(sFILEOUT, ts_tags, cond, menuname)
end,
[13] = function(EXIT_) return true end
}
print_menu(mt, ft, 2) --start at item 2
end
function exit_now()
rb.lcd_update()
os.exit()
end -- exit_now
main_menu()
exit_now()

View file

@ -173,6 +173,16 @@ sub make_install {
}
glob_install("$src/rocks/viewers/lua/*", "$libdir/rocks/viewers/lua");
#lua example scripts
if(-e "$ROOT/apps/plugins/lua_scripts") {
unless (glob_mkdir("$libdir/rocks/demos/lua_scripts")) {
return 0;
}
glob_install("$ROOT/apps/plugins/lua_scripts/*.lua", "$libdir/rocks/demos/lua_scripts");
#glob_mkdir("$temp_dir/rocks/demos/lua_scripts");
#glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/");
}
# all the rest directories
foreach my $t (@userstuff) {
unless (glob_mkdir("$userdir/$t")) {
@ -433,6 +443,12 @@ sub buildzip {
find(find_copyfile(qr/\.(rock|ovl|lua)/, abs_path("$temp_dir/rocks/")), 'apps/plugins');
#lua example scripts
if(-e "$ROOT/apps/plugins/lua_scripts") {
glob_mkdir("$temp_dir/rocks/demos/lua_scripts");
glob_copy("$ROOT/apps/plugins/lua_scripts/*.lua", "$temp_dir/rocks/demos/lua_scripts/");
}
# exclude entries for the image file types not supported by the imageviewer for the target.
my $viewers = "$ROOT/apps/plugins/viewers.config";
my $c="cat $viewers | gcc $cppdef -I. -I$firmdir/export -E -P -include config.h -";