A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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}