A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

rb_info plugin and button,action+context name helper

rb_info is just a test plugin

just some info from the running rockbox install

the real star here is the generator scripts to make actions_helper.c and button_helper.c

Change-Id: I23f7bbdae3f2cffca2490c4df40bb13b0b5d5064

authored by

William Wilgus and committed by
William Wilgus
b39acee3 3d07ec46

+908 -1
+1
apps/action.h
··· 129 129 CONTEXT_USB_HID_MODE_PRESENTATION, 130 130 CONTEXT_USB_HID_MODE_BROWSER, 131 131 CONTEXT_USB_HID_MODE_MOUSE, 132 + LAST_CONTEXT_PLACEHOLDER, 132 133 }; 133 134 134 135
+1
apps/plugins/CATEGORIES
··· 87 87 properties,viewers 88 88 quake,games 89 89 random_folder_advance_config,apps 90 + rb_info,demos 90 91 remote_control,apps 91 92 resistor,apps 92 93 reversi,games
+1
apps/plugins/SOURCES
··· 17 17 main_menu_config.c 18 18 properties.c 19 19 random_folder_advance_config.c 20 + rb_info.c 20 21 rockblox.c 21 22 search.c 22 23 settings_dumper.c
+2
apps/plugins/lib/SOURCES
··· 1 1 sha1.c 2 2 gcc-support.c 3 3 pluginlib_actions.c 4 + action_helper.c 5 + button_helper.c 4 6 helper.c 5 7 icon_helper.c 6 8 arg_helper.c
+1
apps/plugins/lib/action_helper.c
··· 1 + /*DUMMY_FILE_DONT_CHANGEME*/
+34
apps/plugins/lib/action_helper.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2021 William Wilgus 11 + * 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + /* action_helper provides a way to turn numeric action/context into strings 23 + * the file action_helper.c is generated at compile time 24 + * ACTION_ and CONTEXT_ are stripped from the strings and replaced when 25 + * action_name and context_name are called, 26 + * NOTE: both share the same static buffer sized as the largest string possible 27 + */ 28 + #ifndef _ACTION_HELPER_H_ 29 + #define _ACTION_HELPER_H_ 30 + 31 + char* action_name(int action); 32 + char* context_name(int context); 33 + 34 + #endif /* _ACTION_HELPER_H_ */
+209
apps/plugins/lib/action_helper.pl
··· 1 + #!/usr/bin/env perl 2 + ############################################################################ 3 + # __________ __ ___. 4 + # Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + # \/ \/ \/ \/ \/ 9 + # $action_helper$ 10 + # 11 + # Copyright (C) 2021 William Wilgus 12 + # 13 + # All files in this archive are subject to the GNU General Public License. 14 + # See the file COPYING in the source tree root for full license agreement. 15 + # 16 + # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 17 + # KIND, either express or implied. 18 + # 19 + ############################################################################ 20 + #expects -E source input on STDIN 21 + use strict; 22 + use warnings; 23 + 24 + my @actions = (); 25 + my @contexts = (); 26 + my @action_offset = (); 27 + my @context_offset = (); 28 + my $action_ct = 0; 29 + my $context_ct = 0; 30 + my $len_max_action = 0; 31 + my $len_max_context = 0; 32 + my $len_min_action = -1; 33 + my $len_min_context = -1; 34 + while(my $line = <STDIN>) 35 + { 36 + chomp($line); 37 + if($line =~ /^\s*(ACTION_[^\s]+)(\s*=.*)?,\s*$/) 38 + { 39 + $actions[$action_ct] = $1; 40 + $action_ct++; 41 + } 42 + elsif($line =~ /^\s*(LAST_ACTION_PLACEHOLDER)(\s*=.*)?,\s*$/) 43 + { #special case don't save actual name 44 + $actions[$action_ct] = ""; 45 + $action_ct++; 46 + } 47 + elsif($line =~ /^\s*(PLA_[^\s]+)(\s*=.*)?,\s*$/) 48 + { 49 + $actions[$action_ct] = $1; 50 + $action_ct++; 51 + } 52 + elsif($line =~ /^\s*(CONTEXT_[^\s]+)(\s*=.*)?,\s*$/) 53 + { 54 + $contexts[$context_ct] = $1; 55 + $context_ct++; 56 + } 57 + } 58 + 59 + print <<EOF 60 + /* Don't change this file! */ 61 + /* It is automatically generated of action.h */ 62 + #include "plugin.h" 63 + #include "action_helper.h" 64 + EOF 65 + ; 66 + #dump actions 67 + my $offset = 0; 68 + print "static const char action_names[]= \n"; 69 + for(my $i = 0; $i < $action_ct; $i++){ 70 + my $act = $actions[$i]; 71 + $act =~ s/ACTION_USB_HID_/%s/ig; # strip the common part 72 + $act =~ s/ACTION_/%s/ig; # strip the common part 73 + my $actlen = length($act); 74 + if ($actlen < $len_min_action or $len_min_action == -1){ 75 + $len_min_action = $actlen; 76 + } 77 + if ($actions[$i] ne $act){ 78 + printf "/*%s*/\"%s\\0\"\n", substr($actions[$i], 0, -($actlen - 2)), $act; 79 + } else { 80 + print "\"$act\\0\" \n"; 81 + } 82 + my $slen = length($actions[$i]) + 1; #NULL terminator 83 + if ($slen > $len_max_action) { $len_max_action = $slen; } 84 + push(@action_offset, {'name' => $actions[$i], 'offset' => $offset}); 85 + $offset += length($act) + 1; # NULL terminator 86 + } 87 + printf "\"\";/* %d + \\0 */\n\n", $offset; 88 + @actions = (); 89 + 90 + #dump contexts 91 + $offset = 0; 92 + print "static const char context_names[]= \n"; 93 + for(my $i = 0; $i < $context_ct; $i++){ 94 + my $ctx = $contexts[$i]; 95 + $ctx =~ s/CONTEXT_/%s/ig; # strip the common part 96 + my $ctxlen = length($ctx); 97 + 98 + if ($ctxlen < 5){ 99 + $ctx = $contexts[$i]; 100 + $ctxlen = length($ctx); 101 + } 102 + 103 + if ($ctxlen < $len_min_context or $len_min_context == -1){ 104 + $len_min_context = $ctxlen; 105 + } 106 + if ($contexts[$i] ne $ctx){ 107 + printf "/*%s*/\"%s\\0\"\n", substr($contexts[$i], 0, -($ctxlen - 2)), $ctx; 108 + } else { 109 + print "\"$ctx\\0\" \n"; 110 + } 111 + my $slen = length($contexts[$i]) + 1; # NULL terminator 112 + if ($slen > $len_max_context) { $len_max_context = $slen; } 113 + push(@context_offset, {'name' => $contexts[$i], 'offset' => $offset}); 114 + $offset += length($ctx) + 1; # NULL terminator 115 + } 116 + printf "\"\";/* %d + \\0 */\n\n", $offset; 117 + @contexts = (); 118 + 119 + printf "#define ACTION_CT %d\n", $action_ct; 120 + print "static const uint16_t action_offsets[ACTION_CT] = {\n"; 121 + foreach my $define (@action_offset) 122 + { 123 + printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'}); 124 + } 125 + print "};\n\n"; 126 + @action_offset = (); 127 + 128 + printf "#define CONTEXT_CT %d\n", $context_ct; 129 + print "#if 0 /* context_names is small enough to walk the string instead */\n"; 130 + print "static const uint16_t context_offsets[CONTEXT_CT] = {\n"; 131 + foreach my $define (@context_offset) 132 + { 133 + printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'}); 134 + } 135 + print "};\n#endif\n\n"; 136 + @context_offset = (); 137 + 138 + printf "#define ACTIONBUFSZ %d\n", $len_max_action; 139 + printf "#define CONTEXTBUFSZ %d\n\n", $len_max_context; 140 + 141 + if ($len_max_action > $len_max_context) 142 + { 143 + print "static char name_buf[ACTIONBUFSZ];\n"; 144 + } 145 + else 146 + { 147 + print "static char name_buf[CONTEXTBUFSZ];\n"; 148 + } 149 + print <<EOF 150 + 151 + char* action_name(int action) 152 + { 153 + if (action >= 0 && action < ACTION_CT) 154 + { 155 + uint16_t offset = action_offsets[action]; 156 + const char *act = &action_names[offset]; 157 + if (action < ACTION_USB_HID_FIRST) 158 + rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_"); 159 + else 160 + rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_USB_HID_"); 161 + } 162 + else 163 + rb->snprintf(name_buf, ACTIONBUFSZ, "ACTION_UNKNOWN"); 164 + return name_buf; 165 + } 166 + 167 + /* walk string increment offset for each NULL if desired offset found, return */ 168 + static const char *context_getoffset(int offset) 169 + { 170 + const char *names = context_names; 171 + const size_t len = sizeof(context_names) - 1; 172 + int current = 0; 173 + if (offset > 0) 174 + { 175 + const char *pos = names; 176 + const char *end = names + len; 177 + while (pos < end) 178 + { 179 + if (*pos++ == '\\0') 180 + { 181 + current++; 182 + if (offset == current) 183 + return pos; 184 + pos += $len_min_context; /* each string is at least this long */ 185 + } 186 + } 187 + } 188 + return names; 189 + } 190 + 191 + char* context_name(int context) 192 + { 193 + const char *ctx; 194 + if (context >= 0 && context < CONTEXT_CT) 195 + { 196 + #if 0 197 + uint16_t offset = context_offsets[context]; 198 + ctx = &context_names[offset]; 199 + #else 200 + ctx = context_getoffset(context); 201 + #endif 202 + } 203 + else 204 + ctx = "%sUNKNOWN"; 205 + rb->snprintf(name_buf, CONTEXTBUFSZ, ctx, "CONTEXT_"); 206 + return name_buf; 207 + } 208 + EOF 209 + ;
+1
apps/plugins/lib/button_helper.c
··· 1 + /*DUMMY_FILE_DONT_CHANGEME*/
+38
apps/plugins/lib/button_helper.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2021 William Wilgus 11 + * 12 + * 13 + * This program is free software; you can redistribute it and/or 14 + * modify it under the terms of the GNU General Public License 15 + * as published by the Free Software Foundation; either version 2 16 + * of the License, or (at your option) any later version. 17 + * 18 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 + * KIND, either express or implied. 20 + * 21 + ****************************************************************************/ 22 + #ifndef _BUTTON_HELPER_H_ 23 + #define _BUTTON_HELPER_H_ 24 + struct available_button 25 + { 26 + const char* name; 27 + unsigned long value; 28 + }; 29 + 30 + /* *available_buttons is holding a pointer to the first element of an array 31 + * of struct available_button it is set up in such a way due to the file being 32 + * generated at compile time you can still call it as such though 33 + * eg available_buttons[0] or available_buttons[available_button_count] (NULL SENTINEL, 0)*/ 34 + 35 + extern const struct available_button * const available_buttons; 36 + extern const int available_button_count; 37 + int get_button_names(char *buf, size_t bufsz, unsigned long button); 38 + #endif /* _BUTTON_HELPER_H_ */
+98
apps/plugins/lib/button_helper.pl
··· 1 + #!/usr/bin/env perl 2 + ############################################################################ 3 + # __________ __ ___. 4 + # Open \______ \ ____ ____ | | _\_ |__ _______ ___ 5 + # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 6 + # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 7 + # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 8 + # \/ \/ \/ \/ \/ 9 + # $Id$ 10 + # 11 + # Copyright (C) 2009 by Maurus Cuelenaere 12 + # Copyright (C) 2021 by William Wilgus 13 + # 14 + # All files in this archive are subject to the GNU General Public License. 15 + # See the file COPYING in the source tree root for full license agreement. 16 + # 17 + # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + # KIND, either express or implied. 19 + # 20 + ############################################################################ 21 + #expects -dM -E source input on STDIN 22 + use strict; 23 + use warnings; 24 + my $svnrev = '$Revision$'; 25 + my @buttons = (); 26 + my $count = 1; #null sentinel 27 + my $val; 28 + my $def; 29 + while(my $line = <STDIN>) 30 + { 31 + chomp($line); 32 + if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/) 33 + { 34 + $def = "{\"$1\", $2},\n"; 35 + $val = $2; 36 + if($val =~ /^0/) 37 + { 38 + $val = oct($val) 39 + } 40 + else 41 + { 42 + $val = 0xFFFFFFFF; #only used for sorting 43 + } 44 + push(@buttons, {'name' => $1, 'value' => $val, 'def' => $def}); 45 + $count = $count + 1; 46 + } 47 + } 48 + my @sorted = sort { @$a{'value'} <=> @$b{'value'} } @buttons; 49 + print <<EOF 50 + /* Don't change this file! */ 51 + /* It is automatically generated of button.h */ 52 + #include "plugin.h" 53 + #include "button.h" 54 + #include "button_helper.h" 55 + 56 + static const struct available_button buttons[$count] = { 57 + EOF 58 + ; 59 + $count--; # don't count the sentinel 60 + foreach my $button (@sorted) 61 + { 62 + printf " %s", @$button{'def'}; 63 + } 64 + 65 + print <<EOF 66 + {"\\0", 0} /* sentinel */ 67 + }; 68 + const int available_button_count = $count; 69 + const struct available_button * const available_buttons = buttons; 70 + 71 + int get_button_names(char *buf, size_t bufsz, unsigned long button) 72 + { 73 + int len = 0; 74 + buf[0] = '\\0'; 75 + const struct available_button *btn = buttons; 76 + while(btn->name[0] != '\\0') 77 + { 78 + if(btn->value == 0) 79 + { 80 + if (button == 0) 81 + { 82 + buf[0] = '\\0'; 83 + len = rb->strlcat(buf, btn->name, bufsz); 84 + return len; 85 + } 86 + } 87 + else if ((button & btn->value) == btn->value) 88 + { 89 + if (len > 0) 90 + rb->strlcat(buf, " | ", bufsz); 91 + len = rb->strlcat(buf, btn->name, bufsz); 92 + } 93 + btn++; 94 + } 95 + return len; 96 + } 97 + EOF 98 + ;
+28 -1
apps/plugins/plugins.make
··· 101 101 PLUGINLIBFLAGS = $(PLUGINFLAGS) -ffunction-sections -fdata-sections 102 102 endif 103 103 104 + ROOT_PLUGINSLIB_DIR := $(ROOTDIR)/apps/plugins/lib 105 + BUILD_PLUGINSLIB_DIR := $(BUILDDIR)/apps/plugins/lib 106 + 107 + # action_helper # 108 + ACTION_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,action_helper.pl action_helper.h) \ 109 + $(BUILD_PLUGINSLIB_DIR)/pluginlib_actions.o 110 + 111 + # special rule for generating and compiling action_helper 112 + $(BUILD_PLUGINSLIB_DIR)/action_helper.o: $(ACTION_REQ) 113 + $(SILENT)mkdir -p $(dir $@) 114 + $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E -P \ 115 + $(ROOT_PLUGINSLIB_DIR)/pluginlib_actions.h - < /dev/null | $< > $(basename $@).c 116 + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \ 117 + $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@ 118 + 119 + # button_helper # 120 + BUTTON_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,button_helper.pl button_helper.h) \ 121 + $(BUILD_PLUGINSLIB_DIR)/action_helper.o 122 + 123 + # special rule for generating and compiling button_helper 124 + $(BUILD_PLUGINSLIB_DIR)/button_helper.o: $(BUTTON_REQ) $(ROOTDIR)/firmware/export/button.h 125 + $(SILENT)mkdir -p $(dir $@) 126 + $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -dM -E -P \ 127 + $(addprefix -include ,button-target.h button.h) - < /dev/null | $< > $(basename $@).c 128 + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \ 129 + $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@ 130 + 104 131 # special pattern rule for compiling plugin lib (with function and data sections) 105 - $(BUILDDIR)/apps/plugins/lib/%.o: $(ROOTDIR)/apps/plugins/lib/%.c 132 + $(BUILD_PLUGINSLIB_DIR)/%.o: $(ROOT_PLUGINSLIB_DIR)/%.c 106 133 $(SILENT)mkdir -p $(dir $@) 107 134 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PLUGINLIBFLAGS) -c $< -o $@ 108 135
+494
apps/plugins/rb_info.c
··· 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 + 33 + #define MENU_ID(x) (((void*)&"RPBUTACNGX\0" + x)) 34 + enum { 35 + M_ROOT = 0, 36 + M_PATHS, 37 + M_BUFFERS, 38 + M_BUTTONS, 39 + M_BTNTEST, 40 + M_ACTIONS, 41 + M_CONTEXTS, 42 + M_ACTTEST, 43 + M_PLUGINS, 44 + M_EXIT, 45 + M_LAST_ITEM //ITEM COUNT 46 + }; 47 + 48 + #define MENU_ID_PLUGINS_ITEMS 5 49 + 50 + /*Action test and Button test*/ 51 + static struct menu_test_t { 52 + int count; 53 + int context; 54 + int last_btn_or_act; 55 + } m_test; 56 + 57 + struct menu_buffer_t { const char *name; size_t size;}; 58 + static const struct menu_buffer_t m_buffer[] = 59 + { 60 + #ifndef MAX_LOGF_SIZE 61 + #define MAX_LOGF_SIZE (0) 62 + #endif 63 + #ifndef CACHE_SIZE 64 + #define CACHE_SIZE (0) 65 + #endif 66 + {"thread stack", DEFAULT_STACK_SIZE}, 67 + {"plugin buffer", PLUGIN_BUFFER_SIZE}, 68 + {"frame_buffer", FRAMEBUFFER_SIZE}, 69 + {"codec_buffer", CODEC_SIZE}, 70 + {"logf_buffer", MAX_LOGF_SIZE}, 71 + {"cache", CACHE_SIZE}, 72 + }; 73 + 74 + /* stringify the macro value */ 75 + #define MACROVAL(x) MACROSTR(x) 76 + #define MACROSTR(x) #x 77 + static int main_last_sel = 0; 78 + static struct gui_synclist lists; 79 + static void synclist_set(char*, int, int, int); 80 + 81 + struct paths { const char *name; const char *path; }; 82 + static const struct paths paths[] = { 83 + {"Home", ""HOME_DIR}, 84 + {"Rockbox", ""ROCKBOX_DIR}, 85 + {"Plugins", ""PLUGIN_DIR}, 86 + {"Codecs", ""CODECS_DIR}, 87 + {"WPS", ""WPS_DIR}, 88 + {"SBS", ""SBS_DIR}, 89 + {"Theme", ""THEME_DIR}, 90 + {"Font", ""FONT_DIR}, 91 + {"Icon", ""ICON_DIR}, 92 + {"Backdrop", ""BACKDROP_DIR}, 93 + {"Eq", ""EQS_DIR}, 94 + {"Rec Presets", ""RECPRESETS_DIR}, 95 + {"Recordings", ""REC_BASE_DIR,}, 96 + {"Fm Presets", ""FMPRESET_PATH}, 97 + {"MAX_PATH", ""MACROVAL(MAX_PATH)" bytes"}, 98 + }; 99 + 100 + struct mainmenu { const char *name; void *menuid; int items;}; 101 + static struct mainmenu mainmenu[M_LAST_ITEM] = { 102 + #define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)COUNT} 103 + MENU_ITEM(M_ROOT, "Rockbox Info Plugin", M_LAST_ITEM), 104 + MENU_ITEM(M_PATHS, ID2P(LANG_SHOW_PATH), ARRAYLEN(paths)), 105 + MENU_ITEM(M_BUFFERS, ID2P(LANG_BUFFER_STAT), ARRAYLEN(m_buffer)), 106 + MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */ 107 + MENU_ITEM(M_BTNTEST, "Button test", 2), 108 + MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER), 109 + MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ), 110 + MENU_ITEM(M_ACTTEST, "Action test", 3), 111 + MENU_ITEM(M_PLUGINS, ID2P(LANG_PLUGINS), MENU_ID_PLUGINS_ITEMS), 112 + MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0), 113 + #undef MENU_ITEM 114 + }; 115 + 116 + static const struct mainmenu *mainitem(int selected_item) 117 + { 118 + static const struct mainmenu empty = {0}; 119 + if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu)) 120 + return &mainmenu[selected_item]; 121 + else 122 + return &empty; 123 + } 124 + 125 + static void cleanup(void *parameter) 126 + { 127 + (void)parameter; 128 + 129 + } 130 + 131 + static const char *menu_plugin_name_cb(int selected_item, void* data, 132 + char* buf, size_t buf_len) 133 + { 134 + (void)data; 135 + buf[0] = '\0'; 136 + switch(selected_item) 137 + { 138 + case 0: 139 + rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin_api", (int)sizeof(struct plugin_api)); 140 + break; 141 + case 1: 142 + rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "plugin buffer", PLUGIN_BUFFER_SIZE); 143 + break; 144 + case 2: 145 + rb->snprintf(buf, buf_len, "%s: [%d bytes] ", "frame_buffer", (int)FRAMEBUFFER_SIZE); 146 + break; 147 + case 3: 148 + rb->snprintf(buf, buf_len, "%s: [W: %d H:%d] ", "LCD", LCD_WIDTH, LCD_HEIGHT); 149 + break; 150 + case 4: 151 + rb->snprintf(buf, buf_len, "%s: [%d bits] ", "fb_data", (int)(sizeof(fb_data) * CHAR_BIT)); 152 + break; 153 + case 5: 154 + break; 155 + } 156 + return buf; 157 + } 158 + 159 + static const char *menu_button_test_name_cb(int selected_item, void* data, 160 + char* buf, size_t buf_len) 161 + { 162 + (void)data; 163 + int curbtn = BUTTON_NONE; 164 + buf[0] = '\0'; 165 + switch(selected_item) 166 + { 167 + case 0: 168 + rb->snprintf(buf, buf_len, "%s: [%s] ", "Button test", 169 + m_test.count > 0 ? "true":"false"); 170 + break; 171 + case 1: 172 + if (m_test.count > 0) 173 + { 174 + if (m_test.count <= 2) 175 + curbtn = rb->button_get_w_tmo(HZ * 2); 176 + else 177 + m_test.last_btn_or_act = BUTTON_NONE; 178 + if (curbtn == BUTTON_NONE) 179 + { 180 + m_test.count--; 181 + } 182 + else 183 + m_test.last_btn_or_act = curbtn; 184 + } 185 + get_button_names(buf, buf_len, m_test.last_btn_or_act); 186 + 187 + break; 188 + } 189 + return buf; 190 + } 191 + 192 + static const char *menu_action_test_name_cb(int selected_item, void* data, 193 + char* buf, size_t buf_len) 194 + { 195 + (void)data; 196 + const char *fmtstr; 197 + int curact = ACTION_NONE; 198 + buf[0] = '\0'; 199 + switch(selected_item) 200 + { 201 + case 0: 202 + rb->snprintf(buf, buf_len, "%s: [%s] ", "Action test", 203 + m_test.count > 0 ? "true":"false"); 204 + break; 205 + case 1: 206 + if (m_test.count <= 0) 207 + { 208 + if (m_test.context <= 0) 209 + fmtstr = "%s > "; 210 + else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER - 1) 211 + fmtstr = "< %s "; 212 + else 213 + fmtstr = "< %s > "; 214 + } 215 + else 216 + fmtstr = "%s"; 217 + 218 + rb->snprintf(buf, buf_len, fmtstr, context_name(m_test.context)); 219 + break; 220 + case 2: 221 + if (m_test.count > 0) 222 + { 223 + if (m_test.count <= 2) 224 + curact = rb->get_action(m_test.context, HZ * 2); 225 + else 226 + m_test.last_btn_or_act = ACTION_NONE; 227 + if (curact == ACTION_NONE && rb->button_get(false) == BUTTON_NONE) 228 + { 229 + m_test.count--; 230 + } 231 + else 232 + { 233 + m_test.last_btn_or_act = curact; 234 + m_test.count = 2; 235 + } 236 + } 237 + return action_name(m_test.last_btn_or_act); 238 + 239 + break; 240 + } 241 + return buf; 242 + } 243 + 244 + static const char* list_get_name_cb(int selected_item, void* data, 245 + char* buf, size_t buf_len) 246 + { 247 + buf[0] = '\0'; 248 + if (data == MENU_ID(M_ROOT)) 249 + return mainitem(selected_item)->name; 250 + else if (selected_item == 0) /*header text*/ 251 + return mainitem(main_last_sel)->name; 252 + else if (selected_item >= mainitem(main_last_sel)->items - 1) 253 + return ID2P(LANG_BACK); 254 + 255 + if (data == MENU_ID(M_PATHS)) 256 + { 257 + selected_item--; 258 + if (selected_item >= 0 && selected_item < mainitem(M_PATHS)->items) 259 + { 260 + const struct paths *cur = &paths[selected_item]; 261 + rb->snprintf(buf, buf_len, "%s: [%s] ", cur->name, cur->path); 262 + return buf; 263 + } 264 + } 265 + else if (data == MENU_ID(M_BUTTONS)) 266 + { 267 + const struct available_button *btn = &available_buttons[selected_item - 1]; 268 + rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value); 269 + return buf; 270 + } 271 + else if (data == MENU_ID(M_BTNTEST)) 272 + return menu_button_test_name_cb(selected_item - 1, data, buf, buf_len); 273 + else if (data == MENU_ID(M_ACTIONS)) 274 + return action_name(selected_item - 1); 275 + else if (data == MENU_ID(M_CONTEXTS)) 276 + return context_name(selected_item - 1); 277 + else if (data == MENU_ID(M_ACTTEST)) 278 + return menu_action_test_name_cb(selected_item - 1, data, buf, buf_len); 279 + else if (data == MENU_ID(M_BUFFERS)) 280 + { 281 + const struct menu_buffer_t *bufm = &m_buffer[selected_item - 1]; 282 + rb->snprintf(buf, buf_len, "%s: [%ld bytes] ", bufm->name, (long)bufm->size); 283 + return buf; 284 + } 285 + else if (data == MENU_ID(M_PLUGINS)) 286 + { 287 + return menu_plugin_name_cb(selected_item - 1, data, buf, buf_len); 288 + } 289 + return buf; 290 + } 291 + 292 + static int list_voice_cb(int list_index, void* data) 293 + { 294 + if (!rb->global_settings->talk_menu) 295 + return -1; 296 + 297 + if (data == MENU_ID(M_ROOT)) 298 + { 299 + const char * name = mainitem(list_index)->name; 300 + long id = P2ID((const unsigned char *)name); 301 + if(id>=0) 302 + rb->talk_id(id, true); 303 + else 304 + rb->talk_spell(name, true); 305 + } 306 + else if (data == MENU_ID(M_BUFFERS) || data == MENU_ID(M_PLUGINS)) 307 + { 308 + char buf[64]; 309 + const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); 310 + long id = P2ID((const unsigned char *)name); 311 + if(id>=0) 312 + rb->talk_id(id, true); 313 + else 314 + { 315 + char* bytstr = rb->strcasestr(name, "bytes"); 316 + if (bytstr != NULL) 317 + *bytstr = '\0'; 318 + rb->talk_spell(name, true); 319 + } 320 + } 321 + else 322 + { 323 + char buf[64]; 324 + const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf)); 325 + long id = P2ID((const unsigned char *)name); 326 + if(id>=0) 327 + rb->talk_id(id, true); 328 + else 329 + rb->talk_spell(name, true); 330 + } 331 + return 0; 332 + } 333 + 334 + int menu_action_cb(int action, int selected_item, bool* exit, struct gui_synclist *lists) 335 + { 336 + if (lists->data == MENU_ID(M_ACTTEST)) 337 + { 338 + if (selected_item == 2) /* context */ 339 + { 340 + int ctx = m_test.context; 341 + if (action == ACTION_STD_OK) 342 + m_test.context++; 343 + else if (action == ACTION_STD_CANCEL) 344 + m_test.context--; 345 + 346 + if (m_test.context < 0) 347 + m_test.context = 0; 348 + else if (m_test.context >= LAST_CONTEXT_PLACEHOLDER) 349 + m_test.context = LAST_CONTEXT_PLACEHOLDER - 1; 350 + 351 + if (ctx != m_test.context) 352 + rb->gui_synclist_speak_item(lists); 353 + 354 + goto default_handler; 355 + } 356 + if (action == ACTION_STD_OK) 357 + { 358 + if (selected_item == 1 || selected_item == 3) 359 + { 360 + m_test.count = 3; 361 + rb->gui_synclist_select_item(lists, 3); 362 + } 363 + } 364 + } 365 + else if (lists->data == MENU_ID(M_BTNTEST)) 366 + { 367 + if (action == ACTION_STD_OK) 368 + { 369 + if (selected_item == 1 || selected_item == 2) 370 + { 371 + m_test.count = 3; 372 + rb->gui_synclist_select_item(lists, 2); 373 + } 374 + } 375 + } 376 + if (action == ACTION_STD_OK) 377 + { 378 + if (lists->data == MENU_ID(M_ROOT)) 379 + { 380 + rb->memset(&m_test, 0, sizeof(struct menu_test_t)); 381 + const struct mainmenu *cur = mainitem(selected_item); 382 + if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT)) 383 + *exit = true; 384 + else 385 + { 386 + main_last_sel = selected_item; 387 + synclist_set(cur->menuid, 1, cur->items, 1); 388 + rb->gui_synclist_draw(lists); 389 + } 390 + } 391 + else if (selected_item <= 0) /* title */ 392 + { 393 + rb->gui_synclist_select_item(lists, 1); 394 + } 395 + else if (selected_item >= (mainitem(main_last_sel)->items) - 1)/*back*/ 396 + { 397 + action = ACTION_STD_CANCEL; 398 + } 399 + else if (lists->data == MENU_ID(M_ACTIONS) || 400 + lists->data == MENU_ID(M_CONTEXTS)) 401 + { 402 + char buf[MAX_PATH]; 403 + const char *name = list_get_name_cb(selected_item, lists->data, buf, sizeof(buf)); 404 + /* splash long enough to get fingers off button then wait for new button press */ 405 + rb->splashf(HZ / 2, "%s %d (0x%X)", name, selected_item -1, selected_item -1); 406 + rb->button_get(true); 407 + } 408 + } 409 + if (action == ACTION_STD_CANCEL) 410 + { 411 + if (lists->data != MENU_ID(M_ROOT)) 412 + { 413 + const struct mainmenu *mainm = &mainmenu[0]; 414 + synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); 415 + rb->gui_synclist_draw(lists); 416 + } 417 + else 418 + *exit = true; 419 + } 420 + default_handler: 421 + if (rb->default_event_handler_ex(action, cleanup, NULL) == SYS_USB_CONNECTED) 422 + { 423 + *exit = true; 424 + return PLUGIN_USB_CONNECTED; 425 + } 426 + return PLUGIN_OK; 427 + } 428 + 429 + static void synclist_set(char* menu_id, int selected_item, int items, int sel_size) 430 + { 431 + if (items <= 0) 432 + return; 433 + if (selected_item < 0) 434 + selected_item = 0; 435 + 436 + list_voice_cb(0, menu_id); 437 + rb->gui_synclist_init(&lists,list_get_name_cb, 438 + menu_id, false, sel_size, NULL); 439 + 440 + rb->gui_synclist_set_icon_callback(&lists,NULL); 441 + rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); 442 + rb->gui_synclist_set_nb_items(&lists,items); 443 + rb->gui_synclist_limit_scroll(&lists,true); 444 + rb->gui_synclist_select_item(&lists, selected_item); 445 + 446 + } 447 + 448 + enum plugin_status plugin_start(const void* parameter) 449 + { 450 + int ret = PLUGIN_OK; 451 + int selected_item = -1; 452 + int action; 453 + bool redraw = true; 454 + bool exit = false; 455 + if (parameter) 456 + { 457 + // 458 + } 459 + mainmenu[M_BUTTONS].items = available_button_count; 460 + /* add header and back item to each submenu */ 461 + for (int i = 1; i < M_LAST_ITEM; i++) 462 + mainmenu[i].items += 2; 463 + 464 + if (!exit) 465 + { 466 + const struct mainmenu *mainm = &mainmenu[0]; 467 + synclist_set(mainm->menuid, main_last_sel, mainm->items, 1); 468 + rb->gui_synclist_draw(&lists); 469 + 470 + while (!exit) 471 + { 472 + action = rb->get_action(CONTEXT_LIST, HZ / 10); 473 + if (m_test.count > 0) 474 + action = ACTION_REDRAW; 475 + 476 + if (action == ACTION_NONE) 477 + { 478 + if (redraw) 479 + { 480 + action = ACTION_REDRAW; 481 + redraw = false; 482 + } 483 + } 484 + else 485 + redraw = true; 486 + if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD)) 487 + continue; 488 + selected_item = rb->gui_synclist_get_sel_pos(&lists); 489 + ret = menu_action_cb(action, selected_item, &exit, &lists); 490 + } 491 + } 492 + 493 + return ret; 494 + }