A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 513 lines 17 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2010 Daniel Rigby 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 25 26#define MAX_LIST_SIZE 400 27#define DESC_SIZE 40 28#define MAX_LINE_LEN (DESC_SIZE + 1) 29 30enum flag_type { 31 FL_CLEARED = 0, 32 FL_SET, 33 FL_CATEGORY 34}; 35 36enum view_type { 37 EDIT_SHOPPING_LIST = 0, 38 VIEW_SHOPPING_LIST 39}; 40 41#define VIEW_TYPE_SIZE VIEW_SHOPPING_LIST + 1 42 43struct items_list_s { 44 unsigned int id; 45 enum flag_type flag; 46 char desc[DESC_SIZE]; 47}; 48 49static struct items_list_s items_list[MAX_LIST_SIZE]; 50static int total_item_count = 0; 51static int view_id_list[MAX_LIST_SIZE]; 52static int view_item_count; 53static enum view_type view = EDIT_SHOPPING_LIST; 54static char filename[MAX_PATH]; 55static bool changed = false; 56static bool show_categories = true; 57static char category_string[] = "Hide categories"; 58 59static const char *list_get_name_cb(int selected_item, void* data, 60 char* buf, size_t buf_len) 61{ 62 (void)data; 63 rb->strlcpy(buf, items_list[view_id_list[selected_item]].desc, buf_len); 64 return buf; 65} 66 67static enum themable_icons list_get_icon_cb(int selected_item, void *data) 68{ 69 (void)data; 70 if (items_list[view_id_list[selected_item]].flag == FL_CATEGORY) 71 return Icon_Rockbox; 72 else if (items_list[view_id_list[selected_item]].flag == FL_SET) 73 return Icon_Cursor; 74 else 75 return Icon_NOICON; 76} 77 78static bool save_changes(void) 79{ 80 int fd; 81 int i; 82 83 fd = rb->open(filename,O_WRONLY|O_CREAT|O_TRUNC); 84 if (fd < 0) 85 { 86 rb->splash(HZ*2, "Changes NOT saved"); 87 return false; 88 } 89 90 rb->lcd_clear_display(); 91#ifdef HAVE_ADJUSTABLE_CPU_FREQ 92 rb->cpu_boost(1); 93#endif 94 for (i = 0;i < total_item_count; i++) 95 { 96 switch (items_list[i].flag) 97 { 98 case FL_CATEGORY: 99 { 100 rb->fdprintf(fd,"#%s\n",items_list[i].desc); 101 break; 102 } 103 case FL_SET: 104 { 105 rb->fdprintf(fd,"!%s\n",items_list[i].desc); 106 break; 107 } 108 case FL_CLEARED: 109 { 110 rb->fdprintf(fd," %s\n",items_list[i].desc); 111 break; 112 } 113 } 114 } 115 /* save current view */ 116 rb->fdprintf(fd,"$%d%d\n",view, show_categories); 117 118#ifdef HAVE_ADJUSTABLE_CPU_FREQ 119 rb->cpu_boost(0); 120#endif 121 rb->close(fd); 122 123 return true; 124} 125 126static void create_view(struct gui_synclist *lists) 127{ 128 unsigned int cnt = 0; 129 int i, j; 130 131 switch (view) 132 { 133 case EDIT_SHOPPING_LIST: 134 { 135 for (i = 0; i < total_item_count; i++) 136 { 137 if (show_categories || (items_list[i].flag != FL_CATEGORY)) 138 view_id_list[cnt++] = i; 139 } 140 view_item_count = cnt; 141 rb->gui_synclist_set_title(lists,"Select items",Icon_Playlist); 142 break; 143 } 144 case VIEW_SHOPPING_LIST: 145 { 146 for (i = 0; i < total_item_count; i++) 147 { 148 if ((items_list[i].flag == FL_CATEGORY) && show_categories) 149 { 150 for (j = i+1; j < total_item_count; j++) 151 { 152 if (items_list[j].flag == FL_SET) 153 { 154 view_id_list[cnt++] = i; 155 break; 156 } 157 if (items_list[j].flag == FL_CATEGORY) 158 break; 159 } 160 } 161 else if (items_list[i].flag == FL_SET) 162 view_id_list[cnt++] = i; 163 } 164 view_item_count = cnt; 165 rb->gui_synclist_set_title(lists,"Shopping list",Icon_Playlist); 166 break; 167 } 168 } 169} 170 171static bool toggle(int selected_item) 172{ 173 if (items_list[view_id_list[selected_item]].flag == FL_CATEGORY) 174 return false; 175 else if (items_list[view_id_list[selected_item]].flag == FL_SET) 176 items_list[view_id_list[selected_item]].flag = FL_CLEARED; 177 else 178 items_list[view_id_list[selected_item]].flag = FL_SET; 179 return true; 180} 181 182static void update_category_string(void) 183{ 184 if (show_categories) 185 rb->strcpy(category_string,"Hide categories"); 186 else 187 rb->strcpy(category_string,"Show categories"); 188} 189 190static enum plugin_status load_file(void) 191{ 192 int fd; 193 static char temp_line[DESC_SIZE]; 194 static struct items_list_s new_item; 195 static int count = 0; 196 int linelen; 197 total_item_count = 0; 198 199 fd = rb->open(filename,O_RDONLY); 200 if (fd < 0) 201 { 202 rb->splashf(HZ*2,"Couldn't open file: %s",filename); 203 return PLUGIN_ERROR; 204 } 205 206 /* read in the file */ 207 while (rb->read_line(fd,temp_line,MAX_LINE_LEN)) 208 { 209 if (rb->strncmp(temp_line, "$", 1) == 0) 210 { 211 /* read view preferences */ 212 linelen = rb->strlen(temp_line); 213 if (linelen >= 2) 214 { 215 unsigned int val = temp_line[1] - '0'; 216 if (val < VIEW_TYPE_SIZE) 217 { 218 view = val; 219 } 220 } 221 if (linelen >= 3) 222 { 223 unsigned int val = temp_line[2] - '0'; 224 if (val <= 2) 225 { 226 show_categories = val; 227 update_category_string(); 228 } 229 } 230 } 231 else 232 { 233 new_item.id = count; 234 if (rb->strncmp(temp_line, " ", 1) == 0) 235 { 236 /* read description, flag = cleared */ 237 new_item.flag = FL_CLEARED; 238 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE); 239 } 240 else if (rb->strncmp(temp_line, "!", 1) == 0) 241 { 242 /* read description, flag = set */ 243 new_item.flag = FL_SET; 244 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE); 245 } 246 else if (rb->strncmp(temp_line, "#", 1) == 0) 247 { 248 /* read description, flag = category */ 249 new_item.flag = FL_CATEGORY; 250 rb->memcpy(new_item.desc, &temp_line[1], DESC_SIZE); 251 } 252 else 253 { 254 /* read description, flag = cleared */ 255 new_item.flag = FL_CLEARED; 256 rb->memcpy(new_item.desc, temp_line, DESC_SIZE); 257 } 258 items_list[total_item_count] = new_item; 259 total_item_count++; 260 if (total_item_count == MAX_LIST_SIZE) 261 { 262 total_item_count = MAX_LIST_SIZE - 1; 263 rb->splashf(HZ*2, "Truncating shopping list to %d items", 264 MAX_LIST_SIZE - 1); 265 changed = true; 266 rb->close(fd); 267 return PLUGIN_OK; 268 } 269 } 270 } 271 rb->close(fd); 272 changed = false; 273 return PLUGIN_OK; 274} 275 276/* this is the plugin entry point */ 277enum plugin_status plugin_start(const void* parameter) 278{ 279 struct gui_synclist lists; 280 bool exit = false; 281 int button; 282 int cur_sel = 0; 283 284#if LCD_DEPTH > 1 285 rb->lcd_set_backdrop(NULL); 286#endif 287 288#ifdef HAVE_ADJUSTABLE_CPU_FREQ 289 rb->cpu_boost(1); 290#endif 291 if (parameter) 292 { 293 rb->strcpy(filename,(char*)parameter); 294 295 if (load_file() == PLUGIN_ERROR) 296 return PLUGIN_ERROR; 297 } 298 else 299 return PLUGIN_ERROR; 300 301#ifdef HAVE_ADJUSTABLE_CPU_FREQ 302 rb->cpu_boost(0); 303#endif 304 /* now dump it in the list */ 305 rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL); 306 rb->gui_synclist_set_icon_callback(&lists, list_get_icon_cb); 307 create_view(&lists); 308 rb->gui_synclist_set_nb_items(&lists,view_item_count); 309 rb->gui_synclist_select_item(&lists, 0); 310 rb->gui_synclist_draw(&lists); 311 rb->lcd_update(); 312 313 while (!exit) 314 { 315 rb->gui_synclist_draw(&lists); 316 cur_sel = rb->gui_synclist_get_sel_pos(&lists); 317 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); 318 if (rb->gui_synclist_do_button(&lists, &button)) 319 continue; 320 switch (button) 321 { 322 case ACTION_STD_OK: 323 { 324 changed |= toggle(cur_sel); 325 break; 326 } 327 case ACTION_STD_MENU: 328 case ACTION_STD_CONTEXT: 329 { 330 switch(view) 331 { 332 case EDIT_SHOPPING_LIST: 333 { 334 MENUITEM_STRINGLIST(menu, "Options", NULL, 335 "View shopping list", 336 "Clear all items", 337 "Mark all items", 338 category_string, 339 "Revert to saved", 340 "Show Playback Menu", 341 "Quit without saving", 342 "Quit"); 343 344 switch (rb->do_menu(&menu, NULL, NULL, false)) 345 { 346 case 0: 347 { 348 /* view shopping list */ 349 view = VIEW_SHOPPING_LIST; 350 changed = true; 351 break; 352 } 353 case 1: 354 { 355 /* clear all items */ 356 int i; 357 for (i = 0; i < total_item_count; i++) 358 { 359 if (items_list[i].flag == FL_SET) 360 items_list[i].flag = FL_CLEARED; 361 } 362 changed = true; 363 break; 364 } 365 case 2: 366 { 367 /* mark all items */ 368 int i; 369 for (i = 0; i < total_item_count; i++) 370 { 371 if (items_list[i].flag == FL_CLEARED) 372 items_list[i].flag = FL_SET; 373 } 374 changed = true; 375 break; 376 } 377 case 3: 378 { 379 /* toggle categories */ 380 show_categories ^= true; 381 update_category_string(); 382 changed = true; 383 break; 384 } 385 case 4: 386 { 387 /* revert to saved */ 388 if (load_file() == PLUGIN_ERROR) 389 return PLUGIN_ERROR; 390 break; 391 } 392 case 5: 393 { 394 /* playback menu */ 395 playback_control(NULL); 396 break; 397 } 398 case 6: 399 { 400 /* Quit without saving */ 401 exit = 1; 402 break; 403 } 404 case 7: 405 { 406 /* Save and quit */ 407 if (changed) 408 save_changes(); 409 exit = 1; 410 break; 411 } 412 default: 413 { 414 break; 415 } 416 } 417 break; 418 } 419 420 case VIEW_SHOPPING_LIST: 421 { 422 MENUITEM_STRINGLIST(menu, "Options", NULL, 423 "Edit list", 424 "Reset list", 425 category_string, 426 "Revert to saved", 427 "Show Playback Menu", 428 "Quit without saving", 429 "Quit"); 430 431 switch (rb->do_menu(&menu, NULL, NULL, false)) 432 { 433 case 0: 434 { 435 /* edit list */ 436 view = EDIT_SHOPPING_LIST; 437 changed = true; 438 break; 439 } 440 case 1: 441 { 442 /* reset list */ 443 int i; 444 for (i = 0; i < total_item_count; i++) 445 { 446 if (items_list[i].flag == FL_SET) 447 items_list[i].flag = FL_CLEARED; 448 } 449 view = EDIT_SHOPPING_LIST; 450 changed = true; 451 break; 452 } 453 case 2: 454 { 455 /* toggle categories */ 456 show_categories ^= true; 457 update_category_string(); 458 changed = true; 459 break; 460 } 461 case 3: 462 { 463 /* revert to saved */ 464 if (load_file() == PLUGIN_ERROR) 465 return PLUGIN_ERROR; 466 break; 467 } 468 case 4: 469 { 470 /* playback menu */ 471 playback_control(NULL); 472 break; 473 } 474 case 5: 475 { 476 /* Quit without saving */ 477 exit = 1; 478 break; 479 } 480 case 6: 481 { 482 /* Save and quit */ 483 if (changed) 484 save_changes(); 485 exit = 1; 486 break; 487 } 488 default: 489 { 490 break; 491 } 492 } 493 break; 494 } 495 } 496 break; 497 } 498 case ACTION_STD_CANCEL: 499 { 500 if (changed) 501 save_changes(); 502 exit = 1; 503 break; 504 } 505 } 506 507 create_view(&lists); 508 rb->gui_synclist_set_nb_items(&lists,view_item_count); 509 if (view_item_count > 0 && view_item_count <= cur_sel) 510 rb->gui_synclist_select_item(&lists,view_item_count-1); 511 } 512 return PLUGIN_OK; 513}