A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1085 lines 36 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2007 Jonathan Gordon 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#include <stdio.h> 22#include <stdlib.h> 23#include <stdbool.h> 24#include "string-extra.h" 25#include "config.h" 26#include "appevents.h" 27#include "menu.h" 28#include "root_menu.h" 29#include "lang.h" 30#include "settings.h" 31#include "screens.h" 32#include "kernel.h" 33#include "debug.h" 34#include "misc.h" 35#include "open_plugin.h" 36#include "rolo.h" 37#include "powermgmt.h" 38#include "power.h" 39#include "talk.h" 40#include "audio.h" 41#include "shortcuts.h" 42 43#ifdef HAVE_HOTSWAP 44#include "storage.h" 45#include "mv.h" 46#endif 47/* gui api */ 48#include "list.h" 49#include "splash.h" 50#include "action.h" 51#include "yesno.h" 52#include "viewport.h" 53 54#include "tree.h" 55#if CONFIG_TUNER 56#include "radio.h" 57#endif 58#ifdef HAVE_RECORDING 59#include "recording.h" 60#endif 61#include "wps.h" 62#include "bookmark.h" 63#include "playlist.h" 64#include "playlist_viewer.h" 65#include "playlist_catalog.h" 66#include "menus/exported_menus.h" 67#ifdef HAVE_RTC_ALARM 68#include "rtc.h" 69#endif 70#ifdef HAVE_TAGCACHE 71#include "tagcache.h" 72#endif 73#include "language.h" 74#include "plugin.h" 75#include "disk.h" 76 77struct root_items { 78 int (*function)(void* param); 79 void* param; 80 const struct menu_item_ex *context_menu; 81}; 82static int next_screen = GO_TO_ROOT; /* holding info about the upcoming screen 83 * which is the current screen for the 84 * rest of the code after load_screen 85 * is called */ 86static int last_screen = GO_TO_ROOT; /* unfortunatly needed so we can resume 87 or goto current track based on previous 88 screen */ 89 90static int previous_music = GO_TO_WPS; /* Toggles behavior of the return-to 91 * playback-button depending 92 * on FM radio */ 93 94#if (CONFIG_TUNER) 95static void rootmenu_start_playback_callback(unsigned short id, void *param) 96{ 97 (void) id; (void) param; 98 /* Cancel FM radio selection as previous music. For cases where we start 99 playback without going to the WPS, such as playlist insert or 100 playlist catalog. */ 101 previous_music = GO_TO_WPS; 102} 103#endif 104 105static char current_track_path[MAX_PATH]; 106static void rootmenu_track_changed_callback(unsigned short id, void* param) 107{ 108 (void)id; 109 struct mp3entry *id3 = ((struct track_event *)param)->id3; 110 strmemccpy(current_track_path, id3->path, MAX_PATH); 111} 112static int browser(void* param) 113{ 114 int ret_val; 115#ifdef HAVE_TAGCACHE 116 struct tree_context* tc = tree_get_context(); 117#endif 118 int filter = SHOW_SUPPORTED; 119 char folder[MAX_PATH] = "/"; 120 /* stuff needed to remember position in file browser */ 121 static char last_folder[MAX_PATH] = "/"; 122 /* and stuff for the database browser */ 123#ifdef HAVE_TAGCACHE 124 static int last_db_dirlevel = 0, last_db_selection = 0, last_ft_dirlevel = 0; 125#endif 126 127 switch ((intptr_t)param) 128 { 129 case GO_TO_FILEBROWSER: 130 filter = global_settings.dirfilter; 131 if (global_settings.browse_current && 132 last_screen == GO_TO_WPS && 133 current_track_path[0]) 134 { 135 strcpy(folder, current_track_path); 136 } 137 else if (!strcmp(last_folder, "/")) 138 { 139 strcpy(folder, global_settings.start_directory); 140 } 141 else 142 { 143#ifdef HAVE_HOTSWAP 144 bool in_hotswap = false; 145 /* handle entering an ejected drive */ 146 int i; 147 for (i = 0; i < NUM_VOLUMES; i++) 148 { 149 char vol_string[VOL_MAX_LEN + 1]; 150 if (!volume_removable(i)) 151 continue; 152 get_volume_name(i, vol_string); 153 /* test whether we would browse the external card */ 154 if (!volume_present(i) && 155 (strstr(last_folder, vol_string) 156#ifdef HAVE_HOTSWAP_STORAGE_AS_MAIN 157 || (i == 0) 158#endif 159 )) 160 { /* leave folder as "/" to avoid crash when trying 161 * to access an ejected drive */ 162 strcpy(folder, "/"); 163 in_hotswap = true; 164 break; 165 } 166 } 167 if (!in_hotswap) 168#endif /*HAVE_HOTSWAP*/ 169 strcpy(folder, last_folder); 170 } 171 push_current_activity(ACTIVITY_FILEBROWSER); 172 break; 173#ifdef HAVE_TAGCACHE 174 case GO_TO_DBBROWSER: 175 if (!tagcache_is_usable()) 176 { 177 bool reinit_attempted = false; 178 179 /* Now display progress until it's ready or the user exits */ 180 while(!tagcache_is_usable()) 181 { 182 struct tagcache_stat *stat = tagcache_get_stat(); 183 184 /* Allow user to exit */ 185 if (action_userabort(HZ/2)) 186 break; 187 188 /* Maybe just needs to reboot due to delayed commit */ 189 if (stat->commit_delayed) 190 { 191 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); 192 break; 193 } 194 195 /* Check if ready status is known */ 196 if (!stat->readyvalid) 197 { 198 splash(0, ID2P(LANG_TAGCACHE_BUSY)); 199 continue; 200 } 201 202 /* Re-init if required */ 203 if (!reinit_attempted && !stat->ready && 204 stat->processed_entries == 0 && stat->commit_step == 0) 205 { 206 /* Prompt the user */ 207 reinit_attempted = true; 208 static const char *lines[]={ 209 ID2P(LANG_TAGCACHE_BUSY), ID2P(LANG_TAGCACHE_FORCE_UPDATE)}; 210 static const struct text_message message={lines, 2}; 211 if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO) 212 break; 213 FOR_NB_SCREENS(i) 214 screens[i].clear_display(); 215 216 /* Start initialisation */ 217 tagcache_rebuild(); 218 } 219 220 /* Display building progress */ 221 static long talked_tick = 0; 222 if(global_settings.talk_menu && 223 (talked_tick == 0 224 || TIME_AFTER(current_tick, talked_tick+7*HZ))) 225 { 226 talked_tick = current_tick; 227 if (stat->commit_step > 0) 228 { 229 talk_id(LANG_TAGCACHE_INIT, false); 230 talk_number(stat->commit_step, true); 231 talk_id(VOICE_OF, true); 232 talk_number(tagcache_get_max_commit_step(), true); 233 } else if(stat->processed_entries) 234 { 235 talk_number(stat->processed_entries, false); 236 talk_id(LANG_BUILDING_DATABASE, true); 237 } 238 } 239 if (stat->commit_step > 0) 240 { 241 /* (prevent redundant voicing by splash_progress */ 242 bool tmp = global_settings.talk_menu; 243 global_settings.talk_menu = false; 244 245 if (lang_is_rtl()) 246 { 247 splash_progress(stat->commit_step, 248 tagcache_get_max_commit_step(), 249 "[%d/%d] %s", stat->commit_step, 250 tagcache_get_max_commit_step(), 251 str(LANG_TAGCACHE_INIT)); 252 } 253 else 254 { 255 splash_progress(stat->commit_step, 256 tagcache_get_max_commit_step(), 257 "%s [%d/%d]", str(LANG_TAGCACHE_INIT), 258 stat->commit_step, 259 tagcache_get_max_commit_step()); 260 } 261 global_settings.talk_menu = tmp; 262 } 263 else 264 { 265 splashf(0, str(LANG_BUILDING_DATABASE), 266 stat->processed_entries); /* (voiced above) */ 267 } 268 } 269 } 270 if (!tagcache_is_usable()) 271 return GO_TO_PREVIOUS; 272 filter = SHOW_ID3DB; 273 last_ft_dirlevel = tc->dirlevel; 274 tc->dirlevel = last_db_dirlevel; 275 tc->selected_item = last_db_selection; 276 push_current_activity(ACTIVITY_DATABASEBROWSER); 277 break; 278#endif /*HAVE_TAGCACHE*/ 279 } 280 281 struct browse_context browse = { 282 .dirfilter = filter, 283 .icon = Icon_NOICON, 284 .root = folder, 285 }; 286 287 ret_val = rockbox_browse(&browse); 288 289 if (ret_val == GO_TO_WPS 290 || ret_val == GO_TO_PREVIOUS_MUSIC 291 || ret_val == GO_TO_PLUGIN) 292 pop_current_activity_without_refresh(); 293 else 294 pop_current_activity(); 295 296 switch ((intptr_t)param) 297 { 298 case GO_TO_FILEBROWSER: 299 if (!get_current_file(last_folder, MAX_PATH) || 300 (!strchr(&last_folder[1], '/') && 301 global_settings.start_directory[1] != '\0')) 302 { 303 last_folder[0] = '/'; 304 last_folder[1] = '\0'; 305 } 306 break; 307#ifdef HAVE_TAGCACHE 308 case GO_TO_DBBROWSER: 309 last_db_dirlevel = tc->dirlevel; 310 last_db_selection = tc->selected_item; 311 tc->dirlevel = last_ft_dirlevel; 312 break; 313#endif 314 } 315 return ret_val; 316} 317 318#ifdef HAVE_RECORDING 319static int recscrn(void* param) 320{ 321 (void)param; 322 recording_screen(false); 323 return GO_TO_ROOT; 324} 325#endif 326static int wpsscrn(void* param) 327{ 328 int ret_val = GO_TO_PREVIOUS; 329 int audstatus = audio_status(); 330 (void)param; 331 push_current_activity(ACTIVITY_WPS); 332 333#ifdef HAVE_PITCHCONTROL 334 if (!audstatus) 335 { 336 sound_set_pitch(global_status.resume_pitch); 337 dsp_set_timestretch(global_status.resume_speed); 338 } 339#endif 340 341 if (audstatus) 342 { 343 talk_shutup(); 344 ret_val = gui_wps_show(); 345 } 346 else if (global_status.resume_index != -1) 347 { 348 DEBUGF("Resume index %d crc32 %lX offset %lX\n", 349 global_status.resume_index, 350 (unsigned long)global_status.resume_crc32, 351 (unsigned long)global_status.resume_offset); 352 if (playlist_resume() != -1) 353 { 354 playlist_resume_track(global_status.resume_index, 355 global_status.resume_crc32, 356 global_status.resume_elapsed, 357 global_status.resume_offset); 358 ret_val = gui_wps_show(); 359 } 360 } 361 else if (!file_exists(PLAYLIST_CONTROL_FILE)) 362 splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME)); 363 else if (yesno_pop(ID2P(LANG_REPLAY_FINISHED_PLAYLIST)) && 364 playlist_resume() != -1) 365 { 366 playlist_start(0, 0, 0); 367 ret_val = gui_wps_show(); 368 } 369 370 if (ret_val == GO_TO_PLAYLIST_VIEWER 371 || ret_val == GO_TO_PLUGIN 372 || ret_val == GO_TO_WPS 373 || ret_val == GO_TO_PREVIOUS_MUSIC 374 || ret_val == GO_TO_PREVIOUS_BROWSER 375 || (ret_val == GO_TO_PREVIOUS 376 && (last_screen == GO_TO_MAINMENU /* Settings */ 377 || last_screen == GO_TO_BROWSEPLUGINS 378 || last_screen == GO_TO_SYSTEM_SCREEN 379 || last_screen == GO_TO_PLAYLISTS_SCREEN))) 380 { 381 pop_current_activity_without_refresh(); 382 } 383 else 384 pop_current_activity(); 385 386 return ret_val; 387} 388#if CONFIG_TUNER 389static int radio(void* param) 390{ 391 (void)param; 392 radio_screen(); 393 return GO_TO_ROOT; 394} 395#endif 396 397static int miscscrn(void * param) 398{ 399 const struct menu_item_ex *menu = (const struct menu_item_ex*)param; 400 int result = do_menu(menu, NULL, NULL, false); 401 switch (result) 402 { 403 case GO_TO_PLUGIN: 404 case GO_TO_PLAYLIST_VIEWER: 405 case GO_TO_WPS: 406 case GO_TO_PREVIOUS_MUSIC: 407 return result; 408 default: 409 return GO_TO_ROOT; 410 } 411} 412 413 414static int playlist_view_catalog(void * param) 415{ 416 (void)param; 417 push_current_activity(ACTIVITY_PLAYLISTBROWSER); 418 bool item_was_selected = catalog_view_playlists(); 419 420 if (item_was_selected) 421 { 422 pop_current_activity_without_refresh(); 423 return GO_TO_WPS; 424 } 425 pop_current_activity(); 426 return GO_TO_ROOT; 427} 428 429static int playlist_view(void * param) 430{ 431 (void)param; 432 int val; 433 434 val = playlist_viewer(); 435 switch (val) 436 { 437 case PLAYLIST_VIEWER_MAINMENU: 438 case PLAYLIST_VIEWER_USB: 439 return GO_TO_ROOT; 440 case PLAYLIST_VIEWER_OK: 441 return GO_TO_PREVIOUS; 442 } 443 return GO_TO_PREVIOUS; 444} 445 446static int load_bmarks(void* param) 447{ 448 (void)param; 449 if(bookmark_mrb_load()) 450 return GO_TO_WPS; 451 return GO_TO_PREVIOUS; 452} 453 454/* These are all static const'd from apps/menus/ *.c 455 so little hack so we can use them */ 456extern struct menu_item_ex 457 file_menu, 458#ifdef HAVE_TAGCACHE 459 tagcache_menu, 460#endif 461 main_menu_, 462 manage_settings, 463 plugin_menu, 464 playlist_options, 465 info_menu, 466 system_menu; 467static const struct root_items items[] = { 468 [GO_TO_FILEBROWSER] = { browser, (void*)GO_TO_FILEBROWSER, &file_menu}, 469#ifdef HAVE_TAGCACHE 470 [GO_TO_DBBROWSER] = { browser, (void*)GO_TO_DBBROWSER, &tagcache_menu }, 471#endif 472 [GO_TO_WPS] = { wpsscrn, NULL, &playback_settings }, 473 [GO_TO_MAINMENU] = { miscscrn, (struct menu_item_ex*)&main_menu_, 474 &manage_settings }, 475 476#ifdef HAVE_RECORDING 477 [GO_TO_RECSCREEN] = { recscrn, NULL, &recording_settings_menu }, 478#endif 479 480#if CONFIG_TUNER 481 [GO_TO_FM] = { radio, NULL, &radio_settings_menu }, 482#endif 483 484 [GO_TO_RECENTBMARKS] = { load_bmarks, NULL, &bookmark_settings_menu }, 485 [GO_TO_BROWSEPLUGINS] = { miscscrn, &plugin_menu, NULL }, 486 [GO_TO_PLAYLISTS_SCREEN] = { playlist_view_catalog, NULL, 487 &playlist_options }, 488 [GO_TO_PLAYLIST_VIEWER] = { playlist_view, NULL, &playlist_options }, 489 [GO_TO_SYSTEM_SCREEN] = { miscscrn, &info_menu, &system_menu }, 490 [GO_TO_SHORTCUTMENU] = { do_shortcut_menu, NULL, NULL }, 491 492}; 493//static const int nb_items = sizeof(items)/sizeof(*items); 494 495static int item_callback(int action, 496 const struct menu_item_ex *this_item, 497 struct gui_synclist *this_list); 498 499MENUITEM_RETURNVALUE(shortcut_menu, ID2P(LANG_SHORTCUTS), GO_TO_SHORTCUTMENU, 500 NULL, Icon_Bookmark); 501 502MENUITEM_RETURNVALUE(file_browser, ID2P(LANG_DIR_BROWSER), GO_TO_FILEBROWSER, 503 NULL, Icon_file_view_menu); 504#ifdef HAVE_TAGCACHE 505MENUITEM_RETURNVALUE(db_browser, ID2P(LANG_TAGCACHE), GO_TO_DBBROWSER, 506 NULL, Icon_Audio); 507#endif 508MENUITEM_RETURNVALUE(rocks_browser, ID2P(LANG_PLUGINS), GO_TO_BROWSEPLUGINS, 509 NULL, Icon_Plugin); 510 511static char *get_wps_item_name(int selected_item, void * data, 512 char *buffer, size_t buffer_len) 513{ 514 (void)selected_item; (void)data; (void)buffer; (void)buffer_len; 515 if (audio_status()) 516 return ID2P(LANG_NOW_PLAYING); 517 return ID2P(LANG_RESUME_PLAYBACK); 518} 519MENUITEM_RETURNVALUE_DYNTEXT(wps_item, GO_TO_WPS, NULL, get_wps_item_name, 520 NULL, NULL, Icon_Playback_menu); 521#ifdef HAVE_RECORDING 522MENUITEM_RETURNVALUE(rec, ID2P(LANG_RECORDING), GO_TO_RECSCREEN, 523 NULL, Icon_Recording); 524#endif 525#if CONFIG_TUNER 526MENUITEM_RETURNVALUE(fm, ID2P(LANG_FM_RADIO), GO_TO_FM, 527 item_callback, Icon_Radio_screen); 528#endif 529MENUITEM_RETURNVALUE(menu_, ID2P(LANG_SETTINGS), GO_TO_MAINMENU, 530 NULL, Icon_Submenu_Entered); 531MENUITEM_RETURNVALUE(bookmarks, ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS), 532 GO_TO_RECENTBMARKS, item_callback, 533 Icon_Bookmark); 534MENUITEM_RETURNVALUE(playlists, ID2P(LANG_PLAYLISTS), GO_TO_PLAYLISTS_SCREEN, 535 NULL, Icon_Playlist); 536MENUITEM_RETURNVALUE(system_menu_, ID2P(LANG_SYSTEM), GO_TO_SYSTEM_SCREEN, 537 NULL, Icon_System_menu); 538 539struct menu_item_ex root_menu_; 540static struct menu_callback_with_desc root_menu_desc = { 541 item_callback, ID2P(LANG_ROCKBOX_TITLE), Icon_Rockbox }; 542 543static struct menu_table menu_table[] = { 544 /* Order here represents the default ordering */ 545 { "bookmarks", &bookmarks }, 546 { "files", &file_browser }, 547#ifdef HAVE_TAGCACHE 548 { "database", &db_browser }, 549#endif 550 { "wps", &wps_item }, 551 { "settings", &menu_ }, 552#ifdef HAVE_RECORDING 553 { "recording", &rec }, 554#endif 555#if CONFIG_TUNER 556 { "radio", &fm }, 557#endif 558 { "playlists", &playlists }, 559 { "plugins", &rocks_browser }, 560 { "system_menu", &system_menu_ }, 561 { "shortcuts", &shortcut_menu }, 562}; 563#define MAX_MENU_ITEMS (sizeof(menu_table) / sizeof(struct menu_table)) 564static struct menu_item_ex *root_menu__[MAX_MENU_ITEMS]; 565 566struct menu_table *root_menu_get_options(int *nb_options) 567{ 568 *nb_options = MAX_MENU_ITEMS; 569 570 return menu_table; 571} 572 573void root_menu_load_from_cfg(void* setting, char *value) 574{ 575 char *next = value, *start, *end; 576 unsigned int menu_item_count = 0, i; 577 bool main_menu_added = false; 578 579 if (*value == '-') 580 { 581 root_menu_set_default(setting, NULL); 582 return; 583 } 584 root_menu_.flags = MENU_HAS_DESC | MT_MENU; 585 root_menu_.submenus = (const struct menu_item_ex **)&root_menu__; 586 root_menu_.callback_and_desc = &root_menu_desc; 587 588 while (next && menu_item_count < MAX_MENU_ITEMS) 589 { 590 start = next; 591 next = strchr(next, ','); 592 if (next) 593 { 594 *next = '\0'; 595 next++; 596 } 597 start = skip_whitespace(start); 598 if ((end = strchr(start, ' '))) 599 *end = '\0'; 600 for (i=0; i<MAX_MENU_ITEMS; i++) 601 { 602 if (*start && !strcmp(start, menu_table[i].string)) 603 { 604 root_menu__[menu_item_count++] = (struct menu_item_ex *)menu_table[i].item; 605 if (menu_table[i].item == &menu_) 606 main_menu_added = true; 607 break; 608 } 609 } 610 } 611 if (!main_menu_added) 612 root_menu__[menu_item_count++] = (struct menu_item_ex *)&menu_; 613 root_menu_.flags |= MENU_ITEM_COUNT(menu_item_count); 614 *(bool*)setting = true; 615} 616 617char* root_menu_write_to_cfg(void* setting, char*buf, int buf_len) 618{ 619 (void)setting; 620 unsigned i, written, j; 621 for (i = 0; i < MENU_GET_COUNT(root_menu_.flags); i++) 622 { 623 for (j=0; j<MAX_MENU_ITEMS; j++) 624 { 625 if (menu_table[j].item == root_menu__[i]) 626 { 627 written = snprintf(buf, buf_len, "%s, ", menu_table[j].string); 628 buf_len -= written; 629 buf += written; 630 break; 631 } 632 } 633 } 634 return buf; 635} 636 637void root_menu_set_default(void* setting, void* defaultval) 638{ 639 unsigned i; 640 (void)defaultval; 641 642 root_menu_.flags = MENU_HAS_DESC | MT_MENU; 643 root_menu_.submenus = (const struct menu_item_ex **)&root_menu__; 644 root_menu_.callback_and_desc = &root_menu_desc; 645 646 for (i=0; i<MAX_MENU_ITEMS; i++) 647 { 648 root_menu__[i] = (struct menu_item_ex *)menu_table[i].item; 649 } 650 root_menu_.flags |= MENU_ITEM_COUNT(MAX_MENU_ITEMS); 651 *(bool*)setting = false; 652} 653 654bool root_menu_is_changed(void* setting, void* defaultval) 655{ 656 (void)defaultval; 657 return *(bool*)setting; 658} 659 660static int item_callback(int action, 661 const struct menu_item_ex *this_item, 662 struct gui_synclist *this_list) 663{ 664 (void)this_list; 665 switch (action) 666 { 667 case ACTION_TREE_STOP: 668 return ACTION_REDRAW; 669 case ACTION_REQUEST_MENUITEM: 670#if CONFIG_TUNER 671 if (this_item == &fm) 672 { 673 if (radio_hardware_present() == 0) 674 return ACTION_EXIT_MENUITEM; 675 } 676 else 677#endif 678 if (this_item == &bookmarks) 679 { 680 if (global_settings.usemrb == 0) 681 return ACTION_EXIT_MENUITEM; 682 } 683 break; 684 } 685 return action; 686} 687 688static int get_selection(int last_screen) 689{ 690 int i; 691 int len = ARRAYLEN(root_menu__); 692 for(i=0; i < len; i++) 693 { 694 if (((root_menu__[i]->flags&MENU_TYPE_MASK) == MT_RETURN_VALUE) && 695 (root_menu__[i]->value == last_screen)) 696 { 697 return i; 698 } 699 } 700 return 0; 701} 702 703static inline int load_screen(int screen) 704{ 705 /* set the global_status.last_screen before entering, 706 if we dont we will always return to the wrong screen on boot */ 707 int old_previous = last_screen; 708 int ret_val; 709 enum current_activity activity = ACTIVITY_UNKNOWN; 710 if (screen <= GO_TO_ROOT) 711 return screen; 712 if (screen == old_previous) 713 old_previous = GO_TO_ROOT; 714 global_status.last_screen = (char)screen; 715 status_save(false); 716 717 if (screen == GO_TO_BROWSEPLUGINS) 718 activity = ACTIVITY_PLUGINBROWSER; 719 else if (screen == GO_TO_MAINMENU) 720 activity = ACTIVITY_SETTINGS; 721 else if (screen == GO_TO_SYSTEM_SCREEN) 722 activity = ACTIVITY_SYSTEMSCREEN; 723 724 if (activity != ACTIVITY_UNKNOWN) 725 push_current_activity(activity); 726 727 ret_val = items[screen].function(items[screen].param); 728 729 if (activity != ACTIVITY_UNKNOWN) 730 { 731 if (ret_val == GO_TO_PLUGIN 732 || ret_val == GO_TO_WPS 733 || ret_val == GO_TO_PREVIOUS_MUSIC 734 || ret_val == GO_TO_PREVIOUS_BROWSER 735 || ret_val == GO_TO_FILEBROWSER) 736 { 737 pop_current_activity_without_refresh(); 738 } 739 else 740 pop_current_activity(); 741 } 742 743 last_screen = screen; 744 if (ret_val == GO_TO_PREVIOUS) 745 last_screen = old_previous; 746 return ret_val; 747} 748 749static int load_context_screen(int selection) 750{ 751 const struct menu_item_ex *context_menu = NULL; 752 int retval = GO_TO_PREVIOUS; 753 push_current_activity(ACTIVITY_CONTEXTMENU); 754 if ((root_menu__[selection]->flags&MENU_TYPE_MASK) == MT_RETURN_VALUE) 755 { 756 int item = root_menu__[selection]->value; 757 context_menu = items[item].context_menu; 758 } 759 /* special cases */ 760 else if (root_menu__[selection] == &info_menu) 761 { 762 context_menu = &system_menu; 763 } 764 765 if (context_menu) 766 retval = do_menu(context_menu, NULL, NULL, false); 767 pop_current_activity(); 768 return retval; 769} 770 771static int load_plugin_screen(char *key) 772{ 773 int ret_val = PLUGIN_ERROR; 774 int loops = 100; 775 int old_previous = last_screen; 776 int old_global = global_status.last_screen; 777 last_screen = next_screen; 778 global_status.last_screen = (char)next_screen; 779 780 while(loops-- > 0) /* just to keep things from getting out of hand */ 781 { 782 int opret = open_plugin_load_entry(key); 783 struct open_plugin_entry_t *op_entry = open_plugin_get_entry(); 784 char *path = op_entry->path; 785 char *param = op_entry->param; 786 if (param[0] == '\0') 787 param = NULL; 788 if (path[0] == '\0' && key) 789 path = P2STR((unsigned char *)key); 790 int ret = plugin_load(path, param); 791 792 if (ret == PLUGIN_USB_CONNECTED || ret == PLUGIN_ERROR) 793 ret_val = GO_TO_ROOT; 794 else if (ret == PLUGIN_GOTO_WPS) 795 ret_val = GO_TO_WPS; 796 else if (ret == PLUGIN_GOTO_PLUGIN) 797 { 798 if(op_entry->lang_id == LANG_OPEN_PLUGIN) 799 { 800 if (key == (char*)ID2P(LANG_SHORTCUTS)) 801 { 802 op_entry->lang_id = LANG_SHORTCUTS; 803 } 804 else /* Bugfix ensure proper key */ 805 { 806 key = ID2P(LANG_OPEN_PLUGIN); 807 } 808 } 809 continue; 810 } 811 else 812 { 813 ret_val = GO_TO_PREVIOUS; 814 /* Prevents infinite loop with WPS, Plugins, Previous Screen*/ 815 if (ret == PLUGIN_OK && old_global == GO_TO_WPS && !audio_status()) 816 ret_val = GO_TO_ROOT; 817 last_screen = (old_previous == next_screen || old_global == GO_TO_ROOT) 818 ? GO_TO_ROOT : old_previous; 819 if (last_screen == GO_TO_ROOT) 820 global_status.last_screen = GO_TO_ROOT; 821 } 822 /* ret_val != GO_TO_PLUGIN */ 823 824 if (opret != OPEN_PLUGIN_NEEDS_FLUSHED || last_screen != GO_TO_WPS) 825 { 826 /* Keep the entry in case of GO_TO_PREVIOUS */ 827 op_entry->hash = 0; /*remove hash -- prevents flush to disk */ 828 op_entry->lang_id = LANG_PREVIOUS_SCREEN; 829 /*open_plugin_add_path(NULL, NULL, NULL);// clear entry */ 830 } 831 break; 832 } /*while */ 833 return ret_val; 834} 835 836static void ignore_back_button_stub(bool ignore) 837{ 838#if (CONFIG_PLATFORM&PLATFORM_ANDROID) 839 /* BACK button to be handled by Android instead of rockbox */ 840 android_ignore_back_button(ignore); 841#else 842 (void) ignore; 843#endif 844} 845 846static int root_menu_setup_screens(void) 847{ 848 int new_screen = next_screen; 849 if (global_settings.start_in_screen == 0) 850 new_screen = (int)global_status.last_screen; 851 else new_screen = global_settings.start_in_screen - 2; 852 if (new_screen == GO_TO_PLUGIN) 853 { 854 if (global_status.last_screen == GO_TO_SHORTCUTMENU) 855 { 856 /* Can make this any value other than GO_TO_SHORTCUTMENU 857 otherwise it takes over on startup when the user wanted 858 the plugin at key - LANG_START_SCREEN */ 859 global_status.last_screen = GO_TO_PLUGIN; 860 } 861 if(global_status.last_screen == GO_TO_SHORTCUTMENU || 862 global_status.last_screen == GO_TO_PLUGIN) 863 { 864 if (global_settings.start_in_screen == 0) 865 { /* Start in: Previous Screen */ 866 last_screen = GO_TO_PREVIOUS; 867 global_status.last_screen = GO_TO_ROOT; 868 /* since the plugin has GO_TO_PLUGIN as origin it 869 will just return GO_TO_PREVIOUS <=> GO_TO_PLUGIN in a loop 870 To allow exit after restart we check for GO_TO_ROOT 871 if so exit to ROOT after the plugin exits */ 872 } 873 } 874 } 875#if CONFIG_TUNER 876 add_event(PLAYBACK_EVENT_START_PLAYBACK, rootmenu_start_playback_callback); 877#endif 878 add_event(PLAYBACK_EVENT_TRACK_CHANGE, rootmenu_track_changed_callback); 879#ifdef HAVE_RTC_ALARM 880 int alarm_wake_up_screen = 0; 881 if ( rtc_check_alarm_started(true) ) 882 { 883 rtc_enable_alarm(false); 884 885#if (defined(HAVE_RECORDING) || CONFIG_TUNER) 886 alarm_wake_up_screen = global_settings.alarm_wake_up_screen; 887#endif 888 switch (alarm_wake_up_screen) 889 { 890#if CONFIG_TUNER 891 case ALARM_START_FM: 892 new_screen = GO_TO_FM; 893 break; 894#endif 895#ifdef HAVE_RECORDING 896 case ALARM_START_REC: 897 recording_start_automatic = true; 898 new_screen = GO_TO_RECSCREEN; 899 break; 900#endif 901 default: 902 new_screen = GO_TO_WPS; 903 break; 904 } /* switch() */ 905 } 906#endif /* HAVE_RTC_ALARM */ 907 908#if defined(HAVE_HEADPHONE_DETECTION) || defined(HAVE_LINEOUT_DETECTION) 909 if (new_screen == GO_TO_WPS && global_settings.unplug_autoresume) 910 { 911 new_screen = GO_TO_ROOT; 912#ifdef HAVE_HEADPHONE_DETECTION 913 if (headphones_inserted()) 914 new_screen = GO_TO_WPS; 915#endif 916#ifdef HAVE_LINEOUT_DETECTION 917 if (lineout_inserted()) 918 new_screen = GO_TO_WPS; 919#endif 920 } 921#endif /*(HAVE_HEADPHONE_DETECTION) || (HAVE_LINEOUT_DETECTION)*/ 922 return new_screen; 923} 924 925static int browser_default(void) 926{ 927 switch (global_settings.browser_default) 928 { 929#ifdef HAVE_TAGCACHE 930 case BROWSER_DEFAULT_DB: 931 return GO_TO_DBBROWSER; 932#endif 933 case BROWSER_DEFAULT_PL_CAT: 934 return GO_TO_PLAYLISTS_SCREEN; 935 case BROWSER_DEFAULT_FILES: 936 default: 937 return GO_TO_FILEBROWSER; 938 } 939} 940 941void root_menu(void) 942{ 943 int previous_browser = browser_default(); 944 int selected = 0; 945 int shortcut_origin = GO_TO_ROOT; 946 947 push_current_activity(ACTIVITY_MAINMENU); 948 next_screen = root_menu_setup_screens(); 949 950 while (true) 951 { 952 switch (next_screen) 953 { 954 case MENU_ATTACHED_USB: 955 case MENU_SELECTED_EXIT: 956 /* fall through */ 957 case GO_TO_ROOT: 958 if (last_screen != GO_TO_ROOT) 959 selected = get_selection(last_screen); 960 global_status.last_screen = GO_TO_ROOT; /* We've returned to ROOT */ 961 /* When we are in the main menu we want the hardware BACK 962 * button to be handled by HOST instead of rockbox */ 963 ignore_back_button_stub(true); 964 965 next_screen = do_menu(&root_menu_, &selected, NULL, false); 966 967 ignore_back_button_stub(false); 968 969 if (next_screen != GO_TO_PREVIOUS) 970 last_screen = GO_TO_ROOT; 971 break; 972#ifdef HAVE_TAGCACHE 973 case GO_TO_DBBROWSER: 974#endif 975 case GO_TO_FILEBROWSER: 976 case GO_TO_PLAYLISTS_SCREEN: 977 previous_browser = next_screen; 978 goto load_next_screen; 979 break; 980#if CONFIG_TUNER 981 case GO_TO_WPS: 982 case GO_TO_FM: 983 previous_music = next_screen; 984 goto load_next_screen; 985 break; 986#endif /* With !CONFIG_TUNER previous_music is always GO_TO_WPS */ 987 988 case GO_TO_PREVIOUS: 989 { 990 next_screen = last_screen; 991 if (last_screen == GO_TO_PLUGIN)/* for WPS */ 992 last_screen = GO_TO_PREVIOUS; 993 else if (last_screen == GO_TO_PREVIOUS) 994 next_screen = GO_TO_ROOT; 995 break; 996 } 997 998 case GO_TO_PREVIOUS_BROWSER: 999 next_screen = previous_browser; 1000 break; 1001 1002 case GO_TO_PREVIOUS_MUSIC: 1003 next_screen = previous_music; 1004 break; 1005 case GO_TO_ROOTITEM_CONTEXT: 1006 next_screen = load_context_screen(selected); 1007 break; 1008 case GO_TO_PLUGIN: 1009 { 1010 1011 char *key; 1012 if (global_status.last_screen == GO_TO_SHORTCUTMENU) 1013 { 1014 struct open_plugin_entry_t *op_entry = open_plugin_get_entry(); 1015 if (op_entry->lang_id == LANG_OPEN_PLUGIN) 1016 op_entry->lang_id = LANG_SHORTCUTS; 1017 shortcut_origin = last_screen; 1018 key = ID2P(LANG_SHORTCUTS); 1019 } 1020 else 1021 { 1022 switch (last_screen) 1023 { 1024 case GO_TO_ROOT: 1025 key = ID2P(LANG_START_SCREEN); 1026 break; 1027 case GO_TO_WPS: 1028 key = ID2P(LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN); 1029 break; 1030 case GO_TO_SHORTCUTMENU: 1031 key = ID2P(LANG_SHORTCUTS); 1032 break; 1033 case GO_TO_PREVIOUS: 1034 key = ID2P(LANG_PREVIOUS_SCREEN); 1035 break; 1036 default: 1037 key = ID2P(LANG_OPEN_PLUGIN); 1038 break; 1039 } 1040 } 1041 1042 1043 push_activity_without_refresh(ACTIVITY_UNKNOWN); /* prevent plugin_load */ 1044 next_screen = load_plugin_screen(key); /* from flashing root */ 1045 pop_current_activity_without_refresh(); /* menu activity */ 1046 1047 if (next_screen == GO_TO_PREVIOUS) 1048 { 1049 /* shortcuts may take several trips through the GO_TO_PLUGIN 1050 case make sure we preserve and restore the origin */ 1051 if(tree_get_context()->out_of_tree > 0) /* a shortcut has been selected */ 1052 { 1053 next_screen = GO_TO_FILEBROWSER; 1054 shortcut_origin = GO_TO_ROOT; 1055 /* note in some cases there is a screen to return to 1056 but the history is rewritten as if you browsed here 1057 from the root so return there when finished */ 1058 } 1059 else if (shortcut_origin != GO_TO_ROOT) 1060 { 1061 if (shortcut_origin != GO_TO_WPS) 1062 next_screen = shortcut_origin; 1063 shortcut_origin = GO_TO_ROOT; 1064 } 1065 /* skip GO_TO_PREVIOUS */ 1066 if (last_screen == GO_TO_BROWSEPLUGINS) 1067 { 1068 next_screen = last_screen; 1069 last_screen = GO_TO_PLUGIN; 1070 } 1071 } 1072 previous_browser = (next_screen != GO_TO_WPS) ? browser_default() : 1073 GO_TO_PLUGIN; 1074 break; 1075 } 1076 default: 1077 goto load_next_screen; 1078 break; 1079 } /* switch() */ 1080 continue; 1081load_next_screen: /* load_screen is inlined */ 1082 next_screen = load_screen(next_screen); 1083 } 1084 1085}