A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 297 lines 8.8 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2008 Dan Everton (safetydan) 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21 22#include "plugin.h" 23#include "lua.h" 24#include "lauxlib.h" 25#include "lualib.h" 26#include "rocklib.h" 27#include "rocklib_img.h" 28#include "luadir.h" 29#include "rocklib_events.h" 30 31static lua_State *Ls = NULL; 32static int lu_status = 0; 33 34static const luaL_Reg lualibs[] = { 35 {"", luaopen_base}, 36 {LUA_LOADLIBNAME, luaopen_package}, 37 {LUA_TABLIBNAME, luaopen_table}, 38 {LUA_STRLIBNAME, luaopen_string}, 39 {LUA_BITLIBNAME, luaopen_bit}, 40 {LUA_IOLIBNAME, luaopen_io}, 41 {LUA_MATHLIBNAME, luaopen_math}, 42 {LUA_OSLIBNAME, luaopen_os}, 43 {LUA_ROCKLIBNAME, luaopen_rock}, 44 {LUA_ROCKLIBNAME, luaopen_rock_img}, 45 {LUA_ROCKEVENTSNAME, luaopen_rockevents}, 46 {LUA_DIRLIBNAME, luaopen_luadir}, 47 {NULL, NULL} 48}; 49 50static void rocklua_openlibs(lua_State *L) { 51 const luaL_Reg *lib = lualibs; 52 for (; lib->func; lib++) { 53 lua_pushcfunction(L, lib->func); 54 lua_pushstring(L, lib->name); 55 lua_call(L, 1, 0); 56 } 57} 58 59/* ldlib.c */ 60static lua_State *getthread (lua_State *L, int *arg) { 61 if (lua_isthread(L, 1)) { 62 *arg = 1; 63 return lua_tothread(L, 1); 64 } 65 else { 66 *arg = 0; 67 return L; 68 } 69} 70 71#define LEVELS1 12 /* size of the first part of the stack */ 72#define LEVELS2 10 /* size of the second part of the stack */ 73 74static int db_errorfb (lua_State *L) { 75 int level; 76 int firstpart = 1; /* still before eventual `...' */ 77 int arg; 78 lua_State *L1 = getthread(L, &arg); 79 lua_Debug ar; 80 if (lua_isnumber(L, arg+2)) { 81 level = (int)lua_tointeger(L, arg+2); 82 lua_pop(L, 1); 83 } 84 else 85 level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ 86 if (lua_gettop(L) == arg) 87 lua_pushliteral(L, ""); 88 else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ 89 else lua_pushliteral(L, "\n\n"); 90 lua_pushliteral(L, "stack traceback: "); 91 while (lua_getstack(L1, level++, &ar)) { 92 if (level > LEVELS1 && firstpart) { 93 /* no more than `LEVELS2' more levels? */ 94 if (!lua_getstack(L1, level+LEVELS2, &ar)) 95 level--; /* keep going */ 96 else { 97 lua_pushliteral(L, "\n\t..."); /* too many levels */ 98 while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ 99 level++; 100 } 101 firstpart = 0; 102 continue; 103 } 104 lua_pushliteral(L, "\n\n\t"); 105 lua_getinfo(L1, "Snl", &ar); 106 char* filename = rb->strrchr(ar.short_src, '/'); /* remove path */ 107 lua_pushfstring(L, "%s:", filename ? filename : ar.short_src); 108 if (ar.currentline > 0) 109 lua_pushfstring(L, "%d:", ar.currentline); 110 if (*ar.namewhat != '\0') /* is there a name? */ 111 lua_pushfstring(L, " in function " LUA_QS, ar.name); 112 else { 113 if (*ar.what == 'm') /* main? */ 114 lua_pushfstring(L, " in main chunk"); 115 else if (*ar.what == 'C' || *ar.what == 't') 116 lua_pushliteral(L, " ?"); /* C function or tail call */ 117 else 118 lua_pushfstring(L, " in function <%s:%d>", 119 ar.short_src, ar.linedefined); 120 } 121 122 lua_concat(L, lua_gettop(L) - arg); 123 } 124 lua_pushfstring(L, "\n\nRam Used: %d Kb", lua_gc (L, LUA_GCCOUNT, 0)); 125 lua_concat(L, lua_gettop(L) - arg); 126 return 1; 127} 128 129/* lua.c */ 130static int traceback (lua_State *L) { 131 lua_pushcfunction(L, db_errorfb); 132 lua_pushvalue(L, 1); /* pass error message */ 133 lua_pushinteger(L, 2); /* skip this function and traceback */ 134 lua_call(L, 2, 1); /* call debug.traceback */ 135 return 1; 136} 137 138static int docall (lua_State *L) { 139 int status; 140 int base = lua_gettop(L); /* function index */ 141 lua_pushcfunction(L, traceback); /* push traceback function */ 142 lua_insert(L, base); /* put it under chunk and args */ 143 status = lua_pcall(L, 0, 0, base); 144 lua_remove(L, base); /* remove traceback function */ 145 /* force a complete garbage collection in case of errors */ 146 if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); 147 return status; 148} 149 150static void lua_atexit(void); 151static int lua_split_arguments(lua_State *L, const char *filename); 152 153static int loadfile_newstate(lua_State **L, const char *filename) 154{ 155 const char *file; 156 int ret; 157 158 *L = luaL_newstate(); 159 rb_atexit(lua_atexit); 160 161 lua_gc(*L, LUA_GCSTOP, 0); /* stop collector during initialization */ 162 rocklua_openlibs(*L); 163 164 lua_split_arguments(*L, filename); 165 lua_setglobal (*L, "_arguments"); 166 file = lua_tostring (*L, -1); 167 lua_setglobal (*L, "_fullpath"); 168 /* lua manual -> no guarantee pointer valid after value is removed from stack */ 169 ret = luaL_loadfile(*L, file); 170 lua_gc(*L, LUA_GCRESTART, 0); 171 172 return ret; 173} 174 175static void lua_atexit(void) 176{ 177 char *filename; 178 int err_n; 179 if(Ls && lua_gettop(Ls) > 1) 180 { 181 err_n = lua_tointeger(Ls, -1); /* os.exit? */ 182 if (Ls == lua_touserdata(Ls, -1)) /* signal from restart_lua */ 183 { 184 filename = (char *) malloc((MAX_PATH * 2) + 1); 185 186 if (filename) {/* out of memory? */ 187 filename[MAX_PATH * 2] = '\0'; 188 rb->strlcpy(filename, lua_tostring(Ls, -2), MAX_PATH * 2); 189 } else { 190 goto ERR_RUN; 191 } 192 lua_close(Ls); /* close old state */ 193 194 lu_status = loadfile_newstate(&Ls, filename); 195 196 free(filename); 197 plugin_start(NULL); 198 } 199 else if (err_n >= PLUGIN_USB_CONNECTED) /* INTERNAL PLUGIN RETVAL */ 200 { 201 lua_close(Ls); 202 _exit(err_n); /* don't call exit handler */ 203 } 204 else if (err_n != 0) 205 { 206ERR_RUN: 207 lu_status = LUA_ERRRUN; 208 lua_pop(Ls, 1); /* put exit string on top of stack */ 209 plugin_start(NULL); 210 } 211 else 212 lua_close(Ls); 213 } 214 _exit(PLUGIN_OK); /* don't call exit handler */ 215} 216 217/* split filename at argchar 218 * remainder of filename pushed on stack (-1) 219* argument string pushed on stack or nil if doesn't exist (-2) 220 */ 221static int lua_split_arguments(lua_State *L, const char *filename) 222{ 223 const char argchar = '?'; 224 const char* arguments = strchr(filename, argchar); 225 if(arguments) { 226 lua_pushstring(L, (arguments + 1)); 227 } else { 228 arguments = strlen(filename) + filename; 229 lua_pushnil(L); 230 } 231 lua_pushlstring(L, filename, arguments - filename); 232 lua_insert(L, -2); /* swap filename and argument */ 233 return 2; 234} 235 236static void display_traceback(const char *errstr) 237{ 238#if 1 239 splash_scroller(HZ * 5, errstr); /*rockaux.c*/ 240#else 241 rb->splash(10 * HZ, errstr); 242#endif 243} 244 245int browse_scripts(void) 246{ 247 static char buf[MAX_PATH]; 248 const char *fname = rb->plugin_get_current_filename(); 249 /* strip plugin dir to save space in the param buffer */ 250 if (rb->strncmp(fname, PLUGIN_DIR, sizeof(PLUGIN_DIR) - 1) == 0) 251 fname += sizeof(PLUGIN_DIR) - 1; /* leave slash */ 252 /* -r return to this plugin, -f looking for lua files, 253 -s start in lua_scripts, -d lock to that directory */ 254 snprintf(buf, sizeof(buf), "-r'%s'-f'.lua'-s'%s'-d", 255 fname, PLUGIN_DEMOS_DIR"/lua_scripts/"); 256 return rb->plugin_open(VIEWERS_DIR "/file_picker.rock", buf); 257} 258/***************** Plugin Entry Point *****************/ 259enum plugin_status plugin_start(const void* parameter) 260{ 261 const char* filename; 262 263 if (parameter == NULL) 264 { 265 if (!Ls) 266 { 267 rb->splash(HZ, "Play a .lua file!"); 268 return browse_scripts(); 269 } 270 } 271 else 272 { 273 filename = (char*) parameter; 274 lu_status = loadfile_newstate(&Ls, filename); 275 } 276 277 if (Ls) 278 { 279 if (!lu_status) { 280 rb->lcd_scroll_stop(); /* rb doesn't like bg change while scroll */ 281 rb->lcd_clear_display(); 282 lu_status= docall(Ls); 283 } 284 285 if (lu_status) { 286 DEBUGF("%s\n", lua_tostring(Ls, -1)); 287 display_traceback(lua_tostring(Ls, -1)); 288 //rb->splash(10 * HZ, lua_tostring(Ls, -1)); 289 /*lua_pop(Ls, 1);*/ 290 } 291 lua_close(Ls); 292 } 293 else 294 return PLUGIN_ERROR; 295 296 return PLUGIN_OK; 297}