A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 573 lines 18 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / 5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2020 William Wilgus 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/* WIP rb_info common info that you wonder about when rockboxing? 23 */ 24 25#include "plugin.h" 26#include "lang_enum.h" 27#include "../open_plugin.h" 28#include "logf.h" 29#include "lib/action_helper.h" 30#include "lib/button_helper.h" 31#include "lib/pluginlib_actions.h" 32#include "lib/printcell_helper.h" 33 34#define MENU_ID(x) (((void*)&"RPBUTACNGSX\0" + x)) 35enum { 36 M_ROOT = 0, 37 M_PATHS, 38 M_BUFFERS, 39 M_BUTTONS, 40 M_BTNTEST, 41 M_ACTIONS, 42 M_CONTEXTS, 43 M_ACTTEST, 44 M_PLUGINS, 45 M_TESTPUT, 46 M_EXIT, 47 M_LAST_ITEM //ITEM COUNT 48}; 49 50#define MENU_ID_PLUGINS_ITEMS 5 51 52/*Action test and Button test*/ 53static struct menu_test_t { 54 int count; 55 int context; 56 int last_btn_or_act; 57} m_test; 58 59struct menu_buffer_t { const char *name; size_t size;}; 60static const struct menu_buffer_t m_buffer[] = 61{ 62#ifndef MAX_LOGF_SIZE 63#define MAX_LOGF_SIZE (0) 64#endif 65#ifndef CACHE_SIZE 66#define CACHE_SIZE (0) 67#endif 68 {"thread stack", DEFAULT_STACK_SIZE}, 69 {"plugin buffer", PLUGIN_BUFFER_SIZE}, 70 {"frame_buffer", FRAMEBUFFER_SIZE}, 71 {"codec_buffer", CODEC_SIZE}, 72 {"logf_buffer", MAX_LOGF_SIZE}, 73 {"cache", CACHE_SIZE}, 74}; 75 76/* stringify the macro value */ 77#define MACROVAL(x) MACROSTR(x) 78#define MACROSTR(x) #x 79static int main_last_sel = 0; 80static struct gui_synclist lists; 81static void synclist_set(char*, int, int, int); 82 83struct paths { const char *name; const char *path; }; 84static const struct paths paths[] = { 85 {"Home", ""HOME_DIR}, 86 {"Rockbox", ""ROCKBOX_DIR}, 87 {"Plugins", ""PLUGIN_DIR}, 88 {"Codecs", ""CODECS_DIR}, 89 {"WPS", ""WPS_DIR}, 90 {"SBS", ""SBS_DIR}, 91 {"Theme", ""THEME_DIR}, 92 {"Font", ""FONT_DIR}, 93 {"Icon", ""ICON_DIR}, 94 {"Backdrop", ""BACKDROP_DIR}, 95 {"Eq", ""EQS_DIR}, 96 {"Rec Presets", ""RECPRESETS_DIR}, 97 {"Recordings", ""REC_BASE_DIR,}, 98 {"Fm Presets", ""FMPRESET_PATH}, 99 {"MAX_PATH", ""MACROVAL(MAX_PATH)" bytes"}, 100}; 101#define TESTPUT_HEADER "$*64$col1$col2$*128$col3$col4$col5$col6$*64$col7$col8" 102static int testput_cols = 0; 103struct mainmenu { const char *name; void *menuid; int items;}; 104static struct mainmenu mainmenu[M_LAST_ITEM] = { 105#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)COUNT} 106MENU_ITEM(M_ROOT, "Rockbox Info Plugin", M_LAST_ITEM), 107MENU_ITEM(M_PATHS, ID2P(LANG_SHOW_PATH), ARRAYLEN(paths)), 108MENU_ITEM(M_BUFFERS, ID2P(LANG_BUFFER_STAT), ARRAYLEN(m_buffer)), 109MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */ 110MENU_ITEM(M_BTNTEST, "Button test", 2), 111MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER), 112MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ), 113MENU_ITEM(M_ACTTEST, "Action test", 3), 114MENU_ITEM(M_PLUGINS, ID2P(LANG_PLUGINS), MENU_ID_PLUGINS_ITEMS), 115MENU_ITEM(M_TESTPUT, "Printcell test", 36), 116MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0), 117#undef MENU_ITEM 118}; 119 120static const struct mainmenu *mainitem(int selected_item) 121{ 122 static const struct mainmenu empty = {0}; 123 if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu)) 124 return &mainmenu[selected_item]; 125 else 126 return &empty; 127} 128 129static void cleanup(void *parameter) 130{ 131 (void)parameter; 132} 133 134#if 0 135static enum themable_icons menu_icon_cb(int selected_item, void * data) 136{ 137 (void)data; 138 (void)selected_item; 139 return Icon_NOICON; 140} 141#endif 142 143static const char *menu_plugin_name_cb(int selected_item, void* data, 144 char* buf, size_t buf_len) 145{ 146 (void)data; 147 buf[0] = '\0'; 148 switch(selected_item) 149 { 150 case 0: 151 rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin_api", (int)sizeof(struct plugin_api)); 152 break; 153 case 1: 154 rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin buffer", PLUGIN_BUFFER_SIZE); 155 break; 156 case 2: 157 rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "frame_buffer", (int)FRAMEBUFFER_SIZE); 158 break; 159 case 3: 160 rb->snprintf(buf, buf_len, "%s: [W: %d H:%d] ", "LCD", LCD_WIDTH, LCD_HEIGHT); 161 break; 162 case 4: 163 rb->snprintf(buf, buf_len, "%s: [%d bits] ", "fb_data", (int)(sizeof(fb_data) * CHAR_BIT)); 164 break; 165 case 5: 166 break; 167 } 168 return buf; 169} 170 171static const char *menu_button_test_name_cb(int selected_item, void* data, 172 char* buf, size_t buf_len) 173{ 174 (void)data; 175 int curbtn = BUTTON_NONE; 176 buf[0] = '\0'; 177 switch(selected_item) 178 { 179 case 0: 180 rb->snprintf(buf, buf_len, "%s: [%s] ", "Button test", 181 m_test.count > 0 ? "true":"false"); 182 break; 183 case 1: 184 if (m_test.count > 0) 185 { 186 if (m_test.count <= 2) 187 curbtn = rb->button_get_w_tmo(HZ * 2); 188 else 189 m_test.last_btn_or_act = BUTTON_NONE; 190 if (curbtn == BUTTON_NONE) 191 { 192 m_test.count--; 193 } 194 else 195 m_test.last_btn_or_act = curbtn; 196 } 197 get_button_names(buf, buf_len, m_test.last_btn_or_act); 198 199 break; 200 } 201 return buf; 202} 203 204static const char *menu_action_test_name_cb(int selected_item, void* data, 205 char* buf, size_t buf_len) 206{ 207 (void)data; 208 const char *fmtstr; 209 int curact = ACTION_NONE; 210 buf[0] = '\0'; 211 switch(selected_item) 212 { 213 case 0: 214 rb->snprintf(buf, buf_len, "%s: [%s] ", "Action test", 215 m_test.count > 0 ? "true":"false"); 216 break; 217 case 1: 218 if (m_test.count <= 0) 219 { 220 if (m_test.context <= 0) 221 fmtstr = "%s > "; 222 else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER - 1) 223 fmtstr = "< %s "; 224 else 225 fmtstr = "< %s > "; 226 } 227 else 228 fmtstr = "%s"; 229 230 rb->snprintf(buf, buf_len, fmtstr, context_name(m_test.context)); 231 break; 232 case 2: 233 if (m_test.count > 0) 234 { 235 if (m_test.count <= 2) 236 curact = rb->get_action(m_test.context, HZ * 2); 237 else 238 m_test.last_btn_or_act = ACTION_NONE; 239 if (curact == ACTION_NONE && rb->button_get(false) == BUTTON_NONE) 240 { 241 m_test.count--; 242 } 243 else 244 { 245 m_test.last_btn_or_act = curact; 246 m_test.count = 2; 247 } 248 } 249 return action_name(m_test.last_btn_or_act); 250 251 break; 252 } 253 return buf; 254} 255 256static const char* list_get_name_cb(int selected_item, void* data, 257 char* buf, size_t buf_len) 258{ 259 buf[0] = '\0'; 260 if (data == MENU_ID(M_ROOT)) 261 return mainitem(selected_item)->name; 262 else if (selected_item == 0 && data != MENU_ID(M_TESTPUT)) /*header text*/ 263 return mainitem(main_last_sel)->name; 264 else if (selected_item >= mainitem(main_last_sel)->items - 1) 265 return ID2P(LANG_BACK); 266 267 if (data == MENU_ID(M_PATHS)) 268 { 269 selected_item--; 270 if (selected_item >= 0 && selected_item < mainitem(M_PATHS)->items) 271 { 272 const struct paths *cur = &paths[selected_item]; 273 rb->snprintf(buf, buf_len, "%s: [%s] ", cur->name, cur->path); 274 return buf; 275 } 276 } 277 else if (data == MENU_ID(M_BUTTONS)) 278 { 279 const struct available_button *btn = &available_buttons[selected_item - 1]; 280 rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value); 281 return buf; 282 } 283 else if (data == MENU_ID(M_BTNTEST)) 284 return menu_button_test_name_cb(selected_item - 1, data, buf, buf_len); 285 else if (data == MENU_ID(M_ACTIONS)) 286 return action_name(selected_item - 1); 287 else if (data == MENU_ID(M_CONTEXTS)) 288 return context_name(selected_item - 1); 289 else if (data == MENU_ID(M_ACTTEST)) 290 return menu_action_test_name_cb(selected_item - 1, data, buf, buf_len); 291 else if (data == MENU_ID(M_BUFFERS)) 292 { 293 const struct menu_buffer_t *bufm = &m_buffer[selected_item - 1]; 294 rb->snprintf(buf, buf_len, "%s: [%ld bytes] ", bufm->name, (long)bufm->size); 295 return buf; 296 } 297 else if (data == MENU_ID(M_PLUGINS)) 298 { 299 return menu_plugin_name_cb(selected_item - 1, data, buf, buf_len); 300 } 301 else if (data == MENU_ID(M_TESTPUT)) 302 { 303 rb->snprintf(buf, buf_len, "put_line item: [ %d ]$Text %d$Text LONGER TEST text %d $4$5$6$7$8$9", selected_item, 1, 2); 304 return buf; 305 } 306 return buf; 307} 308 309static int list_voice_cb(int list_index, void* data) 310{ 311 if (!rb->global_settings->talk_menu) 312 return -1; 313 314 if (data == MENU_ID(M_ROOT)) 315 { 316 const char * name = mainitem(list_index)->name; 317 long id = P2ID((const unsigned char *)name); 318 if(id>=0) 319 rb->talk_id(id, true); 320 else 321 rb->talk_spell(name, true); 322 } 323 else if (data == MENU_ID(M_BUFFERS) || data == MENU_ID(M_PLUGINS)) 324 { 325 char buf[64]; 326 const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); 327 long id = P2ID((const unsigned char *)name); 328 if(id>=0) 329 rb->talk_id(id, true); 330 else 331 { 332 char* bytstr = rb->strcasestr(name, "bytes"); 333 if (bytstr != NULL) 334 *bytstr = '\0'; 335 rb->talk_spell(name, true); 336 } 337 } 338 else if (data == MENU_ID(M_TESTPUT)) 339 { 340 char buf[64]; 341 const char* name = printcell_get_column_text(printcell_get_column_selected(), 342 buf, sizeof(buf)); 343 long id = P2ID((const unsigned char *)name); 344 if(id>=0) 345 rb->talk_id(id, true); 346 else 347 rb->talk_spell(name, true); 348 } 349 else 350 { 351 char buf[64]; 352 const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); 353 long id = P2ID((const unsigned char *)name); 354 if(id>=0) 355 rb->talk_id(id, true); 356 else 357 rb->talk_spell(name, true); 358 } 359 return 0; 360} 361 362int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists) 363{ 364 if (lists->data == MENU_ID(M_TESTPUT) && (selected_item < (mainitem(M_TESTPUT)->items) - 1)/*back*/) 365 { 366 if (*action == ACTION_STD_OK) 367 { 368 printcell_increment_column(1, true); 369 *action = ACTION_NONE; 370 } 371 else if (*action == ACTION_STD_CANCEL) 372 { 373 if (printcell_increment_column(-1, true) != testput_cols - 1) 374 { 375 *action = ACTION_NONE; 376 } 377 } 378 else if (*action == ACTION_STD_CONTEXT) 379 { 380 char buf[PRINTCELL_MAXLINELEN]; 381 char* bufp = buf; 382 int selcol = printcell_get_column_selected(); 383 bufp = printcell_get_column_text(selcol, bufp, PRINTCELL_MAXLINELEN); 384 rb->splashf(HZ * 2, "Item: %s", bufp); 385 } 386 } 387 else if (lists->data == MENU_ID(M_ACTTEST)) 388 { 389 if (selected_item == 2) /* context */ 390 { 391 int ctx = m_test.context; 392 if (*action == ACTION_STD_OK) 393 m_test.context++; 394 else if (*action == ACTION_STD_CANCEL) 395 m_test.context--; 396 397 if (m_test.context < 0) 398 m_test.context = 0; 399 else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER) 400 m_test.context = LAST_CONTEXT_PLACEHOLDER - 1; 401 402 if (ctx != m_test.context) 403 rb->gui_synclist_speak_item(lists); 404 405 goto default_handler; 406 } 407 if (*action == ACTION_STD_OK) 408 { 409 if (selected_item == 1 || selected_item == 3) 410 { 411 m_test.count = 3; 412 rb->gui_synclist_select_item(lists, 3); 413 } 414 } 415 } 416 else if (lists->data == MENU_ID(M_BTNTEST)) 417 { 418 if (*action == ACTION_STD_OK) 419 { 420 if (selected_item == 1 || selected_item == 2) 421 { 422 m_test.count = 3; 423 rb->gui_synclist_select_item(lists, 2); 424 } 425 } 426 } 427/* common */ 428 if (*action == ACTION_STD_OK) 429 { 430 if (lists->data == MENU_ID(M_ROOT)) 431 { 432 rb->memset(&m_test, 0, sizeof(struct menu_test_t)); 433 const struct mainmenu *cur = mainitem(selected_item); 434 435 if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT)) 436 *exit = true; 437 else 438 { 439 main_last_sel = selected_item; 440 441 if (cur->menuid == MENU_ID(M_TESTPUT)) 442 { 443 synclist_set(cur->menuid, 0, cur->items, 1); 444 printcell_enable(true); 445 //lists->callback_draw_item = test_listdraw_fn; 446 } 447 else 448 { 449 printcell_enable(false); 450 synclist_set(cur->menuid, 1, cur->items, 1); 451 } 452 rb->gui_synclist_draw(lists); 453 } 454 } 455 else if (selected_item <= 0) /* title */ 456 { 457 rb->gui_synclist_select_item(lists, 1); 458 } 459 else if (selected_item >= (mainitem(main_last_sel)->items) - 1)/*back*/ 460 { 461 *action = ACTION_STD_CANCEL; 462 } 463 else if (lists->data == MENU_ID(M_TESTPUT)) 464 { 465 466 } 467 else if (lists->data == MENU_ID(M_ACTIONS) || 468 lists->data == MENU_ID(M_CONTEXTS)) 469 { 470 char buf[MAX_PATH]; 471 const char *name = list_get_name_cb(selected_item, lists->data, buf, sizeof(buf)); 472 /* splash long enough to get fingers off button then wait for new button press */ 473 rb->splashf(HZ / 2, "%s %d (0x%X)", name, selected_item -1, selected_item -1); 474 rb->button_get(true); 475 } 476 } 477 if (*action == ACTION_STD_CANCEL) 478 { 479 if (lists->data == MENU_ID(M_TESTPUT)) 480 { 481 //lists->callback_draw_item = NULL; 482 printcell_enable(false); 483 } 484 if (lists->data != MENU_ID(M_ROOT)) 485 { 486 const struct mainmenu *mainm = &mainmenu[0]; 487 synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); 488 rb->gui_synclist_draw(lists); 489 } 490 else 491 *exit = true; 492 } 493default_handler: 494 if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED) 495 { 496 *exit = true; 497 return PLUGIN_USB_CONNECTED; 498 } 499 return PLUGIN_OK; 500} 501 502static void synclist_set(char* menu_id, int selected_item, int items, int sel_size) 503{ 504 if (items <= 0) 505 return; 506 if (selected_item < 0) 507 selected_item = 0; 508 509 list_voice_cb(0, menu_id); 510 rb->gui_synclist_init(&lists,list_get_name_cb, 511 menu_id, false, sel_size, NULL); 512 if (menu_id == MENU_ID(M_TESTPUT)) 513 { 514 testput_cols = printcell_set_columns(&lists, NULL, 515 TESTPUT_HEADER, Icon_Rockbox); 516 } 517 else 518 { 519 rb->gui_synclist_set_title(&lists, NULL,-1); 520 } 521 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); 522 rb->gui_synclist_set_nb_items(&lists,items); 523 rb->gui_synclist_select_item(&lists, selected_item); 524 525} 526 527enum plugin_status plugin_start(const void* parameter) 528{ 529 int ret = PLUGIN_OK; 530 int selected_item = -1; 531 int action; 532 bool redraw = true; 533 bool exit = false; 534 if (parameter) 535 { 536 // 537 } 538 mainmenu[M_BUTTONS].items = available_button_count; 539 /* add header and back item to each submenu */ 540 for (int i = 1; i < M_LAST_ITEM; i++) 541 mainmenu[i].items += 2; 542 mainmenu[M_TESTPUT].items -= 1; 543 if (!exit) 544 { 545 const struct mainmenu *mainm = &mainmenu[0]; 546 synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); 547 rb->gui_synclist_draw(&lists); 548 549 while (!exit) 550 { 551 action = rb->get_action(CONTEXT_LIST, HZ / 10); 552 if (m_test.count > 0) 553 action = ACTION_REDRAW; 554 555 if (action == ACTION_NONE) 556 { 557 if (redraw) 558 { 559 action = ACTION_REDRAW; 560 redraw = false; 561 } 562 } 563 else 564 redraw = true; 565 ret = menu_action_cb(&action, selected_item, &exit, &lists); 566 if (rb->gui_synclist_do_button(&lists, &action)) 567 continue; 568 selected_item = rb->gui_synclist_get_sel_pos(&lists); 569 } 570 } 571 printcell_enable(false); 572 return ret; 573}