summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2021-04-02 21:34:29 -0400
committerWilliam Wilgus <wilgus.william@gmail.com>2022-02-23 08:47:12 -0500
commitf7bb9e21672566308ab837c370f27c10c154e6fc (patch)
tree688aa4e1a44572452ee192838b0af510b57ce770
parent7952687185aa27fcecc0924efa45c0e64e0ed4fe (diff)
downloadrockbox-f7bb9e21672566308ab837c370f27c10c154e6fc.tar.gz
rockbox-f7bb9e21672566308ab837c370f27c10c154e6fc.zip
Add custom action mapping to core
results of an idea I discussed in IRC changed the way the lookup in the remap file works.. entries consist of 3 int [action, button, prebtn] context look up table is at the beginning action_code contains the (context | CONTEXT_REMAPPED) button_code contains the index of the first remapped action for the matched context [0] CORE_CONTEXT_REMAP(ctx1) offset1=(3), count=(1) [1] CORE_CONTEXT_REMAP(ctx2, offset2=(5), count=(1) [2] sentinel, 0, 0 [3] act0, btn, 0 [4] sentinel 0, 0 [5] act1, btn, 0 [6] sentinel, 0, 0 Note: last entry of each group is always the sentinel [CONTEXT_STOPSEARCHING, BUTTON_NONE, BUTTON_NONE] contexts must match exactly -- re-mapped contexts run before the built in w/ fall through contexts ie. you can't remap std_context and expect it to match std_context actions from the WPS context. -- Done -- Code for reading core remap entries -- Done -- import of core remap entires from disk -- Done -- plugin to set new key mapping (the hard part) The plugin is started and FULLY functional you can add actions and contexts you can change context, action, button, prebtn delete keymap files load keymapfiles save user keymaps test keymaps before applying them loading keymaps to core still requires restart ----------------------------------------------------------------------------------------------- Change-Id: Ib8b88c5ae91af4d540e1829de5db32669cd68203
-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") {