diff --git a/apps/action.h b/apps/action.h index ad91f31535..f94dd3086c 100644 --- a/apps/action.h +++ b/apps/action.h @@ -129,6 +129,7 @@ enum { CONTEXT_USB_HID_MODE_PRESENTATION, CONTEXT_USB_HID_MODE_BROWSER, CONTEXT_USB_HID_MODE_MOUSE, + LAST_CONTEXT_PLACEHOLDER, }; diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 5b672fc8ab..bb0960f501 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -87,6 +87,7 @@ ppm,viewers properties,viewers quake,games random_folder_advance_config,apps +rb_info,demos remote_control,apps resistor,apps reversi,games diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 0635a62d83..ab77dcde58 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -17,6 +17,7 @@ mosaique.c main_menu_config.c properties.c random_folder_advance_config.c +rb_info.c rockblox.c search.c settings_dumper.c diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES index bdea07315e..1cd092f8df 100644 --- a/apps/plugins/lib/SOURCES +++ b/apps/plugins/lib/SOURCES @@ -1,6 +1,8 @@ sha1.c gcc-support.c pluginlib_actions.c +action_helper.c +button_helper.c helper.c icon_helper.c arg_helper.c diff --git a/apps/plugins/lib/action_helper.c b/apps/plugins/lib/action_helper.c new file mode 100644 index 0000000000..906051c1ea --- /dev/null +++ b/apps/plugins/lib/action_helper.c @@ -0,0 +1 @@ +/*DUMMY_FILE_DONT_CHANGEME*/ diff --git a/apps/plugins/lib/action_helper.h b/apps/plugins/lib/action_helper.h new file mode 100644 index 0000000000..58d9c6c303 --- /dev/null +++ b/apps/plugins/lib/action_helper.h @@ -0,0 +1,34 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2021 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. +* +****************************************************************************/ +/* action_helper provides a way to turn numeric action/context into strings +* the file action_helper.c is generated at compile time +* ACTION_ and CONTEXT_ are stripped from the strings and replaced when +* action_name and context_name are called, +* NOTE: both share the same static buffer sized as the largest string possible +*/ +#ifndef _ACTION_HELPER_H_ +#define _ACTION_HELPER_H_ + +char* action_name(int action); +char* context_name(int context); + +#endif /* _ACTION_HELPER_H_ */ diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl new file mode 100755 index 0000000000..1dfdcfd070 --- /dev/null +++ b/apps/plugins/lib/action_helper.pl @@ -0,0 +1,209 @@ +#!/usr/bin/env perl +############################################################################ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $action_helper$ +# +# Copyright (C) 2021 William Wilgus +# +# All files in this archive are subject to the GNU General Public License. +# See the file COPYING in the source tree root for full license agreement. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +############################################################################ +#expects -E source input on STDIN +use strict; +use warnings; + +my @actions = (); +my @contexts = (); +my @action_offset = (); +my @context_offset = (); +my $action_ct = 0; +my $context_ct = 0; +my $len_max_action = 0; +my $len_max_context = 0; +my $len_min_action = -1; +my $len_min_context = -1; +while(my $line = ) +{ + chomp($line); + if($line =~ /^\s*(ACTION_[^\s]+)(\s*=.*)?,\s*$/) + { + $actions[$action_ct] = $1; + $action_ct++; + } + elsif($line =~ /^\s*(LAST_ACTION_PLACEHOLDER)(\s*=.*)?,\s*$/) + { #special case don't save actual name + $actions[$action_ct] = ""; + $action_ct++; + } + elsif($line =~ /^\s*(PLA_[^\s]+)(\s*=.*)?,\s*$/) + { + $actions[$action_ct] = $1; + $action_ct++; + } + elsif($line =~ /^\s*(CONTEXT_[^\s]+)(\s*=.*)?,\s*$/) + { + $contexts[$context_ct] = $1; + $context_ct++; + } +} + +print < $len_max_action) { $len_max_action = $slen; } + push(@action_offset, {'name' => $actions[$i], 'offset' => $offset}); + $offset += length($act) + 1; # NULL terminator +} +printf "\"\";/* %d + \\0 */\n\n", $offset; +@actions = (); + +#dump contexts +$offset = 0; +print "static const char context_names[]= \n"; +for(my $i = 0; $i < $context_ct; $i++){ + my $ctx = $contexts[$i]; + $ctx =~ s/CONTEXT_/%s/ig; # strip the common part + my $ctxlen = length($ctx); + + if ($ctxlen < 5){ + $ctx = $contexts[$i]; + $ctxlen = length($ctx); + } + + if ($ctxlen < $len_min_context or $len_min_context == -1){ + $len_min_context = $ctxlen; + } + if ($contexts[$i] ne $ctx){ + printf "/*%s*/\"%s\\0\"\n", substr($contexts[$i], 0, -($ctxlen - 2)), $ctx; + } else { + print "\"$ctx\\0\" \n"; + } + my $slen = length($contexts[$i]) + 1; # NULL terminator + if ($slen > $len_max_context) { $len_max_context = $slen; } + push(@context_offset, {'name' => $contexts[$i], 'offset' => $offset}); + $offset += length($ctx) + 1; # NULL terminator +} +printf "\"\";/* %d + \\0 */\n\n", $offset; +@contexts = (); + +printf "#define ACTION_CT %d\n", $action_ct; +print "static const uint16_t action_offsets[ACTION_CT] = {\n"; +foreach my $define (@action_offset) +{ + printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'}); +} +print "};\n\n"; +@action_offset = (); + +printf "#define CONTEXT_CT %d\n", $context_ct; +print "#if 0 /* context_names is small enough to walk the string instead */\n"; +print "static const uint16_t context_offsets[CONTEXT_CT] = {\n"; +foreach my $define (@context_offset) +{ + printf("%d, /*%s*/\n", @$define{'offset'}, @$define{'name'}); +} +print "};\n#endif\n\n"; +@context_offset = (); + +printf "#define ACTIONBUFSZ %d\n", $len_max_action; +printf "#define CONTEXTBUFSZ %d\n\n", $len_max_context; + +if ($len_max_action > $len_max_context) +{ + print "static char name_buf[ACTIONBUFSZ];\n"; +} +else +{ + print "static char name_buf[CONTEXTBUFSZ];\n"; +} +print <= 0 && action < ACTION_CT) + { + uint16_t offset = action_offsets[action]; + const char *act = &action_names[offset]; + if (action < ACTION_USB_HID_FIRST) + rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_"); + else + rb->snprintf(name_buf, ACTIONBUFSZ, act, "ACTION_USB_HID_"); + } + else + rb->snprintf(name_buf, ACTIONBUFSZ, "ACTION_UNKNOWN"); + return name_buf; +} + +/* walk string increment offset for each NULL if desired offset found, return */ +static const char *context_getoffset(int offset) +{ + const char *names = context_names; + const size_t len = sizeof(context_names) - 1; + int current = 0; + if (offset > 0) + { + const char *pos = names; + const char *end = names + len; + while (pos < end) + { + if (*pos++ == '\\0') + { + current++; + if (offset == current) + return pos; + pos += $len_min_context; /* each string is at least this long */ + } + } + } + return names; +} + +char* context_name(int context) +{ + const char *ctx; + if (context >= 0 && context < CONTEXT_CT) + { +#if 0 + uint16_t offset = context_offsets[context]; + ctx = &context_names[offset]; +#else + ctx = context_getoffset(context); +#endif + } + else + ctx = "%sUNKNOWN"; + rb->snprintf(name_buf, CONTEXTBUFSZ, ctx, "CONTEXT_"); + return name_buf; +} +EOF +; diff --git a/apps/plugins/lib/button_helper.c b/apps/plugins/lib/button_helper.c new file mode 100644 index 0000000000..906051c1ea --- /dev/null +++ b/apps/plugins/lib/button_helper.c @@ -0,0 +1 @@ +/*DUMMY_FILE_DONT_CHANGEME*/ diff --git a/apps/plugins/lib/button_helper.h b/apps/plugins/lib/button_helper.h new file mode 100644 index 0000000000..1197b172b0 --- /dev/null +++ b/apps/plugins/lib/button_helper.h @@ -0,0 +1,38 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Copyright (C) 2021 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. +* +****************************************************************************/ +#ifndef _BUTTON_HELPER_H_ +#define _BUTTON_HELPER_H_ +struct available_button +{ + const char* name; + unsigned long value; +}; + +/* *available_buttons is holding a pointer to the first element of an array + * of struct available_button it is set up in such a way due to the file being + * generated at compile time you can still call it as such though +* eg available_buttons[0] or available_buttons[available_button_count] (NULL SENTINEL, 0)*/ + +extern const struct available_button * const available_buttons; +extern const int available_button_count; +int get_button_names(char *buf, size_t bufsz, unsigned long button); +#endif /* _BUTTON_HELPER_H_ */ diff --git a/apps/plugins/lib/button_helper.pl b/apps/plugins/lib/button_helper.pl new file mode 100755 index 0000000000..45c3fd9073 --- /dev/null +++ b/apps/plugins/lib/button_helper.pl @@ -0,0 +1,98 @@ +#!/usr/bin/env perl +############################################################################ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# +# Copyright (C) 2009 by Maurus Cuelenaere +# Copyright (C) 2021 by William Wilgus +# +# All files in this archive are subject to the GNU General Public License. +# See the file COPYING in the source tree root for full license agreement. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +############################################################################ +#expects -dM -E source input on STDIN +use strict; +use warnings; +my $svnrev = '$Revision$'; +my @buttons = (); +my $count = 1; #null sentinel +my $val; +my $def; +while(my $line = ) +{ + chomp($line); + if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/) + { + $def = "{\"$1\", $2},\n"; + $val = $2; + if($val =~ /^0/) + { + $val = oct($val) + } + else + { + $val = 0xFFFFFFFF; #only used for sorting + } + push(@buttons, {'name' => $1, 'value' => $val, 'def' => $def}); + $count = $count + 1; + } +} +my @sorted = sort { @$a{'value'} <=> @$b{'value'} } @buttons; +print <name[0] != '\\0') + { + if(btn->value == 0) + { + if (button == 0) + { + buf[0] = '\\0'; + len = rb->strlcat(buf, btn->name, bufsz); + return len; + } + } + else if ((button & btn->value) == btn->value) + { + if (len > 0) + rb->strlcat(buf, " | ", bufsz); + len = rb->strlcat(buf, btn->name, bufsz); + } + btn++; + } + return len; +} +EOF +; diff --git a/apps/plugins/plugins.make b/apps/plugins/plugins.make index ac94a04c59..b430bd2792 100644 --- a/apps/plugins/plugins.make +++ b/apps/plugins/plugins.make @@ -101,8 +101,35 @@ else PLUGINLIBFLAGS = $(PLUGINFLAGS) -ffunction-sections -fdata-sections endif +ROOT_PLUGINSLIB_DIR := $(ROOTDIR)/apps/plugins/lib +BUILD_PLUGINSLIB_DIR := $(BUILDDIR)/apps/plugins/lib + +# action_helper # +ACTION_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,action_helper.pl action_helper.h) \ + $(BUILD_PLUGINSLIB_DIR)/pluginlib_actions.o + +# special rule for generating and compiling action_helper +$(BUILD_PLUGINSLIB_DIR)/action_helper.o: $(ACTION_REQ) + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -E -P \ + $(ROOT_PLUGINSLIB_DIR)/pluginlib_actions.h - < /dev/null | $< > $(basename $@).c + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \ + $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@ + +# button_helper # +BUTTON_REQ := $(addprefix $(ROOT_PLUGINSLIB_DIR)/,button_helper.pl button_helper.h) \ + $(BUILD_PLUGINSLIB_DIR)/action_helper.o + +# special rule for generating and compiling button_helper +$(BUILD_PLUGINSLIB_DIR)/button_helper.o: $(BUTTON_REQ) $(ROOTDIR)/firmware/export/button.h + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,GEN $(@F))$(CC) $(PLUGINFLAGS) $(INCLUDES) -dM -E -P \ + $(addprefix -include ,button-target.h button.h) - < /dev/null | $< > $(basename $@).c + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(ROOT_PLUGINSLIB_DIR) \ + $(PLUGINLIBFLAGS) -c $(basename $@).c -o $@ + # special pattern rule for compiling plugin lib (with function and data sections) -$(BUILDDIR)/apps/plugins/lib/%.o: $(ROOTDIR)/apps/plugins/lib/%.c +$(BUILD_PLUGINSLIB_DIR)/%.o: $(ROOT_PLUGINSLIB_DIR)/%.c $(SILENT)mkdir -p $(dir $@) $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(PLUGINLIBFLAGS) -c $< -o $@ diff --git a/apps/plugins/rb_info.c b/apps/plugins/rb_info.c new file mode 100644 index 0000000000..f82c80c0cf --- /dev/null +++ b/apps/plugins/rb_info.c @@ -0,0 +1,494 @@ +/*************************************************************************** + * __________ __ ___. + * 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; +}