summaryrefslogtreecommitdiff
path: root/apps/plugins/keyremap.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/keyremap.c')
-rw-r--r--apps/plugins/keyremap.c1616
1 files changed, 1616 insertions, 0 deletions
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