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 * Copyright (C) 2009 Maurus Cuelenaere
12 * Copyright (C) 2018 William Wilgus
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#define lrocklib_c
25#define LUA_LIB
26
27#include "lua.h"
28#include "lstring.h"
29
30#include "lauxlib.h"
31#include "rocklib.h"
32#include "lib/helper.h"
33
34/*
35 * http://www.lua.org/manual/5.1/manual.html#lua_CFunction
36 *
37 * In order to communicate properly with Lua, a C function must use the following protocol,
38 * which defines the way parameters and results are passed: a C function receives its arguments
39 * from Lua in its stack in direct order (the first argument is pushed first). To return values to Lua,
40 * a C function just pushes them onto the stack, in direct order (the first result is pushed first),
41 * and returns the number of results. Any other value in the stack below the results will be properly
42 * discarded by Lua. Like a Lua function, a C function called by Lua can also return many results.
43 *
44 * When porting new functions, don't forget to check rocklib_aux.pl whether it automatically creates
45 * wrappers for the function and if so, add the function names to @forbidden_functions. This is to
46 * prevent namespace collisions and adding duplicate wrappers.
47 */
48
49/*
50 * -----------------------------
51 *
52 * Rockbox wrappers start here!
53 *
54 * -----------------------------
55 */
56extern size_t rock_get_allocated_bytes(void); /* tlsf_helper.c */
57extern size_t rock_get_unused_bytes(void);
58
59#define RB_WRAP(func) static int rock_##func(lua_State UNUSED_ATTR *L)
60#define SIMPLE_VOID_WRAPPER(func) RB_WRAP(func) { (void)L; func(); return 0; }
61
62/* KERNEL */
63RB_WRAP(current_tick)
64{
65 lua_pushinteger(L, *rb->current_tick);
66 return 1;
67}
68
69#ifdef HAVE_SCHEDULER_BOOSTCTRL
70RB_WRAP(schedule_cpu_boost)
71{
72 bool boost = luaL_checkboolean(L, 1);
73
74 if(boost)
75 rb->trigger_cpu_boost();
76 else
77 rb->cancel_cpu_boost();
78
79 return 0;
80}
81#endif
82
83RB_WRAP(sleep)
84{
85 unsigned ticks = (unsigned) lua_tonumber(L, 1);
86 rb->sleep(ticks);
87 return 0;
88}
89
90#ifdef HAVE_PRIORITY_SCHEDULING
91RB_WRAP(thread_set_priority)
92{
93 unsigned int thread_id = rb->thread_self();
94 int priority = (int) luaL_checkint(L, 1);
95 int result = rb->thread_set_priority(thread_id, priority);
96 lua_pushinteger(L, result);
97 return 1;
98}
99#endif
100
101RB_WRAP(current_path)
102{
103 return get_current_path(L, 1);
104}
105
106
107/* DEVICE INPUT CONTROL */
108
109RB_WRAP(get_plugin_action)
110{
111 int timeout = luaL_checkint(L, 1);
112 bool with_remote = luaL_optint(L, 2, 0);
113 int btn = get_plugin_action(timeout, with_remote);
114 lua_pushinteger(L, btn);
115 return 1;
116}
117
118#ifdef HAVE_TOUCHSCREEN
119RB_WRAP(action_get_touchscreen_press)
120{
121 short x, y;
122 int result = rb->action_get_touchscreen_press(&x, &y);
123
124 lua_pushinteger(L, result);
125 lua_pushinteger(L, x);
126 lua_pushinteger(L, y);
127 return 3;
128}
129
130RB_WRAP(touchscreen_mode)
131{
132 int origmode = rb->touchscreen_get_mode();
133 if(!lua_isnoneornil(L, 1))
134 {
135 enum touchscreen_mode mode = luaL_checkint(L, 1);
136 rb->touchscreen_set_mode(mode);
137 }
138 lua_pushinteger(L, origmode);
139 return 1;
140}
141
142#endif
143
144// XXX this may be broken with 32-bit ucschar_t
145RB_WRAP(kbd_input)
146{
147 /*kbd_input(text, layout)*
148 note: layout needs special formatting
149 see includes/create_kbd_layout.lua
150 or lib/kbd_helper.c
151 */
152 luaL_Buffer b;
153 luaL_buffinit(L, &b);
154
155 const char *input = lua_tostring(L, 1);
156 size_t layout_len;
157 const unsigned char *layout = lua_tolstring(L, 2, &layout_len);
158 char *buffer = luaL_prepbuffer(&b);
159
160 if(input != NULL)
161 rb->strlcpy(buffer, input, LUAL_BUFFERSIZE);
162 else
163 buffer[0] = '\0';
164
165 if(layout_len <= 2 ||
166 layout[layout_len - 1] != 0xFE ||
167 layout[layout_len - 2] != 0xFF)
168 {
169 layout = NULL;
170 }
171
172 if(!rb->kbd_input(buffer, LUAL_BUFFERSIZE, (ucschar_t *)layout))
173 {
174 luaL_addstring(&b, buffer);
175 luaL_pushresult(&b);
176 }
177 else
178 return 0;
179
180 return 1;
181}
182
183static const char ** get_table_items(lua_State *L, int pos, int *count)
184{
185 int i;
186 luaL_checktype(L, pos, LUA_TTABLE);
187 *count = lua_objlen(L, pos);
188 int n = *count;
189
190 /* newuserdata will be pushed onto stack after args*/
191 const char **items = (const char**) lua_newuserdata(L, n * sizeof(const char*));
192
193 for(i=1; i<= n; i++)
194 {
195 lua_rawgeti(L, pos, i); /* Push item on the stack */
196 items[i-1] = lua_tostring(L, -1);
197 lua_pop(L, 1); /* Pop it */
198 }
199
200 return items;
201}
202
203static inline void fill_text_message(lua_State *L, struct text_message * message,
204 int pos)
205{
206 int n;
207 /* newuserdata will be pushed onto stack after args*/
208 message->message_lines = get_table_items(L, pos, &n);
209 message->nb_lines = n;
210}
211
212RB_WRAP(gui_syncyesno_run)
213{
214 struct text_message main_message, yes_message, no_message;
215 struct text_message *yes = NULL, *no = NULL;
216
217 lua_settop(L, 3); /* newuserdata will be pushed onto stack after args*/
218 fill_text_message(L, &main_message, 1);
219 if(!lua_isnoneornil(L, 2))
220 fill_text_message(L, (yes = &yes_message), 2);
221 if(!lua_isnoneornil(L, 3))
222 fill_text_message(L, (no = &no_message), 3);
223
224 enum yesno_res result = rb->gui_syncyesno_run(&main_message, yes, no);
225
226 lua_pushinteger(L, result);
227 return 1;
228}
229
230static lua_State* store_luastate(lua_State *L, bool bStore)
231{
232 /* it is dangerous to store the lua state byond its guaranteed lifetime
233 be sure to clear state asap (as in before you exit the calling function) */
234 static lua_State *LStored = NULL;
235 if(bStore)
236 LStored = L;
237 return LStored;
238}
239
240static int menu_callback(int action,
241 const struct menu_item_ex *this_item,
242 struct gui_synclist *this_list)
243{
244 (void) this_item;
245 (void) this_list;
246 static int lua_ref = LUA_NOREF;
247 lua_State *L = store_luastate(NULL, false);
248 if(!L)
249 {
250 lua_ref = action;
251 action = ACTION_STD_CANCEL;
252 }
253 else if (lua_ref != LUA_NOREF)
254 {
255 lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
256 lua_pushnumber(L, action);
257 lua_pcall (L, 1, 1, 0);
258 action = luaL_optnumber (L, -1, ACTION_STD_CANCEL);
259 lua_pop(L, 1);
260 }
261
262 return action;
263}
264
265RB_WRAP(do_menu)
266{
267 struct menu_callback_with_desc menu_desc = {NULL, NULL, Icon_NOICON};
268 struct menu_item_ex menu = {MT_RETURN_ID | MENU_HAS_DESC, {.strings = NULL},
269 {.callback_and_desc = &menu_desc}};
270 int n, start_selected;
271 int ref_lua = LUA_NOREF;
272 const char **items, *title;
273
274 title = luaL_checkstring(L, 1);
275
276 start_selected = lua_tointeger(L, 3);
277
278 if (lua_isfunction (L, -1))
279 {
280 /*lua callback function cb(action) return action end */
281 ref_lua = luaL_ref(L, LUA_REGISTRYINDEX);
282 menu_callback(ref_lua, NULL, NULL);
283 store_luastate(L, true);
284 menu_desc.menu_callback = &menu_callback;
285 }
286
287 /* newuserdata will be pushed onto stack after args*/
288 items = get_table_items(L, 2, &n);
289
290 menu.strings = items;
291 menu.flags |= MENU_ITEM_COUNT(n);
292 menu_desc.desc = (unsigned char*) title;
293
294 int result = rb->do_menu(&menu, &start_selected, NULL, false);
295
296 if (ref_lua != LUA_NOREF)
297 {
298 store_luastate(NULL, true);
299 luaL_unref (L, LUA_REGISTRYINDEX, ref_lua);
300 menu_callback(LUA_NOREF, NULL, NULL);
301 }
302
303 lua_pushinteger(L, result);
304 return 1;
305}
306
307RB_WRAP(splash)
308{
309 int timeout = luaL_checkint(L, 1);
310 const char *str = luaL_checkstring(L, 2);
311 rb->splashf(timeout, str); /*rockaux.c*/
312 return 0;
313}
314
315RB_WRAP(splash_scroller)
316{
317 int timeout = luaL_checkint(L, 1);
318 const char *str = luaL_checkstring(L, 2);
319 int action = splash_scroller(timeout, str); /*rockaux.c*/
320 lua_pushinteger(L, action);
321 return 1;
322}
323
324/* DEVICE AUDIO / PLAYLIST CONTROL */
325
326RB_WRAP(playlist)
327{
328 /* just passes NULL to work with the current playlist */
329 enum e_playlist {PLAYL_AMOUNT = 0, PLAYL_CREATE,
330 PLAYL_START, PLAYL_RESUMETRACK, PLAYL_RESUME,
331 PLAYL_SHUFFLE, PLAYL_SYNC, PLAYL_REMOVEALLTRACKS,
332 PLAYL_INSERTTRACK, PLAYL_INSERTDIRECTORY, PLAYL_INSERTPLAYL,
333 PLAYL_ECOUNT};
334
335 const char *playlist_option[] = {"amount", "create", "start", "resume_track",
336 "resume", "shuffle", "sync", "remove_all_tracks",
337 "insert_track", "insert_directory", "insert_playlist", NULL};
338
339 const char *filename, *dir;
340 int result = 0;
341 bool queue, recurse, sync;
342 int pos, crc, index, random_seed;
343 unsigned long elapsed, offset;
344
345 int option = luaL_checkoption (L, 1, NULL, playlist_option);
346 switch(option)
347 {
348 case PLAYL_AMOUNT:
349 result = rb->playlist_amount();
350 break;
351 case PLAYL_CREATE:
352 dir = luaL_checkstring(L, 2);
353 filename = luaL_checkstring(L, 3);
354 result = rb->playlist_create(dir, filename);
355 break;
356 case PLAYL_START:
357 index = luaL_checkint(L, 2);
358 elapsed = luaL_checkint(L, 3);
359 offset = luaL_checkint(L, 4);
360 rb->playlist_start(index, elapsed, offset);
361 break;
362 case PLAYL_RESUMETRACK:
363 index = luaL_checkint(L, 2);
364 crc = luaL_checkint(L, 3);
365 elapsed = luaL_checkint(L, 4);
366 offset = luaL_checkint(L, 5);
367 rb->playlist_resume_track(index, (unsigned) crc, elapsed, offset);
368 break;
369 case PLAYL_RESUME:
370 result = rb->playlist_resume();
371 break;
372 case PLAYL_SHUFFLE:
373 random_seed = luaL_checkint(L, 2);
374 index = luaL_checkint(L, 3);
375 result = rb->playlist_shuffle(random_seed, index);
376 break;
377 case PLAYL_SYNC:
378 rb->playlist_sync(NULL);
379 break;
380 case PLAYL_REMOVEALLTRACKS:
381 result = rb->playlist_remove_all_tracks(NULL);
382 break;
383 case PLAYL_INSERTTRACK:
384 filename = luaL_checkstring(L, 2); /* only required parameter */
385 pos = luaL_optint(L, 3, PLAYLIST_INSERT);
386 queue = lua_toboolean(L, 4); /* default to false */
387 sync = lua_toboolean(L, 5); /* default to false */
388 result = rb->playlist_insert_track(NULL, filename, pos, queue, sync);
389 break;
390 case PLAYL_INSERTDIRECTORY:
391 dir = luaL_checkstring(L, 2); /* only required parameter */
392 pos = luaL_optint(L, 3, PLAYLIST_INSERT);
393 queue = lua_toboolean(L, 4); /* default to false */
394 recurse = lua_toboolean(L, 5); /* default to false */
395 result = rb->playlist_insert_directory(NULL, dir, pos, queue, recurse);
396 break;
397 case PLAYL_INSERTPLAYL:
398 filename = luaL_checkstring(L, 2); /* only required parameter */
399 pos = luaL_optint(L, 3, 0);
400 queue = lua_toboolean(L, 4); /* default to false */
401 result = rb->playlist_insert_playlist(NULL, filename, pos, queue);
402 break;
403 }
404
405 yield();
406 lua_pushinteger(L, result);
407 return 1;
408}
409
410RB_WRAP(audio)
411{
412 enum e_audio {AUDIO_STATUS = 0, AUDIO_PLAY, AUDIO_STOP, AUDIO_PAUSE,
413 AUDIO_RESUME, AUDIO_NEXT, AUDIO_PREV, AUDIO_FFREWIND,
414 AUDIO_FLUSHANDRELOADTRACKS, AUDIO_GETPOS, AUDIO_LENGTH,
415 AUDIO_ELAPSED, AUDIO_ECOUNT};
416 const char *audio_option[] = {"status", "play", "stop",
417 "pause", "resume", "next",
418 "prev", "ff_rewind",
419 "flush_and_reload_tracks",
420 "get_file_pos", "length",
421 "elapsed", NULL};
422 long elapsed, offset, newtime;
423 int status = rb->audio_status();
424
425 int option = luaL_checkoption (L, 1, NULL, audio_option);
426 switch(option)
427 {
428 case AUDIO_STATUS:
429 break;
430 case AUDIO_PLAY:
431 elapsed = luaL_checkint(L, 2);
432 offset = luaL_checkint(L, 3);
433
434 if (status == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE))
435 {
436 /* not perfect but provides a decent compromise */
437 rb->audio_ff_rewind(elapsed + offset);
438 rb->audio_resume();
439 }
440 else if (status != AUDIO_STATUS_PLAY)
441 rb->audio_play((unsigned long) elapsed, (unsigned long) offset);
442
443 break;
444 case AUDIO_STOP:
445 rb->audio_stop();
446 break;
447 case AUDIO_PAUSE:
448 rb->audio_pause();
449 break;
450 case AUDIO_RESUME:
451 rb->audio_resume();
452 break;
453 case AUDIO_NEXT:
454 rb->audio_next();
455 break;
456 case AUDIO_PREV:
457 rb->audio_prev();
458 break;
459 case AUDIO_FFREWIND:
460 newtime = (long) luaL_checkint(L, 2);
461 rb->audio_ff_rewind(newtime);
462 break;
463 case AUDIO_FLUSHANDRELOADTRACKS:
464 rb->audio_flush_and_reload_tracks();
465 break;
466 case AUDIO_GETPOS:
467 lua_pushinteger(L, rb->audio_get_file_pos());
468 return 1;
469 case AUDIO_LENGTH:
470 if ((status & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
471 lua_pushinteger(L, rb->audio_current_track()->length);
472 else
473 lua_pushnil(L);
474 return 1;
475 case AUDIO_ELAPSED:
476 if ((status & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
477 lua_pushinteger(L, rb->audio_current_track()->elapsed);
478 else
479 lua_pushnil(L);
480 return 1;
481 }
482
483 yield();
484 lua_pushinteger(L, status); /* return previous (or current) audio status */
485 return 1;
486}
487
488RB_WRAP(sound)
489{
490 enum e_snd {SOUND_SET = 0, SOUND_CURRENT, SOUND_DEFAULT,
491 SOUND_MIN, SOUND_MAX, SOUND_UNIT, SOUND_SET_PITCH,
492 SOUND_VAL2PHYS, SOUND_ECOUNT};
493
494 const char *snd_option[] = {"set", "current", "default",
495 "min", "max", "unit", "pitch",
496 "val2phys", NULL};
497
498 lua_pushnil(L); /*push nil so options w/o return have something to return */
499
500 int option = luaL_checkoption (L, 1, NULL, snd_option);
501 int setting = luaL_checkint(L, 2);
502 int value, result;
503 switch(option)
504 {
505 case SOUND_SET:
506 value = luaL_checkint(L, 3);
507 rb->sound_set(setting, value);
508 return 1; /*nil*/
509 break;
510 case SOUND_CURRENT:
511 result = rb->sound_current(setting);
512 break;
513 case SOUND_DEFAULT:
514 result = rb->sound_default(setting);
515 break;
516 case SOUND_MIN:
517 result = rb->sound_min(setting);
518 break;
519 case SOUND_MAX:
520 result = rb->sound_max(setting);
521 break;
522 case SOUND_UNIT:
523 lua_pushstring (L, rb->sound_unit(setting));
524 return 1;
525 break;
526#if defined (HAVE_PITCHCONTROL)
527 case SOUND_SET_PITCH:
528 rb->sound_set_pitch(setting);
529 return 1;/*nil*/
530 break;
531#endif
532 case SOUND_VAL2PHYS:
533 value = luaL_checkint(L, 3);
534 result = rb->sound_val2phys(setting, value);
535 break;
536
537 default:
538 return 1;
539 break;
540 }
541
542 lua_pushinteger(L, result);
543 return 1;
544}
545
546RB_WRAP(pcm)
547{
548 enum e_pcm {PCM_APPLYSETTINGS = 0, PCM_ISPLAYING,
549 PCM_PLAYSTOP, PCM_PLAYLOCK, PCM_PLAYUNLOCK,
550 PCM_SETFREQUENCY, PCM_ECOUNT};
551
552 const char *pcm_option[] = {"apply_settings", "is_playing",
553 "play_stop", "play_lock", "play_unlock",
554 "set_frequency", NULL};
555 bool b_result;
556
557 lua_pushnil(L); /*push nil so options w/o return have something to return */
558
559 int option = luaL_checkoption (L, 1, NULL, pcm_option);
560 switch(option)
561 {
562 case PCM_APPLYSETTINGS:
563 rb->pcm_apply_settings();
564 break;
565 case PCM_ISPLAYING:
566 b_result = rb->pcm_is_playing();
567 lua_pushboolean(L, b_result);
568 break;
569 case PCM_PLAYSTOP:
570 rb->pcm_play_stop();
571 break;
572 case PCM_PLAYLOCK:
573 rb->pcm_play_lock();
574 break;
575 case PCM_PLAYUNLOCK:
576 rb->pcm_play_unlock();
577 break;
578 case PCM_SETFREQUENCY:
579 rb->pcm_set_frequency((unsigned int) luaL_checkint(L, 2));
580 break;
581 }
582
583 yield();
584 return 1;
585}
586
587RB_WRAP(mixer_frequency)
588{
589 unsigned int result = rb->mixer_get_frequency();
590
591 if(!lua_isnoneornil(L, 1))
592 {
593 unsigned int samplerate = (unsigned int) luaL_checkint(L, 1);
594 rb->mixer_set_frequency(samplerate);
595 }
596 lua_pushinteger(L, result);
597 return 1;
598}
599
600#ifdef HAVE_BACKLIGHT
601/* DEVICE LIGHTING CONTROL */
602RB_WRAP(backlight_onoff)
603{
604 bool on = luaL_checkboolean(L, 1);
605 if(on)
606 rb->backlight_on();
607 else
608 rb->backlight_off();
609
610 return 0;
611}
612
613SIMPLE_VOID_WRAPPER(backlight_force_on);
614SIMPLE_VOID_WRAPPER(backlight_use_settings);
615
616#ifdef HAVE_REMOTE_LCD
617SIMPLE_VOID_WRAPPER(remote_backlight_force_on);
618SIMPLE_VOID_WRAPPER(remote_backlight_use_settings);
619#endif
620
621#ifdef HAVE_BACKLIGHT_BRIGHTNESS
622RB_WRAP(backlight_brightness_set)
623{
624 if(lua_isnoneornil(L, 1))
625 backlight_brightness_use_setting();
626 else
627 {
628 int brightness = luaL_checkint(L, 1);
629 backlight_brightness_set(brightness);
630 }
631
632 return 0;
633}
634#endif
635#endif /* HAVE_BACKLIGHT */
636
637#ifdef HAVE_BUTTON_LIGHT
638SIMPLE_VOID_WRAPPER(buttonlight_force_on);
639SIMPLE_VOID_WRAPPER(buttonlight_use_settings);
640#endif
641
642#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
643RB_WRAP(buttonlight_brightness_set)
644{
645 if(lua_isnoneornil(L, 1))
646 buttonlight_brightness_use_setting();
647 else
648 {
649 int brightness = luaL_checkint(L, 1);
650 buttonlight_brightness_set(brightness);
651 }
652
653 return 0;
654}
655#endif
656
657
658/* DEVICE STRING / FILENAME MANIPULATION */
659
660#if 0 /*See files.lua */
661RB_WRAP(strip_extension)
662{
663 const char* filename = luaL_checkstring(L, -1);
664 const char* pos = rb->strrchr(filename, '.');
665 if(pos != NULL)
666 lua_pushlstring (L, filename, pos - filename);
667
668 return 1;
669}
670
671RB_WRAP(create_numbered_filename)
672{
673 luaL_Buffer b;
674 luaL_buffinit(L, &b);
675 char *buffer = luaL_prepbuffer(&b);
676 buffer[0] = '\0';
677
678 const char * path = luaL_checkstring(L, 1);
679 const char * prefix = luaL_checkstring(L, 2);
680 const char * suffix = luaL_checkstring(L, 3);
681 int numberlen = luaL_checkint(L, 4);
682 int num = luaL_optint(L, 5, -1);
683 (void) num;
684
685 if(rb->create_numbered_filename(buffer, path, prefix, suffix, numberlen
686 IF_CNFN_NUM_(, &num)))
687 {
688 luaL_addstring(&b, buffer);
689 luaL_pushresult(&b);
690 }
691 else
692 return 0;
693
694 return 1;
695}
696#endif
697
698RB_WRAP(utf8encode)
699{
700 unsigned long ucs = (unsigned long) luaL_checkint(L, 1);
701 unsigned char tmp[9];
702 unsigned char *end = rb->utf8encode(ucs, tmp);
703 *end = '\0';
704 lua_pushstring(L, tmp);
705 return 1;
706}
707
708RB_WRAP(strncasecmp)
709{
710 int result;
711 const char * s1 = luaL_checkstring(L, 1);
712 const char * s2 = luaL_checkstring(L, 2);
713 if(lua_isnoneornil(L, 3))
714 result = rb->strcasecmp(s1, s2);
715 else
716 result = rb->strncasecmp(s1, s2, (size_t) luaL_checkint(L, 3));
717
718 lua_pushinteger(L, result);
719 return 1;
720}
721
722 /* ROCKBOX SETTINGS / INFO */
723static int mem_read_write(lua_State *L, uintptr_t address, size_t maxsize, bool isstr_p)
724{
725 if(isstr_p) /*pointer to string (**char)*/
726 {
727 lua_settop(L, 2); /* no writes allowed */
728 }
729 intptr_t offset = (intptr_t) luaL_optnumber(L, 1, 0);
730 size_t size = (size_t) luaL_optnumber(L, 2, maxsize);
731 size_t written;
732 int type = lua_type(L, 3);
733
734 if(offset < 0)
735 {
736 /* allows pointer within structure to be calculated offset */
737 offset = -(address + offset);
738 luaL_argcheck(L, ((size_t) offset) <= maxsize, 1, ERR_IDX_RANGE);
739 size = (size_t) maxsize - offset;
740 }
741
742 luaL_argcheck(L, ((uintptr_t) offset) + size <= maxsize, 2, ERR_IDX_RANGE);
743
744 char *mem = (char*) address + ((uintptr_t) offset);
745 const void *value = NULL;
746
747 lua_Integer var_luaint;
748#ifdef UINT64_MAX
749 int64_t var_64;
750#endif
751 int32_t var_32;
752 int16_t var_16;
753 int8_t var_8;
754 bool var_bool;
755
756 switch(type)
757 {
758 case LUA_TSTRING:
759 {
760 size_t len;
761 const char* str = lua_tolstring (L, 3, &len);
762
763 luaL_argcheck(L, len + 1 <= size, 3, ERR_DATA_OVF);
764 size = len + 1; /* include \0 */
765 value = str;
766 break;
767 }
768 case LUA_TBOOLEAN:
769 {
770 var_bool = (bool) lua_toboolean(L, 3);
771 value = &var_bool;
772 break;
773 }
774 case LUA_TNUMBER:
775 {
776 var_luaint = lua_tointeger(L, 3);
777 switch(size)
778 {
779 case sizeof(var_8):
780 var_8 = (int8_t) var_luaint;
781 value = &var_8;
782 break;
783 case sizeof(var_16):
784 var_16 = (int16_t) var_luaint;
785 value = &var_16;
786 break;
787 case sizeof(var_32):
788 var_32 = (int32_t) var_luaint;
789 value = &var_32;
790 break;
791#ifdef UINT64_MAX
792 case sizeof(var_64):
793 var_64 = (int64_t) var_luaint;
794 value = &var_64;
795 break;
796#endif
797 } /* switch size */
798 break;
799 }
800 case LUA_TNIL:
801 case LUA_TNONE: /* reader */
802 {
803 if(isstr_p && mem)
804 {
805 lua_pushstring (L, *(char**) mem);
806 return 1;
807 }
808 luaL_Buffer b;
809 luaL_buffinit(L, &b);
810 while(size > 0)
811 {
812 written = MIN(LUAL_BUFFERSIZE, size);
813 luaL_addlstring (&b, mem, written);
814 mem += written;
815 size -= written;
816 }
817
818 luaL_pushresult(&b);
819 return 1;
820 }
821
822 default:
823 break;
824 } /* switch type */
825
826 /* writer */
827 luaL_argcheck(L, value != NULL, 3, "Unknown Type");
828 rb->memcpy(mem, value, size);
829 lua_pushinteger(L, 1);
830
831 return 1;
832}
833
834RB_WRAP(global_status)
835{
836 const uintptr_t address = (uintptr_t) rb->global_status;
837 const size_t maxsize = sizeof(struct system_status);
838 /*const bool isstr_p = lua_toboolean(L, 4);*/
839 return mem_read_write(L, address, maxsize, false);
840}
841
842RB_WRAP(global_settings)
843{
844 const uintptr_t address = (uintptr_t) rb->global_settings;
845 const size_t maxsize = sizeof(struct user_settings);
846 /*const bool isstr_p = lua_toboolean(L, 4);*/
847 return mem_read_write(L, address, maxsize, false);
848}
849
850RB_WRAP(audio_next_track)
851{
852
853 const uintptr_t address = (uintptr_t) rb->audio_next_track();
854 const size_t maxsize = sizeof(struct mp3entry);
855 const bool isstr_p = lua_toboolean(L, 4);
856 lua_settop(L, 2); /* no writes allowed */
857 return mem_read_write(L, address, maxsize, isstr_p);
858}
859
860RB_WRAP(audio_current_track)
861{
862
863 const uintptr_t address = (uintptr_t) rb->audio_current_track();
864 const size_t maxsize = sizeof(struct mp3entry);
865 const bool isstr_p = lua_toboolean(L, 4);
866 lua_settop(L, 2); /* no writes allowed */
867 return mem_read_write(L, address, maxsize, isstr_p);
868}
869
870RB_WRAP(settings_save)
871{
872 rb->settings_save();
873 return 0;
874}
875
876#if 0
877RB_WRAP(read_mem)
878{
879 lua_settop(L, 2); /* no writes allowed */
880 const uintptr_t address = lua_tonumber(L, 1);
881 const size_t maxsize = luaL_optnumber(L, 2, strlen((char *)address));
882 luaL_argcheck(L, address > 0, 1, ERR_IDX_RANGE);
883 lua_pushnil(L);
884 lua_replace(L, -3);/* stk pos 1 is no longer offset it is starting address */
885 return mem_read_write(L, address, maxsize, false);
886}
887
888/* will add this back if anyone finds a target that needs it */
889RB_WRAP(system_memory_guard)
890{
891 int newmode = (int) luaL_checkint(L, 1);
892 int result = rb->system_memory_guard(newmode);
893 lua_pushinteger(L, result);
894 return 1;
895}
896#endif
897
898/* SPEAKING */
899static int rock_talk(lua_State *L)
900{
901 int result;
902 bool enqueue = lua_toboolean(L, 2);
903 if (lua_isnumber(L, 1))
904 {
905 long n = (long) lua_tonumber(L, 1);
906 result = rb->talk_number(n, enqueue);
907 }
908 else
909 {
910 const char* spell = luaL_checkstring(L, 1);
911 result = rb->talk_spell(spell, enqueue);
912 }
913
914 lua_pushinteger(L, result);
915 return 1;
916}
917
918RB_WRAP(talk_shutup)
919{
920 if (lua_toboolean(L, 1))
921 rb->talk_force_shutup();
922 else
923 rb->talk_shutup();
924 return 0;
925}
926
927/* MISC */
928RB_WRAP(restart_lua)
929{
930 /*close lua state, open a new lua state, load script @ filename */
931 luaL_checktype (L, 1, LUA_TSTRING);
932 lua_settop(L, 1);
933 lua_pushlightuserdata(L, L); /* signal exit handler */
934 exit(1); /* atexit in rocklua.c */
935 return -1;
936}
937
938RB_WRAP(mem_stats)
939{
940 /* used, allocd, free = rb.mem_stats() */
941 /* note free is the high watermark */
942 size_t allocd = rock_get_allocated_bytes();
943 size_t free = rock_get_unused_bytes();
944
945 lua_pushinteger(L, allocd - free);
946 lua_pushinteger(L, allocd);
947 lua_pushinteger(L, free);
948 return 3;
949}
950
951#define RB_FUNC(func) {#func, rock_##func}
952#define RB_ALIAS(name, func) {name, rock_##func}
953static const luaL_Reg rocklib[] =
954{
955 /* KERNEL */
956 RB_FUNC(current_tick),
957#ifdef HAVE_SCHEDULER_BOOSTCTRL
958 RB_FUNC(schedule_cpu_boost),
959#endif
960 RB_FUNC(sleep),
961#ifdef HAVE_PRIORITY_SCHEDULING
962 RB_FUNC(thread_set_priority),
963#endif
964
965 RB_FUNC(current_path),
966
967 /* DEVICE INPUT CONTROL */
968 RB_FUNC(get_plugin_action),
969#ifdef HAVE_TOUCHSCREEN
970 RB_FUNC(action_get_touchscreen_press),
971 RB_FUNC(touchscreen_mode),
972#endif
973 RB_FUNC(kbd_input),
974 RB_FUNC(gui_syncyesno_run),
975 RB_FUNC(do_menu),
976 RB_FUNC(splash),
977 RB_FUNC(splash_scroller),
978
979 /* DEVICE AUDIO / SOUND / PLAYLIST CONTROL */
980 RB_FUNC(audio),
981 RB_FUNC(playlist),
982 RB_FUNC(sound),
983 RB_FUNC(pcm),
984 RB_FUNC(mixer_frequency),
985
986#ifdef HAVE_BACKLIGHT
987 /* DEVICE LIGHTING CONTROL */
988 RB_FUNC(backlight_onoff),
989
990 /* Backlight helper */
991 RB_FUNC(backlight_force_on),
992 RB_FUNC(backlight_use_settings),
993
994#ifdef HAVE_REMOTE_LCD
995 RB_FUNC(remote_backlight_force_on),
996 RB_FUNC(remote_backlight_use_settings),
997#endif
998
999#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1000 RB_FUNC(backlight_brightness_set),
1001#endif
1002#endif /* HAVE_BACKLIGHT */
1003
1004#ifdef HAVE_BUTTON_LIGHT
1005 RB_FUNC(buttonlight_force_on),
1006 RB_FUNC(buttonlight_use_settings),
1007#endif
1008
1009#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
1010 RB_FUNC(buttonlight_brightness_set),
1011#endif
1012
1013 /* DEVICE STRING / FILENAME MANIPULATION */
1014#if 0 /*See files.lua */
1015 RB_FUNC(strip_extension),
1016 RB_FUNC(create_numbered_filename),
1017#endif
1018 RB_FUNC(utf8encode),
1019 RB_FUNC(strncasecmp),
1020
1021 /* ROCKBOX SETTINGS / INFO */
1022 RB_FUNC(global_status),
1023 RB_FUNC(global_settings),
1024 RB_FUNC(audio_next_track),
1025 RB_FUNC(audio_current_track),
1026 RB_FUNC(settings_save),
1027
1028 /* SPEAKING */
1029 {"talk_number", rock_talk},
1030 {"talk_spell", rock_talk},
1031 RB_FUNC(talk_shutup),
1032
1033 /* MISC */
1034 RB_FUNC(restart_lua),
1035 RB_FUNC(mem_stats),
1036
1037 {NULL, NULL}
1038};
1039#undef RB_FUNC
1040#undef RB_ALIAS
1041
1042extern const luaL_Reg rocklib_aux[];
1043
1044/*
1045 ** Open Rockbox library
1046 */
1047LUALIB_API int luaopen_rock(lua_State *L)
1048{
1049 luaL_register(L, LUA_ROCKLIBNAME, rocklib);
1050 luaL_register(L, LUA_ROCKLIBNAME, rocklib_aux);
1051 lua_getglobal(L, "require");
1052 lua_pushstring(L, "rb_defines");
1053 if (lua_pcall (L, 1, 0, 0))
1054 lua_pop(L, 1);
1055#if 0 /* see rb_defines.lua */
1056 static const struct lua_int_reg rlib_const_int[] =
1057 {
1058 /* useful integer constants */
1059 RB_CONSTANT(HZ),
1060
1061 RB_CONSTANT(LCD_DEPTH),
1062 RB_CONSTANT(LCD_HEIGHT),
1063 RB_CONSTANT(LCD_WIDTH),
1064 RB_CONSTANT(SCREEN_MAIN),
1065#if LCD_DEPTH > 1
1066 RB_CONSTANT(LCD_DEFAULT_FG),
1067 RB_CONSTANT(LCD_DEFAULT_BG),
1068#endif
1069#ifdef HAVE_REMOTE_LCD
1070 RB_CONSTANT(LCD_REMOTE_DEPTH),
1071 RB_CONSTANT(LCD_REMOTE_HEIGHT),
1072 RB_CONSTANT(LCD_REMOTE_WIDTH),
1073 RB_CONSTANT(SCREEN_REMOTE),
1074#endif
1075
1076 RB_CONSTANT(FONT_SYSFIXED),
1077 RB_CONSTANT(FONT_UI),
1078
1079 RB_CONSTANT(PLAYLIST_INSERT),
1080 RB_CONSTANT(PLAYLIST_INSERT_LAST),
1081 RB_CONSTANT(PLAYLIST_INSERT_FIRST),
1082 RB_CONSTANT(PLAYLIST_INSERT_LAST_SHUFFLED),
1083 RB_CONSTANT(PLAYLIST_INSERT_SHUFFLED),
1084 RB_CONSTANT(PLAYLIST_PREPEND),
1085 RB_CONSTANT(PLAYLIST_REPLACE),
1086
1087/* queue sys events */
1088 RB_CONSTANT(SYS_USB_CONNECTED),
1089 RB_CONSTANT(SYS_USB_DISCONNECTED),
1090 RB_CONSTANT(SYS_TIMEOUT),
1091 RB_CONSTANT(SYS_POWEROFF),
1092 RB_CONSTANT(SYS_REBOOT),
1093 RB_CONSTANT(SYS_CHARGER_CONNECTED),
1094 RB_CONSTANT(SYS_CHARGER_DISCONNECTED),
1095
1096#ifdef HAVE_TOUCHSCREEN
1097 RB_CONSTANT(TOUCHSCREEN_POINT),
1098 RB_CONSTANT(TOUCHSCREEN_BUTTON),
1099#endif
1100
1101 {NULL, 0}
1102 };
1103
1104 static const struct lua_int_reg* rlci = rlib_const_int;
1105 for (; rlci->name; rlci++) {
1106 lua_pushinteger(L, rlci->value);
1107 luaS_newlloc(L, rlci->name, TSTR_INBIN);
1108 lua_setfield(L, -2, rlci->name);
1109 }
1110
1111 static const struct lua_str_reg rlib_const_str[] =
1112 {
1113 /* some useful paths constants */
1114 RB_STRING_CONSTANT(HOME_DIR),
1115 RB_STRING_CONSTANT(PLUGIN_DIR),
1116 RB_STRING_CONSTANT(PLUGIN_APPS_DATA_DIR),
1117 RB_STRING_CONSTANT(PLUGIN_GAMES_DATA_DIR),
1118 RB_STRING_CONSTANT(PLUGIN_DATA_DIR),
1119 RB_STRING_CONSTANT(ROCKBOX_DIR),
1120 RB_STRING_CONSTANT(VIEWERS_DATA_DIR),
1121 {NULL,NULL}
1122 };
1123
1124 static const struct lua_str_reg* rlcs = rlib_const_str;
1125 for (; rlcs->name; rlcs++) {
1126 luaS_newlloc(L, rlcs->value, TSTR_INBIN);
1127 lua_pushstring(L, rlcs->value);
1128 luaS_newlloc(L, rlcs->name, TSTR_INBIN);
1129 lua_setfield(L, -2, rlcs->name);
1130 }
1131#endif
1132 return 1;
1133}