diff options
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/action.c | 104 | ||||
-rw-r--r-- | apps/action.h | 11 | ||||
-rw-r--r-- | apps/core_keymap.c | 106 | ||||
-rw-r--r-- | apps/core_keymap.h | 68 | ||||
-rw-r--r-- | apps/main.c | 10 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 1 | ||||
-rw-r--r-- | apps/plugins/keyremap.c | 1616 | ||||
-rw-r--r-- | apps/plugins/lib/action_helper.h | 2 | ||||
-rwxr-xr-x | apps/plugins/lib/action_helper.pl | 2 | ||||
-rw-r--r-- | apps/plugins/lib/button_helper.h | 2 | ||||
-rwxr-xr-x | apps/plugins/lib/button_helper.pl | 5 | ||||
-rwxr-xr-x | apps/plugins/lua/rbdefines_helper.pl | 2 |
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 |
8 | abrepeat.c | 8 | abrepeat.c |
9 | bookmark.c | 9 | bookmark.c |
10 | core_keymap.c | ||
10 | debug_menu.c | 11 | debug_menu.c |
11 | filetypes.c | 12 | filetypes.c |
12 | language.c | 13 | language.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 | ||
1193 | int 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 | |||
1153 | int get_custom_action(int context,int timeout, | 1253 | int 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 */ |
442 | const struct button_mapping* get_context_mapping(int context); | 450 | const 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 */ | ||
453 | int 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) | ||
27 | int 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 | |||
63 | int 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 | */ | ||
38 | int 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 */ | ||
41 | int 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 | |||
47 | jewels,games | 47 | jewels,games |
48 | jpeg,viewers | 48 | jpeg,viewers |
49 | keybox,apps | 49 | keybox,apps |
50 | keyremap,apps | ||
50 | lamp,apps | 51 | lamp,apps |
51 | logo,demos | 52 | logo,demos |
52 | lrcplayer,apps | 53 | lrcplayer,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 | |||
11 | dict.c | 11 | dict.c |
12 | jackpot.c | 12 | jackpot.c |
13 | keybox.c | 13 | keybox.c |
14 | keyremap.c | ||
14 | logo.c | 15 | logo.c |
15 | lrcplayer.c | 16 | lrcplayer.c |
16 | mosaique.c | 17 | mosaique.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 | |||
47 | static struct keyremap_buffer_t { | ||
48 | char * buffer; | ||
49 | size_t buf_size; | ||
50 | char *front; | ||
51 | char *end; | ||
52 | } keyremap_buffer; | ||
53 | |||
54 | |||
55 | struct action_mapping_t { | ||
56 | int context; | ||
57 | int display_pos; | ||
58 | struct button_mapping map; | ||
59 | }; | ||
60 | |||
61 | static 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 */ | ||
69 | static 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 */ | ||
77 | static struct testkey_data_t { | ||
78 | struct button_mapping *keymap; | ||
79 | int action; | ||
80 | int context; | ||
81 | int index; | ||
82 | int countdown; | ||
83 | } keytest; | ||
84 | |||
85 | static 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 */ | ||
100 | enum { | ||
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 | |||
119 | struct mainmenu { const char *name; void *menuid; int index; int items;}; | ||
120 | static struct mainmenu mainmenu[M_LAST_ITEM] = { | ||
121 | #define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)ID, (int)COUNT} | ||
122 | MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1), | ||
123 | MENU_ITEM(M_SETKEYS, "Edit Keymap", 1), | ||
124 | MENU_ITEM(M_TESTKEYS, "Test Keymap", 4), | ||
125 | MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1), | ||
126 | MENU_ITEM(M_SAVEKEYS, "Save Keymap", 1), | ||
127 | MENU_ITEM(M_LOADKEYS, "Load Keymaps", 1), | ||
128 | MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1), | ||
129 | MENU_ITEM(M_SETCORE, "Set Core Remap", 1), | ||
130 | MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0), | ||
131 | MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER), | ||
132 | MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */ | ||
133 | MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER ), | ||
134 | MENU_ITEM(M_CONTEXT_EDIT, "", 5), | ||
135 | #undef MENU_ITEM | ||
136 | }; | ||
137 | |||
138 | DIR* kmffiles_dirp = NULL; | ||
139 | static int core_savecount = 0;/* so we don't overwrite the last .old file with revisions */ | ||
140 | |||
141 | /* global lists, for everything */ | ||
142 | static struct gui_synclist lists; | ||
143 | |||
144 | static void menu_useract_set_positions(void); | ||
145 | static 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 */ | ||
152 | static 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 | |||
163 | static 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 ∅ | ||
170 | } | ||
171 | |||
172 | static void synclist_set(int id, int selected_item, int items, int sel_size); | ||
173 | static 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 | |||
179 | static 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 | |||
194 | static 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 | |||
210 | static 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 | |||
227 | static 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 | |||
248 | static 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 | |||
260 | static 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; | ||
268 | fail: | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static 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); | ||
278 | fail: | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static 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 | |||
325 | static 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 | |||
338 | static 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 | |||
445 | static 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 | |||
467 | static 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 | |||
486 | static 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; | ||
505 | fail: | ||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | static 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; | ||
533 | fail: | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static 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 | } | ||
624 | fail: | ||
625 | rb->close(fd); | ||
626 | if (count <= 0) | ||
627 | rb->splashf(HZ * 2, "Error Loading %sz", filename); | ||
628 | return count; | ||
629 | } | ||
630 | |||
631 | static 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 | |||
649 | static 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 | |||
680 | static 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 | |||
700 | static 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 | |||
746 | static 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 | |||
765 | static 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 | |||
787 | static 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 | |||
835 | static 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 | |||
862 | int 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; | ||
961 | default_handler: | ||
962 | return GOTO_ACTION_DEFAULT_HANDLER; | ||
963 | } | ||
964 | |||
965 | int 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; | ||
1065 | default_handler: | ||
1066 | return GOTO_ACTION_DEFAULT_HANDLER; | ||
1067 | } | ||
1068 | |||
1069 | int 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; | ||
1141 | default_handler: | ||
1142 | return GOTO_ACTION_DEFAULT_HANDLER; | ||
1143 | } | ||
1144 | |||
1145 | int 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 | |||
1197 | int 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 | |||
1330 | static void cleanup(void *parameter) | ||
1331 | { | ||
1332 | (void)parameter; | ||
1333 | keyremap_save_user_keys(false); | ||
1334 | } | ||
1335 | |||
1336 | int 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 | } | ||
1411 | default_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 | |||
1420 | static 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 | |||
1472 | static 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 | |||
1484 | enum 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 */ | ||
1558 | static 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 | } | ||
1571 | fail: | ||
1572 | rb->close(fd); | ||
1573 | return res; | ||
1574 | } | ||
1575 | |||
1576 | void 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 | 30 | extern const size_t action_helper_maxbuffer; | |
31 | char* action_name(int action); | 31 | char* action_name(int action); |
32 | char* context_name(int context); | 32 | char* 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 | ||
141 | if ($len_max_action > $len_max_context) | 141 | if ($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 | } |
145 | else | 146 | else |
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 | } |
149 | print <<EOF | 151 | print <<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 | ||
35 | extern const size_t button_helper_maxbuffer; | ||
35 | extern const struct available_button * const available_buttons; | 36 | extern const struct available_button * const available_buttons; |
36 | extern const int available_button_count; | 37 | extern const int available_button_count; |
38 | |||
37 | int get_button_names(char *buf, size_t bufsz, unsigned long button); | 39 | int 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 = (); | |||
26 | my $count = 1; #null sentinel | 26 | my $count = 1; #null sentinel |
27 | my $val; | 27 | my $val; |
28 | my $def; | 28 | my $def; |
29 | my $len_max_button = 0; | ||
29 | while(my $line = <STDIN>) | 30 | while(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 | ||
59 | const size_t button_helper_maxbuffer = $len_max_button; | ||
60 | |||
56 | static const struct available_button buttons[$count] = { | 61 | static const struct available_button buttons[$count] = { |
57 | EOF | 62 | EOF |
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 | } |
60 | elsif ($def_type eq "sound_defines") { | 62 | elsif ($def_type eq "sound_defines") { |