A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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
25#include "lib/action_helper.h"
26#include "lib/button_helper.h"
27#include "lib/pluginlib_actions.h"
28#include "lib/printcell_helper.h"
29#include "lib/kbd_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 MAX_MENU_NAME 64
46#define TEST_COUNTDOWN_MS 1590
47
48struct context_flags {
49 char * name;
50 int flag;
51};
52
53/* flags added to context_name[] */
54static struct context_flags context_flags[] = {
55 {"UNKNOWN", 0},/* index 0 is an Error */
56#ifndef HAS_BUTTON_HOLD
57 {"LOCKED", CONTEXT_LOCKED},
58#endif
59 /*{"PLUGIN", CONTEXT_PLUGIN}, need a custom action list and a way to supply */
60#if BUTTON_REMOTE != 0
61 {"REMOTE", CONTEXT_REMOTE},
62#ifndef HAS_BUTTON_HOLD
63 {"REMOTE_LOCKED", CONTEXT_REMOTE | CONTEXT_LOCKED},
64#endif
65#endif /* BUTTON_REMOTE != 0 */
66};
67
68static struct keyremap_buffer_t {
69 char * buffer;
70 size_t buf_size;
71 char *front;
72 char *end;
73} keyremap_buffer;
74
75
76struct action_mapping_t {
77 int context;
78 int display_pos;
79 struct button_mapping map;
80};
81
82static struct user_context_data_t {
83 struct action_mapping_t *ctx_map;
84 int ctx_count;
85 struct action_mapping_t *act_map;
86 int act_count;
87} ctx_data;
88
89/* set keys keymap */
90static struct setkeys_data_t {
91 /* save state in the set keys action view list */
92 int view_columns;
93 int view_lastcol;
94 uint32_t crc32;
95} keyset;
96
97/* test keys data */
98static struct testkey_data_t {
99 struct button_mapping *keymap;
100 int action;
101 int context;
102 int index;
103 int countdown;
104} keytest;
105
106static struct context_menu_data_t {
107 char *menuid;
108 int ctx_index;
109 int act_index;
110 int act_edit_index;
111 //const char * ctx_fmt;
112 const char * act_fmt;
113} ctx_menu_data;
114#define ACTIONFMT_LV0 "$%s$%s$%s$%s"
115#define ACTVIEW_HEADER "$Context$Action$Button$PreBtn"
116
117#define GOTO_ACTION_DEFAULT_HANDLER (PLUGIN_OK + 1)
118#define MENU_ID(x) (((void*)&mainmenu[x]))
119#define MENU_MAX_DEPTH 4
120/* this enum sets menu order */
121enum {
122 M_ROOT = 0,
123 M_SETKEYS,
124 M_TESTKEYS,
125 M_RESETKEYS,
126 M_EXPORTKEYS,
127 M_IMPORTKEYS,
128 M_SAVEKEYS,
129 M_LOADKEYS,
130 M_DELKEYS,
131 M_TMPCORE,
132 M_SETCORE,
133 M_DELCORE,
134 M_EXIT,
135 M_LAST_MAINITEM, //MAIN MENU ITEM COUNT
136/*Menus not directly accessible from main menu*/
137 M_ACTIONS = M_LAST_MAINITEM,
138 M_BUTTONS,
139 M_CONTEXTS,
140 M_CONTEXT_EDIT,
141 M_LAST_ITEM, //ITEM COUNT
142};
143
144struct mainmenu { const char *name; void *menuid; int index; int items;};
145static struct mainmenu mainmenu[M_LAST_ITEM] = {
146#define MENU_ITEM(ID, NAME, COUNT) [ID]{NAME, MENU_ID(ID), (int)ID, (int)COUNT}
147MENU_ITEM(M_ROOT, "Key Remap Plugin", M_LAST_MAINITEM - 1),
148MENU_ITEM(M_SETKEYS, "Edit Keymap", 1),
149MENU_ITEM(M_TESTKEYS, "Test Keymap", 4),
150MENU_ITEM(M_RESETKEYS, "Reset Keymap", 1),
151MENU_ITEM(M_EXPORTKEYS, "Export Text Keymap", 1),
152MENU_ITEM(M_IMPORTKEYS, "Import Text Keymap", 1),
153MENU_ITEM(M_SAVEKEYS, "Save Native Keymap", 1),
154MENU_ITEM(M_LOADKEYS, "Load Native Keymaps", 1),
155MENU_ITEM(M_DELKEYS, "Delete Keymaps", 1),
156MENU_ITEM(M_TMPCORE, "Temp Core Remap", 1),
157MENU_ITEM(M_SETCORE, "Set Core Remap", 1),
158MENU_ITEM(M_DELCORE, "Remove Core Remap", 1),
159MENU_ITEM(M_EXIT, ID2P(LANG_MENU_QUIT), 0),
160MENU_ITEM(M_ACTIONS, "Actions", LAST_ACTION_PLACEHOLDER),
161MENU_ITEM(M_BUTTONS, "Buttons", -1), /* Set at runtime in plugin_start: */
162MENU_ITEM(M_CONTEXTS, "Contexts", LAST_CONTEXT_PLACEHOLDER * ARRAYLEN(context_flags)),
163MENU_ITEM(M_CONTEXT_EDIT, "", 5),
164#undef MENU_ITEM
165};
166
167DIR* kmffiles_dirp = NULL;
168static int core_savecount = 0;/* so we don't overwrite the last .old file with revisions */
169
170/* global lists, for everything */
171static struct gui_synclist lists;
172
173static void menu_useract_set_positions(void);
174static size_t lang_strlcpy(char * dst, const char *src, size_t len)
175{
176 unsigned char **language_strings = rb->language_strings;
177 return rb->strlcpy(dst, P2STR((unsigned char*)src), len);
178}
179
180/* Menu stack macros */
181static int mlastsel[MENU_MAX_DEPTH * 2 + 1] = {0};
182#define PUSH_MENU(id, item) \
183 if(mlastsel[0] < MENU_MAX_DEPTH * 2) {mlastsel[++mlastsel[0]] = id;mlastsel[++mlastsel[0]] = item;}
184#define POP_MENU(id, item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); \
185 id = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]--]:0); }
186#define PEEK_MENU_ITEM(item) { item = (mlastsel[0] > 0 ? mlastsel[mlastsel[0]]:0);}
187#define PEEK_MENU_ID(id) {id = (mlastsel[0] > 1 ? mlastsel[mlastsel[0]-1]:0);}
188#define PEEK_MENU(id, item) PEEK_MENU_ITEM(item) PEEK_MENU_ID(id)
189#define SET_MENU_ITEM(item) \
190 if(mlastsel[0] > 1) {mlastsel[mlastsel[0]] = item;}
191
192static struct mainmenu *mainitem(int selected_item)
193{
194 static struct mainmenu empty = {0};
195 if (selected_item >= 0 && selected_item < (int) ARRAYLEN(mainmenu))
196 return &mainmenu[selected_item];
197 else
198 return ∅
199}
200/* Forward Declarations */
201static const char *edit_keymap_name_cb(int selected_item, void* data,char* buf, size_t buf_len);
202static int keyremap_import_file(char *filenamebuf, size_t bufsz);
203static void synclist_set(int id, int selected_item, int items, int sel_size);
204
205static int prompt_filename(char *buf, size_t bufsz)
206{
207#define KBD_LAYOUT "abcdefghijklmnop\nqrstuvwxyz |()[]\n1234567890 /._-+\n\n" \
208 "\nABCDEFGHIJKLMNOP\nQRSTUVWXYZ |()[]\n1234567890 /._-+"
209 ucschar_t kbd[sizeof(KBD_LAYOUT) + 10];
210 ucschar_t *kbd_p = kbd;
211 if (!kbd_create_layout(KBD_LAYOUT, kbd, sizeof(kbd)))
212 kbd_p = NULL;
213
214#undef KBD_LAYOUT
215 return rb->kbd_input(buf, bufsz, kbd_p) + 1;
216}
217
218static void synclist_set_update(int id, int selected_item, int items, int sel_size)
219{
220 SET_MENU_ITEM(lists.selected_item + 1); /* update selected for previous menu*/
221 synclist_set(id, selected_item, items, sel_size);
222}
223
224/* returns the actual context & index of the flag is passed in *flagidx */
225static int ctx_strip_flagidx(int ctx, int *flagidx)
226{
227 int ctx_out = ctx % (LAST_CONTEXT_PLACEHOLDER);
228 *flagidx = 0;
229 if (ctx_out != ctx)
230 {
231 while (ctx >= LAST_CONTEXT_PLACEHOLDER)
232 {
233 (*flagidx)++;
234 ctx -= LAST_CONTEXT_PLACEHOLDER;
235 }
236 if (*flagidx >= (int)ARRAYLEN(context_flags))
237 *flagidx = 0; /* unknown flag */
238
239 logf("%s ctx: (%d) %s flag idx: (%d) %s\n", __func__,
240 ctx, context_name(ctx), *flagidx, context_flags[*flagidx].name);
241 }
242 return ctx_out;
243}
244
245/* combines context name and flag name using '_' to join them */
246static char *ctx_name_and_flag(int index)
247{
248 static char ctx_namebuf[MAX_MENU_NAME];
249 char *ctx_name = "?";
250 int flagidx;
251 int ctx = ctx_strip_flagidx(index, &flagidx);
252 if (flagidx == 0)
253 {
254 ctx_name = context_name(ctx);
255 }
256 else if (flagidx >= 0 && flagidx < (int)ARRAYLEN(context_flags))
257 {
258 rb->snprintf(ctx_namebuf, sizeof(ctx_namebuf), "%s_%s",
259 context_name(ctx), context_flags[flagidx].name);
260 ctx_name = ctx_namebuf;
261 }
262 return ctx_name;
263}
264
265static int btnval_to_index(unsigned int btnvalue)
266{
267 int index = -1;
268 for (int i = 0; i < available_button_count; i++)
269 {
270 const struct available_button *btn = &available_buttons[i];
271 if (btnvalue == btn->value)
272 {
273 index = i;
274 break;
275 }
276 }
277 return index;
278}
279
280static int btnval_to_name(char *buf, size_t bufsz, unsigned int btnvalue)
281{
282 int index = btnval_to_index(btnvalue);
283 if (index >= 0)
284 {
285 return rb->snprintf(buf, bufsz, "%s", available_buttons[index].name);
286 }
287 else /* this provides names for button combos e.g.(BUTTON_UP|BUTTON_REPEAT) */
288 {
289 int res = get_button_names(buf, bufsz, btnvalue);
290 if (res > 0 && res <= (int)bufsz)
291 return res;
292 }
293 return rb->snprintf(buf, bufsz, "%s", "BUTTON_UNKNOWN");
294}
295
296static int keyremap_check_extension(const char* filename)
297{
298 int found = 0;
299 unsigned int len = rb->strlen(filename);
300 if (len > sizeof(KMFEXT1) &&
301 rb->strcmp(&filename[len - sizeof(KMFEXT1) + 1], KMFEXT1) == 0)
302 {
303 found = 1;
304 }
305 else if (len > sizeof(KMFEXT2) &&
306 rb->strcmp(&filename[len - sizeof(KMFEXT2) + 1], KMFEXT2) == 0)
307 {
308 found = 2;
309 }
310 return found;
311}
312
313static int keyremap_count_files(const char *directory)
314{
315 int nfiles = 0;
316 DIR* kmfdirp = NULL;
317 kmfdirp = rb->opendir(directory);
318 if (kmfdirp != NULL)
319 {
320 struct dirent *entry;
321 while ((entry = rb->readdir(kmfdirp)))
322 {
323 /* skip directories */
324 if ((rb->dir_get_info(kmfdirp, entry).attribute & ATTR_DIRECTORY) != 0)
325 continue;
326 if (keyremap_check_extension(entry->d_name) > 0)
327 nfiles++;
328 }
329 rb->closedir(kmfdirp);
330 }
331 return nfiles;
332}
333
334static void keyremap_reset_buffer(void)
335{
336 keyremap_buffer.front = keyremap_buffer.buffer;
337 keyremap_buffer.end = keyremap_buffer.front + keyremap_buffer.buf_size;
338 rb->memset(keyremap_buffer.front, 0, keyremap_buffer.buf_size);
339 ctx_data.ctx_map = (struct action_mapping_t*)keyremap_buffer.front;
340 ctx_data.ctx_count = 0;
341 ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
342 ctx_data.act_count = 0;
343
344}
345
346static size_t keyremap_write_entries(int fd, struct button_mapping *data, int entry_count)
347{
348 if (fd < 0 || entry_count <= 0 || !data)
349 goto fail;
350 size_t bytes_req = sizeof(struct button_mapping) * entry_count;
351 size_t bytes = rb->write(fd, data, bytes_req);
352 if (bytes == bytes_req)
353 return bytes_req;
354fail:
355 return 0;
356}
357
358static size_t keyremap_write_header(int fd, int entry_count)
359{
360 if (fd < 0 || entry_count < 0)
361 goto fail;
362 struct button_mapping header = {KEYREMAP_VERSION, KEYREMAP_HEADERID, entry_count};
363 return keyremap_write_entries(fd, &header, 1);
364fail:
365 return 0;
366}
367
368static int keyremap_open_file(const char *filename, int *fd, size_t *fsize)
369{
370 int count = 0;
371
372 while (filename && fd && fsize)
373 {
374 *fsize = 0;
375 *fd = rb->open(filename, O_RDONLY);
376 if (*fd)
377 {
378 *fsize = rb->filesize(*fd);
379
380 count = *fsize / sizeof(struct button_mapping);
381
382 if (count * sizeof(struct button_mapping) != *fsize)
383 {
384 count = -10;
385 break;
386 }
387
388 if (count > 1)
389 {
390 struct button_mapping header = {0};
391 rb->read(*fd, &header, sizeof(struct button_mapping));
392 if (KEYREMAP_VERSION == header.action_code &&
393 KEYREMAP_HEADERID == header.button_code &&
394 header.pre_button_code == count)
395 {
396 count--;
397 *fsize -= sizeof(struct button_mapping);
398 }
399 else /* Header mismatch */
400 {
401 count = -20;
402 break;
403 }
404 }
405 }
406 break;
407 }
408 return count;
409}
410
411static int keyremap_map_is_valid(struct action_mapping_t *amap, int context)
412{
413 int ret = (amap->context == context ? 1: 0);
414 struct button_mapping entry = amap->map;
415 if (entry.action_code == (int)CONTEXT_STOPSEARCHING ||
416 entry.button_code == BUTTON_NONE)
417 {
418 ret = 0;
419 }
420
421 return ret;
422}
423
424static struct button_mapping *keyremap_create_temp(int *entries)
425{
426 logf("%s()", __func__);
427 struct button_mapping *tempkeymap;
428 int action_offset = 1; /* start of action entries (ctx_count + 1)*/
429 int entry_count = 1;/* (ctx_count + ctx_count + act_count) */
430 int index = 0;
431 int i, j;
432
433 /* count includes a single stop sentinel for the list of contexts */
434 /* and a stop sentinel for each group of action remaps as well */
435 for (i = 0; i < ctx_data.ctx_count; i++)
436 {
437 int actions_this_ctx = 0;
438 int context = ctx_data.ctx_map[i].context;
439 /* how many actions are contained in each context? */
440 for (j = 0; j < ctx_data.act_count; j++)
441 {
442 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
443 {
444 actions_this_ctx++;
445 }
446 }
447 /* only count contexts with remapped actions */
448 if (actions_this_ctx != 0)
449 {
450 action_offset++;
451 entry_count += actions_this_ctx + 2;
452 }
453 }
454 size_t keymap_bytes = (entry_count) * sizeof(struct button_mapping);
455 *entries = entry_count;
456 logf("%s() entry count: %d", __func__, entry_count);
457 logf("keyremap bytes: %zu, avail: %zu", keymap_bytes,
458 (keyremap_buffer.end - keyremap_buffer.front));
459 if (keyremap_buffer.front + keymap_bytes < keyremap_buffer.end)
460 {
461 tempkeymap = (struct button_mapping *) keyremap_buffer.front;
462 rb->memset(tempkeymap, 0, keymap_bytes);
463 struct button_mapping *entry = &tempkeymap[index++];
464 for (i = 0; i < ctx_data.ctx_count; i++)
465 {
466 int actions_this_ctx = 0;
467 int flagidx;
468 int context = ctx_data.ctx_map[i].context;
469 /* how many actions are contained in each context? */
470 for (j = 0; j < ctx_data.act_count; j++)
471 {
472 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
473 {
474 actions_this_ctx += 1;
475 }
476 }
477 /*Don't save contexts with no actions */
478 if (actions_this_ctx == 0){ continue; }
479
480 /* convert context x flag to context | flag */
481 context = ctx_strip_flagidx(ctx_data.ctx_map[i].context, &flagidx);
482 context |= context_flags[flagidx].flag;
483
484 entry->action_code = CORE_CONTEXT_REMAP(context);
485 entry->button_code = action_offset; /* offset of first action entry */
486 entry->pre_button_code = actions_this_ctx; /* entries (excluding sentinel) */
487 entry = &tempkeymap[index++];
488 logf("keyremap found context: %d index: %d entries: %d",
489 context, action_offset, actions_this_ctx);
490 action_offset += actions_this_ctx + 1; /* including sentinel */
491 }
492 /* context sentinel */
493 entry->action_code = CONTEXT_STOPSEARCHING;;
494 entry->button_code = BUTTON_NONE;
495 entry->pre_button_code = BUTTON_NONE;
496 entry = &tempkeymap[index++];
497
498 for (i = 0; i < ctx_data.ctx_count; i++)
499 {
500 int actions_this_ctx = 0;
501 int context = ctx_data.ctx_map[i].context;
502
503 for (int j = 0; j < ctx_data.act_count; j++)
504 {
505 if (keyremap_map_is_valid(&ctx_data.act_map[j], context) > 0)
506 {
507 struct button_mapping map = ctx_data.act_map[j].map;
508 entry->action_code = map.action_code;
509 entry->button_code = map.button_code;
510 entry->pre_button_code = map.pre_button_code;
511 entry = &tempkeymap[index++];
512 actions_this_ctx++;
513 logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
514 context, map.action_code, map.button_code, map.pre_button_code);
515 }
516 }
517 /*Don't save sentinel for contexts with no actions */
518 if (actions_this_ctx == 0){ continue; }
519
520 /* action sentinel */
521 entry->action_code = CONTEXT_STOPSEARCHING;;
522 entry->button_code = BUTTON_NONE;
523 entry->pre_button_code = BUTTON_NONE;
524 entry = &tempkeymap[index++];
525 }
526 }
527 else
528 {
529 rb->splashf(HZ *2, "Out of Memory");
530 logf("keyremap: create temp OOM");
531 *entries = 0;
532 tempkeymap = NULL;
533 }
534
535 return tempkeymap;
536}
537
538static int keyremap_save_current(const char *filename)
539{
540 int status = 0;
541 int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
542
543 struct button_mapping *keymap = keyremap_create_temp(&entry_count);
544
545 if (keymap == NULL || entry_count <= 3) /* there should be atleast 4 entries */
546 return status;
547 keyset.crc32 =
548 rb->crc_32(keymap, entry_count * sizeof(struct button_mapping), 0xFFFFFFFF);
549 int keyfd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
550 if (keyremap_write_header(keyfd, entry_count + 1) > 0)
551 {
552 if (keyremap_write_entries(keyfd, keymap, entry_count) > 0)
553 status = 1;
554 }
555 rb->close(keyfd);
556
557 return status;
558}
559
560static void keyremap_save_user_keys(bool notify)
561{
562 char buf[MAX_PATH];
563 int i = 0;
564 do
565 {
566 i++;
567 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", KMFDIR, KMFUSER, i, KMFEXT1);
568 } while (i < 100 && rb->file_exists(buf));
569
570 if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
571 {
572 return;
573 }
574
575 if (keyremap_save_current(buf) == 0)
576 {
577 logf("Error Saving");
578 if(notify)
579 rb->splash(HZ *2, "Error Saving");
580 }
581 else if (notify)
582 rb->splashf(HZ *2, "Saved %s", buf);
583}
584
585static int keyremap_export_current(char *filenamebuf, size_t bufsz)
586{
587 filenamebuf[bufsz - 1] = '\0';
588 int i, j;
589 int ctx_count = 0;
590 size_t entrylen;
591
592 int entry_count = ctx_data.ctx_count + ctx_data.act_count + 1;
593
594 if (entry_count < 3) /* the header is not counted should be at least 3 entries */
595 {
596 logf("%s: Not enough entries", __func__);
597 return 0;
598 }
599 int fd = rb->open(filenamebuf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
600
601 if (fd < 0)
602 return -1;
603 rb->fdprintf(fd, "# Key Remap\n# Device: %s\n" \
604 "# Entries: %d\n\n", MODEL_NAME, entry_count - 1);
605 rb->fdprintf(fd, "# Each entry should be PROPER_CASE and on its own line\n" \
606 "# Comments run to end of line \n");
607
608 for (i = 0; i <= entry_count; i++)
609 {
610 entrylen = 0;
611 rb->memset(filenamebuf, 0, bufsz);
612 edit_keymap_name_cb(i, MENU_ID(M_EXPORTKEYS), filenamebuf, bufsz);
613 if (i == 0)
614 {
615 ctx_menu_data.act_fmt = " {%s%s, %s, %s},\n\n";
616 continue;
617 }
618 else if (i == entry_count)
619 {
620 rb->strlcpy(filenamebuf, "}\n\n", bufsz);
621 }
622 char last = '\0';
623 for (j = 0; j < (int)bufsz ;j++)
624 {
625 char ch = filenamebuf[j];
626 if (ch == '$' && last == '\0') /*CONTEXT*/
627 {
628 if (ctx_count > 0)
629 rb->fdprintf(fd, "}\n");
630 ctx_count++;
631 filenamebuf[j] = '\n';
632 }
633 if (ch == '\n' && last == '\n')
634 {
635 entrylen = j;
636 break;
637 }
638 else if (ch == '\0')
639 filenamebuf[j] = ',';
640 last = ch;
641 }
642
643 size_t bytes = rb->write(fd, filenamebuf, entrylen);
644 if (bytes != entrylen)
645 {
646 entry_count = -2;
647 goto fail;
648 }
649 }
650
651fail:
652 rb->close(fd);
653
654 return entry_count;
655}
656
657static void keyremap_export_user_keys(void)
658{
659 const bool notify = true;
660 char buf[MAX_PATH];
661 int i = 0;
662 do
663 {
664 i++;
665 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
666 } while (i < 100 && rb->file_exists(buf));
667
668 if (notify && prompt_filename(buf, sizeof(buf)) <= 0)
669 {
670 return;
671 }
672
673 if (keyremap_export_current(buf, sizeof(buf)) <= 0)
674 {
675 if(notify)
676 rb->splash(HZ *2, "Error Saving");
677 }
678 else if (notify)
679 {
680 rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", "", KMFUSER, i, ".txt");
681 rb->splashf(HZ *2, "Saved %s", buf);
682 }
683}
684
685static void keyremap_import_user_keys(void)
686{
687 char buf[MAX_PATH];
688 struct browse_context browse = {
689 .dirfilter = SHOW_ALL,
690 .flags = BROWSE_SELECTONLY,
691 .title = "Select Keymap",
692 .icon = Icon_Plugin,
693 .buf = buf,
694 .bufsize = sizeof(buf),
695 .root = "/",
696 };
697
698 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
699 {
700 int ret = keyremap_import_file(buf, sizeof(buf));
701 if (ret <= 0)
702 {
703 keyremap_reset_buffer();
704 rb->splash(HZ *2, "Error Opening");
705 }
706 else
707 rb->splashf(HZ * 2, "Loaded Text Keymap ");
708 }
709}
710
711static int keymap_add_context_entry(int context)
712{
713 int remap_context = CORE_CONTEXT_REMAP(context);
714 for (int i = 0; i < ctx_data.ctx_count; i++)
715 {
716 if (ctx_data.ctx_map[i].map.action_code == remap_context)
717 goto fail; /* context exists */
718 }
719 if (keyremap_buffer.front + sizeof(struct action_mapping_t) > keyremap_buffer.end)
720 goto fail;
721 keyremap_buffer.front += sizeof(struct action_mapping_t);
722 ctx_data.ctx_map[ctx_data.ctx_count].context = context;
723 ctx_data.ctx_map[ctx_data.ctx_count].display_pos = -1;
724 ctx_data.ctx_map[ctx_data.ctx_count].map.action_code = remap_context;
725 ctx_data.ctx_map[ctx_data.ctx_count].map.button_code = 0;
726 ctx_data.ctx_map[ctx_data.ctx_count].map.pre_button_code = 0;
727 ctx_data.ctx_count++;
728 menu_useract_set_positions();
729 return ctx_data.ctx_count;
730fail:
731 return 0;
732}
733
734static int keymap_add_button_entry(int context, int action_code,
735 int button_code, int pre_button_code)
736{
737 bool hasctx = false;
738 for (int i = 0; i < ctx_data.ctx_count; i++)
739 {
740 if (ctx_data.ctx_map[i].context == context)
741 {
742 hasctx = true;
743 break;
744 }
745 }
746 if (!hasctx || keyremap_buffer.end - sizeof(struct action_mapping_t) < keyremap_buffer.front)
747 goto fail;
748 keyremap_buffer.end -= sizeof(struct action_mapping_t);
749 ctx_data.act_map = (struct action_mapping_t*) keyremap_buffer.end;
750 ctx_data.act_map[0].context = context;
751 ctx_data.act_map[0].display_pos = -1;
752 ctx_data.act_map[0].map.action_code = action_code;
753 ctx_data.act_map[0].map.button_code = button_code;
754 ctx_data.act_map[0].map.pre_button_code = pre_button_code;
755 ctx_data.act_count++;
756 menu_useract_set_positions();
757 return ctx_data.act_count;
758fail:
759 return 0;
760}
761
762static int get_action_from_str(char *pfield, size_t bufsz)
763{
764 int act = -1;
765 for (int i=0;i < LAST_ACTION_PLACEHOLDER; i++)
766 {
767 if (rb->strncasecmp(pfield, action_name(i), bufsz) == 0)
768 {
769 logf("Action Found: %s (%d)", pfield, i);
770 act = i;
771 break;
772 }
773 }
774 return act;
775}
776
777static int get_button_from_str(char *pfield, size_t bufsz)
778{
779 int btn = -1;
780 for (int i=0;i < available_button_count; i++)
781 {
782 const struct available_button* abtn = &available_buttons[i];
783 if (rb->strncasecmp(pfield, abtn->name, bufsz) == 0)
784 {
785 logf("Button Found: %s (%lx)", abtn->name, abtn->value);
786 btn = abtn->value;
787 break;
788 }
789 }
790 if (btn < 0) /* Not Fatal */
791 {
792 logf("Invalid Button %s", pfield);
793 rb->splashf(HZ, "Invalid Button %s", pfield);
794 }
795 return btn;
796}
797
798static int parse_action_import_entry(int context, char * pbuf, size_t bufsz)
799{
800 size_t bufleft;
801 int count = 0;
802 char ch;
803 char *pfirst = NULL;
804 char *pfield;
805 int field = -1;
806 int act = -1;
807 int button = BUTTON_NONE;
808 int prebtn = BUTTON_NONE;
809 while ((ch = *(pbuf)) != '\0')
810 {
811 pfield = NULL; /* Valid names */
812 if ((ch >= 'A' && ch <= 'Z') || ch == '_')
813 {
814 if (pfirst == NULL)
815 pfirst = pbuf;
816 }
817 else if (ch == ',')
818 {
819 if (pfirst != NULL)
820 {
821 field++;
822 pfield = pfirst;
823 pfirst = NULL;
824 *pbuf = '\0';
825 }
826 }
827 else if (ch == ' ' || ch == '|'){;}
828 else
829 pfirst = NULL;
830
831 if (field == 1 && pfirst != NULL && pbuf[1] == '\0')
832 {
833 field++;
834 pfield = pfirst;
835 pfirst = NULL;
836 }
837
838 if (pfield != NULL)
839 {
840 char *pf;
841
842 if (field == 0) /* action */
843 {
844 char *pact = pfield;
845 pf = pfield;
846 while ((ch = *(pf)) != '\0')
847 {
848 if(ch == ' ')
849 *pf = '\0';
850 else
851 pf++;
852 }
853 bufleft = bufsz - (pact - pbuf);
854 act = get_action_from_str(pact, bufleft);
855
856 if (act < 0)
857 {
858 logf("Error Action Expected: %s", pact);
859 return -1;
860 }
861 }
862 else if (field == 1 || field == 2) /* button / pre_btn */
863 {
864 char *pbtn = pfield;
865 pf = pfield;
866 while ((ch = *(pf)) != '\0')
867 {
868 if (pf[1] == '\0') /* last item? */
869 {
870 pf++;
871 ch = '|';
872 pfield = NULL;
873 }
874 else if (ch == ' ' || ch == '|')
875 {
876 *pf = '\0';
877 }
878
879 if(ch == '|')
880 {
881 bufleft = bufsz - (pbtn - pbuf);
882 int btn = get_button_from_str(pbtn, bufleft);
883
884 if (btn > BUTTON_NONE)
885 {
886 if (field == 1)
887 button |= btn;
888 else if (field == 2)
889 prebtn |= btn;
890 }
891
892 if (pfield != NULL)
893 {
894 pf++;
895 while ((ch = *(pf)) != '\0')
896 {
897 if (*pf == ' ')
898 pf++;
899 else
900 break;
901 }
902 pbtn = pf;
903 }
904 }
905 else
906 pf++;
907 }
908
909 if (act < 0)
910 {
911 logf("Error Action Expected: %s", pfield);
912 return -1;
913 }
914 }
915
916 pfield = NULL;
917 }
918
919 pbuf++;
920 }
921 if (field == 2)
922 {
923 count = keymap_add_button_entry(context, act, button, prebtn);
924 if (count > 0)
925 {
926 logf("Added: Ctx: %d, Act: %d, Btn: %d PBtn: %d",
927 context, act, button, prebtn);
928 }
929 }
930 return count;
931}
932
933static int keyremap_import_file(char *filenamebuf, size_t bufsz)
934{
935 size_t bufleft;
936 int count = 0;
937 int line = 0;
938 filenamebuf[bufsz - 1] = '\0';
939 logf("keyremap: import %s", filenamebuf);
940 int fd = rb->open(filenamebuf, O_RDONLY);
941 if (fd < 0)
942 return -1;
943
944 char ch;
945 char *pbuf;
946 char *pfirst;
947
948 char *pctx = NULL;
949 char *pact;
950 int ctx = -1;
951
952 keyremap_reset_buffer();
953next_line:
954 while (rb->read_line(fd, filenamebuf, (int) bufsz) > 0)
955 {
956 line++;
957
958
959 pbuf = filenamebuf;
960 pfirst = NULL;
961 pact = NULL;
962 char *pcomment = rb->strchr(pbuf, '#');
963 if (pcomment != NULL)
964 {
965 logf("ln: %d: Skipped Comment: %s", line, pcomment);
966 *pcomment = '\0';
967 }
968
969 while ((ch = *(pbuf)) != '\0')
970 {
971 /* PARSE CONTEXT = { */
972 if ((ch >= 'A' && ch <= 'Z') || ch == '_')
973 {
974 if (pfirst == NULL)
975 pfirst = pbuf;
976 }
977 else if (ch == ' ')
978 {
979 if (ctx < 0 && pfirst != NULL)
980 {
981 *pbuf = '\0';
982 pctx = pfirst;
983 pfirst = NULL;
984 }
985 }
986 else if (ch == '=')
987 {
988 if (ctx < 0 && pfirst != NULL)
989 {
990 *pbuf = '\0';
991 pbuf++;
992 pctx = pfirst;
993 pfirst = NULL;
994 }
995 while ((ch = *(pbuf)) != '\0')
996 {
997 if (ch == '{')
998 break;
999 pbuf++;
1000 }
1001 if (ch == '{' && pctx != NULL)
1002 {
1003 bufleft = bufsz - (pctx - filenamebuf);
1004 ctx = -1;
1005 int ctx_x_flag_count = (LAST_CONTEXT_PLACEHOLDER
1006 * ARRAYLEN(context_flags));
1007
1008 for (int i=0;i < ctx_x_flag_count ;i++)
1009 {
1010 /* context x flag */
1011 if (rb->strncasecmp(pctx, ctx_name_and_flag(i), bufleft) == 0)
1012 {
1013 logf("ln: %d: Context Found: %s (%d)", line, pctx, i);
1014 if (keymap_add_context_entry(i) <= 0)
1015 logf("ln: %d: Context Exists: %s (%d)", line, pctx, i);
1016 ctx = i;
1017 goto next_line;
1018
1019 }
1020 }
1021 logf("ln: %d: ERROR { Context Expected got: %s", line, pctx);
1022 goto fail;
1023 }
1024 }
1025 else if (ch == '}')
1026 {
1027 if (ctx >= 0)
1028 ctx = -1;
1029 else
1030 {
1031 logf("ln: %d: ERROR no context, unexpected close {", line);
1032 goto fail;
1033 }
1034 }
1035 else if (ch == '{') /* PARSE FIELDS { ACTION, BUTTON, PREBTN } */
1036 {
1037 int res = 0;
1038 if (ctx >= 0)
1039 {
1040 pfirst = pbuf;
1041
1042 while ((ch = *(pbuf)) != '\0')
1043 {
1044 if (ch == '}')
1045 {
1046 pact = pfirst + 1;
1047 pfirst = NULL;
1048 *pbuf = '\0';
1049 pbuf = "";
1050 continue;
1051 }
1052 pbuf++;
1053 }
1054 if (pact != NULL)
1055 {
1056 bufleft = bufsz - (pact - filenamebuf);
1057 logf("ln: %d: Entry Found: {%s} (%d)", line, pact, 0);
1058 res = parse_action_import_entry(ctx, pact, bufleft);
1059 }
1060 }
1061 if (res <= 0)
1062 {
1063 logf("ln: %d: ERROR action entry expected", line);
1064 goto fail;
1065 }
1066 else
1067 {
1068 pbuf = "";
1069 continue;
1070 }
1071 }
1072 else
1073 pfirst = NULL;
1074 pbuf++;
1075 }
1076
1077 }
1078 rb->close(fd);
1079 count = ctx_data.ctx_count + ctx_data.act_count;
1080 return count;
1081
1082fail:
1083 rb->close(fd);
1084 rb->splashf(HZ * 2, "Error @ line %d", line);
1085 return 0;
1086}
1087
1088static int keyremap_load_file(const char *filename)
1089{
1090 logf("keyremap: load %s", filename);
1091 int fd = -1;
1092 size_t fsize = 0;
1093 size_t bytes;
1094 struct button_mapping entry;
1095 int count = keyremap_open_file(filename, &fd, &fsize);
1096 logf("keyremap: entries %d", count);
1097 /* actions are indexed from the first entry after the header save this pos */
1098 off_t firstpos = rb->lseek(fd, 0, SEEK_CUR);
1099 off_t ctxpos = firstpos;
1100
1101 if (count > 0)
1102 {
1103 keyremap_reset_buffer();
1104 while(--count > 0)
1105 {
1106 rb->lseek(fd, ctxpos, SEEK_SET); /* next context remap entry */
1107 bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
1108 ctxpos = rb->lseek(fd, 0, SEEK_CUR);
1109 if (bytes != sizeof(struct button_mapping))
1110 {
1111 count = -10;
1112 goto fail;
1113 }
1114 if (entry.action_code == (int)CONTEXT_STOPSEARCHING)
1115 {
1116 logf("keyremap: end of context entries ");
1117 break;
1118 }
1119 if ((entry.action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1120 {
1121 int context = (entry.action_code & ~CONTEXT_REMAPPED);
1122 for (int i = ARRAYLEN(context_flags) - 1; i > 0; i--) /* don't check idx 0*/
1123 {
1124 /* convert context | flag to context x flag */
1125 if ((context & context_flags[i].flag) == context_flags[i].flag)
1126 {
1127 logf("found ctx flag %s", context_flags[i].name);
1128 context &= ~context_flags[i].flag;
1129 context += i * LAST_CONTEXT_PLACEHOLDER;
1130 }
1131 }
1132 int offset = entry.button_code;
1133 int entries = entry.pre_button_code;
1134 if (offset == 0 || entries <= 0)
1135 {
1136 logf("keyremap: error reading offset");
1137 count = -15;
1138 goto fail;
1139 }
1140 logf("keyremap found context: %d file offset: %d entries: %d",
1141 context, offset, entries);
1142
1143 keymap_add_context_entry(context);
1144
1145 off_t entrypos = firstpos + (offset * sizeof(struct button_mapping));
1146 rb->lseek(fd, entrypos, SEEK_SET);
1147 for (int i = 0; i < entries; i++)
1148 {
1149 bytes = rb->read(fd, &entry, sizeof(struct button_mapping));
1150 if (bytes == sizeof(struct button_mapping))
1151 {
1152 int act = entry.action_code;
1153 int button = entry.button_code;
1154 int prebtn = entry.pre_button_code;
1155
1156 if (act == (int)CONTEXT_STOPSEARCHING || button == BUTTON_NONE)
1157 {
1158 logf("keyremap: entry invalid");
1159 goto fail;
1160 }
1161 logf("keyremap: found ctx: %d, act: %d btn: %d pbtn: %d",
1162 context, act, button, prebtn);
1163 keymap_add_button_entry(context, act, button, prebtn);
1164 }
1165 else
1166 goto fail;
1167 }
1168 }
1169 else
1170 {
1171 logf("keyremap: Invalid context entry");
1172 keyremap_reset_buffer();
1173 count = -20;
1174 goto fail;
1175 }
1176 }
1177 }
1178
1179 int entries = 0;
1180 struct button_mapping *keymap = keyremap_create_temp(&entries);
1181 if (keymap != NULL)
1182 {
1183 keyset.crc32 =
1184 rb->crc_32(keymap, entries * sizeof(struct button_mapping), 0xFFFFFFFF);
1185 }
1186fail:
1187 rb->close(fd);
1188 if (count <= 0)
1189 rb->splashf(HZ * 2, "Error Loading %sz", filename);
1190 return count;
1191}
1192
1193static const struct button_mapping* test_get_context_map(int context)
1194{
1195 (void)context;
1196
1197 if (keytest.keymap != NULL && keytest.context >= 0)
1198 {
1199 int mapidx = keytest.keymap[keytest.index].button_code;
1200 int mapcnt = keytest.keymap[keytest.index].pre_button_code;
1201 /* make fallthrough to the test context*/
1202 keytest.keymap[mapidx + mapcnt].action_code = keytest.context;
1203 static const struct button_mapping *testctx[] = { NULL };
1204 testctx[0] = &keytest.keymap[mapidx];
1205 return testctx[0];
1206 }
1207 else
1208 return NULL;
1209}
1210
1211static const char *kmffiles_name_cb(int selected_item, void* data,
1212 char* buf, size_t buf_len)
1213{
1214 /* found kmf filenames returned by each call kmffiles_dirp keeps state
1215 * selected_item = 0 resets state */
1216
1217 (void)data;
1218 buf[0] = '\0';
1219 if (selected_item == 0)
1220 {
1221 rb->closedir(kmffiles_dirp);
1222 kmffiles_dirp = rb->opendir(KMFDIR);
1223 }
1224 if (kmffiles_dirp != NULL)
1225 {
1226 struct dirent *entry;
1227 while ((entry = rb->readdir(kmffiles_dirp)))
1228 {
1229 /* skip directories */
1230 if ((rb->dir_get_info(kmffiles_dirp, entry).attribute & ATTR_DIRECTORY) != 0)
1231 continue;
1232 if (keyremap_check_extension(entry->d_name) > 0)
1233 {
1234 rb->snprintf(buf, buf_len, "%s", entry->d_name);
1235 return buf;
1236 }
1237 }
1238 }
1239 return "Error!";
1240}
1241
1242static void menu_useract_set_positions(void)
1243{
1244 /* responsible for item ordering to display action edit interface */
1245 int display_pos = 0; /* start at item 0*/
1246 int i, j;
1247 for (i = 0; i < ctx_data.ctx_count; i++)
1248 {
1249 int context = ctx_data.ctx_map[i].context;
1250 ctx_data.ctx_map[i].display_pos = display_pos++;
1251 /* how many actions are contained in this context? */
1252 for (j = 0; j < ctx_data.act_count; j++)
1253 {
1254 if (ctx_data.act_map[j].context == context)
1255 {
1256 ctx_data.act_map[j].display_pos = display_pos++;
1257 }
1258 }
1259 }
1260}
1261
1262static const char *menu_useract_items_cb(int selected_item, void* data,
1263 char* buf, size_t buf_len)
1264{
1265 static char buf_button[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
1266 static char buf_prebtn[MAX_BUTTON_NAME * MAX_BUTTON_COMBO];
1267 int i;
1268 int szbtn = sizeof("BUTTON");
1269 int szctx = sizeof("CONTEXT");
1270 int szact = sizeof("ACTION");
1271 const char* ctxfmt = "%s";
1272
1273 if (data == MENU_ID(M_EXPORTKEYS))
1274 {
1275 szbtn = 0;
1276 szctx = 0;
1277 szact = 0;
1278 ctxfmt = "$%s = {\n\n";
1279 }
1280 buf[0] = '\0';
1281 for(i = 0; i < ctx_data.ctx_count; i ++)
1282 {
1283 if (ctx_data.ctx_map[i].display_pos == selected_item)
1284 {
1285 if (ctx_data.act_count == 0)
1286 rb->snprintf(buf, buf_len, "%s$%s",
1287 ctx_name_and_flag(ctx_data.ctx_map[i].context),
1288 "Select$to add$actions");
1289 else
1290 rb->snprintf(buf, buf_len, ctxfmt, ctx_name_and_flag(ctx_data.ctx_map[i].context));
1291 return buf;
1292 }
1293 }
1294 for(i = 0; i < ctx_data.act_count; i ++)
1295 {
1296 if (ctx_data.act_map[i].display_pos == selected_item)
1297 {
1298 int context = ctx_data.act_map[i].context;
1299 char ctxbuf[action_helper_maxbuffer];
1300 char *pctxbuf = "\0";
1301 char *pactname;
1302 if (data != MENU_ID(M_EXPORTKEYS))
1303 {
1304 pctxbuf = ctxbuf;
1305 rb->snprintf(ctxbuf, sizeof(ctxbuf), ctxfmt, ctx_name_and_flag(context));
1306 pctxbuf += szctx;//sizeof("CONTEXT")
1307 }
1308 struct button_mapping * bm = &ctx_data.act_map[i].map;
1309 pactname = action_name(bm->action_code);
1310 pactname += szact;//sizeof("ACTION")
1311 /* BUTTON & PRE_BUTTON */
1312 btnval_to_name(buf_button, sizeof(buf_button), bm->button_code);
1313 btnval_to_name(buf_prebtn, sizeof(buf_prebtn), bm->pre_button_code);
1314
1315 rb->snprintf(buf, buf_len, ctx_menu_data.act_fmt, pctxbuf,
1316 pactname, buf_button + szbtn, buf_prebtn + szbtn);
1317 return buf;
1318 }
1319 }
1320 return "Error!";
1321}
1322
1323static const char *edit_keymap_name_cb(int selected_item, void* data,
1324 char* buf, size_t buf_len)
1325{
1326 buf[0] = '\0';
1327 if (selected_item == 0)
1328 {
1329 rb->snprintf(buf, buf_len, "Add Context");
1330 ctx_menu_data.act_index = -1;
1331 ctx_menu_data.ctx_index = -1;
1332 ctx_menu_data.act_fmt = ACTIONFMT_LV0;
1333 }
1334 else if (ctx_data.ctx_count > 0)
1335 {
1336 return menu_useract_items_cb(selected_item - 1, data, buf, buf_len);
1337 }
1338 return buf;
1339}
1340
1341static const char *test_keymap_name_cb(int selected_item, void* data,
1342 char* buf, size_t buf_len)
1343{
1344 (void)data;
1345 buf[0] = '\0';
1346 if (keytest.context >= 0)
1347 {
1348 if (selected_item == 0)
1349 rb->snprintf(buf, buf_len, "< %s >", ctx_name_and_flag(keytest.context));
1350 else if (selected_item == 1)
1351 {
1352 if (keytest.countdown >= 10)
1353 rb->snprintf(buf, buf_len, "Testing %d", keytest.countdown / 10);
1354 else
1355 rb->snprintf(buf, buf_len, "Start test");
1356 }
1357 else if (selected_item == 2)
1358 rb->snprintf(buf, buf_len, "%s", action_name(keytest.action));
1359 }
1360 return buf;
1361}
1362
1363static const char* list_get_name_cb(int selected_item, void* data,
1364 char* buf, size_t buf_len)
1365{
1366 buf[0] = '\0';
1367 const struct mainmenu *cur = (struct mainmenu *) data;
1368 if (data == MENU_ID(M_ROOT))
1369 return mainitem(selected_item + 1)->name;
1370 else if (selected_item >= cur->items - 1)
1371 {
1372 return ID2P(LANG_BACK);
1373 }
1374 if (data == MENU_ID(M_SETKEYS))
1375 {
1376 return edit_keymap_name_cb(selected_item, data, buf, buf_len);
1377 }
1378 else if (data == MENU_ID(M_BUTTONS))
1379 {
1380 const struct available_button *btn = &available_buttons[selected_item];
1381 rb->snprintf(buf, buf_len, "%s: [0x%X] ", btn->name, (unsigned int) btn->value);
1382 return buf;
1383 }
1384 else if (data == MENU_ID(M_ACTIONS))
1385 {
1386 return action_name(selected_item);
1387 }
1388 else if (data == MENU_ID(M_CONTEXTS))
1389 {
1390 return ctx_name_and_flag(selected_item);
1391 }
1392 else if (data == MENU_ID(M_DELKEYS) || data == MENU_ID(M_LOADKEYS))
1393 {
1394 /* need to iterate the callback for the items off screen to
1395 * keep ordering, this limits the menu to only the main screen :( */
1396 int start_item = lists.start_item[SCREEN_MAIN];
1397 if (start_item != 0 && start_item == selected_item)
1398 {
1399 for (int i = 0; i < start_item; i++)
1400 kmffiles_name_cb(i, data, buf, buf_len);
1401 }
1402 return kmffiles_name_cb(selected_item, data, buf, buf_len);
1403 }
1404 else if (data == MENU_ID(M_TESTKEYS))
1405 {
1406 return test_keymap_name_cb(selected_item, data, buf, buf_len);
1407 }
1408 return buf;
1409}
1410
1411static int list_voice_cb(int list_index, void* data)
1412{
1413 if (!rb->global_settings->talk_menu)
1414 return -1;
1415
1416 if (data == MENU_ID(M_ROOT))
1417 {
1418 const char * name = mainitem(list_index)->name;
1419 long id = P2ID((const unsigned char *)name);
1420 if(id>=0)
1421 rb->talk_id(id, true);
1422 else
1423 rb->talk_spell(name, true);
1424 }
1425 else if(data == MENU_ID(M_SETKEYS))
1426 {
1427 char buf[MAX_MENU_NAME];
1428 int selcol = printcell_get_column_selected();
1429 const char* name = printcell_get_column_text(selcol, buf, sizeof(buf));
1430 long id = P2ID((const unsigned char *)name);
1431 if(id>=0)
1432 rb->talk_id(id, true);
1433 else
1434 rb->talk_spell(name, true);
1435 }
1436 else
1437 {
1438 char buf[MAX_MENU_NAME];
1439 const char* name = list_get_name_cb(list_index, data, buf, sizeof(buf));
1440 long id = P2ID((const unsigned char *)name);
1441 if(id>=0)
1442 rb->talk_id(id, true);
1443 else
1444 rb->talk_spell(name, true);
1445 }
1446 return 0;
1447}
1448
1449int menu_action_root(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1450{
1451#ifdef ROCKBOX_HAS_LOGF
1452 char logfnamebuf[64];
1453#endif
1454
1455 if (*action == ACTION_STD_OK)
1456 {
1457 struct mainmenu *cur = mainitem(selected_item + 1);
1458 if (cur != NULL)
1459 {
1460#ifdef ROCKBOX_HAS_LOGF
1461 lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
1462 logf("Root select menu -> %s(%d) ", logfnamebuf, cur->index);
1463#endif
1464 if (cur->menuid == MENU_ID(M_SETKEYS))
1465 {
1466 keyset.view_lastcol = -1;
1467 }
1468 else if (cur->menuid == MENU_ID(M_TMPCORE))
1469 {
1470 int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
1471 struct button_mapping *keymap = keyremap_create_temp(&entry_count);
1472 if (rb->core_set_keyremap(keymap, entry_count) >= 0)
1473 rb->splash(HZ *2, "Keymap Applied");
1474 else
1475 rb->splash(HZ *2, "Error Applying");
1476
1477 goto default_handler;
1478 }
1479 else if (cur->menuid == MENU_ID(M_SETCORE))
1480 {
1481 if (rb->file_exists(CORE_KEYREMAP_FILE) && 0 == core_savecount++)
1482 {
1483 rb->rename(CORE_KEYREMAP_FILE, CORE_KEYREMAP_FILE".old"); /* make a backup */
1484 }
1485 if (keyremap_save_current(CORE_KEYREMAP_FILE) == 0)
1486 rb->splash(HZ *2, "Error Saving");
1487 else
1488 {
1489 rb->splash(HZ *2, "Saved");
1490 int entry_count;/* (ctx_count + ctx_count + act_count + 1) */
1491 struct button_mapping *keymap = keyremap_create_temp(&entry_count);
1492 rb->core_set_keyremap(keymap, entry_count);
1493 }
1494 goto default_handler;
1495 }
1496 else if (cur->menuid == MENU_ID(M_DELCORE))
1497 {
1498 rb->core_set_keyremap(NULL, -1);
1499 if (rb->file_exists(CORE_KEYREMAP_FILE))
1500 {
1501 rb->rename(CORE_KEYREMAP_FILE, KMFDIR "/core_deleted" KMFEXT2);
1502 rb->splash(HZ *2, "Removed");
1503 }
1504 else
1505 rb->splash(HZ *2, "Error Removing");
1506
1507 goto default_handler;
1508 }
1509 else if (cur->menuid == MENU_ID(M_SAVEKEYS))
1510 {
1511 keyremap_save_user_keys(true);
1512 goto default_handler;
1513 }
1514 else if (cur->menuid == MENU_ID(M_EXPORTKEYS))
1515 {
1516 keyremap_export_user_keys();
1517 goto default_handler;
1518 }
1519 else if (cur->menuid == MENU_ID(M_IMPORTKEYS))
1520 {
1521 keyremap_import_user_keys();
1522 goto default_handler;
1523 }
1524 else if (cur->menuid == MENU_ID(M_DELKEYS) ||
1525 cur->menuid == MENU_ID(M_LOADKEYS))
1526 {
1527 cur->items = keyremap_count_files(KMFDIR) + 1;
1528 }
1529 else if (cur->menuid == MENU_ID(M_TESTKEYS))
1530 {
1531 int entries = 0;
1532 keytest.keymap = keyremap_create_temp(&entries);
1533 if (entries > 0)
1534 {
1535 struct button_mapping *entry = &keytest.keymap[0];
1536 if (entry->action_code != (int)CONTEXT_STOPSEARCHING
1537 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1538 {
1539 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
1540 }
1541 else
1542 keytest.context = -1;
1543 }
1544 else
1545 {
1546 keytest.keymap = NULL;
1547 keytest.context = -1;
1548 }
1549 keytest.action = ACTION_NONE;
1550 keytest.countdown = 0;
1551 }
1552 else if (cur->menuid == MENU_ID(M_RESETKEYS))
1553 {
1554 if (rb->yesno_pop("Delete Current Entries?") == true)
1555 {
1556 keyremap_reset_buffer();
1557 }
1558 goto default_handler;
1559 }
1560 }
1561
1562 if (cur->menuid == NULL || cur->menuid == MENU_ID(M_EXIT))
1563 {
1564 logf("Root menu %s", (cur->menuid) == NULL ? "NULL":"Exit");
1565 *action = ACTION_STD_CANCEL;
1566 *exit = true;
1567 }
1568 else
1569 {
1570#ifdef ROCKBOX_HAS_LOGF
1571 lang_strlcpy(logfnamebuf, cur->name, sizeof(logfnamebuf));
1572 logf("Root load menu -> %s(%d) ", logfnamebuf, cur->index);
1573#endif
1574 synclist_set_update(cur->index, 0, cur->items, 1);
1575 rb->gui_synclist_draw(lists);
1576 *action = ACTION_NONE;
1577 }
1578 }
1579
1580 return PLUGIN_OK;
1581default_handler:
1582 return GOTO_ACTION_DEFAULT_HANDLER;
1583}
1584
1585int menu_action_setkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1586{
1587 (void) exit;
1588 int i;
1589 struct mainmenu *cur = (struct mainmenu *)lists->data;
1590 if (*action == ACTION_STD_OK)
1591 {
1592 if (selected_item == 0) /*add_context*/
1593 {
1594 const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
1595 synclist_set_update(mainm->index, 0, mainm->items, 1);
1596 rb->gui_synclist_draw(lists);
1597 goto default_handler;
1598 }
1599 else if (selected_item < lists->nb_items - 1)/* not back*/
1600 {
1601 bool add_action = false;
1602 for (i = 0; i < ctx_data.ctx_count; i++)
1603 {
1604 if (ctx_data.ctx_map[i].display_pos == selected_item - 1)
1605 {
1606 add_action = true;
1607 break;
1608 }
1609 }
1610
1611 if (add_action)
1612 {
1613 keymap_add_button_entry(ctx_data.ctx_map[i].context, ACTION_NONE, BUTTON_NONE, BUTTON_NONE);
1614 cur->items++;
1615 lists->nb_items++;
1616 goto default_handler;
1617 }
1618 else
1619 {
1620 keyset.view_lastcol = printcell_increment_column(1, true);
1621 *action = ACTION_NONE;
1622 }
1623 }
1624 }
1625 else if (*action == ACTION_STD_CANCEL)
1626 {
1627 keyset.view_lastcol = printcell_increment_column(-1, true);
1628 if (keyset.view_lastcol != keyset.view_columns - 1)
1629 {
1630 *action = ACTION_NONE;
1631 }
1632 }
1633 else if (*action == ACTION_STD_CONTEXT)
1634 {
1635 int col = keyset.view_lastcol;
1636 for (i = 0; i < ctx_data.act_count; i++)
1637 {
1638 if (ctx_data.act_map[i].display_pos == selected_item - 1)
1639 {
1640 int context = ctx_data.act_map[i].context;
1641 int act = ctx_data.act_map[i].map.action_code;
1642 int button = ctx_data.act_map[i].map.button_code;
1643 int prebtn = ctx_data.act_map[i].map.pre_button_code;
1644
1645 if (col < 0)
1646 {
1647 rb->splashf(HZ, "short press increments columns");
1648
1649 }
1650 else if (col == 0) /* Context */
1651 {
1652 const struct mainmenu *mainm = &mainmenu[M_CONTEXTS];
1653 synclist_set_update(mainm->index, context, mainm->items, 1);
1654 rb->gui_synclist_draw(lists);
1655 goto default_handler;
1656
1657 }
1658 else if (col == 1) /* Action */
1659 {
1660 const struct mainmenu *mainm = &mainmenu[M_ACTIONS];
1661 synclist_set_update(mainm->index, act, mainm->items, 1);
1662 rb->gui_synclist_draw(lists);
1663 goto default_handler;
1664 }
1665 else if (col == 2) /* Button */
1666 {
1667 const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
1668 int btnidx = btnval_to_index(button);
1669 synclist_set_update(mainm->index, btnidx, mainm->items, 1);
1670 rb->gui_synclist_draw(lists);
1671 goto default_handler;
1672 }
1673 else if (col == 3) /* PreBtn */
1674 {
1675 const struct mainmenu *mainm = &mainmenu[M_BUTTONS];
1676 int pbtnidx = btnval_to_index(prebtn);
1677 synclist_set_update(mainm->index, pbtnidx, mainm->items, 1);
1678 rb->gui_synclist_draw(lists);
1679 goto default_handler;
1680 }
1681 }
1682 }
1683 }
1684 return PLUGIN_OK;
1685default_handler:
1686 return GOTO_ACTION_DEFAULT_HANDLER;
1687}
1688
1689int menu_action_testkeys(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1690{
1691 (void) exit;
1692 (void) lists;
1693 static int last_count = 0;
1694 if (keytest.keymap != NULL && keytest.countdown >= 10 && keytest.context >= 0)
1695 {
1696 int newact = rb->get_custom_action(CONTEXT_PLUGIN, HZ / 10, test_get_context_map);
1697 if (newact == ACTION_NONE)
1698 {
1699 keytest.countdown--;
1700 if (last_count - keytest.countdown > 10)
1701 keytest.action = newact;
1702 }
1703 else
1704 {
1705 last_count = keytest.countdown;
1706 keytest.action = newact;
1707 }
1708 *action = ACTION_REDRAW;
1709 goto default_handler;
1710 }
1711
1712 if (*action == ACTION_STD_CANCEL && selected_item == 0 && keytest.keymap != NULL)
1713 {
1714 keytest.index -= 2;
1715 if (keytest.index < 0)
1716 keytest.index = 0;
1717 *action = ACTION_STD_OK;
1718 }
1719
1720 if (*action == ACTION_STD_OK)
1721 {
1722 if (selected_item == 0)
1723 {
1724 if (keytest.keymap != NULL)
1725 {
1726 struct button_mapping *entry = &keytest.keymap[++keytest.index];
1727 if (entry->action_code != (int) CONTEXT_STOPSEARCHING
1728 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1729 {
1730 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
1731 }
1732 else
1733 {
1734 entry = &keytest.keymap[0];
1735 if (entry->action_code != (int)CONTEXT_STOPSEARCHING
1736 && (entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
1737 {
1738 keytest.context = (entry->action_code & ~CONTEXT_REMAPPED);
1739 keytest.index = 0;
1740 }
1741 else
1742 {
1743 keytest.keymap = NULL;
1744 keytest.context = -1;
1745 keytest.index = -1;
1746 keytest.action = ACTION_NONE;
1747 keytest.countdown = 0;
1748 }
1749 }
1750 }
1751 }
1752 else if (selected_item == 1)
1753 {
1754 keytest.countdown = TEST_COUNTDOWN_MS / 10;
1755 keytest.action = ACTION_NONE;
1756 }
1757 }
1758
1759 return PLUGIN_OK;
1760default_handler:
1761 return GOTO_ACTION_DEFAULT_HANDLER;
1762}
1763
1764int menu_action_listfiles(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1765{
1766 (void) exit;
1767 int i;
1768 if (*action == ACTION_STD_OK)
1769 {
1770 struct mainmenu *cur = (struct mainmenu *)lists->data;
1771 if (selected_item >= (cur->items) - 1)/*back*/
1772 {
1773 *action = ACTION_STD_CANCEL;
1774 }
1775 else
1776 {
1777 /* iterate to the desired file */
1778 char buf[MAX_PATH];
1779 int len = rb->snprintf(buf, sizeof(buf), "%s/", KMFDIR);
1780 if (len < (int) sizeof(buf))
1781 {
1782 char *name = &buf[len];
1783 size_t bufleft = sizeof(buf) - len;
1784 list_get_name_cb(0, lists->data, name, bufleft);
1785 for (i = 1; i <= selected_item; i++)
1786 {
1787 list_get_name_cb(i, lists->data, name, bufleft);
1788 }
1789
1790 if (lists->data == MENU_ID(M_DELKEYS))
1791 {
1792 const char *lines[] = {ID2P(LANG_DELETE), buf};
1793 const struct text_message message={lines, 2};
1794 if (rb->gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
1795 {
1796 rb->remove(buf);
1797 cur->items--;
1798 lists->nb_items--;
1799 *action = ACTION_NONE;
1800 }
1801 }
1802 else if (lists->data == MENU_ID(M_LOADKEYS))
1803 {
1804 if (keyremap_load_file(buf) > 0)
1805 {
1806 rb->splashf(HZ * 2, "Loaded %s ", buf);
1807 *action = ACTION_STD_CANCEL;
1808 }
1809 }
1810 }
1811 }
1812 }
1813 return PLUGIN_OK;
1814}
1815
1816int menu_action_submenus(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1817{
1818#ifdef ROCKBOX_HAS_LOGF
1819 char logfnamebuf[64];
1820#endif
1821 (void) exit;
1822 if (*action == ACTION_STD_OK)
1823 {
1824 struct mainmenu *cur = (struct mainmenu *)lists->data;
1825 if (selected_item >= (cur->items) - 1)/*back*/
1826 {
1827 *action = ACTION_STD_CANCEL;
1828 }
1829 else if (lists->data == MENU_ID(M_ACTIONS))
1830 {
1831#ifdef ROCKBOX_HAS_LOGF
1832 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1833 logf("ACT %s %d (0x%X)", name, selected_item, selected_item);
1834#endif
1835 int id, item;
1836 POP_MENU(id, item);
1837 POP_MENU(id, item);
1838 const struct mainmenu *lastm = &mainmenu[id];
1839
1840 if (id == M_SETKEYS)
1841 {
1842 for (int i = 0; i < ctx_data.act_count; i++)
1843 {
1844 if (ctx_data.act_map[i].display_pos == item - 2)
1845 {
1846 int col = keyset.view_lastcol;
1847 struct button_mapping * bm = &ctx_data.act_map[i].map;
1848 if (col == 1) /* Action */
1849 {
1850 bm->action_code = selected_item;
1851 }
1852 break;
1853 }
1854 }
1855 }
1856 synclist_set(lastm->index, item-1, lastm->items, 1);
1857 rb->gui_synclist_draw(lists);
1858 }
1859 else if (lists->data == MENU_ID(M_CONTEXTS))
1860 {
1861#ifdef ROCKBOX_HAS_LOGF
1862 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1863 logf("CTX %s %d (0x%X)", name, selected_item, selected_item);
1864#endif
1865 int id, item;
1866 POP_MENU(id, item);
1867 POP_MENU(id, item);
1868 struct mainmenu *lastm = &mainmenu[id];
1869 if (id == M_SETKEYS)
1870 {
1871 bool found = false;
1872 int newctx = selected_item;
1873
1874 for (int i = 0; i < ctx_data.act_count; i++)
1875 {
1876 if (ctx_data.act_map[i].display_pos == item - 2)
1877 {
1878 int col = keyset.view_lastcol;
1879 if (col == 0) /* Context */
1880 {
1881 ctx_data.act_map[i].context = newctx;
1882 /* check if this context exists (if not create it) */
1883 for (int j = 0; j < ctx_data.ctx_count; j++)
1884 {
1885 if (ctx_data.ctx_map[j].context == newctx)
1886 {
1887 found = true;;
1888 break;
1889 }
1890 }
1891 }
1892 break;
1893 }
1894 }
1895 if (found == false)
1896 {
1897 keymap_add_context_entry(newctx);
1898 lastm->items++;
1899 }
1900 }
1901 synclist_set(lastm->index, item-1, lastm->items, 1);
1902 rb->gui_synclist_draw(lists);
1903 }
1904 else if (lists->data == MENU_ID(M_BUTTONS))
1905 {
1906#ifdef ROCKBOX_HAS_LOGF
1907 const char *name = list_get_name_cb(selected_item, lists->data, logfnamebuf, sizeof(logfnamebuf));
1908 logf("BTN %s", name);
1909#endif
1910 int id, item;
1911 POP_MENU(id, item);
1912 POP_MENU(id, item);
1913 const struct mainmenu *lastm = &mainmenu[id];
1914
1915 if (id == M_SETKEYS)
1916 {
1917 for (int i = 0; i < ctx_data.act_count; i++)
1918 {
1919 if (ctx_data.act_map[i].display_pos == item - 2)
1920 {
1921 int col = keyset.view_lastcol;
1922 struct button_mapping * bm = &ctx_data.act_map[i].map;
1923 const struct available_button *btn = &available_buttons[selected_item];
1924 if (col == 2) /* BUTTON*/
1925 {
1926 if (btn->value == BUTTON_NONE)
1927 bm->button_code = btn->value;
1928 else
1929 bm->button_code |= btn->value;
1930 }
1931 else if (col == 3) /* PREBTN */
1932 {
1933 if (btn->value == BUTTON_NONE)
1934 bm->pre_button_code = btn->value;
1935 else
1936 bm->pre_button_code |= btn->value;
1937 }
1938 break;
1939 }
1940 }
1941 }
1942 synclist_set(lastm->index, item-1, lastm->items, 1);
1943 rb->gui_synclist_draw(lists);
1944 }
1945 }
1946 return PLUGIN_OK;
1947}
1948
1949static void cleanup(void *parameter)
1950{
1951 (void)parameter;
1952 keyremap_save_user_keys(false);
1953}
1954
1955int menu_action_cb(int *action, int selected_item, bool* exit, struct gui_synclist *lists)
1956{
1957 int status = PLUGIN_OK;
1958 /* Top Level Menu Actions */
1959 if (lists->data == MENU_ID(M_ROOT))
1960 {
1961 status = menu_action_root(action, selected_item, exit, lists);
1962 }
1963 else if (lists->data == MENU_ID(M_SETKEYS))
1964 {
1965 status = menu_action_setkeys(action, selected_item, exit, lists);
1966 }
1967 else if (lists->data == MENU_ID(M_TESTKEYS))
1968 {
1969 status = menu_action_testkeys(action, selected_item, exit, lists);
1970 }
1971 else if (lists->data == MENU_ID(M_DELKEYS))
1972 {
1973 status = menu_action_listfiles(action, selected_item, exit, lists);
1974 }
1975 else if (lists->data == MENU_ID(M_LOADKEYS))
1976 {
1977 status = menu_action_listfiles(action, selected_item, exit, lists);
1978 }
1979
1980 if (status == GOTO_ACTION_DEFAULT_HANDLER)
1981 goto default_handler;
1982 /* Submenu Actions */
1983 menu_action_submenus(action, selected_item, exit, lists);
1984
1985 /* Global cancel */
1986 if (*action == ACTION_STD_CANCEL)
1987 {
1988 logf("CANCEL BUTTON");
1989 if (lists->data == MENU_ID(M_TESTKEYS))
1990 {
1991 keytest.keymap = NULL;
1992 keytest.context = -1;
1993 }
1994
1995 if (lists->data != MENU_ID(M_ROOT))
1996 {
1997 int id, item;
1998 POP_MENU(id, item);
1999 POP_MENU(id, item);
2000 const struct mainmenu *lastm = &mainmenu[id];
2001 synclist_set(lastm->index, item-1, lastm->items, 1);
2002 rb->gui_synclist_draw(lists);
2003 }
2004 else
2005 {
2006 /* save changes? */
2007 if (ctx_data.act_count + ctx_data.ctx_count >= 2)
2008 {
2009 int entries = 0;
2010 struct button_mapping *keymap = keyremap_create_temp(&entries);
2011 if (keymap != NULL && keyset.crc32 != rb->crc_32(keymap,
2012 entries * sizeof(struct button_mapping), 0xFFFFFFFF))
2013 {
2014 if (rb->yesno_pop("Save Keymap?") == true)
2015 {
2016 keyremap_save_user_keys(true);
2017 goto default_handler;
2018 }
2019 }
2020 }
2021 *exit = true;
2022 }
2023 }
2024default_handler:
2025 if (rb->default_event_handler_ex(*action, cleanup, NULL) == SYS_USB_CONNECTED)
2026 {
2027 *exit = true;
2028 return PLUGIN_USB_CONNECTED;
2029 }
2030 return PLUGIN_OK;
2031}
2032
2033static void synclist_set(int id, int selected_item, int items, int sel_size)
2034{
2035 void* menu_id = MENU_ID(id);
2036 static char menu_title[64]; /* title is used by every menu */
2037 PUSH_MENU(id, selected_item);
2038
2039 if (items <= 0)
2040 return;
2041 if (selected_item < 0)
2042 selected_item = 0;
2043
2044 list_voice_cb(0, menu_id);
2045 rb->gui_synclist_init(&lists,list_get_name_cb,
2046 menu_id, false, sel_size, NULL);
2047
2048 rb->gui_synclist_set_icon_callback(&lists,NULL);
2049 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
2050 rb->gui_synclist_set_nb_items(&lists,items);
2051 rb->gui_synclist_select_item(&lists, selected_item);
2052 printcell_enable(false);
2053
2054 if (menu_id == MENU_ID(M_ROOT))
2055 {
2056 lang_strlcpy(menu_title, mainmenu[M_ROOT].name, sizeof(menu_title));
2057 rb->gui_synclist_set_title(&lists, menu_title, Icon_Plugin);
2058 }
2059 else if (menu_id == MENU_ID(M_SETKEYS))
2060 {
2061 keyset.view_columns = printcell_set_columns(&lists, NULL,
2062 ACTVIEW_HEADER, Icon_Rockbox);
2063 printcell_enable(true);
2064 int curcol = printcell_get_column_selected();
2065 if (keyset.view_lastcol >= keyset.view_columns)
2066 keyset.view_lastcol = -1;
2067 /* restore column position */
2068 while (keyset.view_lastcol > -1 && curcol != keyset.view_lastcol)
2069 {
2070 curcol = printcell_increment_column(1, true);
2071 }
2072 keyset.view_lastcol = curcol;
2073 }
2074 else
2075 {
2076 int id;
2077 PEEK_MENU_ID(id);
2078 lang_strlcpy(menu_title, mainitem(id)->name, sizeof(menu_title));
2079 rb->gui_synclist_set_title(&lists, menu_title, Icon_Submenu_Entered);
2080 /* if (menu_title[0] == '$'){ printcell_enable(true); } */
2081 }
2082}
2083
2084static void keyremap_set_buffer(void* buffer, size_t buf_size)
2085{
2086 /* set up the keyremap action buffer
2087 * contexts start at the front and work towards the back
2088 * actions start at the back and work towards front
2089 * if they meet buffer is out of space (checked by ctx & btn add entry fns)
2090 */
2091 keyremap_buffer.buffer = buffer;
2092 keyremap_buffer.buf_size = buf_size;
2093 keyremap_reset_buffer();
2094}
2095
2096enum plugin_status plugin_start(const void* parameter)
2097{
2098 int ret = PLUGIN_OK;
2099 int selected_item = -1;
2100 int action;
2101 bool redraw = true;
2102 bool exit = false;
2103 if (parameter)
2104 {
2105 //
2106 }
2107
2108 size_t buf_size;
2109 void* buffer = rb->plugin_get_buffer(&buf_size);
2110 keyremap_set_buffer(buffer, buf_size);
2111
2112 mainmenu[M_BUTTONS].items = available_button_count;
2113 /* add back item to each submenu */
2114 for (int i = 1; i < M_LAST_ITEM; i++)
2115 mainmenu[i].items += 1;
2116
2117#if 0
2118 keymap_add_context_entry(CONTEXT_WPS);
2119 keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_PLAY, BUTTON_VOL_UP, BUTTON_NONE);
2120 keymap_add_button_entry(CONTEXT_WPS, ACTION_WPS_STOP, BUTTON_VOL_DOWN | BUTTON_REPEAT, BUTTON_NONE);
2121
2122 keymap_add_context_entry(CONTEXT_MAINMENU);
2123 keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_PREV, BUTTON_VOL_UP, BUTTON_NONE);
2124 keymap_add_button_entry(CONTEXT_MAINMENU, ACTION_STD_NEXT, BUTTON_VOL_DOWN, BUTTON_NONE);
2125
2126 keymap_add_context_entry(CONTEXT_STD);
2127 keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_UP, BUTTON_NONE);
2128 keymap_add_button_entry(CONTEXT_STD, ACTION_STD_OK, BUTTON_VOL_DOWN, BUTTON_NONE);
2129#endif
2130
2131 keyset.crc32 = 0;
2132 keyset.view_lastcol = -1;
2133 if (!exit)
2134 {
2135 const struct mainmenu *mainm = &mainmenu[M_ROOT];
2136 synclist_set(mainm->index, 0, mainm->items, 1);
2137 rb->gui_synclist_draw(&lists);
2138
2139 while (!exit)
2140 {
2141 action = rb->get_action(CONTEXT_LIST, HZ / 10);
2142
2143 if (action == ACTION_NONE)
2144 {
2145 if (redraw)
2146 {
2147 action = ACTION_REDRAW;
2148 redraw = false;
2149 }
2150 }
2151 else
2152 redraw = true;
2153
2154 ret = menu_action_cb(&action, selected_item, &exit, &lists);
2155 if (rb->gui_synclist_do_button(&lists, &action))
2156 continue;
2157 selected_item = rb->gui_synclist_get_sel_pos(&lists);
2158
2159 mainmenu[M_SETKEYS].items = ctx_data.ctx_count + ctx_data.act_count + 2;
2160 }
2161 }
2162 rb->closedir(kmffiles_dirp);
2163
2164 return ret;
2165}
2166
2167
2168
2169#if 0 /* Alt Example */
2170static int write_keyremap(const char*filename, struct button_mapping *remap_data, int count)
2171{
2172 size_t res = 0;
2173 if (!filename || !remap_data || count <= 0)
2174 goto fail;
2175 int fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
2176 if (fd < 0)
2177 goto fail;
2178 if (keyremap_write_header(fd, count + 1) > 0)
2179 {
2180 res = keyremap_write_entries(fd, remap_data, count);
2181 res += sizeof(struct button_mapping); /*for header */
2182 }
2183fail:
2184 rb->close(fd);
2185 return res;
2186}
2187
2188void alt_example(void)
2189{
2190 struct button_mapping kmap[8];
2191
2192 kmap[0].action_code = CORE_CONTEXT_REMAP(CONTEXT_WPS);
2193 kmap[0].button_code = 3; /*OFFSET*/
2194 kmap[0].pre_button_code = 1; /*COUNT*/
2195
2196 kmap[1].action_code = CORE_CONTEXT_REMAP(CONTEXT_MAINMENU);
2197 kmap[1].button_code = 5; /*OFFSET*/
2198 kmap[1].pre_button_code = 2; /*COUNT*/
2199
2200 kmap[2].action_code = CONTEXT_STOPSEARCHING;
2201 kmap[2].button_code = BUTTON_NONE;
2202 kmap[2].pre_button_code = BUTTON_NONE;
2203
2204 kmap[3].action_code = ACTION_WPS_PLAY;
2205 kmap[3].button_code = BUTTON_VOL_UP;
2206 kmap[3].pre_button_code = BUTTON_NONE;
2207
2208 kmap[4].action_code = CONTEXT_STOPSEARCHING;
2209 kmap[4].button_code = BUTTON_NONE;
2210 kmap[4].pre_button_code = BUTTON_NONE;
2211
2212 kmap[5].action_code = ACTION_STD_NEXT;
2213 kmap[5].button_code = BUTTON_VOL_DOWN;
2214 kmap[5].pre_button_code = BUTTON_NONE;
2215
2216 kmap[6].action_code = ACTION_STD_PREV;
2217 kmap[6].button_code = BUTTON_VOL_UP;
2218 kmap[6].pre_button_code = BUTTON_NONE;
2219
2220 kmap[7].action_code = CONTEXT_STOPSEARCHING;
2221 kmap[7].button_code = BUTTON_NONE;
2222 kmap[7].pre_button_code = BUTTON_NONE;
2223
2224 write_keyremap(CORE_KEYREMAP_FILE, kmap, 8);
2225
2226 return ret;
2227}
2228#endif