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