summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/action.c104
-rw-r--r--apps/action.h11
-rw-r--r--apps/core_keymap.c106
-rw-r--r--apps/core_keymap.h68
-rw-r--r--apps/main.c10
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/keyremap.c1616
-rw-r--r--apps/plugins/lib/action_helper.h2
-rwxr-xr-xapps/plugins/lib/action_helper.pl2
-rw-r--r--apps/plugins/lib/button_helper.h2
-rwxr-xr-xapps/plugins/lib/button_helper.pl5
-rwxr-xr-xapps/plugins/lua/rbdefines_helper.pl2
14 files changed, 1928 insertions, 3 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 1628524805..f440104a19 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -7,6 +7,7 @@ alarm_menu.c
7#endif 7#endif
8abrepeat.c 8abrepeat.c
9bookmark.c 9bookmark.c
10core_keymap.c
10debug_menu.c 11debug_menu.c
11filetypes.c 12filetypes.c
12language.c 13language.c
diff --git a/apps/action.c b/apps/action.c
index b31c4fa927..3a4cc2ff64 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -69,6 +69,11 @@ static action_last_t action_last =
69 .tick = 0, 69 .tick = 0,
70 .wait_for_release = false, 70 .wait_for_release = false,
71 71
72#ifndef DISABLE_ACTION_REMAP
73 .check_remap = false,
74 .core_keymap = NULL,
75#endif
76
72#ifdef HAVE_TOUCHSCREEN 77#ifdef HAVE_TOUCHSCREEN
73 .ts_data = 0, 78 .ts_data = 0,
74 .ts_short_press = false, 79 .ts_short_press = false,
@@ -499,7 +504,7 @@ static inline int action_code_worker(action_last_t *last,
499 int *end ) 504 int *end )
500{ 505{
501 int ret = ACTION_UNKNOWN; 506 int ret = ACTION_UNKNOWN;
502 int i = 0; 507 int i = *end;
503 unsigned int found = 0; 508 unsigned int found = 0;
504 while (cur->items[i].button_code != BUTTON_NONE) 509 while (cur->items[i].button_code != BUTTON_NONE)
505 { 510 {
@@ -588,7 +593,9 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
588 int action = ACTION_NONE; 593 int action = ACTION_NONE;
589 int context = cur->context; 594 int context = cur->context;
590 int i = 0; 595 int i = 0;
591 596#ifndef DISABLE_ACTION_REMAP
597 last->check_remap = (last->core_keymap != NULL);
598#endif
592 cur->is_prebutton = false; 599 cur->is_prebutton = false;
593 600
594#ifdef HAVE_LOCKED_ACTIONS 601#ifdef HAVE_LOCKED_ACTIONS
@@ -609,9 +616,42 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
609#endif 616#endif
610 617
611 if ((context & CONTEXT_PLUGIN) && cur->get_context_map) 618 if ((context & CONTEXT_PLUGIN) && cur->get_context_map)
619 {
612 cur->items = cur->get_context_map(context); 620 cur->items = cur->get_context_map(context);
621 }
622#ifndef DISABLE_ACTION_REMAP
623 else if(last->check_remap) /* attempt to look up the button in user supplied remap */
624 {
625 cur->items = last->core_keymap;
626 i = 0;
627 action = ACTION_UNKNOWN;
628 /* check the lut at the beginning for the desired context */
629 while (cur->items[i].action_code != (int) CONTEXT_STOPSEARCHING)
630 {
631 if (cur->items[i].action_code == CORE_CONTEXT_REMAP(context))
632 {
633 i = cur->items[i].button_code;
634 action = action_code_worker(last, cur, &i);
635 break;
636 }
637 i++;
638 }
639
640 if (action != ACTION_UNKNOWN)
641 break;
642 else
643 {
644 /* Not found -- fall through to inbuilt keymaps */
645 i = 0;
646 last->check_remap = false;
647 cur->items = get_context_mapping(context);
648 }
649 }
650#endif
613 else 651 else
652 {
614 cur->items = get_context_mapping(context); 653 cur->items = get_context_mapping(context);
654 }
615 655
616 if (cur->items != NULL) 656 if (cur->items != NULL)
617 { 657 {
@@ -1150,6 +1190,66 @@ int get_action(int context, int timeout)
1150 return action; 1190 return action;
1151} 1191}
1152 1192
1193int action_set_keymap(struct button_mapping* core_keymap, int count)
1194{
1195
1196#ifdef DISABLE_ACTION_REMAP
1197 count = -1;
1198#else
1199 if (count > 0 && core_keymap != NULL) /* saf-tey checks :) */
1200 {
1201 int i = 0;
1202 if (core_keymap[count - 1].action_code != (int) CONTEXT_STOPSEARCHING ||
1203 core_keymap[count - 1].button_code != BUTTON_NONE) /* check for sentinel at end*/
1204 count = -1;
1205
1206 /* check the lut at the beginning for invalid offsets */
1207 while (count > 0 && core_keymap[i].action_code != (int) CONTEXT_STOPSEARCHING)
1208 {
1209 if ((core_keymap[i].action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1210 {
1211 int firstbtn = core_keymap[i].button_code;
1212 int endpos = firstbtn + core_keymap[i].pre_button_code;
1213 if (firstbtn > count || firstbtn < i || endpos > count)
1214 {
1215 /* offset out of bounds */
1216 count = -2;
1217 break;
1218 }
1219
1220 if (core_keymap[endpos].action_code != (int) CONTEXT_STOPSEARCHING)
1221 {
1222 /* stop sentinel is not at end of action lut*/
1223 count = -3;
1224 }
1225 }
1226 else /* something other than a context remap in the lut */
1227 {
1228 count = -4;
1229 break;
1230 }
1231
1232 i++;
1233
1234 if (i >= count) /* no sentinel in the lut */
1235 {
1236 count = -5;
1237 break;
1238 }
1239 }
1240
1241 if (count <= 0)
1242 core_keymap = NULL;
1243 }
1244 else
1245#endif
1246 {
1247 core_keymap = NULL;
1248 }
1249 action_last.core_keymap = core_keymap;
1250 return count;
1251}
1252
1153int get_custom_action(int context,int timeout, 1253int get_custom_action(int context,int timeout,
1154 const struct button_mapping* (*get_context_map)(int)) 1254 const struct button_mapping* (*get_context_map)(int))
1155{ 1255{
diff --git a/apps/action.h b/apps/action.h
index 3217ce4d6f..7fadc015c8 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -28,10 +28,13 @@
28#define TIMEOUT_NOBLOCK 0 28#define TIMEOUT_NOBLOCK 0
29 29
30#define CONTEXT_STOPSEARCHING 0xFFFFFFFF 30#define CONTEXT_STOPSEARCHING 0xFFFFFFFF
31
31#define CONTEXT_REMOTE 0x80000000 /* | this against another context to get remote buttons for that context */ 32#define CONTEXT_REMOTE 0x80000000 /* | this against another context to get remote buttons for that context */
32#define CONTEXT_CUSTOM 0x40000000 /* | this against anything to get your context number */ 33#define CONTEXT_CUSTOM 0x40000000 /* | this against anything to get your context number */
33#define CONTEXT_CUSTOM2 0x20000000 /* as above */ 34#define CONTEXT_CUSTOM2 0x20000000 /* as above */
34#define CONTEXT_PLUGIN 0x10000000 /* for plugins using get_custom_action */ 35#define CONTEXT_PLUGIN 0x10000000 /* for plugins using get_custom_action */
36#define CONTEXT_REMAPPED 0x08000000 /* marker for key remap context table */
37#define CORE_CONTEXT_REMAP(context) (CONTEXT_REMAPPED | context)
35#ifdef HAVE_LOCKED_ACTIONS 38#ifdef HAVE_LOCKED_ACTIONS
36#define CONTEXT_LOCKED 0x04000000 /* flag to use alternate keymap when screen is locked */ 39#define CONTEXT_LOCKED 0x04000000 /* flag to use alternate keymap when screen is locked */
37#endif 40#endif
@@ -415,6 +418,11 @@ typedef struct
415 bool repeated; 418 bool repeated;
416 bool wait_for_release; 419 bool wait_for_release;
417 420
421#ifndef DISABLE_ACTION_REMAP
422 bool check_remap;
423 struct button_mapping* core_keymap;
424#endif
425
418#ifdef HAVE_TOUCHSCREEN 426#ifdef HAVE_TOUCHSCREEN
419 bool ts_short_press; 427 bool ts_short_press;
420 int ts_data; 428 int ts_data;
@@ -441,6 +449,9 @@ bool action_userabort(int timeout);
441/* no other code should need this apart from action.c */ 449/* no other code should need this apart from action.c */
442const struct button_mapping* get_context_mapping(int context); 450const struct button_mapping* get_context_mapping(int context);
443 451
452/* load a key map to allow buttons for actions to be remapped see: core_keymap */
453int action_set_keymap(struct button_mapping* core_button_map, int count);
454
444/* returns the status code variable from action.c for the button just pressed 455/* returns the status code variable from action.c for the button just pressed
445 If button != NULL it will be set to the actual button code */ 456 If button != NULL it will be set to the actual button code */
446#define ACTION_REMOTE 0x1 /* remote was pressed */ 457#define ACTION_REMOTE 0x1 /* remote was pressed */
diff --git a/apps/core_keymap.c b/apps/core_keymap.c
new file mode 100644
index 0000000000..0a7241a9e0
--- /dev/null
+++ b/apps/core_keymap.c
@@ -0,0 +1,106 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2020 by 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#include "action.h"
23#include "core_alloc.h"
24#include "core_keymap.h"
25
26#if !defined(__PCTOOL__) || defined(CHECKWPS)
27int core_load_key_remap(const char *filename)
28{
29 static int keymap_handle = -1;
30 char *buf;
31 int fd = -1;
32 int count = 0;
33 size_t fsize = 0;
34 if (keymap_handle > 0) /* free old buffer */
35 {
36 action_set_keymap(NULL, -1);
37 keymap_handle = core_free(keymap_handle);
38 }
39 if (filename != NULL)
40 count = open_key_remap(filename, &fd, &fsize);
41 while (count > 0)
42 {
43
44 keymap_handle = core_alloc_ex("key remap", fsize, &buflib_ops_locked);
45 if (keymap_handle <= 0)
46 {
47 count = -30;
48 break;
49 }
50 buf = core_get_data(keymap_handle);
51 if (read(fd, buf, fsize) == (ssize_t) fsize)
52 {
53 count = action_set_keymap((struct button_mapping *) buf, count);
54 }
55 else
56 count = -40;
57 break;
58 }
59 close(fd);
60 return count;
61}
62
63int open_key_remap(const char *filename, int *fd, size_t *fsize)
64{
65 int count = 0;
66
67 while (filename && fd && fsize)
68 {
69 *fsize = 0;
70 *fd = open(filename, O_RDONLY);
71 if (*fd)
72 {
73 *fsize = filesize(*fd);
74
75 count = *fsize / sizeof(struct button_mapping);
76
77 if (count * sizeof(struct button_mapping) != *fsize)
78 {
79 count = -10;
80 break;
81 }
82
83 if (count > 1)
84 {
85 struct button_mapping header = {0};
86 read(*fd, &header, sizeof(struct button_mapping));
87 if (KEYREMAP_VERSION == header.action_code &&
88 KEYREMAP_HEADERID == header.button_code &&
89 header.pre_button_code == count)
90 {
91 count--;
92 *fsize -= sizeof(struct button_mapping);
93 }
94 else /* Header mismatch */
95 {
96 count = -20;
97 break;
98 }
99 }
100 }
101 break;
102 }
103 return count;
104}
105
106#endif /* !defined(__PCTOOL__) */
diff --git a/apps/core_keymap.h b/apps/core_keymap.h
new file mode 100644
index 0000000000..39d35e9cd9
--- /dev/null
+++ b/apps/core_keymap.h
@@ -0,0 +1,68 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2020 by 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#ifndef CORE_KEYMAP_H
22#define CORE_KEYMAP_H
23
24#include <stdbool.h>
25#include <inttypes.h>
26#include "config.h"
27#define KEYREMAP_VERSION 1
28#define KEYREMAP_HEADERID (LAST_ACTION_PLACEHOLDER | (TARGET_ID << 8))
29
30/* If exists remap file will be loaded at startup */
31#define CORE_KEYREMAP_FILE ROCKBOX_DIR "/keyremap.kmf"
32
33/* open_key_remap(filename , *fd (you must close file_descriptor), *fsize)
34 * checks/strips header and returns remaining count
35 * fd is opened and set to first record
36 * filesize contains the size of the remaining records
37*/
38int open_key_remap(const char *filename, int *fd, size_t *filesize);
39
40/* load a remap file to allow buttons for actions to be remapped */
41int core_load_key_remap(const char *filename);
42
43/*
44 * entries consist of 3 int [action, button, prebtn]
45 * the header (VERSION, LAST_DEFINED_ACTION, count) is stripped by open_key_remap
46 *
47 * context look up table is at the beginning
48 * action_code contains (context | CONTEXT_REMAPPED)
49 * button_code contains index of first remapped action for the matched context
50 * prebtn_code contains count of actions in this remapped context
51 * [-1] REMAP_VERSION, REMAP_HEADERID, entry count(9) / DISCARDED AFTER LOAD
52 * [0] CORE_CONTEXT_REMAP(ctx1), offset1=(3), count=(1)
53 * [1] CORE_CONTEXT_REMAP(ctx2, offset2=(5), count=(2)
54 * [2] sentinel, 0, 0
55 * [3] act0, btn, 0
56 * [4] sentinel 0, 0
57 * [5] act1, btn, 0
58 * [6] act2, btn1
59 * [7] sentinel, 0, 0
60 *
61 * Note:
62 * last entry of each group is always the sentinel [CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE]
63 * contexts must match exactly -- re-mapped contexts run before the built in w/ fall through contexts
64 * ie. you can't remap std_context and expect it to match std_context actions from the WPS context.
65 */
66
67#endif /* CORE_KEYMAP_H */
68
diff --git a/apps/main.c b/apps/main.c
index a88cd73ef7..2f3b246210 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -31,6 +31,7 @@
31#include "led.h" 31#include "led.h"
32#include "../kernel-internal.h" 32#include "../kernel-internal.h"
33#include "button.h" 33#include "button.h"
34#include "core_keymap.h"
34#include "tree.h" 35#include "tree.h"
35#include "filetypes.h" 36#include "filetypes.h"
36#include "panic.h" 37#include "panic.h"
@@ -175,6 +176,15 @@ int main(void)
175 usb_start_monitoring(); 176 usb_start_monitoring();
176#endif 177#endif
177 178
179#if !defined(DISABLE_ACTION_REMAP) && defined(CORE_KEYREMAP_FILE)
180 if (file_exists(CORE_KEYREMAP_FILE))
181 {
182 int mapct = core_load_key_remap(CORE_KEYREMAP_FILE);
183 if (mapct <= 0)
184 splashf(HZ, "key remap failed: %d, %s", mapct, CORE_KEYREMAP_FILE);
185 }
186#endif
187
178#ifdef AUTOROCK 188#ifdef AUTOROCK
179 { 189 {
180 char filename[MAX_PATH]; 190 char filename[MAX_PATH];
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index bb0960f501..89aba0e32f 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -47,6 +47,7 @@ jackpot,games
47jewels,games 47jewels,games
48jpeg,viewers 48jpeg,viewers
49keybox,apps 49keybox,apps
50keyremap,apps
50lamp,apps 51lamp,apps
51logo,demos 52logo,demos
52lrcplayer,apps 53lrcplayer,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index ab77dcde58..d2f3c39d54 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -11,6 +11,7 @@ cube.c
11dict.c 11dict.c
12jackpot.c 12jackpot.c
13keybox.c 13keybox.c
14keyremap.c
14logo.c 15logo.c
15lrcplayer.c 16lrcplayer.c
16mosaique.c 17mosaique.c
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
new file mode 100644
index 0000000000..acd23172f0
--- /dev/null
+++ b/apps/plugins/keyremap.c
@@ -0,0 +1,1616 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2022 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#include "plugin.h"
23#include "lang_enum.h"
24#include "../open_plugin.h"
25
26#include "lib/action_helper.h"
27#include "lib/button_helper.h"
28#include "lib/pluginlib_actions.h"
29#include "lib/printcell_helper.h"
30
31#ifdef ROCKBOX_HAS_LOGF
32#define logf rb->logf
33#else
34#define logf(...) do { } while(0)
35#endif
36
37/* CORE_KEYREMAP_FILE */
38#include "../core_keymap.h"
39#define KMFDIR ROCKBOX_DIR
40#define KMFEXT1 ".kmf"
41#define KMFEXT2 ".kmf.old"
42#define KMFUSER "user_keyremap"
43#define MAX_BUTTON_COMBO 5
44#define MAX_BUTTON_NAME 32
45#define TEST_COUNTDOWN_MS 1590
46
47static struct keyremap_buffer_t {
48 char * buffer;
49 size_t buf_size;
50 char *front;
51 char *end;
52} keyremap_buffer;
53
54
55struct action_mapping_t {
56 int context;
57 int display_pos;
58 struct button_mapping map;
59};
60
61static struct user_context_data_t {
62 struct action_mapping_t *ctx_map;
63 int ctx_count;
64 struct action_mapping_t *act_map;
65 int act_count;
66} ctx_data;
67
68/* set keys keymap */
69static struct setkeys_data_t {
70 /* save state in the set keys action view list */
71 int view_columns;
72 int view_lastcol;
73 uint32_t crc32;
74} keyset;
75
76/* test keys data */
77static struct testkey_data_t {
78 struct button_mapping *keymap;
79 int action;
80 int context;
81 int index;
82 int countdown;
83} keytest;
84
85static struct context_menu_data_t {
86 char *menuid;
87 int ctx_index;
88 int act_index;
89 int act_edit_index;
90 //const char * ctx_fmt;
91 const char * act_fmt;
92} ctx_menu_data;
93#define ACTIONFMT_LV0 "$%s$%s$%s$%s"
94#define ACTVIEW_HEADER "$Context$Action$Button$PreBtn"
95
96#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
97#define MENU_ID(x) (((void*)&mainmenu[x]))
98#define MENU_MAX_DEPTH 4
99/* this enum sets menu order */
100enum {
101 M_ROOT = 0,
102 M_SETKEYS,
103 M_TESTKEYS,
104 M_RESETKEYS,
105 M_SAVEKEYS,
106 M_LOADKEYS,
107 M_DELKEYS,
108 M_SETCORE,
109 M_EXIT,
110 M_LAST_MAINITEM, //MAIN MENU ITEM COUNT
111/*Menus not directly accessible from main menu*/
112 M_ACTIONS = M_LAST_MAINITEM,
113 M_BUTTONS,
114 M_CONTEXTS,
115 M_CONTEXT_EDIT,
116 M_LAST_ITEM, //ITEM COUNT
117};
118
119struct mainmenu { const char *name; void *menuid; int index; int items;};
120static struct mainmenu mainmenu[M_LAST_ITEM] = {
121#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)ID, (int)COUNT}
122MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
123MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
124MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
125MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
126MENU_ITEM(M_SAVEKEYS, "Save Keymap", 1),
127MENU_ITEM(M_LOADKEYS, "Load Keymaps", 1),
128MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
129MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
130MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0),
131MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER),
132MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */
133MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ),
134MENU_ITEM(M_CONTEXT_EDIT, "", 5),
135#undef MENU_ITEM
136};
137
138DIR* kmffiles_dirp = NULL;
139static int core_savecount = 0;/* so we don't overwrite the last .old file with revisions */
140
141/* global lists, for everything */
142static struct gui_synclist lists;
143
144static void menu_useract_set_positions(void);
145static size_t lang_strlcpy(char * dst, const char *src, size_t len)
146{
147 unsigned char **language_strings = rb->language_strings;
148 return rb->strlcpy(dst, P2STR((unsigned char*)src), len);
149}
150
151/* Menu stack macros */
152static int mlastsel[MENU_MAX_DEPTH * 2 + 1] = {0};
153#define PUSH_MENU(id, item) \
154 if(mlastsel[0] < MENU_MAX_DEPTH * 2) {mlastsel[++mlastsel[0]] = id;mlastsel[++mlastsel[0]] = item;}
155#define POP_MENU(id, item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); \
156 id = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); }
157#define PEEK_MENU_ITEM(item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]]:0);}
158#define PEEK_MENU_ID(id) {id = (mlastsel[0] > 1 ? mlastsel[mlastsel[0]-1]:0);}
159#define PEEK_MENU(id, item) PEEK_MENU_ITEM(item) PEEK_MENU_ID(id)
160#define SET_MENU_ITEM(item) \
161 if(mlastsel[0] > 1) {mlastsel[mlastsel[0]] = item;}
162
163static struct mainmenu *mainitem(int selected_item)
164{
165 static struct mainmenu empty = {0};
166 if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu))
167 return &mainmenu[selected_item];
168 else
169 return &empty;
170}
171
172static void synclist_set(int id, int selected_item, int items, int sel_size);
173static void synclist_set_update(int id, int selected_item, int items, int sel_size)
174{
175 SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
176 synclist_set(id, selected_item, items, sel_size);
177}
178
179static int btnval_to_index(unsigned int btnvalue)
180{
181 int index = -1;
182 for (int i = 0; i < available_button_count; i++)
183 {
184 const struct available_button *btn = &available_buttons[i];
185 if (btnvalue == btn->value)
186 {
187 index = i;
188 break;
189 }
190 }
191 return index;
192}
193
194static int btnval_to_name(char *buf, size_t bufsz, unsigned int btnvalue)
195{
196 int index = btnval_to_index(btnvalue);
197 if (index >= 0)
198 {
199 return rb->snprintf(buf, bufsz, "%s", available_buttons[index].name);
200 }
201 else /* this provides names for button combos e.g.(BUTTON_UP|BUTTON_REPEAT) */
202 {
203 int res = get_button_names(buf, bufsz, btnvalue);
204 if (res > 0 && res <= (int)bufsz)
205 return res;
206 }
207 return rb->snprintf(buf, bufsz, "%s", "BUTTON_UNKNOWN");
208}
209
210static int keyremap_check_extension(const char* filename)
211{
212 int found = 0;
213 unsigned int len = rb->strlen(filename);
214 if (len > sizeof(KMFEXT1) &&
215 rb->strcmp(&filename[len - sizeof(KMFEXT1) + 1], KMFEXT1) == 0)
216 {
217 found = 1;
218 }
219 else if (len > sizeof(KMFEXT2) &&
220 rb->strcmp(&filename[len - sizeof(KMFEXT2) + 1], KMFEXT2) == 0)
221 {
222 found = 2;
223 }
224 return found;
225}
226
227static int keyremap_count_files(const char *directory)
228{
229 int nfiles = 0;
230 DIR* kmfdirp = NULL;
231 kmfdirp = rb->opendir(directory);
232 if (kmfdirp != NULL)
233 {
234 struct dirent *entry;
235 while ((entry = rb->readdir(kmfdirp)))
236 {
237 /* skip directories */
238 if ((rb->dir_get_info(kmfdirp, entry).attribute & ATTR_DIRECTORY) != 0)
239 continue;
240 if (keyremap_check_extension(entry->d_name) > 0)
241 nfiles++;
242 }
243 rb->closedir(kmfdirp);
244 }
245 return nfiles;
246}
247
248static void keyremap_reset_buffer(void)
249{
250 keyremap_buffer.front = keyremap_buffer.buffer;
251 keyremap_buffer.end = keyremap_buffer.front + keyremap_buffer.buf_size;
252 rb->memset(keyremap_buffer.front, 0, keyremap_buffer.buf_size);
253 ctx_data.ctx_map = (struct action_mapping_t*)keyremap_buffer.front;
254 ctx_data.ctx_count = 0;
255 ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
256 ctx_data.act_count = 0;
257
258}
259
260static size_t keyremap_write_entries(int fd, struct button_mapping *data, int entry_count)
261{
262 if (fd < 0 || entry_count <= 0 || !data)
263 goto fail;
264 size_t bytes_req = sizeof(struct button_mapping) * entry_count;
265 size_t bytes = rb->write(fd, data, bytes_req);
266 if (bytes == bytes_req)
267 return bytes_req;
268fail:
269 return 0;
270}
271
272static size_t keyremap_write_header(int fd, int entry_count)
273{
274 if (fd < 0 || entry_count < 0)
275 goto fail;
276 struct button_mapping header = {KEYREMAP_VERSION, KEYREMAP_HEADERID, entry_count};
277 return keyremap_write_entries(fd, &header, 1);
278fail:
279 return 0;
280}
281
282static int keyremap_open_file(const char *filename, int *fd, size_t *fsize)
283{
284 int count = 0;
285
286 while (filename && fd && fsize)
287 {
288 *fsize = 0;
289 *fd = rb->open(filename, O_RDONLY);
290 if (*fd)
291 {
292 *fsize = rb->filesize(*fd);
293
294 count = *fsize / sizeof(struct button_mapping);
295
296 if (count * sizeof(struct button_mapping) != *fsize)
297 {
298 count = -10;
299 break;
300 }
301
302 if (count > 1)
303 {
304 struct button_mapping header = {0};
305 rb->read(*fd, &header, sizeof(struct button_mapping));
306 if (KEYREMAP_VERSION == header.action_code &&
307 KEYREMAP_HEADERID == header.button_code &&
308 header.pre_button_code == count)
309 {
310 count--;
311 *fsize -= sizeof(struct button_mapping);
312 }
313 else /* Header mismatch */
314 {
315 count = -20;
316 break;
317 }
318 }
319 }
320 break;
321 }
322 return count;
323}
324
325static int keyremap_map_is_valid(struct action_mapping_t *amap, int context)
326{
327 int ret = (amap->context == context ? 1: 0);
328 struct button_mapping entry = amap->map;
329 if (entry.action_code == (int)CONTEXT_STOPSEARCHING ||
330 entry.button_code == BUTTON_NONE)
331 {
332 ret = 0;
333 }
334
335 return ret;
336}
337
338static struct button_mapping *keyremap_create_temp(int *entries)
339{
340 struct button_mapping *tempkeymap;
341 int action_offset = 1; /* start of action entries (ctx_count + 1)*/
342 int entry_count = 1;/* (ctx_count + ctx_count + act_count) */
343 int index = 0;
344 int i, j;
345
346 /* count includes a single stop sentinel for the list of contexts */
347 /* and a stop sentinel for each group of action remaps as well */
348 for (i = 0; i < ctx_data.ctx_count; i++)
349 {
350 int actions_this_ctx = 0;
351 int context = ctx_data.ctx_map[i].context;
352 /* how many actions are contained in each context? */
353 for (j = 0; j < ctx_data.act_count; j++)
354 {
355 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
356 {
357 actions_this_ctx++;
358 }
359 }
360 /* only count contexts with remapped actions */
361 if (actions_this_ctx != 0)
362 {
363 action_offset++;
364 entry_count += actions_this_ctx + 2;
365 }
366 }
367 size_t keymap_bytes = (entry_count) * sizeof(struct button_mapping);
368 *entries = entry_count;
369 logf("keyremap create temp entry count: %d", entry_count);
370 logf("keyremap bytes: %ld, avail: %ld", keymap_bytes,
371 (keyremap_buffer.end - keyremap_buffer.front));
372 if (keyremap_buffer.front + keymap_bytes < keyremap_buffer.end)
373 {
374 tempkeymap = (struct button_mapping *) keyremap_buffer.front;
375 rb->memset(tempkeymap, 0, keymap_bytes);
376 struct button_mapping *entry = &tempkeymap[index++];
377 for (i = 0; i < ctx_data.ctx_count; i++)
378 {
379 int actions_this_ctx = 0;
380 int context = ctx_data.ctx_map[i].context;
381 /* how many actions are contained in each context? */
382 for (j = 0; j < ctx_data.act_count; j++)
383 {
384 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
385 {
386 actions_this_ctx += 1;
387 }
388 }
389 /*Don't save contexts with no actions */
390 if (actions_this_ctx == 0){ continue; }
391
392 entry->action_code = CORE_CONTEXT_REMAP(context);
393 entry->button_code = action_offset; /* offset of first action entry */
394 entry->pre_button_code = actions_this_ctx; /* entries (excluding sentinel) */
395 entry = &tempkeymap[index++];
396 logf("keyremap found context: %d index: %d entries: %d",
397 context, action_offset, actions_this_ctx);
398 action_offset += actions_this_ctx + 1; /* including sentinel */
399 }
400 /* context sentinel */
401 entry->action_code = CONTEXT_STOPSEARCHING;;
402 entry->button_code = BUTTON_NONE;
403 entry->pre_button_code = BUTTON_NONE;
404 entry = &tempkeymap[index++];
405
406 for (i = 0; i < ctx_data.ctx_count; i++)
407 {
408 int actions_this_ctx = 0;
409 int context = ctx_data.ctx_map[i].context;
410 for (int j = 0; j < ctx_data.act_count; j++)
411 {
412 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
413 {
414 struct button_mapping map = ctx_data.act_map[j].map;
415 entry->action_code = map.action_code;
416 entry->button_code = map.button_code;
417 entry->pre_button_code = map.pre_button_code;
418 entry = &tempkeymap[index++];
419 actions_this_ctx++;
420 logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
421 context, map.action_code, map.button_code, map.pre_button_code);
422 }
423 }
424 /*Don't save sentinel for contexts with no actions */
425 if (actions_this_ctx == 0){ continue; }
426
427 /* action sentinel */
428 entry->action_code = CONTEXT_STOPSEARCHING;;
429 entry->button_code = BUTTON_NONE;
430 entry->pre_button_code = BUTTON_NONE;
431 entry = &tempkeymap[index++];
432 }
433 }
434 else
435 {
436 rb->splashf(HZ *2, "Out of Memory");
437 logf("keyremap: create temp OOM");
438 *entries = 0;
439 tempkeymap = NULL;
440 }
441
442 return tempkeymap;
443}
444
445static int keyremap_save_current(const char *filename)
446{
447 int status = 0;
448 int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
449
450 struct button_mapping *keymap = keyremap_create_temp(&entry_count);
451
452 if (keymap == NULL || entry_count <= 3)
453 return status;
454 keyset.crc32 =
455 rb->crc_32(keymap, entry_count * sizeof(struct button_mapping), 0xFFFFFFFF);
456 int keyfd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
457 if (keyremap_write_header(keyfd, entry_count + 1) > 0)
458 {
459 if (keyremap_write_entries(keyfd, keymap, entry_count) > 0)
460 status = 1;
461 }
462 rb->close(keyfd);
463
464 return status;
465}
466
467static void keyremap_save_user_keys(bool notify)
468{
469 char buf[MAX_PATH];
470 int i = 1;
471 do
472 {
473 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
474 i++;
475 } while (i < 100 && rb->file_exists(buf));
476
477 if (keyremap_save_current(buf) == 0)
478 {
479 if(notify)
480 rb->splash(HZ *2, "Error Saving");
481 }
482 else if (notify)
483 rb->splashf(HZ *2, "Saved %s", buf);
484}
485
486static int keymap_add_context_entry(int context)
487{
488 int remap_context = CORE_CONTEXT_REMAP(context);
489 for (int i = 0; i < ctx_data.ctx_count; i++)
490 {
491 if (ctx_data.ctx_map[i].map.action_code == remap_context)
492 goto fail; /* context exists */
493 }
494 if (keyremap_buffer.front + sizeof(struct action_mapping_t) > keyremap_buffer.end)
495 goto fail;
496 keyremap_buffer.front += sizeof(struct action_mapping_t);
497 ctx_data.ctx_map[ctx_data.ctx_count].context = context;
498 ctx_data.ctx_map[ctx_data.ctx_count].display_pos = -1;
499 ctx_data.ctx_map[ctx_data.ctx_count].map.action_code = remap_context;
500 ctx_data.ctx_map[ctx_data.ctx_count].map.button_code = 0;
501 ctx_data.ctx_map[ctx_data.ctx_count].map.pre_button_code = 0;
502 ctx_data.ctx_count++;
503 menu_useract_set_positions();
504 return ctx_data.ctx_count;
505fail:
506 return 0;
507}
508
509static int keymap_add_button_entry(int context, int action_code,
510 int button_code, int pre_button_code)
511{
512 bool hasctx = false;
513 for (int i = 0; i < ctx_data.ctx_count; i++)
514 {
515 if (ctx_data.ctx_map[i].context == context)
516 {
517 hasctx = true;
518 break;
519 }
520 }
521 if (!hasctx || keyremap_buffer.end - sizeof(struct action_mapping_t) < keyremap_buffer.front)
522 goto fail;
523 keyremap_buffer.end -= sizeof(struct action_mapping_t);
524 ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
525 ctx_data.act_map[0].context = context;
526 ctx_data.act_map[0].display_pos = -1;
527 ctx_data.act_map[0].map.action_code = action_code;
528 ctx_data.act_map[0].map.button_code = button_code;
529 ctx_data.act_map[0].map.pre_button_code = pre_button_code;
530 ctx_data.act_count++;
531 menu_useract_set_positions();
532 return ctx_data.act_count;
533fail:
534 return 0;
535}
536
537static int keyremap_load_file(const char *filename)
538{
539 logf("keyremap: load %s", filename);
540 int fd = -1;
541 size_t fsize = 0;
542 size_t bytes;
543 struct button_mapping entry;
544 int count = keyremap_open_file(filename, &fd, &fsize);
545 logf("keyremap: entries %d", count);
546 /* actions are indexed from the first entry after the header save this pos */
547 off_t firstpos = rb->lseek(fd, 0, SEEK_CUR);
548 off_t ctxpos = firstpos;
549
550 if (count > 0)
551 {
552 keyremap_reset_buffer();
553 while(--count > 0)
554 {
555 rb->lseek(fd, ctxpos, SEEK_SET); /* next context remap entry */
556 bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
557 ctxpos = rb->lseek(fd, 0, SEEK_CUR);
558 if (bytes != sizeof(struct button_mapping))
559 {
560 count = -10;
561 goto fail;
562 }
563 if (entry.action_code == (int)CONTEXT_STOPSEARCHING)
564 {
565 logf("keyremap: end of context entries ");
566 break;
567 }
568 if ((entry.action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
569 {
570
571 int context = (entry.action_code & ~CONTEXT_REMAPPED);
572 int offset = entry.button_code;
573 int entries = entry.pre_button_code;
574 if (offset == 0 || entries <= 0)
575 {
576 logf("keyremap: error reading offset");
577 }
578 logf("keyremap found context: %d file offset: %d entries: %d",
579 context, offset, entries);
580
581 keymap_add_context_entry(context);
582
583 off_t entrypos = firstpos + (offset * sizeof(struct button_mapping));
584 rb->lseek(fd, entrypos, SEEK_SET);
585 for (int i = 0; i < entries; i++)
586 {
587 bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
588 if (bytes == sizeof(struct button_mapping))
589 {
590 int action = entry.action_code;
591 int button = entry.button_code;
592 int prebtn = entry.pre_button_code;
593
594 if (action == (int)CONTEXT_STOPSEARCHING || button == BUTTON_NONE)
595 {
596 logf("keyremap: entry invalid");
597 goto fail;
598 }
599 logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
600 context, action, button, prebtn);
601 keymap_add_button_entry(context, action, button, prebtn);
602 }
603 else
604 goto fail;
605 }
606 }
607 else
608 {
609 logf("keyremap: Invalid context entry");
610 keyremap_reset_buffer();
611 count = -20;
612 goto fail;
613 }
614 }
615 }
616
617 int entries = 0;
618 struct button_mapping *keymap = keyremap_create_temp(&entries);
619 if (keymap != NULL)
620 {
621 keyset.crc32 =
622 rb->crc_32(keymap, entries * sizeof(struct button_mapping), 0xFFFFFFFF);
623 }
624fail:
625 rb->close(fd);
626 if (count <= 0)
627 rb->splashf(HZ * 2, "Error Loading %sz", filename);
628 return count;
629}
630
631static const struct button_mapping* test_get_context_map(int context)
632{
633 (void)context;
634
635 if (keytest.keymap != NULL && keytest.context >= 0)
636 {
637 int mapidx = keytest.keymap[keytest.index].button_code;
638 int mapcnt = keytest.keymap[keytest.index].pre_button_code;
639 /* make fallthrough to the test context*/
640 keytest.keymap[mapidx + mapcnt].action_code = keytest.context;
641 static const struct button_mapping *testctx[] = { NULL };
642 testctx[0] = &keytest.keymap[mapidx];
643 return testctx[0];
644 }
645 else
646 return NULL;
647}
648
649static const char *kmffiles_name_cb(int selected_item, void* data,
650 char* buf, size_t buf_len)
651{
652 /* found kmf filenames returned by each call kmffiles_dirp keeps state
653 * selected_item = 0 resets state */
654
655 (void)data;
656 buf[0] = '\0';
657 if (selected_item == 0)
658 {
659 rb->closedir(kmffiles_dirp);
660 kmffiles_dirp = rb->opendir(KMFDIR);
661 }
662 if (kmffiles_dirp != NULL)
663 {
664 struct dirent *entry;
665 while ((entry = rb->readdir(kmffiles_dirp)))
666 {
667 /* skip directories */
668 if ((rb->dir_get_info(kmffiles_dirp, entry).attribute & ATTR_DIRECTORY) != 0)
669 continue;
670 if (keyremap_check_extension(entry->d_name) > 0)
671 {
672 rb->snprintf(buf, buf_len, "%s", entry->d_name);
673 return buf;
674 }
675 }
676 }
677 return "Error!";
678}
679
680static void menu_useract_set_positions(void)
681{
682 /* responsible for item ordering to display action edit interface */
683 int display_pos = 0; /* start at item 0*/
684 int i, j;
685 for (i = 0; i < ctx_data.ctx_count; i++)
686 {
687 int context = ctx_data.ctx_map[i].context;
688 ctx_data.ctx_map[i].display_pos = display_pos++;
689 /* how many actions are contained in this context? */
690 for (j = 0; j < ctx_data.act_count; j++)
691 {
692 if (ctx_data.act_map[j].context == context)
693 {
694 ctx_data.act_map[j].display_pos = display_pos++;
695 }
696 }
697 }
698}
699
700static const char *menu_useract_items_cb(int selected_item, void* data,
701 char* buf, size_t buf_len)
702{
703 static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
704 static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
705 int i;
706 (void)data;
707 buf[0] = '\0';
708 for(i = 0; i < ctx_data.ctx_count; i ++)
709 {
710 if (ctx_data.ctx_map[i].display_pos == selected_item)
711 {
712 if (ctx_data.act_count == 0)
713 rb->snprintf(buf, buf_len, "%s$%s",
714 context_name(ctx_data.ctx_map[i].context),
715 "Select$to add$actions");
716 else
717 rb->snprintf(buf, buf_len, "%s", context_name(ctx_data.ctx_map[i].context));
718 return buf;
719 }
720 }
721 for(i = 0; i < ctx_data.act_count; i ++)
722 {
723 if (ctx_data.act_map[i].display_pos == selected_item)
724 {
725 int context = ctx_data.act_map[i].context;
726 char ctxbuf[action_helper_maxbuffer];
727 char *pctxbuf = ctxbuf;
728 char *pactname;
729 rb->snprintf(ctxbuf, sizeof(ctxbuf), "%s", context_name(context));
730 pctxbuf += sizeof("CONTEXT");
731 struct button_mapping * bm = &ctx_data.act_map[i].map;
732 pactname = action_name(bm->action_code);
733 pactname += sizeof("ACTION");
734 /* BUTTON & PRE_BUTTON */
735 btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
736 btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
737
738 rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
739 pactname, buf_button + sizeof("BUTTON"), buf_prebtn + sizeof("BUTTON"));
740 return buf;
741 }
742 }
743 return "Error!";
744}
745
746static const char *edit_keymap_name_cb(int selected_item, void* data,
747 char* buf, size_t buf_len)
748{
749 (void)data;
750 buf[0] = '\0';
751 if (selected_item == 0)
752 {
753 rb->snprintf(buf, buf_len, "Add Context");
754 ctx_menu_data.act_index = -1;
755 ctx_menu_data.ctx_index = -1;
756 ctx_menu_data.act_fmt = ACTIONFMT_LV0;
757 }
758 else if (ctx_data.ctx_count > 0)
759 {
760 return menu_useract_items_cb(selected_item - 1, data, buf, buf_len);
761 }
762 return buf;
763}
764
765static const char *test_keymap_name_cb(int selected_item, void* data,
766 char* buf, size_t buf_len)
767{
768 (void)data;
769 buf[0] = '\0';
770 if (keytest.context >= 0)
771 {
772 if (selected_item == 0)
773 rb->snprintf(buf, buf_len, "< %s >", context_name(keytest.context));
774 else if (selected_item == 1)
775 {
776 if (keytest.countdown >= 10)
777 rb->snprintf(buf, buf_len, "Testing %d", keytest.countdown / 10);
778 else
779 rb->snprintf(buf, buf_len, "Start test");
780 }
781 else if (selected_item == 2)
782 rb->snprintf(buf, buf_len, "%s", action_name(keytest.action));
783 }
784 return buf;
785}
786
787static const char* list_get_name_cb(int selected_item, void* data,
788 char* buf, size_t buf_len)
789{
790 buf[0] = '\0';
791 const struct mainmenu *cur = (struct mainmenu *) data;
792 if (data == MENU_ID(M_ROOT))
793 return mainitem(selected_item + 1)->name;
794 else if (selected_item >= cur->items - 1)
795 {
796 return ID2P(LANG_BACK);
797 }
798 if (data == MENU_ID(M_SETKEYS))
799 {
800 return edit_keymap_name_cb(selected_item, data, buf, buf_len);
801 }
802 else if (data == MENU_ID(M_BUTTONS))
803 {
804 const struct available_button *btn = &available_buttons[selected_item];
805 rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value);
806 return buf;
807 }
808 else if (data == MENU_ID(M_ACTIONS))
809 {
810 return action_name(selected_item);
811 }
812 else if (data == MENU_ID(M_CONTEXTS))
813 {
814 return context_name(selected_item);
815 }
816 else if (data == MENU_ID(M_DELKEYS) || data == MENU_ID(M_LOADKEYS))
817 {
818 /* need to iterate the callback for the items off screen to
819 * keep ordering, this limits the menu to only the main screen :( */
820 int start_item = lists.start_item[SCREEN_MAIN];
821 if (start_item != 0 && start_item == selected_item)
822 {
823 for (int i = 0; i < start_item; i++)
824 kmffiles_name_cb(i, data, buf, buf_len);
825 }
826 return kmffiles_name_cb(selected_item, data, buf, buf_len);
827 }
828 else if (data == MENU_ID(M_TESTKEYS))
829 {
830 return test_keymap_name_cb(selected_item, data, buf, buf_len);
831 }
832 return buf;
833}
834
835static int list_voice_cb(int list_index, void* data)
836{
837 if (!rb->global_settings->talk_menu)
838 return -1;
839
840 if (data == MENU_ID(M_ROOT))
841 {
842 const char * name = mainitem(list_index)->name;
843 long id = P2ID((const unsigned char *)name);
844 if(id>=0)
845 rb->talk_id(id, true);
846 else
847 rb->talk_spell(name, true);
848 }
849 else
850 {
851 char buf[64];
852 const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
853 long id = P2ID((const unsigned char *)name);
854 if(id>=0)
855 rb->talk_id(id, true);
856 else
857 rb->talk_spell(name, true);
858 }
859 return 0;
860}
861
862int menu_action_root(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
863{
864#ifdef ROCKBOX_HAS_LOGF
865 char logfnamebuf[64];
866#endif
867
868 if (*action == ACTION_STD_OK)
869 {
870 struct mainmenu *cur = mainitem(selected_item + 1);
871 if (cur != NULL)
872 {
873#ifdef ROCKBOX_HAS_LOGF
874 lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
875 logf("Root select menu -> %s(%d) ", logfnamebuf, cur->index);
876#endif
877 if (cur->menuid == MENU_ID(M_SETKEYS))
878 {
879 keyset.view_lastcol = -1;
880 }
881 else if (cur->menuid == MENU_ID(M_SETCORE))
882 {
883 if (rb->file_exists(CORE_KEYREMAP_FILE) && 0 == core_savecount++)
884 {
885 rb->rename(CORE_KEYREMAP_FILE, CORE_KEYREMAP_FILE".old"); /* make a backup */
886 }
887 if (keyremap_save_current(CORE_KEYREMAP_FILE) == 0)
888 rb->splash(HZ *2, "Error Saving");
889 else
890 rb->splash(HZ *2, "Saved, Restart Device");
891 goto default_handler;
892 }
893 else if (cur->menuid == MENU_ID(M_SAVEKEYS))
894 {
895 keyremap_save_user_keys(true);
896 goto default_handler;
897 }
898 else if (cur->menuid == MENU_ID(M_DELKEYS) ||
899 cur->menuid == MENU_ID(M_LOADKEYS))
900 {
901 cur->items = keyremap_count_files(KMFDIR) + 1;
902 }
903 else if (cur->menuid == MENU_ID(M_TESTKEYS))
904 {
905 int entries = 0;
906 keytest.keymap = keyremap_create_temp(&entries);
907 if (entries > 0)
908 {
909 struct button_mapping *entry = &keytest.keymap[0];
910 if (entry->action_code != (int)CONTEXT_STOPSEARCHING
911 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
912 {
913 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
914 }
915 else
916 keytest.context = -1;
917 }
918 else
919 {
920 keytest.keymap = NULL;
921 keytest.context = -1;
922 }
923 keytest.action = ACTION_NONE;
924 keytest.countdown = 0;
925 }
926 else if (cur->menuid == MENU_ID(M_RESETKEYS))
927 {
928 rb->splashf(HZ / 2, "Delete Current?");
929 int usract = ACTION_NONE;
930 while (usract <= ACTION_UNKNOWN)
931 {
932 usract = rb->get_action(CONTEXT_STD, HZ / 10);
933 }
934 if (usract == ACTION_STD_OK)
935 {
936 keyremap_reset_buffer();
937 }
938 goto default_handler;
939 }
940 }
941
942 if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT))
943 {
944 logf("Root menu %s", (cur->menuid) == NULL ? "NULL":"Exit");
945 *action = ACTION_STD_CANCEL;
946 *exit = true;
947 }
948 else
949 {
950#ifdef ROCKBOX_HAS_LOGF
951 lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
952 logf("Root load menu -> %s(%d) ", logfnamebuf, cur->index);
953#endif
954 synclist_set_update(cur->index, 0, cur->items, 1);
955 rb->gui_synclist_draw(lists);
956 *action = ACTION_NONE;
957 }
958 }
959
960 return PLUGIN_OK;
961default_handler:
962 return GOTO_ACTION_DEFAULT_HANDLER;
963}
964
965int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
966{
967 (void) exit;
968 int i;
969 struct mainmenu *cur = (struct mainmenu *)lists->data;
970 if (*action == ACTION_STD_OK)
971 {
972 if (selected_item == 0) /*add_context*/
973 {
974 const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
975 synclist_set_update(mainm->index, 0, mainm->items, 1);
976 rb->gui_synclist_draw(lists);
977 goto default_handler;
978 }
979 else if (selected_item < lists->nb_items - 1)/* not back*/
980 {
981 bool add_action = false;
982 for (i = 0; i < ctx_data.ctx_count; i++)
983 {
984 if (ctx_data.ctx_map[i].display_pos == selected_item - 1)
985 {
986 add_action = true;
987 break;
988 }
989 }
990
991 if (add_action)
992 {
993 keymap_add_button_entry(ctx_data.ctx_map[i].context, ACTION_NONE, BUTTON_NONE, BUTTON_NONE);
994 cur->items++;
995 lists->nb_items++;
996 goto default_handler;
997 }
998 else
999 {
1000 keyset.view_lastcol = printcell_increment_column(lists, 1, true);
1001 *action = ACTION_NONE;
1002 }
1003 }
1004 }
1005 else if (*action == ACTION_STD_CANCEL)
1006 {
1007 keyset.view_lastcol = printcell_increment_column(lists, -1, true);
1008 if (keyset.view_lastcol != keyset.view_columns - 1)
1009 {
1010 *action = ACTION_NONE;
1011 }
1012 }
1013 else if (*action == ACTION_STD_CONTEXT)
1014 {
1015 int col = keyset.view_lastcol;
1016 for (i = 0; i < ctx_data.act_count; i++)
1017 {
1018 if (ctx_data.act_map[i].display_pos == selected_item - 1)
1019 {
1020 int context = ctx_data.act_map[i].context;
1021 int action = ctx_data.act_map[i].map.action_code;
1022 int button = ctx_data.act_map[i].map.button_code;
1023 int prebtn = ctx_data.act_map[i].map.pre_button_code;
1024
1025 if (col < 0)
1026 {
1027 rb->splashf(HZ, "short press increments columns");
1028
1029 }
1030 else if (col == 0) /* Context */
1031 {
1032 const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
1033 synclist_set_update(mainm->index, context, mainm->items, 1);
1034 rb->gui_synclist_draw(lists);
1035 goto default_handler;
1036
1037 }
1038 else if (col == 1) /* Action */
1039 {
1040 const struct mainmenu *mainm = &mainmenu[M_ACTIONS];
1041 synclist_set_update(mainm->index, action, mainm->items, 1);
1042 rb->gui_synclist_draw(lists);
1043 goto default_handler;
1044 }
1045 else if (col == 2) /* Button */
1046 {
1047 const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
1048 int btnidx = btnval_to_index(button);
1049 synclist_set_update(mainm->index, btnidx, mainm->items, 1);
1050 rb->gui_synclist_draw(lists);
1051 goto default_handler;
1052 }
1053 else if (col == 3) /* PreBtn */
1054 {
1055 const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
1056 int pbtnidx = btnval_to_index(prebtn);
1057 synclist_set_update(mainm->index, pbtnidx, mainm->items, 1);
1058 rb->gui_synclist_draw(lists);
1059 goto default_handler;
1060 }
1061 }
1062 }
1063 }
1064 return PLUGIN_OK;
1065default_handler:
1066 return GOTO_ACTION_DEFAULT_HANDLER;
1067}
1068
1069int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1070{
1071 (void) exit;
1072 (void) lists;
1073 static int last_count = 0;
1074 if (keytest.keymap != NULL && keytest.countdown >= 10 && keytest.context >= 0)
1075 {
1076 int newact = rb->get_custom_action(CONTEXT_PLUGIN, HZ / 10, test_get_context_map);
1077 if (newact == ACTION_NONE)
1078 {
1079 keytest.countdown--;
1080 if (last_count - keytest.countdown > 10)
1081 keytest.action = newact;
1082 }
1083 else
1084 {
1085 last_count = keytest.countdown;
1086 keytest.action = newact;
1087 }
1088 *action = ACTION_REDRAW;
1089 goto default_handler;
1090 }
1091
1092
1093 if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
1094 {
1095 keytest.index -= 2;
1096 if (keytest.index < 0)
1097 keytest.index = 0;
1098 *action = ACTION_STD_OK;
1099 }
1100
1101 if (*action == ACTION_STD_OK)
1102 {
1103 if (selected_item == 0)
1104 {
1105 if (keytest.keymap != NULL)
1106 {
1107 struct button_mapping *entry = &keytest.keymap[++keytest.index];
1108 if (entry->action_code != (int) CONTEXT_STOPSEARCHING
1109 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1110 {
1111 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
1112 }
1113 else
1114 {
1115 entry = &keytest.keymap[0];
1116 if (entry->action_code != (int)CONTEXT_STOPSEARCHING
1117 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1118 {
1119 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
1120 keytest.index = 0;
1121 }
1122 else
1123 {
1124 keytest.keymap = NULL;
1125 keytest.context = -1;
1126 keytest.index = -1;
1127 keytest.action = ACTION_NONE;
1128 keytest.countdown = 0;
1129 }
1130 }
1131 }
1132 }
1133 else if (selected_item == 1)
1134 {
1135 keytest.countdown = TEST_COUNTDOWN_MS / 10;
1136 keytest.action = ACTION_NONE;
1137 }
1138 }
1139
1140 return PLUGIN_OK;
1141default_handler:
1142 return GOTO_ACTION_DEFAULT_HANDLER;
1143}
1144
1145int menu_action_listfiles(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1146{
1147 (void) exit;
1148 int i;
1149 if (*action == ACTION_STD_OK)
1150 {
1151 struct mainmenu *cur = (struct mainmenu *)lists->data;
1152 if (selected_item >= (cur->items) - 1)/*back*/
1153 {
1154 *action = ACTION_STD_CANCEL;
1155 }
1156 else
1157 {
1158 /* iterate to the desired file */
1159 char buf[MAX_PATH];
1160 int len = rb->snprintf(buf, sizeof(buf), "%s/", KMFDIR);
1161 if (len < (int) sizeof(buf))
1162 {
1163 char *name = &buf[len];
1164 size_t bufleft = sizeof(buf) - len;
1165 list_get_name_cb(0, lists->data, name, bufleft);
1166 for (i = 1; i <= selected_item; i++)
1167 {
1168 list_get_name_cb(i, lists->data, name, bufleft);
1169 }
1170
1171 if (lists->data == MENU_ID(M_DELKEYS))
1172 {
1173 rb->splashf(HZ / 2, "Delete %s ?", buf);
1174 int action = ACTION_NONE;
1175 while (action <= ACTION_UNKNOWN)
1176 {
1177 action = rb->get_action(CONTEXT_STD, HZ / 10);
1178 }
1179 if (action == ACTION_STD_OK)
1180 {
1181 rb->remove(buf);
1182 cur->items--;
1183 lists->nb_items--;
1184 }
1185 }
1186 else if (lists->data == MENU_ID(M_LOADKEYS))
1187 {
1188 keyremap_load_file(buf);
1189 rb->splashf(HZ * 2, "Loaded %s ", buf);
1190 }
1191 }
1192 }
1193 }
1194 return PLUGIN_OK;
1195}
1196
1197int menu_action_submenus(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1198{
1199#ifdef ROCKBOX_HAS_LOGF
1200 char logfnamebuf[64];
1201#endif
1202 (void) exit;
1203 if (*action == ACTION_STD_OK)
1204 {
1205 struct mainmenu *cur = (struct mainmenu *)lists->data;
1206 if (selected_item >= (cur->items) - 1)/*back*/
1207 {
1208 *action = ACTION_STD_CANCEL;
1209 }
1210 else if (lists->data == MENU_ID(M_ACTIONS))
1211 {
1212#ifdef ROCKBOX_HAS_LOGF
1213 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1214 logf("ACT %s %d (0x%X)", name, selected_item, selected_item);
1215#endif
1216 int id, item;
1217 POP_MENU(id, item);
1218 POP_MENU(id, item);
1219 const struct mainmenu *lastm = &mainmenu[id];
1220
1221 if (id == M_SETKEYS)
1222 {
1223 for (int i = 0; i < ctx_data.act_count; i++)
1224 {
1225 if (ctx_data.act_map[i].display_pos == item - 2)
1226 {
1227 int col = keyset.view_lastcol;
1228 struct button_mapping * bm = &ctx_data.act_map[i].map;
1229 if (col == 1) /* Action */
1230 {
1231 bm->action_code = selected_item;
1232 }
1233 break;
1234 }
1235 }
1236 }
1237 synclist_set(lastm->index, item-1, lastm->items, 1);
1238 rb->gui_synclist_draw(lists);
1239 }
1240 else if (lists->data == MENU_ID(M_CONTEXTS))
1241 {
1242#ifdef ROCKBOX_HAS_LOGF
1243 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1244 logf("CTX %s %d (0x%X)", name, selected_item, selected_item);
1245#endif
1246 int id, item;
1247 POP_MENU(id, item);
1248 POP_MENU(id, item);
1249 struct mainmenu *lastm = &mainmenu[id];
1250 if (id == M_SETKEYS)
1251 {
1252 bool found = false;
1253 int newctx = selected_item;
1254
1255 for (int i = 0; i < ctx_data.act_count; i++)
1256 {
1257 if (ctx_data.act_map[i].display_pos == item - 2)
1258 {
1259 int col = keyset.view_lastcol;
1260 if (col == 0) /* Context */
1261 {
1262 ctx_data.act_map[i].context = newctx;
1263 /* check if this context exists (if not create it) */
1264 for (int j = 0; j < ctx_data.ctx_count; j++)
1265 {
1266 if (ctx_data.ctx_map[j].context == newctx)
1267 {
1268 found = true;;
1269 break;
1270 }
1271 }
1272 }
1273 break;
1274 }
1275 }
1276 if (found == false)
1277 {
1278 keymap_add_context_entry(newctx);
1279 lastm->items++;
1280 }
1281 }
1282 synclist_set(lastm->index, item-1, lastm->items, 1);
1283 rb->gui_synclist_draw(lists);
1284 }
1285 else if (lists->data == MENU_ID(M_BUTTONS))
1286 {
1287#ifdef ROCKBOX_HAS_LOGF
1288 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1289 logf("BTN %s", name);
1290#endif
1291 int id, item;
1292 POP_MENU(id, item);
1293 POP_MENU(id, item);
1294 const struct mainmenu *lastm = &mainmenu[id];
1295
1296 if (id == M_SETKEYS)
1297 {
1298 for (int i = 0; i < ctx_data.act_count; i++)
1299 {
1300 if (ctx_data.act_map[i].display_pos == item - 2)
1301 {
1302 int col = keyset.view_lastcol;
1303 struct button_mapping * bm = &ctx_data.act_map[i].map;
1304 const struct available_button *btn = &available_buttons[selected_item];
1305 if (col == 2) /* BUTTON*/
1306 {
1307 if (btn->value == BUTTON_NONE)
1308 bm->button_code = btn->value;
1309 else
1310 bm->button_code |= btn->value;
1311 }
1312 else if (col == 3) /* PREBTN */
1313 {
1314 if (btn->value == BUTTON_NONE)
1315 bm->pre_button_code = btn->value;
1316 else
1317 bm->pre_button_code |= btn->value;
1318 }
1319 break;
1320 }
1321 }
1322 }
1323 synclist_set(lastm->index, item-1, lastm->items, 1);
1324 rb->gui_synclist_draw(lists);
1325 }
1326 }
1327 return PLUGIN_OK;
1328}
1329
1330static void cleanup(void *parameter)
1331{
1332 (void)parameter;
1333 keyremap_save_user_keys(false);
1334}
1335
1336int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1337{
1338 int status = PLUGIN_OK;
1339 /* Top Level Menu Actions */
1340 if (lists->data == MENU_ID(M_ROOT))
1341 {
1342 status = menu_action_root(action, selected_item, exit, lists);
1343 }
1344 else if (lists->data == MENU_ID(M_SETKEYS))
1345 {
1346 status = menu_action_setkeys(action, selected_item, exit, lists);
1347 }
1348 else if (lists->data == MENU_ID(M_TESTKEYS))
1349 {
1350 status = menu_action_testkeys(action, selected_item, exit, lists);
1351 }
1352 else if (lists->data == MENU_ID(M_DELKEYS))
1353 {
1354 status = menu_action_listfiles(action, selected_item, exit, lists);
1355 }
1356 else if (lists->data == MENU_ID(M_LOADKEYS))
1357 {
1358 status = menu_action_listfiles(action, selected_item, exit, lists);
1359 }
1360
1361 if (status == GOTO_ACTION_DEFAULT_HANDLER)
1362 goto default_handler;
1363 /* Submenu Actions */
1364 menu_action_submenus(action, selected_item, exit, lists);
1365
1366 /* Global cancel */
1367 if (*action == ACTION_STD_CANCEL)
1368 {
1369 logf("CANCEL BUTTON");
1370 if (lists->data == MENU_ID(M_TESTKEYS))
1371 {
1372 keytest.keymap = NULL;
1373 keytest.context = -1;
1374 }
1375
1376 if (lists->data != MENU_ID(M_ROOT))
1377 {
1378 int id, item;
1379 POP_MENU(id, item);
1380 POP_MENU(id, item);
1381 const struct mainmenu *lastm = &mainmenu[id];
1382 synclist_set(lastm->index, item-1, lastm->items, 1);
1383 rb->gui_synclist_draw(lists);
1384 }
1385 else
1386 {
1387 /* save changes? */
1388 if (ctx_data.act_count + ctx_data.ctx_count >= 2)
1389 {
1390 int entries = 0;
1391 struct button_mapping *keymap = keyremap_create_temp(&entries);
1392 if (keymap != NULL && keyset.crc32 != rb->crc_32(keymap,
1393 entries * sizeof(struct button_mapping), 0xFFFFFFFF))
1394 {
1395 rb->splashf(HZ / 2, "Save?");
1396 int action = ACTION_NONE;
1397 while (action <= ACTION_UNKNOWN)
1398 {
1399 action = rb->get_action(CONTEXT_STD, HZ / 10);
1400 }
1401 if (action == ACTION_STD_OK)
1402 {
1403 keyremap_save_user_keys(true);
1404 goto default_handler;
1405 }
1406 }
1407 }
1408 *exit = true;
1409 }
1410 }
1411default_handler:
1412 if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED)
1413 {
1414 *exit = true;
1415 return PLUGIN_USB_CONNECTED;
1416 }
1417 return PLUGIN_OK;
1418}
1419
1420static void synclist_set(int id, int selected_item, int items, int sel_size)
1421{
1422 void* menu_id = MENU_ID(id);
1423 static char menu_title[64]; /* title is used by every menu */
1424 PUSH_MENU(id, selected_item);
1425
1426 if (items <= 0)
1427 return;
1428 if (selected_item < 0)
1429 selected_item = 0;
1430
1431 list_voice_cb(0, menu_id);
1432 rb->gui_synclist_init(&lists,list_get_name_cb,
1433 menu_id, false, sel_size, NULL);
1434
1435 rb->gui_synclist_set_icon_callback(&lists,NULL);
1436 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
1437 rb->gui_synclist_set_nb_items(&lists,items);
1438 rb->gui_synclist_limit_scroll(&lists,true);
1439 rb->gui_synclist_select_item(&lists, selected_item);
1440 printcell_enable(&lists, false, false);
1441
1442 if (menu_id == MENU_ID(M_ROOT))
1443 {
1444 lang_strlcpy(menu_title, mainmenu[M_ROOT].name, sizeof(menu_title));
1445 rb->gui_synclist_set_title(&lists, menu_title, Icon_Plugin);
1446 }
1447 else if (menu_id == MENU_ID(M_SETKEYS))
1448 {
1449 printcell_enable(&lists, true, true);
1450 keyset.view_columns = printcell_set_columns(&lists, ACTVIEW_HEADER, Icon_Rockbox);
1451 int curcol = printcell_increment_column(&lists, 0, true);
1452 if (keyset.view_lastcol >= keyset.view_columns)
1453 keyset.view_lastcol = -1;
1454 /* restore column position */
1455 while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol)
1456 {
1457 curcol = printcell_increment_column(&lists, 1, true);
1458 }
1459 keyset.view_lastcol = curcol;
1460 }
1461 else
1462 {
1463 int id;
1464 PEEK_MENU_ID(id);
1465 lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title));
1466 rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered);
1467 /* if (menu_title[0] == '$'){ printcell_enable(&lists, true, true); } */
1468 }
1469
1470}
1471
1472static void keyremap_set_buffer(void* buffer, size_t buf_size)
1473{
1474 /* set up the keyremap action buffer
1475 * contexts start at the front and work towards the back
1476 * actions start at the back and work towards front
1477 * if they meet buffer is out of space (checked by ctx & btn add entry fns)
1478 */
1479 keyremap_buffer.buffer = buffer;
1480 keyremap_buffer.buf_size = buf_size;
1481 keyremap_reset_buffer();
1482}
1483
1484enum plugin_status plugin_start(const void* parameter)
1485{
1486 int ret = PLUGIN_OK;
1487 int selected_item = -1;
1488 int action;
1489 bool redraw = true;
1490 bool exit = false;
1491 if (parameter)
1492 {
1493 //
1494 }
1495
1496 size_t buf_size;
1497 void* buffer = rb->plugin_get_buffer(&buf_size);
1498 keyremap_set_buffer(buffer, buf_size);
1499
1500 mainmenu[M_BUTTONS].items = available_button_count;
1501 /* add back item to each submenu */
1502 for (int i = 1; i < M_LAST_ITEM; i++)
1503 mainmenu[i].items += 1;
1504
1505#if 0
1506 keymap_add_context_entry(CONTEXT_WPS);
1507 keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_PLAY, BUTTON_VOL_UP, BUTTON_NONE);
1508 keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_STOP, BUTTON_VOL_DOWN | BUTTON_REPEAT, BUTTON_NONE);
1509
1510 keymap_add_context_entry(CONTEXT_MAINMENU);
1511 keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_PREV, BUTTON_VOL_UP, BUTTON_NONE);
1512 keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_NEXT, BUTTON_VOL_DOWN, BUTTON_NONE);
1513
1514 keymap_add_context_entry(CONTEXT_STD);
1515 keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_UP, BUTTON_NONE);
1516 keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_DOWN, BUTTON_NONE);
1517#endif
1518
1519 keyset.crc32 = 0;
1520 keyset.view_lastcol = -1;
1521 if (!exit)
1522 {
1523 const struct mainmenu *mainm = &mainmenu[M_ROOT];
1524 synclist_set(mainm->index, 0, mainm->items, 1);
1525 rb->gui_synclist_draw(&lists);
1526
1527 while (!exit)
1528 {
1529 action = rb->get_action(CONTEXT_LIST, HZ / 10);
1530
1531 if (action == ACTION_NONE)
1532 {
1533 if (redraw)
1534 {
1535 action = ACTION_REDRAW;
1536 redraw = false;
1537 }
1538 }
1539 else
1540 redraw = true;
1541
1542 ret = menu_action_cb(&action, selected_item, &exit, &lists);
1543 if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
1544 continue;
1545 selected_item = rb->gui_synclist_get_sel_pos(&lists);
1546
1547 mainmenu[M_SETKEYS].items = ctx_data.ctx_count + ctx_data.act_count + 2;
1548 }
1549 }
1550 rb->closedir(kmffiles_dirp);
1551
1552 return ret;
1553}
1554
1555
1556
1557#if 0 /* Alt Example */
1558static int write_keyremap(const char*filename, struct button_mapping *remap_data, int count)
1559{
1560 size_t res = 0;
1561 if (!filename || !remap_data || count <= 0)
1562 goto fail;
1563 int fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
1564 if (fd < 0)
1565 goto fail;
1566 if (keyremap_write_header(fd, count + 1) > 0)
1567 {
1568 res = keyremap_write_entries(fd, remap_data, count);
1569 res += sizeof(struct button_mapping); /*for header */
1570 }
1571fail:
1572 rb->close(fd);
1573 return res;
1574}
1575
1576void alt_example(void)
1577{
1578 struct button_mapping kmap[8];
1579
1580 kmap[0].action_code = CORE_CONTEXT_REMAP(CONTEXT_WPS);
1581 kmap[0].button_code = 3; /*OFFSET*/
1582 kmap[0].pre_button_code = 1; /*COUNT*/
1583
1584 kmap[1].action_code = CORE_CONTEXT_REMAP(CONTEXT_MAINMENU);
1585 kmap[1].button_code = 5; /*OFFSET*/
1586 kmap[1].pre_button_code = 2; /*COUNT*/
1587
1588 kmap[2].action_code = CONTEXT_STOPSEARCHING;
1589 kmap[2].button_code = BUTTON_NONE;
1590 kmap[2].pre_button_code = BUTTON_NONE;
1591
1592 kmap[3].action_code = ACTION_WPS_PLAY;
1593 kmap[3].button_code = BUTTON_VOL_UP;
1594 kmap[3].pre_button_code = BUTTON_NONE;
1595
1596 kmap[4].action_code = CONTEXT_STOPSEARCHING;
1597 kmap[4].button_code = BUTTON_NONE;
1598 kmap[4].pre_button_code = BUTTON_NONE;
1599
1600 kmap[5].action_code = ACTION_STD_NEXT;
1601 kmap[5].button_code = BUTTON_VOL_DOWN;
1602 kmap[5].pre_button_code = BUTTON_NONE;
1603
1604 kmap[6].action_code = ACTION_STD_PREV;
1605 kmap[6].button_code = BUTTON_VOL_UP;
1606 kmap[6].pre_button_code = BUTTON_NONE;
1607
1608 kmap[7].action_code = CONTEXT_STOPSEARCHING;
1609 kmap[7].button_code = BUTTON_NONE;
1610 kmap[7].pre_button_code = BUTTON_NONE;
1611
1612 write_keyremap(CORE_KEYREMAP_FILE, kmap, 8);
1613
1614 return ret;
1615}
1616#endif
diff --git a/apps/plugins/lib/action_helper.h b/apps/plugins/lib/action_helper.h
index 58d9c6c303..53f5c840f8 100644
--- a/apps/plugins/lib/action_helper.h
+++ b/apps/plugins/lib/action_helper.h
@@ -27,7 +27,7 @@
27*/ 27*/
28#ifndef _ACTION_HELPER_H_ 28#ifndef _ACTION_HELPER_H_
29#define _ACTION_HELPER_H_ 29#define _ACTION_HELPER_H_
30 30extern const size_t action_helper_maxbuffer;
31char* action_name(int action); 31char* action_name(int action);
32char* context_name(int context); 32char* context_name(int context);
33 33
diff --git a/apps/plugins/lib/action_helper.pl b/apps/plugins/lib/action_helper.pl
index 1dfdcfd070..742419e23b 100755
--- a/apps/plugins/lib/action_helper.pl
+++ b/apps/plugins/lib/action_helper.pl
@@ -140,10 +140,12 @@ printf "#define CONTEXTBUFSZ %d\n\n", $len_max_context;
140 140
141if ($len_max_action > $len_max_context) 141if ($len_max_action > $len_max_context)
142{ 142{
143 print "const size_t action_helper_maxbuffer = ACTIONBUFSZ;\n";
143 print "static char name_buf[ACTIONBUFSZ];\n"; 144 print "static char name_buf[ACTIONBUFSZ];\n";
144} 145}
145else 146else
146{ 147{
148 print "const size_t action_helper_maxbuffer = CONTEXTBUFSZ;\n";
147 print "static char name_buf[CONTEXTBUFSZ];\n"; 149 print "static char name_buf[CONTEXTBUFSZ];\n";
148} 150}
149print <<EOF 151print <<EOF
diff --git a/apps/plugins/lib/button_helper.h b/apps/plugins/lib/button_helper.h
index 1197b172b0..4087ba898a 100644
--- a/apps/plugins/lib/button_helper.h
+++ b/apps/plugins/lib/button_helper.h
@@ -32,7 +32,9 @@ struct available_button
32 * generated at compile time you can still call it as such though 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)*/ 33* eg available_buttons[0] or available_buttons[available_button_count] (NULL SENTINEL, 0)*/
34 34
35extern const size_t button_helper_maxbuffer;
35extern const struct available_button * const available_buttons; 36extern const struct available_button * const available_buttons;
36extern const int available_button_count; 37extern const int available_button_count;
38
37int get_button_names(char *buf, size_t bufsz, unsigned long button); 39int get_button_names(char *buf, size_t bufsz, unsigned long button);
38#endif /* _BUTTON_HELPER_H_ */ 40#endif /* _BUTTON_HELPER_H_ */
diff --git a/apps/plugins/lib/button_helper.pl b/apps/plugins/lib/button_helper.pl
index 45c3fd9073..192df18d7f 100755
--- a/apps/plugins/lib/button_helper.pl
+++ b/apps/plugins/lib/button_helper.pl
@@ -26,12 +26,15 @@ my @buttons = ();
26my $count = 1; #null sentinel 26my $count = 1; #null sentinel
27my $val; 27my $val;
28my $def; 28my $def;
29my $len_max_button = 0;
29while(my $line = <STDIN>) 30while(my $line = <STDIN>)
30{ 31{
31 chomp($line); 32 chomp($line);
32 if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/) 33 if($line =~ /^#define (BUTTON_[^\s]+) (.+)$/)
33 { 34 {
34 $def = "{\"$1\", $2},\n"; 35 $def = "{\"$1\", $2},\n";
36 my $slen = length($1) + 1; # NULL terminator
37 if ($slen > $len_max_button) { $len_max_button = $slen; }
35 $val = $2; 38 $val = $2;
36 if($val =~ /^0/) 39 if($val =~ /^0/)
37 { 40 {
@@ -53,6 +56,8 @@ print <<EOF
53#include "button.h" 56#include "button.h"
54#include "button_helper.h" 57#include "button_helper.h"
55 58
59const size_t button_helper_maxbuffer = $len_max_button;
60
56static const struct available_button buttons[$count] = { 61static const struct available_button buttons[$count] = {
57EOF 62EOF
58; 63;
diff --git a/apps/plugins/lua/rbdefines_helper.pl b/apps/plugins/lua/rbdefines_helper.pl
index 5fb0946a6a..095ec55515 100755
--- a/apps/plugins/lua/rbdefines_helper.pl
+++ b/apps/plugins/lua/rbdefines_helper.pl
@@ -55,6 +55,8 @@ if ($def_type eq "rb_defines") {
55 '^PLUGIN(_APPS_|_GAMES_|_)DATA_DIR$', 55 '^PLUGIN(_APPS_|_GAMES_|_)DATA_DIR$',
56 '^ROCKBOX_DIR$', 56 '^ROCKBOX_DIR$',
57 '^STYLE_(NONE|DEFAULT|INVERT|COLORBAR|GRADIENT|COLORED)', 57 '^STYLE_(NONE|DEFAULT|INVERT|COLORBAR|GRADIENT|COLORED)',
58 '^CORE_KEYMAP_FILE$',
59 'CONTEXT_(STOPSEARCHING|REMOTE|CUSTOM|CUSTOM2|PLUGIN|REMAPPED)$',
58 '^VIEWERS_DATA_DIR$'); 60 '^VIEWERS_DATA_DIR$');
59} 61}
60elsif ($def_type eq "sound_defines") { 62elsif ($def_type eq "sound_defines") {