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