A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2228 lines 72 kB view raw
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 &empty; 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