/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2020 William Wilgus * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* WIP rb_info common info that you wonder about when rockboxing? */ #include "plugin.h" #include "lang_enum.h" #include "../open_plugin.h" #include "logf.h" #include "lib/action_helper.h" #include "lib/button_helper.h" #include "lib/pluginlib_actions.h" #define MENU_ID(x) (((void*)&"RPBUTACNGX\0" + x)) enum { M_ROOT = 0, M_PATHS, M_BUFFERS, M_BUTTONS, M_BTNTEST, M_ACTIONS, M_CONTEXTS, M_ACTTEST, M_PLUGINS, M_EXIT, M_LAST_ITEM //ITEM COUNT }; #define MENU_ID_PLUGINS_ITEMS 5 /*Action test and Button test*/ static struct menu_test_t { int count; int context; int last_btn_or_act; } m_test; struct menu_buffer_t { const char *name; size_t size;}; static const struct menu_buffer_t m_buffer[] = { #ifndef MAX_LOGF_SIZE #define MAX_LOGF_SIZE (0) #endif #ifndef CACHE_SIZE #define CACHE_SIZE (0) #endif {"thread stack", DEFAULT_STACK_SIZE}, {"plugin buffer", PLUGIN_BUFFER_SIZE}, {"frame_buffer", FRAMEBUFFER_SIZE}, {"codec_buffer", CODEC_SIZE}, {"logf_buffer", MAX_LOGF_SIZE}, {"cache", CACHE_SIZE}, }; /* stringify the macro value */ #define MACROVAL(x) MACROSTR(x) #define MACROSTR(x) #x static int main_last_sel = 0; static struct gui_synclist lists; static void synclist_set(char*, int, int, int); struct paths { const char *name; const char *path; }; static const struct paths paths[] = { {"Home", ""HOME_DIR}, {"Rockbox", ""ROCKBOX_DIR}, {"Plugins", ""PLUGIN_DIR}, {"Codecs", ""CODECS_DIR}, {"WPS", ""WPS_DIR}, {"SBS", ""SBS_DIR}, {"Theme", ""THEME_DIR}, {"Font", ""FONT_DIR}, {"Icon", ""ICON_DIR}, {"Backdrop", ""BACKDROP_DIR}, {"Eq", ""EQS_DIR}, {"Rec Presets", ""RECPRESETS_DIR}, {"Recordings", ""REC_BASE_DIR,}, {"Fm Presets", ""FMPRESET_PATH}, {"MAX_PATH", ""MACROVAL(MAX_PATH)" bytes"}, }; struct mainmenu { const char *name; void *menuid; int items;}; static struct mainmenu mainmenu[M_LAST_ITEM] = { #define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)COUNT} MENU_ITEM(M_ROOT, "Rockbox Info Plugin", M_LAST_ITEM), MENU_ITEM(M_PATHS, ID2P(LANG_SHOW_PATH), ARRAYLEN(paths)), MENU_ITEM(M_BUFFERS, ID2P(LANG_BUFFER_STAT), ARRAYLEN(m_buffer)), MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */ MENU_ITEM(M_BTNTEST, "Button test", 2), MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER), MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ), MENU_ITEM(M_ACTTEST, "Action test", 3), MENU_ITEM(M_PLUGINS, ID2P(LANG_PLUGINS), MENU_ID_PLUGINS_ITEMS), MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0), #undef MENU_ITEM }; static const struct mainmenu *mainitem(int selected_item) { static const struct mainmenu empty = {0}; if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu)) return &mainmenu[selected_item]; else return ∅ } static void cleanup(void *parameter) { (void)parameter; } static const char *menu_plugin_name_cb(int selected_item, void* data, char* buf, size_t buf_len) { (void)data; buf[0] = '\0'; switch(selected_item) { case 0: rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin_api", (int)sizeof(struct plugin_api)); break; case 1: rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin buffer", PLUGIN_BUFFER_SIZE); break; case 2: rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "frame_buffer", (int)FRAMEBUFFER_SIZE); break; case 3: rb->snprintf(buf, buf_len, "%s: [W: %d H:%d] ", "LCD", LCD_WIDTH, LCD_HEIGHT); break; case 4: rb->snprintf(buf, buf_len, "%s: [%d bits] ", "fb_data", (int)(sizeof(fb_data) * CHAR_BIT)); break; case 5: break; } return buf; } static const char *menu_button_test_name_cb(int selected_item, void* data, char* buf, size_t buf_len) { (void)data; int curbtn = BUTTON_NONE; buf[0] = '\0'; switch(selected_item) { case 0: rb->snprintf(buf, buf_len, "%s: [%s] ", "Button test", m_test.count > 0 ? "true":"false"); break; case 1: if (m_test.count > 0) { if (m_test.count <= 2) curbtn = rb->button_get_w_tmo(HZ * 2); else m_test.last_btn_or_act = BUTTON_NONE; if (curbtn == BUTTON_NONE) { m_test.count--; } else m_test.last_btn_or_act = curbtn; } get_button_names(buf, buf_len, m_test.last_btn_or_act); break; } return buf; } static const char *menu_action_test_name_cb(int selected_item, void* data, char* buf, size_t buf_len) { (void)data; const char *fmtstr; int curact = ACTION_NONE; buf[0] = '\0'; switch(selected_item) { case 0: rb->snprintf(buf, buf_len, "%s: [%s] ", "Action test", m_test.count > 0 ? "true":"false"); break; case 1: if (m_test.count <= 0) { if (m_test.context <= 0) fmtstr = "%s > "; else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER - 1) fmtstr = "< %s "; else fmtstr = "< %s > "; } else fmtstr = "%s"; rb->snprintf(buf, buf_len, fmtstr, context_name(m_test.context)); break; case 2: if (m_test.count > 0) { if (m_test.count <= 2) curact = rb->get_action(m_test.context, HZ * 2); else m_test.last_btn_or_act = ACTION_NONE; if (curact == ACTION_NONE && rb->button_get(false) == BUTTON_NONE) { m_test.count--; } else { m_test.last_btn_or_act = curact; m_test.count = 2; } } return action_name(m_test.last_btn_or_act); break; } return buf; } static const char* list_get_name_cb(int selected_item, void* data, char* buf, size_t buf_len) { buf[0] = '\0'; if (data == MENU_ID(M_ROOT)) return mainitem(selected_item)->name; else if (selected_item == 0) /*header text*/ return mainitem(main_last_sel)->name; else if (selected_item >= mainitem(main_last_sel)->items - 1) return ID2P(LANG_BACK); if (data == MENU_ID(M_PATHS)) { selected_item--; if (selected_item >= 0 && selected_item < mainitem(M_PATHS)->items) { const struct paths *cur = &paths[selected_item]; rb->snprintf(buf, buf_len, "%s: [%s] ", cur->name, cur->path); return buf; } } else if (data == MENU_ID(M_BUTTONS)) { const struct available_button *btn = &available_buttons[selected_item - 1]; rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value); return buf; } else if (data == MENU_ID(M_BTNTEST)) return menu_button_test_name_cb(selected_item - 1, data, buf, buf_len); else if (data == MENU_ID(M_ACTIONS)) return action_name(selected_item - 1); else if (data == MENU_ID(M_CONTEXTS)) return context_name(selected_item - 1); else if (data == MENU_ID(M_ACTTEST)) return menu_action_test_name_cb(selected_item - 1, data, buf, buf_len); else if (data == MENU_ID(M_BUFFERS)) { const struct menu_buffer_t *bufm = &m_buffer[selected_item - 1]; rb->snprintf(buf, buf_len, "%s: [%ld bytes] ", bufm->name, (long)bufm->size); return buf; } else if (data == MENU_ID(M_PLUGINS)) { return menu_plugin_name_cb(selected_item - 1, data, buf, buf_len); } return buf; } static int list_voice_cb(int list_index, void* data) { if (!rb->global_settings->talk_menu) return -1; if (data == MENU_ID(M_ROOT)) { const char * name = mainitem(list_index)->name; long id = P2ID((const unsigned char *)name); if(id>=0) rb->talk_id(id, true); else rb->talk_spell(name, true); } else if (data == MENU_ID(M_BUFFERS) || data == MENU_ID(M_PLUGINS)) { char buf[64]; const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); long id = P2ID((const unsigned char *)name); if(id>=0) rb->talk_id(id, true); else { char* bytstr = rb->strcasestr(name, "bytes"); if (bytstr != NULL) *bytstr = '\0'; rb->talk_spell(name, true); } } else { char buf[64]; const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); long id = P2ID((const unsigned char *)name); if(id>=0) rb->talk_id(id, true); else rb->talk_spell(name, true); } return 0; } int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclist *lists) { if (lists->data == MENU_ID(M_ACTTEST)) { if (selected_item == 2) /* context */ { int ctx = m_test.context; if (action == ACTION_STD_OK) m_test.context++; else if (action == ACTION_STD_CANCEL) m_test.context--; if (m_test.context < 0) m_test.context = 0; else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER) m_test.context = LAST_CONTEXT_PLACEHOLDER - 1; if (ctx != m_test.context) rb->gui_synclist_speak_item(lists); goto default_handler; } if (action == ACTION_STD_OK) { if (selected_item == 1 || selected_item == 3) { m_test.count = 3; rb->gui_synclist_select_item(lists, 3); } } } else if (lists->data == MENU_ID(M_BTNTEST)) { if (action == ACTION_STD_OK) { if (selected_item == 1 || selected_item == 2) { m_test.count = 3; rb->gui_synclist_select_item(lists, 2); } } } if (action == ACTION_STD_OK) { if (lists->data == MENU_ID(M_ROOT)) { rb->memset(&m_test, 0, sizeof(struct menu_test_t)); const struct mainmenu *cur = mainitem(selected_item); if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT)) *exit = true; else { main_last_sel = selected_item; synclist_set(cur->menuid, 1, cur->items, 1); rb->gui_synclist_draw(lists); } } else if (selected_item <= 0) /* title */ { rb->gui_synclist_select_item(lists, 1); } else if (selected_item >= (mainitem(main_last_sel)->items) - 1)/*back*/ { action = ACTION_STD_CANCEL; } else if (lists->data == MENU_ID(M_ACTIONS) || lists->data == MENU_ID(M_CONTEXTS)) { char buf[MAX_PATH]; const char *name = list_get_name_cb(selected_item, lists->data, buf, sizeof(buf)); /* splash long enough to get fingers off button then wait for new button press */ rb->splashf(HZ / 2, "%s %d (0x%X)", name, selected_item -1, selected_item -1); rb->button_get(true); } } if (action == ACTION_STD_CANCEL) { if (lists->data != MENU_ID(M_ROOT)) { const struct mainmenu *mainm = &mainmenu[0]; synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); rb->gui_synclist_draw(lists); } else *exit = true; } default_handler: if (rb->default_event_handler_ex(action, cleanup, NULL) == SYS_USB_CONNECTED) { *exit = true; return PLUGIN_USB_CONNECTED; } return PLUGIN_OK; } static void synclist_set(char* menu_id, int selected_item, int items, int sel_size) { if (items <= 0) return; if (selected_item < 0) selected_item = 0; list_voice_cb(0, menu_id); rb->gui_synclist_init(&lists,list_get_name_cb, menu_id, false, sel_size, NULL); rb->gui_synclist_set_icon_callback(&lists,NULL); rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); rb->gui_synclist_set_nb_items(&lists,items); rb->gui_synclist_limit_scroll(&lists,true); rb->gui_synclist_select_item(&lists, selected_item); } enum plugin_status plugin_start(const void* parameter) { int ret = PLUGIN_OK; int selected_item = -1; int action; bool redraw = true; bool exit = false; if (parameter) { // } mainmenu[M_BUTTONS].items = available_button_count; /* add header and back item to each submenu */ for (int i = 1; i < M_LAST_ITEM; i++) mainmenu[i].items += 2; if (!exit) { const struct mainmenu *mainm = &mainmenu[0]; synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); rb->gui_synclist_draw(&lists); while (!exit) { action = rb->get_action(CONTEXT_LIST, HZ / 10); if (m_test.count > 0) action = ACTION_REDRAW; if (action == ACTION_NONE) { if (redraw) { action = ACTION_REDRAW; redraw = false; } } else redraw = true; if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD)) continue; selected_item = rb->gui_synclist_get_sel_pos(&lists); ret = menu_action_cb(action, selected_item, &exit, &lists); } } return ret; }