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 /apps/plugins/lua | |
parent | 60c5a29408f7ca05a88ce1a98a4858293925169f (diff) | |
download | rockbox-90118f14cf078358f9ebdee110450b976c9a9e11.tar.gz rockbox-90118f14cf078358f9ebdee110450b976c9a9e11.zip |
lua add demo scripts, atexit handler, gui_scrollbar_draw
Change-Id: Ie8794e8a487f73952dae43e036787b6972fdbbee
Diffstat (limited to 'apps/plugins/lua')
-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 |
7 files changed, 481 insertions, 22 deletions
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 @@ +--[[ +/*************************************************************************** + * __________ __ ___. + * 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 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) { 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 */ } 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 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 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) 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; } - 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 { 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: 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 [] = #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 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 @@ #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; } - |