A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 951 lines 28 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ / 5 * Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) ( 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2020 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/* open_plugins.rock interfaces with the open_plugin core 23 * 24 * When opened directly it acts as a viewer for the plugin.dat file 25 * this allows you to edit the paths and parameters for 26 * core shortcuts as well as your added plugins 27 * 28 * If a plugin is supplied to the viewer it is added to the dat file 29 * 30 * If instead the plugin has previously been added then it is run 31 * with the parameters previously supplied 32 */ 33 34#include "plugin.h" 35#include "lang_enum.h" 36#include "../open_plugin.h" 37 38#define ROCK_EXT "rock" 39#define ROCK_LEN 5 40 41#define OP_EXT "opx" 42#define OP_LEN 4 43 44#define OP_PLUGIN_RESTART (PLUGIN_GOTO_PLUGIN | 0x8000) 45 46#define MENU_ID_MAIN "0" 47#define MENU_ID_EDIT "1" 48 49static int fd_dat; 50static struct gui_synclist lists; 51struct open_plugin_entry_t op_entry; 52static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM; 53static const off_t op_entry_sz = sizeof(struct open_plugin_entry_t); 54 55/* we only need the names for the first menu so don't bother reading paths yet */ 56const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry); 57 58static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key); 59 60static bool _yesno_pop(const char* text) 61{ 62 const char *lines[]={text}; 63 const struct text_message message={lines, 1}; 64 bool ret = (rb->gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES); 65 FOR_NB_SCREENS(i) 66 rb->screens[i]->clear_viewport(); 67 return ret; 68} 69 70static size_t pathbasename(const char *name, const char **nameptr) 71{ 72 const char *p = name; 73 const char *q = p; 74 const char *r = q; 75 76 while (*(p = GOBBLE_PATH_SEPCH(p))) 77 { 78 q = p; 79 p = GOBBLE_PATH_COMP(++p); 80 r = p; 81 } 82 83 if (r == name && p > name) 84 q = p, r = q--; /* root - return last slash */ 85 /* else path is an empty string */ 86 87 *nameptr = q; 88 return r - q; 89} 90static int op_entry_checksum(void) 91{ 92 if (op_entry.checksum != open_plugin_csum + 93 (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY)) 94 { 95 return 0; 96 } 97 return 1; 98} 99 100static bool op_entry_read(int fd, int selected_item, off_t data_sz) 101{ 102 rb->memset(&op_entry, 0, op_entry_sz); 103 op_entry.lang_id = -1; 104 return ((selected_item >= 0) && (fd >= 0) && 105 (rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) && 106 (rb->read(fd, &op_entry, data_sz) == data_sz) && op_entry_checksum() > 0); 107} 108 109static bool op_entry_read_name(int fd, int selected_item) 110{ 111 return op_entry_read(fd, selected_item, op_name_sz); 112} 113 114static int op_entry_read_opx(const char *path) 115{ 116 int ret = -1; 117 off_t filesize; 118 int fd_opx; 119 120 if(rb->filetype_get_attr(path) == FILE_ATTR_OPX) 121 { 122 fd_opx = rb->open(path, O_RDONLY); 123 if (fd_opx >= 0) 124 { 125 filesize = rb->filesize(fd_opx); 126 ret = filesize; 127 if (filesize == op_entry_sz && !op_entry_read(fd_opx, 0, op_entry_sz)) 128 ret = 0; 129 else if (op_entry_checksum() <= 0) 130 ret = 0; 131 rb->close(fd_opx); 132 } 133 } 134 return ret; 135} 136 137static void op_entry_export(int selection) 138{ 139 int len; 140 int fd = -1; 141 char filename [MAX_PATH + 1]; 142 143 if (!op_entry_read(fd_dat, selection, op_entry_sz) || op_entry_checksum() <= 0) 144 goto failure; 145 146 rb->snprintf(filename, MAX_PATH, "%s/%s", PLUGIN_APPS_DIR, op_entry.name); 147 148 if( !rb->kbd_input( filename, MAX_PATH, NULL ) ) 149 { 150 len = rb->strlen(filename); 151 if(len > OP_LEN && filename[len] != PATH_SEPCH && 152 rb->strcasecmp(&((filename)[len-OP_LEN]), "." OP_EXT) != 0) 153 { 154 rb->strcat(filename, "." OP_EXT); 155 } 156 157 fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); 158 159 if (fd >= 0 && rb->write(fd, &op_entry, op_entry_sz) == op_entry_sz) 160 { 161 rb->close(fd); 162 rb->splashf( 1*HZ, "File Saved (%s)", filename ); 163 return; 164 } 165 rb->close(fd); 166 } 167 168failure: 169 rb->splashf( 2*HZ, "Save Failed (%s)", filename ); 170 171} 172 173static void op_entry_set_checksum(void) 174{ 175 op_entry.checksum = open_plugin_csum + 176 (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY); 177} 178 179static void op_entry_set_name(void) 180{ 181 char tmp_buf[OPEN_PLUGIN_NAMESZ+1]; 182 rb->strlcpy(tmp_buf, op_entry.name, OPEN_PLUGIN_NAMESZ); 183 if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_NAMESZ, NULL) >= 0) 184 rb->strlcpy(op_entry.name, tmp_buf, OPEN_PLUGIN_NAMESZ); 185} 186 187static int op_entry_set_path(void) 188{ 189 int ret = 0; 190 char tmp_buf[OPEN_PLUGIN_BUFSZ+1]; 191 192 if (op_entry.path[0] == '\0') 193 rb->strcpy(op_entry.path, PLUGIN_DIR"/"); 194 195 struct browse_context browse = { 196 .dirfilter = SHOW_ALL, 197 .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER, 198 .title = rb->str(LANG_ADD), 199 .icon = Icon_Plugin, 200 .root = op_entry.path, 201 .buf = tmp_buf, 202 .bufsize = sizeof(tmp_buf), 203 }; 204 205 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS) 206 { 207 ret = rb->strlcpy(op_entry.path, tmp_buf, OPEN_PLUGIN_BUFSZ); 208 if (ret > OPEN_PLUGIN_BUFSZ) 209 ret = 0; 210 } 211 return ret; 212} 213 214static int op_entry_set_param_path(void) 215{ 216 int ret = 0; 217 char tmp_buf[OPEN_PLUGIN_BUFSZ+1]; 218 219 if (op_entry.param[0] == '\0') 220 rb->strcpy(tmp_buf, "/"); 221 else 222 rb->strcpy(tmp_buf, op_entry.param); 223 224 struct browse_context browse = { 225 .dirfilter = SHOW_ALL, 226 .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER, 227 .title = rb->str(LANG_PARAMETER), 228 .icon = Icon_Plugin, 229 .root = tmp_buf, 230 .buf = tmp_buf, 231 .bufsize = sizeof(tmp_buf), 232 }; 233 234 if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS) 235 { 236 ret = rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ); 237 if (ret > OPEN_PLUGIN_BUFSZ) 238 ret = 0; 239 } 240 return ret; 241} 242 243static void op_entry_set_param(void) 244{ 245 if (_yesno_pop(ID2P(LANG_BROWSE))) 246 op_entry_set_param_path(); 247 248 char tmp_buf[OPEN_PLUGIN_BUFSZ+1]; 249 rb->strlcpy(tmp_buf, op_entry.param, OPEN_PLUGIN_BUFSZ); 250 if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_BUFSZ, NULL) >= 0) 251 rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ); 252} 253 254static int op_et_exclude_hash(struct open_plugin_entry_t *op_entry, int item, void *data) 255{ 256 (void)item; 257 258 if (op_entry->hash == 0 || op_entry->name[0] == '\0') 259 return 0; 260 261 if (data) 262 { 263 uint32_t *hash = data; 264 if (op_entry->hash != *hash) 265 return 1; 266 } 267 return 0; 268} 269 270static int op_et_exclude_builtin(struct open_plugin_entry_t *op_entry, int item, void *data) 271{ 272 (void)item; 273 (void)data; 274 275 if (op_entry->lang_id >= 0) 276 return 0; 277 else if(op_entry->hash == 0 || op_entry->name[0] == '\0') 278 return 0; 279 280 return 1; 281} 282 283static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, void *data) 284{ 285 (void)item; 286 (void)data; 287 288 if (op_entry->lang_id < 0) 289 return 0; 290 else if (op_entry->hash == 0 || op_entry->name[0] == '\0') 291 return 0; 292 293 return 1; 294} 295 296static int op_entry_transfer(int fd, int fd_tmp, 297 int(*compfn)(struct open_plugin_entry_t*, int, void*), 298 void *data) 299{ 300 int entries = -1; 301 if (fd_tmp >= 0 && fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0) 302 { 303 entries = 0; 304 while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz) 305 { 306 if (compfn && compfn(&op_entry, entries, data) > 0 && op_entry_checksum() > 0) 307 { 308 rb->write(fd_tmp, &op_entry, op_entry_sz); 309 entries++; 310 } 311 } 312 } 313 return entries + 1; 314} 315 316static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key) 317{ 318 char buf[MAX_PATH]; 319 uint32_t hash; 320 uint32_t newhash; 321 char *pos = "";; 322 int fd_tmp = -1; 323 use_key = (use_key == true && key != NULL); 324 325 if (key) 326 { 327 open_plugin_get_hash(key, &hash); 328 op_entry.hash = hash; 329 } 330 else if (op_entry.lang_id < 0 && plugin) 331 { 332 /* need to keep the old hash so we can remove the old entry */ 333 hash = op_entry.hash; 334 open_plugin_get_hash(plugin, &newhash); 335 op_entry.hash = newhash; 336 } 337 else 338 hash = op_entry.hash; 339 340 if (plugin) 341 { 342 int fattr = rb->filetype_get_attr(plugin); 343 /* name */ 344 if (use_key) 345 { 346 op_entry.lang_id = -1; 347 rb->strlcpy(op_entry.name, key, OPEN_PLUGIN_NAMESZ); 348 } 349 350 if (pathbasename(plugin, (const char **)&pos) == 0) 351 pos = "\0"; 352 if (op_entry.name[0] == '\0' || op_entry.lang_id >= 0) 353 rb->strlcpy(op_entry.name, pos, OPEN_PLUGIN_NAMESZ); 354 355 356 357 if ((!parameter || parameter[0] == '\0') && fattr != FILE_ATTR_ROCK && fattr != FILE_ATTR_OPX) 358 { 359 rb->strlcpy(op_entry.param, plugin, OPEN_PLUGIN_BUFSZ); 360 parameter = op_entry.param; 361 plugin = rb->filetype_get_plugin(fattr, buf, sizeof(buf)); 362 if (!plugin) 363 { 364 rb->splashf(HZ * 2, ID2P(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos); 365 return 0; 366 } 367 } 368 369 if(fattr == FILE_ATTR_ROCK) 370 { 371 fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666); 372 if (fd_tmp < 0) 373 return 0; 374 375 /* path */ 376 if (plugin != op_entry.path) 377 rb->strlcpy(op_entry.path, plugin, OPEN_PLUGIN_BUFSZ); 378 379 if(parameter) 380 { 381 /* param matches lang_id so we want to set parameter */ 382 bool needs_param = op_entry.lang_id >= 0 383 && (rb->strcmp(parameter, rb->str(op_entry.lang_id)) == 0); 384 if (needs_param 385 || (parameter[0] == '\0' && _yesno_pop(ID2P(LANG_PARAMETER)))) 386 { 387 if (needs_param) 388 op_entry.param[0] = '\0'; 389 op_entry_set_param(); 390 } 391 else if (parameter != op_entry.param) 392 rb->strlcpy(op_entry.param, parameter, OPEN_PLUGIN_BUFSZ); 393 394 /* hash on the parameter path if it is a file */ 395 if (op_entry.lang_id <0 && key == op_entry.path && 396 rb->file_exists(op_entry.param)) 397 { 398 open_plugin_get_hash(op_entry.path, &newhash); 399 op_entry.hash = newhash; 400 } 401 } 402 op_entry_set_checksum(); 403 rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */ 404 } 405 else if(op_entry_read_opx(plugin) == op_entry_sz) 406 { 407 fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666); 408 if (fd_tmp < 0) 409 return 0; 410 411 if (op_entry.lang_id <0 && rb->file_exists(op_entry.param)) 412 open_plugin_get_hash(op_entry.param, &hash); 413 else 414 open_plugin_get_hash(op_entry.path, &hash); 415 416 op_entry.hash = hash; 417 op_entry_set_checksum(); 418 rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */ 419 } 420 else 421 { 422 if (op_entry.lang_id != LANG_SHORTCUTS) 423 rb->splashf(HZ * 2, ID2P(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos); 424 return 0; 425 } 426 } 427 428 if (op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_hash, &hash) > 0) 429 { 430 rb->close(fd_tmp); 431 rb->close(fd_dat); 432 fd_dat = -1; 433 rb->remove(OPEN_PLUGIN_DAT); 434 rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT); 435 } 436 else 437 { 438 rb->close(fd_tmp); 439 rb->remove(OPEN_PLUGIN_DAT ".tmp"); 440 hash = 0; 441 } 442 443 return hash; 444} 445 446void op_entry_browse_add(int selection) 447{ 448 char* key; 449 op_entry_read(fd_dat, selection, op_entry_sz); 450 if (op_entry_set_path() > 0) 451 { 452 if (op_entry.lang_id >= 0) 453 key = rb->str(op_entry.lang_id); 454 else 455 key = op_entry.path; 456 457 op_entry_add_path(key, op_entry.path, NULL, false); 458 } 459} 460 461static void op_entry_remove(int selection) 462{ 463 464 int entries = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz; 465 int32_t hash = 0; 466 int lang_id = -1; 467 468 if (entries > 0 && _yesno_pop(ID2P(LANG_REMOVE))) 469 { 470 op_entry_read(fd_dat, selection, op_entry_sz); 471 if (rb->lseek(fd_dat, selection * op_entry_sz, SEEK_SET) >= 0) 472 { 473 if (op_entry.lang_id >= 0) 474 { 475 lang_id = op_entry.lang_id; 476 hash = op_entry.hash; 477 } 478 rb->memset(&op_entry, 0, op_entry_sz); 479 op_entry.lang_id = lang_id; 480 op_entry.hash = hash; 481 rb->write(fd_dat, &op_entry, op_entry_sz); 482 } 483 } 484} 485 486static void op_entry_remove_empty(void) 487{ 488 bool resave = false; 489 if (fd_dat >= 0 && rb->lseek(fd_dat, 0, SEEK_SET) == 0) 490 { 491 while (resave == false && 492 rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz) 493 { 494 if (op_entry.hash == 0 || !op_entry_checksum()) 495 resave = true; 496 } 497 } 498 499 if (resave) 500 { 501 int fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666); 502 if (fd_tmp < 0) 503 return; 504 505 if ((op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_user, NULL) 506 + op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_builtin, NULL)) > 0) 507 { 508 rb->close(fd_tmp); 509 rb->close(fd_dat); 510 fd_dat = -1; 511 rb->remove(OPEN_PLUGIN_DAT); 512 rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT); 513 } 514 else 515 { 516 rb->close(fd_tmp); 517 rb->remove(OPEN_PLUGIN_DAT ".tmp"); 518 } 519 } 520 521} 522 523static int op_entry_run(void) 524{ 525 int ret = PLUGIN_ERROR; 526 char* path; 527 char* param; 528 if (op_entry.hash != 0 && op_entry.path[0] != '\0') 529 { 530 //rb->splash(1, ID2P(LANG_OPEN_PLUGIN)); 531 path = op_entry.path; 532 param = op_entry.param; 533 if (param[0] == '\0') 534 param = NULL; 535 536 ret = rb->plugin_open(path, param); 537 } 538 return ret; 539} 540 541static const char* list_get_name_cb(int selected_item, void* data, 542 char* buf, size_t buf_len) 543{ 544 /*TODO memoize names so we don't keep reading the disk when not necessary */ 545 if (data == (void*) &MENU_ID_MAIN) /* check address */ 546 { 547 if (op_entry_read_name(fd_dat, selected_item)) 548 { 549 if (op_entry.lang_id >= 0) 550 rb->snprintf(buf, buf_len, "%s [%s] ", 551 rb->str(op_entry.lang_id), op_entry.name); 552 else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len) 553 rb->strcpy(&buf[buf_len-10], " ..."); 554 } 555 else 556 return "?"; 557 } 558 else /* op_entry should already be loaded */ 559 { 560 switch(selected_item) 561 { 562 case 0: 563 return ID2P(LANG_NAME); 564 case 1: 565 if (op_entry.lang_id >= 0) 566 rb->snprintf(buf, buf_len, "%s [%s] ", 567 rb->str(op_entry.lang_id), op_entry.name); 568 else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len) 569 rb->strcpy(&buf[buf_len-10], " ..."); 570 break; 571 case 2: 572 return ID2P(LANG_DISPLAY_FULL_PATH); 573 case 3: 574 if (rb->strlcpy(buf, op_entry.path, buf_len) >= buf_len) 575 rb->strcpy(&buf[buf_len-10], " ..."); 576 break; 577 case 4: 578 return ID2P(LANG_PARAMETER); 579 case 5: 580 if (op_entry.param[0] == '\0') 581 return "[NULL]"; 582 else if (rb->strlcpy(buf, op_entry.param, buf_len) >= buf_len) 583 rb->strcpy(&buf[buf_len-10], " ..."); 584 break; 585 case 6: 586 return ""; 587 case 7: 588 return ID2P(LANG_BACK); 589 default: 590 return "?"; 591 } 592 } 593 594 return buf; 595} 596 597static int list_voice_cb(int list_index, void* data) 598{ 599 if (data == (void*) &MENU_ID_MAIN) /* check address */ 600 { 601 if (op_entry_read_name(fd_dat, list_index)) 602 { 603 if (op_entry.lang_id >= 0) 604 { 605 rb->talk_id(op_entry.lang_id, false); 606 rb->talk_id(VOICE_PAUSE, true); 607 rb->talk_force_enqueue_next(); 608 } 609 return rb->talk_spell(op_entry.name, false); 610 } 611 } 612 else 613 { 614 switch(list_index) 615 { 616 case 0: 617 rb->talk_id(LANG_NAME, false); 618 rb->talk_id(VOICE_PAUSE, true); 619 620 if (op_entry.lang_id >= 0) 621 { 622 rb->talk_id(op_entry.lang_id, true); 623 rb->talk_id(VOICE_PAUSE, true); 624 rb->talk_force_enqueue_next(); 625 } 626 return rb->talk_spell(op_entry.name, false); 627 case 2: 628 return rb->talk_id(LANG_DISPLAY_FULL_PATH, false); 629 case 4: 630 return rb->talk_id(LANG_PARAMETER, false); 631 case 6: 632 return rb->talk_id(LANG_BACK, false); 633 default: 634 return 0; 635 } 636 } 637 638 return 0; 639} 640 641static void synclist_set(char* menu_id, int selection, int items, int sel_size) 642{ 643 if (selection < 0) 644 selection = 0; 645 646 rb->gui_synclist_init(&lists,list_get_name_cb, 647 menu_id, false, sel_size, NULL); 648 649 rb->gui_synclist_set_voice_callback(&lists, list_voice_cb); 650 rb->gui_synclist_set_nb_items(&lists,items); 651 rb->gui_synclist_select_item(&lists, selection); 652 list_voice_cb(selection, menu_id); 653} 654 655static int context_menu_cb(int action, 656 const struct menu_item_ex *this_item, 657 struct gui_synclist *this_list) 658{ 659 (void)this_item; 660 661 int selection = rb->gui_synclist_get_sel_pos(this_list); 662 663 if(action == ACTION_ENTER_MENUITEM) 664 { 665 if (selection == 0 && 666 op_entry.lang_id >= 0 && op_entry.lang_id != LANG_OPEN_PLUGIN) 667 { 668 rb->gui_synclist_set_title(this_list, 669 rb->str(op_entry.lang_id), 0); 670 } 671 } 672 else if ((action == ACTION_STD_OK)) 673 { 674 /*Run, Edit, Remove, Export, Blank, Import, Add, Back*/ 675 switch(selection) 676 { 677 case 0:case 1:case 2:case 3:case 5: 678 return ACTION_STD_OK; 679 case 4: /*blank*/ 680 break; 681 default: 682 return ACTION_STD_CANCEL; 683 } 684 rb->gui_synclist_draw(this_list); /* redraw */ 685 return 0; 686 } 687 688 return action; 689} 690 691static void edit_menu(int selection) 692{ 693 int selected_item; 694 bool exit = false; 695 int action = 0; 696 697 if (!op_entry_read(fd_dat, selection, op_entry_sz)) 698 return; 699 700 uint32_t crc = rb->crc_32(&op_entry, op_entry_sz, 0xffffffff); 701 702 synclist_set(MENU_ID_EDIT, 2, 8, 2); 703 rb->gui_synclist_draw(&lists); 704 705 while (!exit) 706 { 707 action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); 708 709 if (rb->gui_synclist_do_button(&lists, &action)) 710 continue; 711 selected_item = rb->gui_synclist_get_sel_pos(&lists); 712 switch (action) 713 { 714 case ACTION_STD_OK: 715 if (selected_item == 0) 716 op_entry_set_name(); 717 else if (selected_item == 2) 718 op_entry_set_path(); 719 else if (selected_item == 4) 720 op_entry_set_param(); 721 else 722 exit = true; 723 724 rb->gui_synclist_draw(&lists); 725 break; 726 case ACTION_STD_CANCEL: 727 exit = true; 728 break; 729 } 730 } 731 732 if (crc != rb->crc_32(&op_entry, op_entry_sz, 0xffffffff) && 733 _yesno_pop(ID2P(LANG_SAVE)) == true) 734 { 735 char *param = op_entry.param; 736 if (param[0] == '\0') 737 param = NULL; 738 739 op_entry_add_path(NULL, op_entry.path, param, false); 740 fd_dat = rb->open(OPEN_PLUGIN_DAT, O_RDWR, 0666); 741 } 742} 743 744static int context_menu(int selection) 745{ 746 int selected_item; 747 if (op_entry_read(fd_dat, selection, op_entry_sz)) 748 { 749 MENUITEM_STRINGLIST(menu, op_entry.name, context_menu_cb, 750 ID2P(LANG_RUN), ID2P(LANG_EDIT), ID2P(LANG_REMOVE), ID2P(LANG_EXPORT), 751 ID2P(VOICE_BLANK), ID2P(LANG_ADD), ID2P(LANG_BACK)); 752 753 selected_item = rb->do_menu(&menu, 0, NULL, false); 754 switch (selected_item) 755 { 756 case 0: /*run*/ 757 return PLUGIN_GOTO_PLUGIN; 758 case 1: /*edit*/ 759 edit_menu(selection); 760 break; 761 case 2: /*remove*/ 762 op_entry_remove(selection); 763 break; 764 case 3: /*export*/ 765 op_entry_export(selection); 766 break; 767 case 4: /*blank*/ 768 break; 769 case 5: /*add*/ 770 op_entry_browse_add(-1); 771 rb->plugin_open(rb->plugin_get_current_filename(), "\0"); 772 return OP_PLUGIN_RESTART; 773 default: 774 break; 775 776 } 777 return PLUGIN_OK; 778 } 779 return PLUGIN_ERROR; 780} 781 782enum plugin_status plugin_start(const void* parameter) 783{ 784 int ret = PLUGIN_OK; 785 uint32_t hash = 0; 786 int item = -1; 787 int selection = -1; 788 int action; 789 int items; 790 int res; 791 char *path; 792 bool exit = false; 793 794 const int creat_flags = O_RDWR | O_CREAT; 795 796reopen_datfile: 797 fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666); 798 if (!fd_dat) 799 exit = true; 800 801 items = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz; 802 if (parameter) 803 { 804 path = (char*)parameter; 805 while (path[0] == ' ') 806 path++; 807 808 if (rb->strncasecmp(path, "-add", 4) == 0) 809 { 810 parameter = NULL; 811 op_entry_browse_add(-1); 812 rb->close(fd_dat); 813 goto reopen_datfile; 814 } 815 } 816 817 if (parameter) 818 { 819 path = (char*)parameter; 820 res = op_entry_read_opx(path); 821 if (res >= 0) 822 { 823 if (res == op_entry_sz) 824 { 825 exit = true; 826 ret = op_entry_run(); 827 } 828 } 829 else 830 { 831 open_plugin_get_hash(parameter, &hash); 832 rb->lseek(fd_dat, 0, SEEK_SET); 833 while (rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz) 834 { 835 item++; 836 if (op_entry.hash == hash) 837 { 838 selection = item; 839 break; 840 } 841 } 842 843 if (selection >= 0) 844 { 845 if (op_entry_read(fd_dat, selection, op_entry_sz)) 846 { 847 /* param matches lang_id so we want to set the parameter */ 848 if (op_entry.lang_id >= 0 849 && rb->strcmp(op_entry.param, rb->str(op_entry.lang_id)) == 0) 850 { 851 op_entry_add_path(NULL, op_entry.path, op_entry.param, false); 852 exit = true; 853 } 854 else 855 { 856 ret = op_entry_run(); 857 if (ret == PLUGIN_GOTO_PLUGIN) 858 exit = true; 859 } 860 } 861 } 862 else 863 { 864 op_entry_read(fd_dat, selection, op_entry_sz); 865 if (op_entry_add_path(parameter, parameter, "\0", false) > 0) 866 { 867 selection = 0; 868 items++; 869 fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666); 870 if (!fd_dat) 871 exit = true; 872 } 873 } 874 }/* OP_EXT */ 875 } 876 877 for (int i = items - 1; i > 0 && !exit; i--) 878 { 879 if (!op_entry_read(fd_dat, i, op_entry_sz)) 880 items--; 881 } 882 883 if (items < 1 && !exit) 884 { 885 char* cur_filename = rb->plugin_get_current_filename(); 886 887 if (op_entry_add_path(rb->str(LANG_ADD), cur_filename, "-add", true)) 888 { 889 rb->close(fd_dat); 890 parameter = NULL; 891 goto reopen_datfile; 892 } 893 rb->close(fd_dat); 894 return PLUGIN_ERROR; 895 } 896 897 898 899 if (!exit) 900 { 901 synclist_set(MENU_ID_MAIN, selection, items, 1); 902 rb->gui_synclist_draw(&lists); 903 904 while (!exit && fd_dat >= 0) 905 { 906 action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); 907 908 if (rb->gui_synclist_do_button(&lists, &action)) 909 continue; 910 selection = rb->gui_synclist_get_sel_pos(&lists); 911 switch (action) 912 { 913 case ACTION_STD_CONTEXT: 914 ret = context_menu(selection); 915 if (ret == OP_PLUGIN_RESTART) 916 { 917 ret = PLUGIN_GOTO_PLUGIN; 918 exit = true; 919 break; 920 } 921 else if (ret != PLUGIN_GOTO_PLUGIN) 922 { 923 synclist_set(MENU_ID_MAIN, selection, items, 1); 924 rb->gui_synclist_draw(&lists); 925 break; 926 } 927 /* Inentional fallthrough */ 928 case ACTION_STD_OK: 929 if (op_entry_read(fd_dat, selection, op_entry_sz)) 930 { 931 ret = op_entry_run(); 932 exit = true; 933 } 934 break; 935 case ACTION_STD_CANCEL: 936 case ACTION_STD_MENU: 937 { 938 selection = -2; 939 exit = true; 940 break; 941 } 942 } 943 } 944 op_entry_remove_empty(); 945 } 946 rb->close(fd_dat); 947 if (ret != PLUGIN_OK) 948 return ret; 949 else 950 return PLUGIN_OK; 951}