From 80c3b84e08a1debc0e5af8567f9eeb0b1716303a Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Fri, 2 Oct 2020 14:30:41 -0400 Subject: lua Add scrollable stack traceback WIP lua currently splashes a stack traceback on error for deep tracebacks and especially on devices with smaller screens this leaves out a lot of vital information in the past I have resorted to splitting the traceback string or even saving the return to a file This patch provides a scrollable buffer with rudimentary text reflow to allow you to read the whole traceback string Upon traceback if you press nothing the screen will display for 5 seconds If you press OK or CANCEL it will quit immediately PREV/NEXT scrolls the list on button press timeout is disabled lua now provides rb.splash_scroller(timeout, str) example script provided too Change-Id: Idbc8ce0c514196f0fae48c43aeaea8b60d6da1a5 --- apps/plugins/lua/rockaux.c | 116 ++++++++++++++++++++++++++++ apps/plugins/lua/rockconf.h | 1 + apps/plugins/lua/rocklib.c | 9 +++ apps/plugins/lua/rocklua.c | 17 +++- apps/plugins/lua_scripts/splashscroller.lua | 1 + 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 apps/plugins/lua_scripts/splashscroller.lua diff --git a/apps/plugins/lua/rockaux.c b/apps/plugins/lua/rockaux.c index 734b6a8324..7ed82f616d 100644 --- a/apps/plugins/lua/rockaux.c +++ b/apps/plugins/lua/rockaux.c @@ -41,6 +41,122 @@ char *strerror(int errnum) return NULL; } +/* splash string and allow user to scroll around + * provides rudimentary text reflow + * timeout is disabled on user interaction + * returns the action that caused quit + * [ACTION_NONE, ACTION_STD_CANCEL, ACTION_STD_OK] + * !ACTION_NONE (only on initial timeout)! + * TIMEOUT can be TIMEOUT_BLOCK or time in ticks +*/ +int splash_scroller(int timeout, const char* str) +{ + int w, ch_w, ch_h; + rb->lcd_getstringsize("W", &ch_w, &ch_h); + + const int max_w = LCD_WIDTH - (ch_w * 2); + const int max_lines = LCD_HEIGHT / ch_h - 1; + const int wrap_thresh = (LCD_WIDTH / 3); + const char *ch; + char *brk; + + const int max_ch = (LCD_WIDTH / ch_w - 1) * 2; + char line[max_ch + 2]; /* display buffer */ + const char break_chars[] = "@#$%^&*+-{}[]()/\\|<>:;.,? _\n\r\t"; + + int linepos, curline, linesdisp, realline, chars_next_break; + int action = ACTION_NONE; + int firstline = 0; + int cycles = 2; /* get action timeout returns immediately on first call */ + + while (cycles > 0) + { + /* walk whole buffer every refresh, only display relevant portion */ + rb->lcd_clear_display(); + curline = 0; + linepos = 0; + linesdisp = 0; + ch = str; + for (; *ch && linepos < max_ch; ch++) + { + if (ch[0] == '\t') + { + line[linepos++] = ' '; + line[linepos] = ' '; + } + else if (ch[0] == '\b' && timeout > 0) + { + line[linepos] = ' '; + rb->beep_play(1000, HZ, 1000); + } + else if (ch[0] < ' ') /* Dont copy control characters */ + line[linepos] = (linepos == 0) ? '\0' : ' '; + else + line[linepos] = ch[0]; + + line[linepos + 1] = '\0'; /* terminate to check text extent */ + rb->lcd_getstringsize(line, &w, NULL); + + /* try to not split in middle of words */ + if (w + wrap_thresh >= max_w && strpbrk (&line[linepos], break_chars)) + { + brk = strpbrk(ch+1, break_chars); + chars_next_break = (brk - ch); + if (chars_next_break < 2 || w + (ch_w * chars_next_break) > max_w) + { + if (!isprint(line[linepos])) + { + line[linepos] = '\0'; + ch--; /* back-up we want it on the next line */ + } + w += max_w; + } + } + + if (w > max_w || + (ch[0] >= '\n' && iscntrl(ch[0])) || + ch[1] == '\0') + { + realline = curline - firstline; + if (realline >= 0 && realline < max_lines) + { + rb->lcd_putsxy(0, realline * ch_h, line); + linesdisp++; + } + linepos = 0; + curline++; + continue; + } + linepos++; + } + + rb->lcd_update(); + + action = rb->get_action(CONTEXT_STD, timeout); + switch(action) + { + case ACTION_STD_OK: + case ACTION_STD_CANCEL: + cycles--; + /* Fall Through */ + case ACTION_NONE: + cycles--; + break; + case ACTION_STD_PREV: + timeout = TIMEOUT_BLOCK; /* disable timeout */ + if(firstline > 0) + firstline--; + break; + case ACTION_STD_NEXT: + timeout = TIMEOUT_BLOCK; /* disable timeout */ + if (linesdisp == max_lines) + firstline++; + break; + } + } + return action; +} + long rb_pow(long x, long n) { long pow = 1; diff --git a/apps/plugins/lua/rockconf.h b/apps/plugins/lua/rockconf.h index 6a1141f86a..503c2c7dec 100644 --- a/apps/plugins/lua/rockconf.h +++ b/apps/plugins/lua/rockconf.h @@ -48,6 +48,7 @@ unsigned long strtoul(const char *str, char **endptr, int base); size_t strftime(char* dst, size_t max, const char* format, const struct tm* tm); long lfloor(long x); long lpow(long x, long y); +int splash_scroller(int timeout, const char* str); #define floor lfloor #define pow lpow diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c index 8eab08f987..5f0143efbb 100644 --- a/apps/plugins/lua/rocklib.c +++ b/apps/plugins/lua/rocklib.c @@ -287,6 +287,14 @@ RB_WRAP(do_menu) return 1; } +RB_WRAP(splash_scroller) +{ + int timeout = luaL_checkint(L, 1); + const char *str = luaL_checkstring(L, 2); + int action = splash_scroller(timeout, str); /*rockaux.c*/ + lua_pushinteger(L, action); + return 1; +} /* DEVICE AUDIO / PLAYLIST CONTROL */ @@ -948,6 +956,7 @@ static const luaL_Reg rocklib[] = RB_FUNC(kbd_input), RB_FUNC(gui_syncyesno_run), RB_FUNC(do_menu), + RB_FUNC(splash_scroller), /* DEVICE AUDIO / SOUND / PLAYLIST CONTROL */ RB_FUNC(audio), diff --git a/apps/plugins/lua/rocklua.c b/apps/plugins/lua/rocklua.c index 79f6bae953..3909f3008f 100644 --- a/apps/plugins/lua/rocklua.c +++ b/apps/plugins/lua/rocklua.c @@ -86,7 +86,7 @@ static int db_errorfb (lua_State *L) { if (lua_gettop(L) == arg) lua_pushliteral(L, ""); else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ - else lua_pushliteral(L, "\n"); + else lua_pushliteral(L, "\n\n"); lua_pushliteral(L, "stack traceback: "); while (lua_getstack(L1, level++, &ar)) { if (level > LEVELS1 && firstpart) { @@ -101,7 +101,7 @@ static int db_errorfb (lua_State *L) { firstpart = 0; continue; } - lua_pushliteral(L, "\n\t"); + lua_pushliteral(L, "\n\n\t"); lua_getinfo(L1, "Snl", &ar); char* filename = rb->strrchr(ar.short_src, '/'); /* remove path */ lua_pushfstring(L, "%s:", filename ? filename : ar.short_src); @@ -118,8 +118,10 @@ static int db_errorfb (lua_State *L) { lua_pushfstring(L, " in function <%s:%d>", ar.short_src, ar.linedefined); } + lua_concat(L, lua_gettop(L) - arg); } + lua_pushfstring(L, "\n\nRam Used: %d Kb", lua_gc (L, LUA_GCCOUNT, 0)); lua_concat(L, lua_gettop(L) - arg); return 1; } @@ -225,6 +227,14 @@ static int lua_split_arguments(lua_State *L, const char *filename) return 2; } +static void display_traceback(const char *errstr) +{ +#if 1 + splash_scroller(HZ * 5, errstr); /*rockaux.c*/ +#else + rb->splash(10 * HZ, errstr); +#endif +} /***************** Plugin Entry Point *****************/ enum plugin_status plugin_start(const void* parameter) { @@ -251,7 +261,8 @@ enum plugin_status plugin_start(const void* parameter) if (lu_status) { DEBUGF("%s\n", lua_tostring(Ls, -1)); - rb->splash(10 * HZ, lua_tostring(Ls, -1)); + display_traceback(lua_tostring(Ls, -1)); + //rb->splash(10 * HZ, lua_tostring(Ls, -1)); /*lua_pop(Ls, 1);*/ } lua_close(Ls); diff --git a/apps/plugins/lua_scripts/splashscroller.lua b/apps/plugins/lua_scripts/splashscroller.lua new file mode 100644 index 0000000000..78a7266de8 --- /dev/null +++ b/apps/plugins/lua_scripts/splashscroller.lua @@ -0,0 +1 @@ +rb.splash_scroller(rb.HZ * 5, "This is a rb.splash_scroller test, it is meant to reflow text into a format easily digestable on small screen devices.\n\n it is not particularly adept at this as we strive for the bare minimum in order to leave room for YOUR code. :) \n\nHowever, it does recognize \t\\t \r\\r\n\\n\n\\b (bell)\b") -- cgit v1.2.3