A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 652 lines 22 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2005 by Kevin Ferrare 11 * Copyright (C) 2007 by Jonathan Gordon 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 2 16 * of the License, or (at your option) any later version. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ****************************************************************************/ 22#include <stdlib.h> 23#include "string-extra.h" 24#include "config.h" 25#include "system.h" 26#include "option_select.h" 27#include "kernel.h" 28#include "lang.h" 29#include "talk.h" 30#include "settings_list.h" 31#include "sound.h" 32#include "list.h" 33#include "action.h" 34#include "misc.h" 35#include "splash.h" 36#include "menu.h" 37#include "quickscreen.h" 38 39/* HASFLAG compares value to flags returns true if set, false otherwise */ 40#define HASFLAG(settings_list, flag) ((settings_list->flags & (flag)) == (flag)) 41 42static int selection_to_val(const struct settings_list *setting, int selection); 43int option_value_as_int(const struct settings_list *setting) 44{ 45 int type = (setting->flags & F_T_MASK); 46 int temp = 0; 47 if (type == F_T_BOOL) 48 temp = *(bool*)setting->setting?1:0; 49 else if (type == F_T_UINT || type == F_T_INT) 50 temp = *(int*)setting->setting; 51 return temp; 52} 53 54/* return an auto ranged time string, unit_idx specifies lowest or 55 base index of the passed value -- flag F_TIME_SETTING calls this */ 56static const char *option_get_timestring(char *buf, int buf_len, 57 int val, int unit_idx) 58{ 59 return format_time_auto(buf, buf_len, val, unit_idx | UNIT_TRIM_ZERO, false); 60} 61 62/* these two vars are needed so arbitrary values can be added to the 63 TABLE_SETTING settings if the F_ALLOW_ARBITRARY_VALS flag is set */ 64static int table_setting_oldval = 0, table_setting_array_position = 0; 65const char *option_get_valuestring(const struct settings_list *setting, 66 char *buffer, int buf_len, 67 intptr_t temp_var) 68{ 69 const char* str = buffer; 70 if (HASFLAG(setting, F_BOOL_SETTING)) 71 { 72 bool val = (bool)temp_var; 73 strmemccpy(buffer, str(val? setting->bool_setting->lang_yes : 74 setting->bool_setting->lang_no), buf_len); 75 } 76#if 0 /* probably dont need this one */ 77 else if (HASFLAG(setting, F_FILENAME)) 78 { 79 struct filename_setting *info = setting->filename_setting; 80 snprintf(buffer, buf_len, "%s%s%s", info->prefix, 81 (char*)temp_var, info->suffix); 82 } 83#endif 84 else if ((HASFLAG(setting, F_INT_SETTING)) || 85 HASFLAG(setting, F_TABLE_SETTING)) 86 { 87 const struct int_setting *int_info = setting->int_setting; 88 const struct table_setting *tbl_info = setting->table_setting; 89 int info_unit; 90 const char *str_unit; 91 const char* (*formatter)(char*, size_t, int, const char*); 92 if (HASFLAG(setting, F_INT_SETTING)) 93 { 94 formatter = int_info->formatter; 95 info_unit = int_info->unit; 96 } 97 else 98 { 99 formatter = tbl_info->formatter; 100 info_unit = tbl_info->unit; 101 } 102 103 bool is_time_setting = HASFLAG(setting, F_TIME_SETTING); 104 if (is_time_setting) 105 str = option_get_timestring(buffer, buf_len, (long)temp_var, info_unit); 106 107 str_unit = unit_strings_core[info_unit]; 108 109 if (formatter) 110 str = formatter(buffer, buf_len, (int)temp_var, str_unit); 111 else if (!is_time_setting) 112 snprintf(buffer, buf_len, "%d %s", (int)temp_var, str_unit?str_unit:""); 113 } 114 else if (HASFLAG(setting, F_T_SOUND)) 115 { 116 format_sound_value(buffer, buf_len, 117 setting->sound_setting->setting, 118 temp_var); 119 } 120 else if (HASFLAG(setting, F_CHOICE_SETTING)) 121 { 122 if (HASFLAG(setting, F_CHOICETALKS)) 123 { 124 const struct choice_setting *info = setting->choice_setting; 125 if (info->talks[(int)temp_var] < LANG_LAST_INDEX_IN_ARRAY) 126 { 127 strmemccpy(buffer, str(info->talks[(int)temp_var]), buf_len); 128 } 129 else 130 { 131 cfg_int_to_string(setting, (int)temp_var, buffer, buf_len); 132 } 133 } 134 else 135 { 136 int value = (int)temp_var; 137 char *val = P2STR(setting->choice_setting->desc[value]); 138 strmemccpy(buffer, val, buf_len); 139 } 140 } 141 return str; 142} 143void option_talk_value(const struct settings_list *setting, int value, bool enqueue) 144{ 145 146 if (HASFLAG(setting, F_BOOL_SETTING)) 147 { 148 bool val = (value==1); 149 talk_id(val? setting->bool_setting->lang_yes : 150 setting->bool_setting->lang_no, enqueue); 151 } 152#if 0 /* probably dont need this one */ 153 else if (HASFLAG(setting, F_FILENAME)) 154 { 155} 156#endif 157 else if ((HASFLAG(setting, F_INT_SETTING)) || 158 HASFLAG(setting, F_TABLE_SETTING)) 159 { 160 const struct int_setting *int_info = setting->int_setting; 161 const struct table_setting *tbl_info = setting->table_setting; 162 int unit; 163 int32_t (*get_talk_id)(int, int); 164 if (HASFLAG(setting, F_INT_SETTING)) 165 { 166 unit = int_info->unit; 167 get_talk_id = int_info->get_talk_id; 168 } 169 else 170 { 171 unit = tbl_info->unit; 172 get_talk_id = tbl_info->get_talk_id; 173 } 174 if (get_talk_id) 175 talk_id(get_talk_id(value, unit), enqueue); 176 else if (HASFLAG(setting, F_TIME_SETTING)) 177 talk_time_intervals(value, unit, enqueue); 178 else 179 talk_value(value, unit, enqueue); 180 } 181 else if (HASFLAG(setting, F_T_SOUND)) 182 { 183 int talkunit = UNIT_INT; 184 int sound_setting = setting->sound_setting->setting; 185 const char *unit = sound_unit(sound_setting); 186 int decimals = sound_numdecimals(sound_setting); 187 int phys = sound_val2phys(sound_setting, value); 188 if (!strcmp(unit, "dB")) 189 talkunit = UNIT_DB; 190 else if (!strcmp(unit, "%")) 191 talkunit = UNIT_PERCENT; 192 else if (!strcmp(unit, "Hz")) 193 talkunit = UNIT_HERTZ; 194 talk_value_decimal(phys, talkunit, decimals, enqueue); 195 } 196 else if (HASFLAG(setting, F_CHOICE_SETTING)) 197 { 198 if (HASFLAG(setting, F_CHOICETALKS)) 199 { 200 talk_id(setting->choice_setting->talks[value], enqueue); 201 } 202 else 203 { 204 talk_id(P2ID(setting->choice_setting->desc[value]), enqueue); 205 } 206 } 207} 208 209static int option_talk(int selected_item, void * data) 210{ 211 struct settings_list *setting = (struct settings_list *)data; 212 int temp_var = selection_to_val(setting, selected_item); 213 option_talk_value(setting, temp_var, false); 214 return 0; 215} 216 217#if defined(HAVE_QUICKSCREEN) || defined(HAVE_RECORDING) || defined(HAVE_TOUCHSCREEN) 218 /* only the quickscreen and recording trigger needs this */ 219void option_select_next_val(const struct settings_list *setting, 220 bool previous, bool apply) 221{ 222 bool repeated = get_action_statuscode(NULL) & ACTION_REPEAT; 223 224 int val = 0; 225 int *value = setting->setting; 226 if (HASFLAG(setting, F_BOOL_SETTING)) 227 { 228 if (repeated) 229 return; 230 231 *(bool*)value = !*(bool*)value; 232 if (apply && setting->bool_setting->option_callback) 233 setting->bool_setting->option_callback(*(bool*)value); 234 return; 235 } 236 else if (HASFLAG(setting, F_INT_SETTING)) 237 { 238 struct int_setting *info = (struct int_setting *)setting->int_setting; 239 bool neg_step = (info->step < 0); 240 if (!previous) 241 { 242 val = *value + info->step; 243 if (neg_step ? (val < info->max) : (val > info->max)) 244 val = repeated ? *value : info->min; 245 } 246 else 247 { 248 val = *value - info->step; 249 if (neg_step ? (val > info->min) : (val < info->min)) 250 val = repeated ? *value : info->max; 251 } 252 *value = val; 253 if (apply && info->option_callback) 254 info->option_callback(val); 255 } 256 else if (HASFLAG(setting, F_T_SOUND)) 257 { 258 int setting_id = setting->sound_setting->setting; 259 int steps = sound_steps(setting_id); 260 int min = sound_min(setting_id); 261 int max = sound_max(setting_id); 262 if (!previous) 263 { 264 val = *value + steps; 265 if (val >= max) 266 val = repeated ? *value : min; 267 } 268 else 269 { 270 val = *value - steps; 271 if (val < min) 272 val = repeated ? *value : max; 273 } 274 *value = val; 275 if (apply) 276 sound_set(setting_id, val); 277 } 278 else if (HASFLAG(setting, F_CHOICE_SETTING)) 279 { 280 struct choice_setting *info = (struct choice_setting *)setting->choice_setting; 281 if (!previous) 282 { 283 val = *value + 1; 284 if (val >= info->count) 285 val = repeated ? *value : 0; 286 } 287 else 288 { 289 val = *value - 1; 290 if (val < 0) 291 val = repeated ? *value : info->count-1; 292 } 293 *value = val; 294 if (apply && info->option_callback) 295 info->option_callback(val); 296 } 297 else if (HASFLAG(setting, F_TABLE_SETTING)) 298 { 299 const struct table_setting *tbl_info = setting->table_setting; 300 int i, add; 301 add = previous?tbl_info->count-1:1; 302 for (i=0; i<tbl_info->count;i++) 303 { 304 if ((*value == tbl_info->values[i]) || 305 (settings->flags&F_ALLOW_ARBITRARY_VALS && 306 *value < tbl_info->values[i])) 307 { 308 int index = (i+add)%tbl_info->count; 309 if (repeated && ((i == 0 && previous) || (!previous && i == tbl_info->count -1))) 310 val = *value; 311 else 312 val = tbl_info->values[index]; 313 break; 314 } 315 } 316 *value = val; 317 if (apply && tbl_info->option_callback) 318 tbl_info->option_callback(val); 319 } 320} 321#endif 322 323static int selection_to_val(const struct settings_list *setting, int selection) 324{ 325 /* rockbox: comment 'set but unused' variables 326 int min = 0; 327 */ 328 int max = 0, step = 1; 329 if ((HASFLAG(setting, F_BOOL_SETTING)) || 330 HASFLAG(setting, F_CHOICE_SETTING)) 331 { 332 return selection; 333 } 334 else if (HASFLAG(setting, F_TABLE_SETTING)) 335 { 336 const struct table_setting *info = setting->table_setting; 337 if (HASFLAG(setting, F_ALLOW_ARBITRARY_VALS) && 338 table_setting_array_position != -1 && 339 (selection >= table_setting_array_position)) 340 { 341 if (selection == table_setting_array_position) 342 return table_setting_oldval; 343 return info->values[selection-1]; 344 } 345 else 346 return info->values[selection]; 347 } 348 else if (HASFLAG(setting, F_T_SOUND)) 349 { 350 int setting_id = setting->sound_setting->setting; 351 if(global_settings.list_order == LIST_ORDER_DESCENDING) 352 { 353 step = sound_steps(setting_id); 354 max = (setting_id == SOUND_VOLUME) ? 355 global_settings.volume_limit : sound_max(setting_id); 356 /* min = sound_min(setting_id); */ 357 } 358 else 359 { 360 step = -sound_steps(setting_id); 361 /* min = sound_max(setting_id); */ 362 max = sound_min(setting_id); 363 } 364 } 365 else if (HASFLAG(setting, F_INT_SETTING)) 366 { 367 const struct int_setting *info = setting->int_setting; 368 if(global_settings.list_order == LIST_ORDER_DESCENDING) 369 { 370 /* min = info->min; */ 371 max = info->max; 372 step = info->step; 373 } 374 else 375 { 376 max = info->min; 377 /* min = info->max; */ 378 step = -info->step; 379 } 380 } 381 return max- (selection * step); 382} 383 384static const char * value_setting_get_name_cb(int selected_item, 385 void * data, 386 char *buffer, 387 size_t buffer_len) 388{ 389 selected_item = selection_to_val(data, selected_item); 390 return option_get_valuestring(data, buffer, buffer_len, selected_item); 391} 392 393/* wrapper to convert from int param to bool param in option_screen */ 394static void (*boolfunction)(bool); 395static void bool_funcwrapper(int value) 396{ 397 if (value) 398 boolfunction(true); 399 else 400 boolfunction(false); 401} 402 403static void val_to_selection(const struct settings_list *setting, int oldvalue, 404 int *nb_items, int *selected, 405 void (**function)(int)) 406{ 407 int var_type = setting->flags&F_T_MASK; 408 /* set the number of items and current selection */ 409 if (var_type == F_T_INT || var_type == F_T_UINT) 410 { 411 if (HASFLAG(setting, F_CHOICE_SETTING)) 412 { 413 *nb_items = setting->choice_setting->count; 414 *selected = oldvalue; 415 *function = setting->choice_setting->option_callback; 416 } 417 else if (HASFLAG(setting, F_TABLE_SETTING)) 418 { 419 const struct table_setting *info = setting->table_setting; 420 int i; 421 *nb_items = info->count; 422 *selected = -1; 423 table_setting_array_position = -1; 424 for (i=0;*selected==-1 && i<*nb_items;i++) 425 { 426 if (HASFLAG(setting, F_ALLOW_ARBITRARY_VALS) && 427 (oldvalue < info->values[i])) 428 { 429 table_setting_oldval = oldvalue; 430 table_setting_array_position = i; 431 *selected = i; 432 (*nb_items)++; 433 } 434 else if (oldvalue == info->values[i]) 435 *selected = i; 436 } 437 *function = info->option_callback; 438 } 439 else if (HASFLAG(setting, F_T_SOUND)) 440 { 441 int setting_id = setting->sound_setting->setting; 442 int steps = sound_steps(setting_id); 443 int min = sound_min(setting_id); 444 int max = (setting_id == SOUND_VOLUME) ? 445 global_settings.volume_limit : sound_max(setting_id); 446 *nb_items = (max-min)/steps + 1; 447 if (global_settings.list_order == LIST_ORDER_DESCENDING) 448 *selected = (max - oldvalue) / steps; 449 else 450 *selected = (oldvalue - min) / steps; 451 *function = sound_get_fn(setting_id); 452 } 453 else 454 { 455 const struct int_setting *info = setting->int_setting; 456 int min, max, step; 457 max = info->max; 458 min = info->min; 459 step = info->step; 460 *nb_items = (max-min)/step + 1; 461 if(global_settings.list_order == LIST_ORDER_DESCENDING) 462 *selected = (max - oldvalue) / step; 463 else 464 *selected = (oldvalue - min) / step; 465 *function = info->option_callback; 466 } 467 } 468 else if (var_type == F_T_BOOL) 469 { 470 *selected = oldvalue; 471 *nb_items = 2; 472 boolfunction = setting->bool_setting->option_callback; 473 if (boolfunction) 474 *function = bool_funcwrapper; 475 } 476} 477 478bool option_screen(const struct settings_list *setting, 479 struct viewport parent[NB_SCREENS], 480 bool use_temp_var, const unsigned char* option_title) 481{ 482 int action; 483 bool done = false; 484 struct gui_synclist lists; 485 int oldvalue, nb_items = 0, selected = 0, temp_var; 486 int *variable; 487 bool allow_wrap = (!HASFLAG(setting, F_NO_WRAP)); 488 bool cb_on_select_only = HASFLAG(setting, F_CB_ON_SELECT_ONLY); 489 bool cb_on_changed = HASFLAG(setting, F_CB_ONLY_IF_CHANGED); 490 491 int var_type = setting->flags&F_T_MASK; 492 void (*function)(int) = NULL; 493 const char *title = NULL; 494 if (var_type == F_T_INT || var_type == F_T_UINT) 495 { 496 variable = use_temp_var ? &temp_var: (int*)setting->setting; 497 temp_var = oldvalue = *(int*)setting->setting; 498 } 499 else if (var_type == F_T_BOOL) 500 { 501 /* bools always use the temp variable... 502 if use_temp_var is false it will be copied to setting->setting every change */ 503 variable = &temp_var; 504 temp_var = oldvalue = *(bool*)setting->setting?1:0; 505 } 506 else return false; /* only int/bools can go here */ 507 push_current_activity(ACTIVITY_OPTIONSELECT); 508 gui_synclist_init(&lists, value_setting_get_name_cb, 509 (void*)setting, false, 1, parent); 510 if (setting->lang_id == -1) 511 { 512 title = setting_get_cfgvals(setting); 513 } 514 515 if (!title) 516 title = P2STR(option_title); 517 518 gui_synclist_set_title(&lists, title, Icon_Questionmark); 519 if(global_settings.talk_menu) 520 gui_synclist_set_voice_callback(&lists, option_talk); 521 522 val_to_selection(setting, oldvalue, &nb_items, &selected, &function); 523 gui_synclist_set_nb_items(&lists, nb_items); 524 gui_synclist_select_item(&lists, selected); 525 526 gui_synclist_draw(&lists); 527 /* talk the item */ 528 gui_synclist_speak_item(&lists); 529 while (!done) 530 { 531 /* override user wraparound setting; used mainly by EQ settings. 532 * Not sure this is justified? */ 533 if (!allow_wrap) 534 lists.wraparound = false; 535 536 if (list_do_action(CONTEXT_LIST, HZ, /* HZ so the status bar redraws */ 537 &lists, &action)) 538 { 539 /* setting changed */ 540 selected = gui_synclist_get_sel_pos(&lists); 541 *variable = selection_to_val(setting, selected); 542 if (var_type == F_T_BOOL && !use_temp_var) 543 *(bool*)setting->setting = (*variable==1); 544 } 545 else if (action == ACTION_NONE) 546 continue; 547 else if (action == ACTION_STD_CANCEL || action == ACTION_STD_MENU) 548 { 549 /* setting canceled, restore old value if changed */ 550 if (*variable != oldvalue) 551 { 552 *variable = oldvalue; 553 if (var_type == F_T_BOOL && !use_temp_var) 554 *(bool*)setting->setting = (oldvalue==1); 555 splash(HZ/2, ID2P(LANG_CANCEL)); 556 } 557 done = true; 558 } 559 else if (action == ACTION_STD_CONTEXT) 560 { 561 /* reset setting to default */ 562 reset_setting(setting, variable); 563 if (var_type == F_T_BOOL && !use_temp_var) 564 *(bool*)setting->setting = (*variable==1); 565 val_to_selection(setting, *variable, &nb_items, 566 &selected, &function); 567 gui_synclist_select_item(&lists, selected); 568 gui_synclist_draw(&lists); 569 gui_synclist_speak_item(&lists); 570 } 571 else if (action == ACTION_STD_OK) 572 { 573 /* setting accepted, store now if it used a temp var */ 574 if (use_temp_var) 575 { 576 if (var_type == F_T_INT || var_type == F_T_UINT) 577 *(int*)setting->setting = *variable; 578 else 579 *(bool*)setting->setting = (*variable==1); 580 } 581 settings_save(); 582 done = true; 583 cb_on_select_only = false; /* unset the flag so callback can be called */ 584 } 585 else if(default_event_handler(action) == SYS_USB_CONNECTED) 586 { 587 pop_current_activity(); 588 return true; 589 } 590 591 /* callback */ 592 if (!cb_on_select_only && function) 593 { 594 if (!cb_on_changed || (*variable != oldvalue)) 595 { 596 function(*variable); 597 /* if the volume is changing we need to let the skins know */ 598 if (function == sound_get_fn(SOUND_VOLUME)) 599 global_status.last_volume_change = current_tick; 600 } 601 } 602 603 } 604 pop_current_activity(); 605 return false; 606} 607 608int get_setting_info_for_bar(const struct settings_list *setting, int offset, int *count, int *val) 609{ 610 int var_type = setting->flags&F_T_MASK; 611 void (*function)(int) = NULL; 612 int oldvalue; 613 614 if (var_type == F_T_INT || var_type == F_T_UINT) 615 { 616 if (!(setting->flags&F_EQSETTING) || offset > 2) 617 offset = 0; 618 oldvalue = ((int*)setting->setting)[offset]; 619 } 620 else if (var_type == F_T_BOOL) 621 { 622 oldvalue = *(bool*)setting->setting?1:0; 623 } 624 else 625 { 626 *val = 0; 627 *count = 1; 628 return false; /* only int/bools can go here */ 629 } 630 631 val_to_selection(setting, oldvalue, count, val, &function); 632 return true; 633} 634 635#ifdef HAVE_TOUCHSCREEN 636void update_setting_value_from_touch(const struct settings_list *setting, int offset, int selection) 637{ 638 int new_val = selection_to_val(setting, selection); 639 int var_type = setting->flags&F_T_MASK; 640 641 if (var_type == F_T_INT || var_type == F_T_UINT) 642 { 643 if (!(setting->flags&F_EQSETTING) || offset > 2) 644 offset = 0; 645 ((int*)setting->setting)[offset] = new_val; 646 } 647 else if (var_type == F_T_BOOL) 648 { 649 *(bool*)setting->setting = new_val ? true : false; 650 } 651} 652#endif