A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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