A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 528 lines 16 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2006 Jonathan Gordon 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#include "plugin.h" 22#include "lib/playback_control.h" 23 24#define MAX_LINE_LEN 2048 25 26 27static unsigned char *buffer; 28static size_t buffer_size; 29static size_t char_count = 0; 30static int line_count = 0; 31static int last_action_line = 0; 32static int last_char_index = 0; 33static bool audio_buf = false; 34 35static char temp_line[MAX_LINE_LEN]; 36static char copy_buffer[MAX_LINE_LEN]; 37static char filename[MAX_PATH]; 38static char eol[3]; 39static bool newfile; 40 41#define ACTION_INSERT 0 42#define ACTION_GET 1 43#define ACTION_REMOVE 2 44#define ACTION_UPDATE 3 45#define ACTION_CONCAT 4 46 47static char* _do_action(int action, char* str, int line); 48#ifndef HAVE_ADJUSTABLE_CPU_FREQ 49#define do_action _do_action 50#else 51static char* do_action(int action, char* str, int line) 52{ 53 char *r; 54 rb->cpu_boost(1); 55 r = _do_action(action,str,line); 56 rb->cpu_boost(0); 57 return r; 58} 59#endif 60 61static char* _do_action(int action, char* str, int line) 62{ 63 int len, lennew; 64 int i=0,c=0; 65 if (line>=last_action_line) 66 { 67 i = last_action_line; 68 c = last_char_index; 69 } 70 while (i<line && i<line_count) 71 { 72 c += rb->strlen(&buffer[c])+1; 73 i++; 74 } 75 switch (action) 76 { 77 case ACTION_INSERT: 78 len = rb->strlen(str)+1; 79 if ( char_count+ len > buffer_size ) 80 return NULL; 81 rb->memmove(&buffer[c+len],&buffer[c],char_count-c); 82 rb->strcpy(&buffer[c],str); 83 char_count += len; 84 line_count++; 85 break; 86 case ACTION_GET: 87 if (line > line_count) 88 return &buffer[0]; 89 break; 90 case ACTION_REMOVE: 91 if (line > line_count) 92 return NULL; 93 len = rb->strlen(&buffer[c])+1; 94 rb->memmove(&buffer[c],&buffer[c+len],char_count-c-len); 95 char_count -= len; 96 line_count--; 97 break; 98 case ACTION_UPDATE: 99 if (line > line_count) 100 return NULL; 101 len = rb->strlen(&buffer[c])+1; 102 lennew = rb->strlen(str)+1; 103 if ( char_count+ lennew-len > buffer_size ) 104 return NULL; 105 rb->memmove(&buffer[c+lennew],&buffer[c+len],char_count-c-len); 106 rb->strcpy(&buffer[c],str); 107 char_count += lennew-len; 108 break; 109 case ACTION_CONCAT: 110 if (line > line_count) 111 return NULL; 112 rb->memmove(&buffer[c-1],&buffer[c],char_count-c); 113 char_count--; 114 line_count--; 115 break; 116 default: 117 return NULL; 118 } 119 last_action_line = i; 120 last_char_index = c; 121 return &buffer[c]; 122} 123static const char* list_get_name_cb(int selected_item, void* data, 124 char* buf, size_t buf_len) 125{ 126 (void)data; 127 char *b = do_action(ACTION_GET, 0, selected_item); 128 /* strlcpy(dst, src, siz) returns strlen(src) */ 129 if (rb->strlcpy(buf, b, buf_len) >= buf_len) 130 { 131 rb->strcpy(&buf[buf_len-10], " ..."); 132 } 133 return buf; 134} 135 136static void get_eol_string(char* fn) 137{ 138 int fd; 139 char t; 140 141 /* assume LF first */ 142 rb->strcpy(eol,"\n"); 143 144 if (!fn || !fn[0]) 145 return; 146 fd = rb->open(fn,O_RDONLY); 147 if (fd<0) 148 return; 149 150 while (1) 151 { 152 if (!rb->read(fd,&t,1) || t == '\n') 153 { 154 break; 155 } 156 if (t == '\r') 157 { 158 if (rb->read(fd,&t,1) && t=='\n') 159 rb->strcpy(eol,"\r\n"); 160 else 161 rb->strcpy(eol,"\r"); 162 break; 163 } 164 } 165 rb->close(fd); 166 return; 167} 168 169static bool save_changes(int overwrite) 170{ 171 int fd; 172 int i; 173 174 if (newfile || !overwrite) 175 { 176 if(rb->kbd_input(filename,MAX_PATH, NULL) < 0) 177 { 178 newfile = true; 179 return false; 180 } 181 } 182 183 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC, 0666); 184 if (fd < 0) 185 { 186 newfile = true; 187 rb->splash(HZ*2, "Changes NOT saved"); 188 return false; 189 } 190 191 rb->lcd_clear_display(); 192#ifdef HAVE_ADJUSTABLE_CPU_FREQ 193 rb->cpu_boost(1); 194#endif 195 for (i=0;i<line_count;i++) 196 { 197 rb->fdprintf(fd,"%s%s", do_action(ACTION_GET, 0, i), eol); 198 } 199#ifdef HAVE_ADJUSTABLE_CPU_FREQ 200 rb->cpu_boost(0); 201#endif 202 rb->close(fd); 203 204 if (newfile || !overwrite) 205 /* current directory may have changed */ 206 rb->reload_directory(); 207 208 newfile = false; 209 return true; 210} 211 212static void setup_lists(struct gui_synclist *lists, int sel) 213{ 214 rb->gui_synclist_init(lists,list_get_name_cb,0, false, 1, NULL); 215 rb->gui_synclist_set_nb_items(lists,line_count); 216 rb->gui_synclist_select_item(lists, sel); 217 rb->gui_synclist_draw(lists); 218} 219 220enum { 221 MENU_RET_SAVE = -1, 222 MENU_RET_NO_UPDATE, 223 MENU_RET_UPDATE, 224}; 225static int do_item_menu(int cur_sel) 226{ 227 int ret = MENU_RET_NO_UPDATE; 228 MENUITEM_STRINGLIST(menu, "Line Options", NULL, 229 "Cut/Delete", "Copy", 230 "Insert Above", "Insert Below", 231 "Concat To Above", 232 "Save", "Playback Control"); 233 234 switch (rb->do_menu(&menu, NULL, NULL, false)) 235 { 236 case 0: /* cut */ 237 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), 238 MAX_LINE_LEN); 239 do_action(ACTION_REMOVE, 0, cur_sel); 240 ret = MENU_RET_UPDATE; 241 break; 242 case 1: /* copy */ 243 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), 244 MAX_LINE_LEN); 245 ret = MENU_RET_NO_UPDATE; 246 break; 247 case 2: /* insert above */ 248 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN, NULL)) 249 { 250 do_action(ACTION_INSERT,copy_buffer,cur_sel); 251 copy_buffer[0]='\0'; 252 ret = MENU_RET_UPDATE; 253 } 254 break; 255 case 3: /* insert below */ 256 if (!rb->kbd_input(copy_buffer,MAX_LINE_LEN, NULL)) 257 { 258 do_action(ACTION_INSERT,copy_buffer,cur_sel+1); 259 copy_buffer[0]='\0'; 260 ret = MENU_RET_UPDATE; 261 } 262 break; 263 case 4: /* cat to above */ 264 if (cur_sel>0) 265 { 266 do_action(ACTION_CONCAT,0,cur_sel); 267 ret = MENU_RET_UPDATE; 268 } 269 break; 270 case 5: /* save */ 271 ret = MENU_RET_SAVE; 272 break; 273 case 6: /* playback menu */ 274 if (!audio_buf) 275 playback_control(NULL); 276 else 277 rb->splash(HZ, "Cannot restart playback"); 278 ret = MENU_RET_NO_UPDATE; 279 break; 280 default: 281 ret = MENU_RET_NO_UPDATE; 282 break; 283 } 284 return ret; 285} 286 287#ifdef HAVE_LCD_COLOR 288 289#define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \ 290 (toupper(c)) - 'A' + 10) 291 292static int my_hex_to_rgb(const char* hex, int* color) 293{ int ok = 1; 294 int i; 295 int red, green, blue; 296 297 if (rb->strlen(hex) == 6) { 298 for (i=0; i < 6; i++ ) { 299 if (!isxdigit(hex[i])) { 300 ok=0; 301 break; 302 } 303 } 304 305 if (ok) { 306 red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]); 307 green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]); 308 blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]); 309 *color = LCD_RGBPACK(red,green,blue); 310 return 0; 311 } 312 } 313 314 return -1; 315} 316#endif /* HAVE_LCD_COLOR */ 317 318/* this is the plugin entry point */ 319enum plugin_status plugin_start(const void* parameter) 320{ 321 int fd; 322 323 struct gui_synclist lists; 324 bool exit = false; 325 int button; 326 bool changed = false; 327 int cur_sel=0; 328#ifdef HAVE_LCD_COLOR 329 bool edit_colors_file = false; 330#endif 331 332 copy_buffer[0]='\0'; 333 334#if LCD_DEPTH > 1 335 rb->lcd_set_backdrop(NULL); 336#endif 337 buffer = rb->plugin_get_buffer(&buffer_size); 338 339#ifdef HAVE_ADJUSTABLE_CPU_FREQ 340 rb->cpu_boost(1); 341#endif 342 if (parameter) 343 { 344#ifdef HAVE_LCD_COLOR 345 char *c = NULL; 346#endif 347 rb->strlcpy(filename, (char*)parameter, MAX_PATH); 348 get_eol_string(filename); 349 fd = rb->open(filename,O_RDONLY); 350 if (fd<0) 351 { 352 rb->splashf(HZ*2, "Couldnt open file: %s", filename); 353 return PLUGIN_ERROR; 354 } 355#ifdef HAVE_LCD_COLOR 356 c = rb->strrchr(filename, '.'); 357 if (c && !rb->strcmp(c, ".colours")) 358 edit_colors_file = true; 359#endif 360 if (buffer_size <= (size_t)rb->filesize(fd) + 0x400) 361 { 362 buffer = rb->plugin_get_audio_buffer(&buffer_size); 363 audio_buf = true; 364 } 365 /* read in the file */ 366 while (rb->read_line(fd,temp_line,MAX_LINE_LEN) > 0) 367 { 368 if (!do_action(ACTION_INSERT,temp_line,line_count)) 369 { 370 rb->splashf(HZ*2,"Error reading file: %s",(char*)parameter); 371 rb->close(fd); 372 return PLUGIN_ERROR; 373 } 374 } 375 rb->close(fd); 376 newfile = false; 377 } 378 else 379 { 380 rb->strcpy(filename,"/"); 381 rb->strcpy(eol,"\n"); 382 newfile = true; 383 } 384 385#ifdef HAVE_ADJUSTABLE_CPU_FREQ 386 rb->cpu_boost(0); 387#endif 388 /* now dump it in the list */ 389 setup_lists(&lists,0); 390 rb->lcd_update(); 391 while (!exit) 392 { 393 rb->gui_synclist_draw(&lists); 394 cur_sel = rb->gui_synclist_get_sel_pos(&lists); 395 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); 396 if (rb->gui_synclist_do_button(&lists, &button)) 397 continue; 398 switch (button) 399 { 400 case ACTION_STD_OK: 401 { 402 if (line_count) 403 rb->strlcpy(temp_line, do_action(ACTION_GET, 0, cur_sel), 404 MAX_LINE_LEN); 405#ifdef HAVE_LCD_COLOR 406 if (edit_colors_file && line_count) 407 { 408 char *name = temp_line, *value = NULL; 409 char extension[16]; 410 int color, old_color; 411 bool temp_changed = false; 412 413 MENUITEM_STRINGLIST(menu, "Edit What?", NULL, 414 "Extension", "Colour"); 415 416 rb->settings_parseline(temp_line, &name, &value); 417 rb->strlcpy(extension, name, sizeof(extension)); 418 if (value) 419 my_hex_to_rgb(value, &color); 420 else 421 color = 0; 422 423 switch (rb->do_menu(&menu, NULL, NULL, false)) 424 { 425 case 0: 426 temp_changed = !rb->kbd_input(extension, sizeof(extension), NULL); 427 break; 428 case 1: 429 old_color = color; 430 rb->set_color(rb->screens[SCREEN_MAIN], name, &color, -1); 431 temp_changed = (value == NULL) || (color != old_color); 432 break; 433 } 434 435 if (temp_changed) 436 { 437 rb->snprintf(temp_line, MAX_LINE_LEN, "%s: %02X%02X%02X", 438 extension, RGB_UNPACK_RED(color), 439 RGB_UNPACK_GREEN(color), 440 RGB_UNPACK_BLUE(color)); 441 do_action(ACTION_UPDATE, temp_line, cur_sel); 442 changed = true; 443 } 444 } 445 else 446#endif 447 if (!rb->kbd_input(temp_line,MAX_LINE_LEN, NULL)) 448 { 449 if (line_count) 450 do_action(ACTION_UPDATE,temp_line,cur_sel); 451 else 452 do_action(ACTION_INSERT,temp_line,cur_sel); 453 changed = true; 454 } 455 } 456 break; 457 case ACTION_STD_CONTEXT: 458/* These targets have unintuitive STD_MENU keymaps, so we use context keymap instead; 459 We don't need the "delete line" action, since this can be done via the menu. */ 460#if (CONFIG_KEYPAD != SAMSUNG_YH92X_PAD) && (CONFIG_KEYPAD != SAMSUNG_YH820_PAD) 461 if (!line_count) break; 462 rb->strlcpy(copy_buffer, do_action(ACTION_GET, 0, cur_sel), 463 MAX_LINE_LEN); 464 do_action(ACTION_REMOVE, 0, cur_sel); 465 changed = true; 466 break; 467 case ACTION_STD_MENU: 468#endif 469 { 470 /* do the item menu */ 471 switch (do_item_menu(cur_sel)) 472 { 473 case MENU_RET_SAVE: 474 if(save_changes(1)) 475 changed = false; 476 break; 477 case MENU_RET_UPDATE: 478 changed = true; 479 break; 480 case MENU_RET_NO_UPDATE: 481 break; 482 } 483 } 484 break; 485 case ACTION_STD_CANCEL: 486 if (changed) 487 { 488 MENUITEM_STRINGLIST(menu, "Do What?", NULL, 489 "Return", 490 "Playback Control", "Save Changes", 491 "Save As...", "Save and Exit", 492 "Ignore Changes and Exit"); 493 switch (rb->do_menu(&menu, NULL, NULL, false)) 494 { 495 case 0: 496 break; 497 case 1: 498 if (!audio_buf) 499 playback_control(NULL); 500 else 501 rb->splash(HZ, "Cannot restart playback"); 502 break; 503 case 2: //save to disk 504 if(save_changes(1)) 505 changed = 0; 506 break; 507 case 3: 508 if(save_changes(0)) 509 changed = 0; 510 break; 511 case 4: 512 if(save_changes(1)) 513 exit=1; 514 break; 515 case 5: 516 exit=1; 517 break; 518 } 519 } 520 else exit=1; 521 break; 522 } 523 rb->gui_synclist_set_nb_items(&lists,line_count); 524 if(line_count > 0 && line_count <= cur_sel) 525 rb->gui_synclist_select_item(&lists,line_count-1); 526 } 527 return PLUGIN_OK; 528}