1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2006 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "options.h"
20 #include "list.h"
21 #include "utils.h"
22 #include "xmalloc.h"
23 #include "player.h"
24 #include "buffer.h"
25 #include "ui_curses.h"
26 #include "cmus.h"
27 #include "misc.h"
28 #include "lib.h"
29 #include "pl.h"
30 #include "browser.h"
31 #include "keys.h"
32 #include "filters.h"
33 #include "command_mode.h"
34 #include "file.h"
35 #include "prog.h"
36 #include "output.h"
37 #include "input.h"
38 #include "xstrjoin.h"
39 #include "track_info.h"
40 #include "cache.h"
41 #include "debug.h"
42 #include "discid.h"
43 #include "mpris.h"
44
45 #include <stdio.h>
46 #include <errno.h>
47 #include <strings.h>
48
49 #if defined(__sun__)
50 #include <ncurses.h>
51 #else
52 #include <curses.h>
53 #endif
54
55 /* initialized option variables */
56
57 char *cdda_device = NULL;
58 char *output_plugin = NULL;
59 char *status_display_program = NULL;
60 char *server_password;
61 int auto_reshuffle = 1;
62 int confirm_run = 1;
63 int resume_cmus = 0;
64 int show_hidden = 0;
65 int show_current_bitrate = 0;
66 int show_playback_position = 1;
67 int show_remaining_time = 0;
68 int set_term_title = 1;
69 int wrap_search = 1;
70 int play_library = 1;
71 int repeat = 0;
72 int shuffle = 0;
73 int follow = 0;
74 int display_artist_sort_name;
75 int smart_artist_sort = 1;
76 int scroll_offset = 2;
77 int rewind_offset = 5;
78 int skip_track_info = 0;
79 int auto_expand_albums_follow = 1;
80 int auto_expand_albums_search = 1;
81 int auto_expand_albums_selcur = 1;
82 int show_all_tracks = 1;
83 int mouse = 0;
84 int mpris = 1;
85 int time_show_leading_zero = 1;
86 int start_view = TREE_VIEW;
87 int stop_after_queue = 0;
88 int tree_width_percent = 33;
89 int tree_width_max = 0;
90
91 int colors[NR_COLORS] = {
92 -1,
93 -1,
94 COLOR_RED | BRIGHT,
95 COLOR_YELLOW | BRIGHT,
96
97 COLOR_BLUE,
98 COLOR_WHITE,
99 COLOR_BLACK,
100 COLOR_BLUE,
101
102 COLOR_WHITE | BRIGHT,
103 -1,
104 COLOR_YELLOW | BRIGHT,
105 COLOR_BLUE,
106
107 COLOR_YELLOW | BRIGHT,
108 COLOR_BLUE | BRIGHT,
109 -1,
110 COLOR_WHITE,
111
112 COLOR_YELLOW | BRIGHT,
113 COLOR_WHITE,
114 COLOR_BLACK,
115 COLOR_BLUE,
116
117 COLOR_WHITE | BRIGHT,
118 COLOR_BLUE,
119 COLOR_WHITE | BRIGHT,
120 -1,
121
122 -1,
123 };
124
125 int attrs[NR_ATTRS] = {
126 A_NORMAL,
127 A_NORMAL,
128 A_NORMAL,
129 A_NORMAL,
130 A_NORMAL,
131 A_NORMAL,
132 A_NORMAL,
133 A_NORMAL,
134 A_NORMAL,
135 A_NORMAL,
136 A_BOLD,
137 A_NORMAL,
138 };
139
140 /* uninitialized option variables */
141 char *tree_win_format = NULL;
142 char *tree_win_artist_format = NULL;
143 char *track_win_album_format = NULL;
144 char *track_win_format = NULL;
145 char *track_win_format_va = NULL;
146 char *track_win_alt_format = NULL;
147 char *list_win_format = NULL;
148 char *list_win_format_va = NULL;
149 char *list_win_alt_format = NULL;
150 char *current_format = NULL;
151 char *current_alt_format = NULL;
152 char *statusline_format = NULL;
153 char *window_title_format = NULL;
154 char *window_title_alt_format = NULL;
155 char *id3_default_charset = NULL;
156 char *icecast_default_charset = NULL;
157 char *lib_add_filter = NULL;
158
buf_int(char * buf,int val,size_t size)159 static void buf_int(char *buf, int val, size_t size)
160 {
161 snprintf(buf, size, "%d", val);
162 }
163
parse_int(const char * buf,int minval,int maxval,int * val)164 static int parse_int(const char *buf, int minval, int maxval, int *val)
165 {
166 long int tmp;
167
168 if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
169 error_msg("integer in range %d..%d expected", minval, maxval);
170 return 0;
171 }
172 *val = tmp;
173 return 1;
174 }
175
parse_enum(const char * buf,int minval,int maxval,const char * const names[],int * val)176 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
177 {
178 long int tmp;
179 int i;
180
181 if (str_to_int(buf, &tmp) == 0) {
182 if (tmp < minval || tmp > maxval)
183 goto err;
184 *val = tmp;
185 return 1;
186 }
187
188 for (i = 0; names[i]; i++) {
189 if (strcasecmp(buf, names[i]) == 0) {
190 *val = i + minval;
191 return 1;
192 }
193 }
194 err:
195 error_msg("name or integer in range %d..%d expected", minval, maxval);
196 return 0;
197 }
198
199 static const char * const bool_names[] = {
200 "false", "true", NULL
201 };
202
parse_bool(const char * buf,int * val)203 static int parse_bool(const char *buf, int *val)
204 {
205 return parse_enum(buf, 0, 1, bool_names, val);
206 }
207
208 /* this is used as id in struct cmus_opt */
209 enum format_id {
210 FMT_CURRENT,
211 FMT_CURRENT_ALT,
212 FMT_STATUSLINE,
213 FMT_PLAYLIST,
214 FMT_PLAYLIST_ALT,
215 FMT_PLAYLIST_VA,
216 FMT_TITLE,
217 FMT_TITLE_ALT,
218 FMT_TRACKWIN,
219 FMT_TRACKWIN_ALBUM,
220 FMT_TRACKWIN_ALT,
221 FMT_TRACKWIN_VA,
222 FMT_TREEWIN,
223 FMT_TREEWIN_ARTIST,
224
225 NR_FMTS
226 };
227
228 /* default values for the variables which we must initialize but
229 * can't do it statically */
230 static const struct {
231 const char *name;
232 const char *value;
233 } str_defaults[] = {
234 [FMT_CURRENT_ALT] = { "altformat_current" , " %F " },
235 [FMT_CURRENT] = { "format_current" , " %a - %l -%3n. %t%= %y " },
236 [FMT_STATUSLINE] = { "format_statusline" ,
237 " %{status} %{?show_playback_position?%{position} %{?duration?/ %{duration} }?%{?duration?%{duration} }}"
238 "- %{total} %{?bpm>0?at %{bpm} BPM }"
239 "%{?volume>=0?vol: %{?lvolume!=rvolume?%{lvolume},%{rvolume} ?%{volume} }}"
240 "%{?stream?buf: %{buffer} }"
241 "%{?show_current_bitrate & bitrate>=0? %{bitrate} kbps }"
242 "%="
243 "%{?repeat_current?repeat current?%{?play_library?%{playlist_mode} from %{?play_sorted?sorted }library?playlist}}"
244 " | %1{continue}%1{follow}%1{repeat}%1{shuffle} "
245 },
246 [FMT_PLAYLIST_ALT] = { "altformat_playlist" , " %f%= %d " },
247 [FMT_PLAYLIST] = { "format_playlist" , " %-21%a %3n. %t%= %y %d %{?X!=0?%3X ? }" },
248 [FMT_PLAYLIST_VA] = { "format_playlist_va" , " %-21%A %3n. %t (%a)%= %y %d %{?X!=0?%3X ? }" },
249 [FMT_TITLE_ALT] = { "altformat_title" , "%f" },
250 [FMT_TITLE] = { "format_title" , "%a - %l - %t (%y)" },
251 [FMT_TRACKWIN_ALBUM] = { "format_trackwin_album" , " %l %= %{albumduration} " },
252 [FMT_TRACKWIN_ALT] = { "altformat_trackwin" , " %f%= %d " },
253 [FMT_TRACKWIN] = { "format_trackwin" , "%3n. %t%= %y %d " },
254 [FMT_TRACKWIN_VA] = { "format_trackwin_va" , "%3n. %t (%a)%= %y %d " },
255 [FMT_TREEWIN] = { "format_treewin" , " %l" },
256 [FMT_TREEWIN_ARTIST] = { "format_treewin_artist" , "%a" },
257
258 [NR_FMTS] =
259
260 { "lib_sort", "albumartist date album discnumber tracknumber title filename play_count" },
261 { "pl_sort", "" },
262 { "id3_default_charset", "ISO-8859-1" },
263 { "icecast_default_charset", "ISO-8859-1" },
264 { NULL, NULL }
265 };
266
267 /* callbacks for normal options {{{ */
268
get_device(void * data,char * buf,size_t size)269 static void get_device(void *data, char *buf, size_t size)
270 {
271 strscpy(buf, cdda_device, size);
272 }
273
set_device(void * data,const char * buf)274 static void set_device(void *data, const char *buf)
275 {
276 free(cdda_device);
277 cdda_device = expand_filename(buf);
278 }
279
280 #define SECOND_SIZE (44100 * 16 / 8 * 2)
get_buffer_seconds(void * data,char * buf,size_t size)281 static void get_buffer_seconds(void *data, char *buf, size_t size)
282 {
283 int val = (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) /
284 SECOND_SIZE;
285 buf_int(buf, val, size);
286 }
287
set_buffer_seconds(void * data,const char * buf)288 static void set_buffer_seconds(void *data, const char *buf)
289 {
290 int sec;
291
292 if (parse_int(buf, 1, 300, &sec))
293 player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
294 }
295
get_scroll_offset(void * data,char * buf,size_t size)296 static void get_scroll_offset(void *data, char *buf, size_t size)
297 {
298 buf_int(buf, scroll_offset, size);
299 }
300
set_scroll_offset(void * data,const char * buf)301 static void set_scroll_offset(void *data, const char *buf)
302 {
303 int offset;
304
305 if (parse_int(buf, 0, 9999, &offset))
306 scroll_offset = offset;
307 }
308
get_rewind_offset(void * data,char * buf,size_t size)309 static void get_rewind_offset(void *data, char *buf, size_t size)
310 {
311 buf_int(buf, rewind_offset, size);
312 }
313
set_rewind_offset(void * data,const char * buf)314 static void set_rewind_offset(void *data, const char *buf)
315 {
316 int offset;
317
318 if (parse_int(buf, -1, 9999, &offset))
319 rewind_offset = offset;
320 }
321
get_id3_default_charset(void * data,char * buf,size_t size)322 static void get_id3_default_charset(void *data, char *buf, size_t size)
323 {
324 strscpy(buf, id3_default_charset, size);
325 }
326
get_icecast_default_charset(void * data,char * buf,size_t size)327 static void get_icecast_default_charset(void *data, char *buf, size_t size)
328 {
329 strscpy(buf, icecast_default_charset, size);
330 }
331
set_id3_default_charset(void * data,const char * buf)332 static void set_id3_default_charset(void *data, const char *buf)
333 {
334 free(id3_default_charset);
335 id3_default_charset = xstrdup(buf);
336 }
337
set_icecast_default_charset(void * data,const char * buf)338 static void set_icecast_default_charset(void *data, const char *buf)
339 {
340 free(icecast_default_charset);
341 icecast_default_charset = xstrdup(buf);
342 }
343
get_lib_sort(void * data,char * buf,size_t size)344 static void get_lib_sort(void *data, char *buf, size_t size)
345 {
346 strscpy(buf, lib_editable.shared->sort_str, size);
347 }
348
set_lib_sort(void * data,const char * buf)349 static void set_lib_sort(void *data, const char *buf)
350 {
351 sort_key_t *keys = parse_sort_keys(buf);
352
353 if (keys) {
354 editable_shared_set_sort_keys(lib_editable.shared, keys);
355 editable_sort(&lib_editable);
356 sort_keys_to_str(keys, lib_editable.shared->sort_str,
357 sizeof(lib_editable.shared->sort_str));
358 }
359 }
360
get_pl_sort(void * data,char * buf,size_t size)361 static void get_pl_sort(void *data, char *buf, size_t size)
362 {
363 pl_get_sort_str(buf, size);
364 }
365
set_pl_sort(void * data,const char * buf)366 static void set_pl_sort(void *data, const char *buf)
367 {
368 pl_set_sort_str(buf);
369 }
370
get_output_plugin(void * data,char * buf,size_t size)371 static void get_output_plugin(void *data, char *buf, size_t size)
372 {
373 const char *value = op_get_current();
374
375 if (value)
376 strscpy(buf, value, size);
377 }
378
set_output_plugin(void * data,const char * buf)379 static void set_output_plugin(void *data, const char *buf)
380 {
381 if (ui_initialized) {
382 if (!soft_vol)
383 mixer_close();
384 player_set_op(buf);
385 if (!soft_vol)
386 mixer_open();
387 } else {
388 /* must set it later manually */
389 output_plugin = xstrdup(buf);
390 }
391 }
392
get_passwd(void * data,char * buf,size_t size)393 static void get_passwd(void *data, char *buf, size_t size)
394 {
395 if (server_password)
396 strscpy(buf, server_password, size);
397 }
398
set_passwd(void * data,const char * buf)399 static void set_passwd(void *data, const char *buf)
400 {
401 int len = strlen(buf);
402
403 if (len == 0) {
404 free(server_password);
405 server_password = NULL;
406 } else if (len < 6) {
407 error_msg("unsafe password");
408 } else {
409 free(server_password);
410 server_password = xstrdup(buf);
411 }
412 }
413
get_replaygain_preamp(void * data,char * buf,size_t size)414 static void get_replaygain_preamp(void *data, char *buf, size_t size)
415 {
416 snprintf(buf, size, "%f", replaygain_preamp);
417 }
418
set_replaygain_preamp(void * data,const char * buf)419 static void set_replaygain_preamp(void *data, const char *buf)
420 {
421 double val;
422 char *end;
423
424 val = strtod(buf, &end);
425 if (end == buf) {
426 error_msg("floating point number expected (dB)");
427 return;
428 }
429 player_set_rg_preamp(val);
430 }
431
get_softvol_state(void * data,char * buf,size_t size)432 static void get_softvol_state(void *data, char *buf, size_t size)
433 {
434 snprintf(buf, size, "%d %d", soft_vol_l, soft_vol_r);
435 }
436
set_softvol_state(void * data,const char * buf)437 static void set_softvol_state(void *data, const char *buf)
438 {
439 char buffer[OPTION_MAX_SIZE];
440 char *ptr;
441 long int l, r;
442
443 strscpy(buffer, buf, sizeof(buffer));
444 ptr = strchr(buffer, ' ');
445 if (!ptr)
446 goto err;
447 while (*ptr == ' ')
448 *ptr++ = 0;
449
450 if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
451 goto err;
452 if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
453 goto err;
454
455 player_set_soft_volume(l, r);
456 return;
457 err:
458 error_msg("two integers in range 0..100 expected");
459 }
460
get_status_display_program(void * data,char * buf,size_t size)461 static void get_status_display_program(void *data, char *buf, size_t size)
462 {
463 if (status_display_program)
464 strscpy(buf, status_display_program, size);
465 }
466
set_status_display_program(void * data,const char * buf)467 static void set_status_display_program(void *data, const char *buf)
468 {
469 free(status_display_program);
470 status_display_program = NULL;
471 if (buf[0])
472 status_display_program = expand_filename(buf);
473 }
474
get_tree_width_percent(void * data,char * buf,size_t size)475 static void get_tree_width_percent(void *data, char *buf, size_t size)
476 {
477 buf_int(buf, tree_width_percent, size);
478 }
479
set_tree_width_percent(void * data,const char * buf)480 static void set_tree_width_percent(void *data, const char *buf)
481 {
482 int percent;
483
484 if (parse_int(buf, 1, 100, &percent))
485 tree_width_percent = percent;
486 update_size();
487 }
488
get_tree_width_max(void * data,char * buf,size_t size)489 static void get_tree_width_max(void *data, char *buf, size_t size)
490 {
491 buf_int(buf, tree_width_max, size);
492 }
493
set_tree_width_max(void * data,const char * buf)494 static void set_tree_width_max(void *data, const char *buf)
495 {
496 int cols;
497
498 if (parse_int(buf, 0, 9999, &cols))
499 tree_width_max = cols;
500 update_size();
501 }
502
503 /* }}} */
504
505 /* callbacks for toggle options {{{ */
506
get_auto_reshuffle(void * data,char * buf,size_t size)507 static void get_auto_reshuffle(void *data, char *buf, size_t size)
508 {
509 strscpy(buf, bool_names[auto_reshuffle], size);
510 }
511
set_auto_reshuffle(void * data,const char * buf)512 static void set_auto_reshuffle(void *data, const char *buf)
513 {
514 parse_bool(buf, &auto_reshuffle);
515 }
516
toggle_auto_reshuffle(void * data)517 static void toggle_auto_reshuffle(void *data)
518 {
519 auto_reshuffle ^= 1;
520 }
521
get_follow(void * data,char * buf,size_t size)522 static void get_follow(void *data, char *buf, size_t size)
523 {
524 strscpy(buf, bool_names[follow], size);
525 }
526
set_follow(void * data,const char * buf)527 static void set_follow(void *data, const char *buf)
528 {
529 if (!parse_bool(buf, &follow))
530 return;
531 update_statusline();
532 }
533
toggle_follow(void * data)534 static void toggle_follow(void *data)
535 {
536 follow ^= 1;
537 update_statusline();
538 }
539
get_continue(void * data,char * buf,size_t size)540 static void get_continue(void *data, char *buf, size_t size)
541 {
542 strscpy(buf, bool_names[player_cont], size);
543 }
544
set_continue(void * data,const char * buf)545 static void set_continue(void *data, const char *buf)
546 {
547 if (!parse_bool(buf, &player_cont))
548 return;
549 update_statusline();
550 }
551
toggle_continue(void * data)552 static void toggle_continue(void *data)
553 {
554 player_cont ^= 1;
555 update_statusline();
556 }
557
get_continue_album(void * data,char * buf,size_t size)558 static void get_continue_album(void *data, char *buf, size_t size)
559 {
560 strscpy(buf, bool_names[player_cont_album], size);
561 }
562
set_continue_album(void * data,const char * buf)563 static void set_continue_album(void *data, const char *buf)
564 {
565 if (!parse_bool(buf, &player_cont_album))
566 return;
567 update_statusline();
568 }
569
toggle_continue_album(void * data)570 static void toggle_continue_album(void *data)
571 {
572 player_cont_album ^= 1;
573 update_statusline();
574 }
575
get_repeat_current(void * data,char * buf,size_t size)576 static void get_repeat_current(void *data, char *buf, size_t size)
577 {
578 strscpy(buf, bool_names[player_repeat_current], size);
579 }
580
set_repeat_current(void * data,const char * buf)581 static void set_repeat_current(void *data, const char *buf)
582 {
583 int old = player_repeat_current;
584 if (!parse_bool(buf, &player_repeat_current))
585 return;
586 if (old != player_repeat_current)
587 mpris_loop_status_changed();
588 update_statusline();
589 }
590
toggle_repeat_current(void * data)591 static void toggle_repeat_current(void *data)
592 {
593 player_repeat_current ^= 1;
594 mpris_loop_status_changed();
595 update_statusline();
596 }
597
get_confirm_run(void * data,char * buf,size_t size)598 static void get_confirm_run(void *data, char *buf, size_t size)
599 {
600 strscpy(buf, bool_names[confirm_run], size);
601 }
602
set_confirm_run(void * data,const char * buf)603 static void set_confirm_run(void *data, const char *buf)
604 {
605 parse_bool(buf, &confirm_run);
606 }
607
toggle_confirm_run(void * data)608 static void toggle_confirm_run(void *data)
609 {
610 confirm_run ^= 1;
611 }
612
613 const char * const view_names[NR_VIEWS + 1] = {
614 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
615 };
616
get_play_library(void * data,char * buf,size_t size)617 static void get_play_library(void *data, char *buf, size_t size)
618 {
619 strscpy(buf, bool_names[play_library], size);
620 }
621
set_play_library(void * data,const char * buf)622 static void set_play_library(void *data, const char *buf)
623 {
624 if (!parse_bool(buf, &play_library))
625 return;
626 update_statusline();
627 }
628
toggle_play_library(void * data)629 static void toggle_play_library(void *data)
630 {
631 play_library ^= 1;
632 update_statusline();
633 }
634
get_play_sorted(void * data,char * buf,size_t size)635 static void get_play_sorted(void *data, char *buf, size_t size)
636 {
637 strscpy(buf, bool_names[play_sorted], size);
638 }
639
set_play_sorted(void * data,const char * buf)640 static void set_play_sorted(void *data, const char *buf)
641 {
642 int tmp;
643
644 if (!parse_bool(buf, &tmp))
645 return;
646
647 play_sorted = tmp;
648
649 update_statusline();
650 }
651
toggle_play_sorted(void * data)652 static void toggle_play_sorted(void *data)
653 {
654 play_sorted = play_sorted ^ 1;
655
656 /* shuffle would override play_sorted... */
657 if (play_sorted) {
658 /* play_sorted makes no sense in playlist */
659 play_library = 1;
660 shuffle = 0;
661 }
662
663 update_statusline();
664 }
665
get_smart_artist_sort(void * data,char * buf,size_t size)666 static void get_smart_artist_sort(void *data, char *buf, size_t size)
667 {
668 strscpy(buf, bool_names[smart_artist_sort], size);
669 }
670
set_smart_artist_sort(void * data,const char * buf)671 static void set_smart_artist_sort(void *data, const char *buf)
672 {
673 if (parse_bool(buf, &smart_artist_sort))
674 tree_sort_artists();
675 }
676
toggle_smart_artist_sort(void * data)677 static void toggle_smart_artist_sort(void *data)
678 {
679 smart_artist_sort ^= 1;
680 tree_sort_artists();
681 }
682
get_display_artist_sort_name(void * data,char * buf,size_t size)683 static void get_display_artist_sort_name(void *data, char *buf, size_t size)
684 {
685 strscpy(buf, bool_names[display_artist_sort_name], size);
686 }
687
set_display_artist_sort_name(void * data,const char * buf)688 static void set_display_artist_sort_name(void *data, const char *buf)
689 {
690 parse_bool(buf, &display_artist_sort_name);
691 lib_tree_win->changed = 1;
692 }
693
toggle_display_artist_sort_name(void * data)694 static void toggle_display_artist_sort_name(void *data)
695 {
696 display_artist_sort_name ^= 1;
697 lib_tree_win->changed = 1;
698 }
699
700 const char * const aaa_mode_names[] = {
701 "all", "artist", "album", NULL
702 };
703
get_aaa_mode(void * data,char * buf,size_t size)704 static void get_aaa_mode(void *data, char *buf, size_t size)
705 {
706 strscpy(buf, aaa_mode_names[aaa_mode], size);
707 }
708
set_aaa_mode(void * data,const char * buf)709 static void set_aaa_mode(void *data, const char *buf)
710 {
711 int tmp;
712
713 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
714 return;
715
716 aaa_mode = tmp;
717 update_statusline();
718 }
719
toggle_aaa_mode(void * data)720 static void toggle_aaa_mode(void *data)
721 {
722 /* aaa mode makes no sense in playlist */
723 play_library = 1;
724
725 aaa_mode++;
726 aaa_mode %= 3;
727 update_statusline();
728 }
729
get_repeat(void * data,char * buf,size_t size)730 static void get_repeat(void *data, char *buf, size_t size)
731 {
732 strscpy(buf, bool_names[repeat], size);
733 }
734
set_repeat(void * data,const char * buf)735 static void set_repeat(void *data, const char *buf)
736 {
737 int old = repeat;
738 if (!parse_bool(buf, &repeat))
739 return;
740 if (!player_repeat_current && old != repeat)
741 mpris_loop_status_changed();
742 update_statusline();
743 }
744
toggle_repeat(void * data)745 static void toggle_repeat(void *data)
746 {
747 repeat ^= 1;
748 if (!player_repeat_current)
749 mpris_loop_status_changed();
750 update_statusline();
751 }
752
753 static const char * const replaygain_names[] = {
754 "disabled", "track", "album", "track-preferred", "album-preferred", NULL
755 };
756
get_replaygain(void * data,char * buf,size_t size)757 static void get_replaygain(void *data, char *buf, size_t size)
758 {
759 strscpy(buf, replaygain_names[replaygain], size);
760 }
761
set_replaygain(void * data,const char * buf)762 static void set_replaygain(void *data, const char *buf)
763 {
764 int tmp;
765
766 if (!parse_enum(buf, 0, 4, replaygain_names, &tmp))
767 return;
768 player_set_rg(tmp);
769 }
770
toggle_replaygain(void * data)771 static void toggle_replaygain(void *data)
772 {
773 player_set_rg((replaygain + 1) % 5);
774 }
775
get_replaygain_limit(void * data,char * buf,size_t size)776 static void get_replaygain_limit(void *data, char *buf, size_t size)
777 {
778 strscpy(buf, bool_names[replaygain_limit], size);
779 }
780
set_replaygain_limit(void * data,const char * buf)781 static void set_replaygain_limit(void *data, const char *buf)
782 {
783 int tmp;
784
785 if (!parse_bool(buf, &tmp))
786 return;
787 player_set_rg_limit(tmp);
788 }
789
toggle_replaygain_limit(void * data)790 static void toggle_replaygain_limit(void *data)
791 {
792 player_set_rg_limit(replaygain_limit ^ 1);
793 }
794
get_resume(void * data,char * buf,size_t size)795 static void get_resume(void *data, char *buf, size_t size)
796 {
797 strscpy(buf, bool_names[resume_cmus], size);
798 }
799
set_resume(void * data,const char * buf)800 static void set_resume(void *data, const char *buf)
801 {
802 parse_bool(buf, &resume_cmus);
803 }
804
toggle_resume(void * data)805 static void toggle_resume(void *data)
806 {
807 resume_cmus ^= 1;
808 }
809
get_show_hidden(void * data,char * buf,size_t size)810 static void get_show_hidden(void *data, char *buf, size_t size)
811 {
812 strscpy(buf, bool_names[show_hidden], size);
813 }
814
set_show_hidden(void * data,const char * buf)815 static void set_show_hidden(void *data, const char *buf)
816 {
817 if (!parse_bool(buf, &show_hidden))
818 return;
819 browser_reload();
820 }
821
toggle_show_hidden(void * data)822 static void toggle_show_hidden(void *data)
823 {
824 show_hidden ^= 1;
825 browser_reload();
826 }
827
828 static void set_show_all_tracks_int(int); /* defined below */
829
get_auto_expand_albums_follow(void * data,char * buf,size_t size)830 static void get_auto_expand_albums_follow(void *data, char *buf, size_t size)
831 {
832 strscpy(buf, bool_names[auto_expand_albums_follow], size);
833 }
834
set_auto_expand_albums_follow_int(int value)835 static void set_auto_expand_albums_follow_int(int value)
836 {
837 auto_expand_albums_follow = !!value;
838 if (!auto_expand_albums_follow && !show_all_tracks)
839 set_show_all_tracks_int(1);
840 }
841
set_auto_expand_albums_follow(void * data,const char * buf)842 static void set_auto_expand_albums_follow(void *data, const char *buf)
843 {
844 int tmp = 0;
845 parse_bool(buf, &tmp);
846 set_auto_expand_albums_follow_int(tmp);
847 }
848
toggle_auto_expand_albums_follow(void * data)849 static void toggle_auto_expand_albums_follow(void *data)
850 {
851 set_auto_expand_albums_follow_int(!auto_expand_albums_follow);
852 }
853
get_auto_expand_albums_search(void * data,char * buf,size_t size)854 static void get_auto_expand_albums_search(void *data, char *buf, size_t size)
855 {
856 strscpy(buf, bool_names[auto_expand_albums_search], size);
857 }
858
set_auto_expand_albums_search_int(int value)859 static void set_auto_expand_albums_search_int(int value)
860 {
861 auto_expand_albums_search = !!value;
862 if (!auto_expand_albums_search && !show_all_tracks)
863 set_show_all_tracks_int(1);
864 }
865
set_auto_expand_albums_search(void * data,const char * buf)866 static void set_auto_expand_albums_search(void *data, const char *buf)
867 {
868 int tmp = 0;
869 parse_bool(buf, &tmp);
870 set_auto_expand_albums_search_int(tmp);
871 }
872
toggle_auto_expand_albums_search(void * data)873 static void toggle_auto_expand_albums_search(void *data)
874 {
875 set_auto_expand_albums_search_int(!auto_expand_albums_search);
876 }
877
get_auto_expand_albums_selcur(void * data,char * buf,size_t size)878 static void get_auto_expand_albums_selcur(void *data, char *buf, size_t size)
879 {
880 strscpy(buf, bool_names[auto_expand_albums_selcur], size);
881 }
882
set_auto_expand_albums_selcur_int(int value)883 static void set_auto_expand_albums_selcur_int(int value)
884 {
885 auto_expand_albums_selcur = !!value;
886 if (!auto_expand_albums_selcur && !show_all_tracks)
887 set_show_all_tracks_int(1);
888 }
889
set_auto_expand_albums_selcur(void * data,const char * buf)890 static void set_auto_expand_albums_selcur(void *data, const char *buf)
891 {
892 int tmp = 0;
893 parse_bool(buf, &tmp);
894 set_auto_expand_albums_selcur_int(tmp);
895 }
896
toggle_auto_expand_albums_selcur(void * data)897 static void toggle_auto_expand_albums_selcur(void *data)
898 {
899 set_auto_expand_albums_selcur_int(!auto_expand_albums_selcur);
900 }
901
902
get_show_all_tracks(void * data,char * buf,size_t size)903 static void get_show_all_tracks(void *data, char *buf, size_t size)
904 {
905 strscpy(buf, bool_names[show_all_tracks], size);
906 }
907
set_show_all_tracks_int(int value)908 static void set_show_all_tracks_int(int value)
909 {
910 value = !!value;
911 if (show_all_tracks == value)
912 return;
913 show_all_tracks = value;
914 if (!show_all_tracks) {
915 if (!auto_expand_albums_follow)
916 set_auto_expand_albums_follow_int(1);
917 if (!auto_expand_albums_search)
918 set_auto_expand_albums_search_int(1);
919 if (!auto_expand_albums_selcur)
920 set_auto_expand_albums_selcur_int(1);
921 }
922 tree_sel_update(0);
923 }
924
set_show_all_tracks(void * data,const char * buf)925 static void set_show_all_tracks(void *data, const char *buf)
926 {
927 int tmp = 0;
928 parse_bool(buf, &tmp);
929 set_show_all_tracks_int(tmp);
930 }
931
toggle_show_all_tracks(void * data)932 static void toggle_show_all_tracks(void *data)
933 {
934 set_show_all_tracks_int(!show_all_tracks);
935 }
936
get_show_current_bitrate(void * data,char * buf,size_t size)937 static void get_show_current_bitrate(void *data, char *buf, size_t size)
938 {
939 strscpy(buf, bool_names[show_current_bitrate], size);
940 }
941
set_show_current_bitrate(void * data,const char * buf)942 static void set_show_current_bitrate(void *data, const char *buf)
943 {
944 if (parse_bool(buf, &show_current_bitrate))
945 update_statusline();
946 }
947
toggle_show_current_bitrate(void * data)948 static void toggle_show_current_bitrate(void *data)
949 {
950 show_current_bitrate ^= 1;
951 update_statusline();
952 }
953
get_show_playback_position(void * data,char * buf,size_t size)954 static void get_show_playback_position(void *data, char *buf, size_t size)
955 {
956 strscpy(buf, bool_names[show_playback_position], size);
957 }
958
set_show_playback_position(void * data,const char * buf)959 static void set_show_playback_position(void *data, const char *buf)
960 {
961 if (!parse_bool(buf, &show_playback_position))
962 return;
963 update_statusline();
964 }
965
toggle_show_playback_position(void * data)966 static void toggle_show_playback_position(void *data)
967 {
968 show_playback_position ^= 1;
969 update_statusline();
970 }
971
get_show_remaining_time(void * data,char * buf,size_t size)972 static void get_show_remaining_time(void *data, char *buf, size_t size)
973 {
974 strscpy(buf, bool_names[show_remaining_time], size);
975 }
976
set_show_remaining_time(void * data,const char * buf)977 static void set_show_remaining_time(void *data, const char *buf)
978 {
979 if (!parse_bool(buf, &show_remaining_time))
980 return;
981 update_statusline();
982 }
983
toggle_show_remaining_time(void * data)984 static void toggle_show_remaining_time(void *data)
985 {
986 show_remaining_time ^= 1;
987 update_statusline();
988 }
989
get_set_term_title(void * data,char * buf,size_t size)990 static void get_set_term_title(void *data, char *buf, size_t size)
991 {
992 strscpy(buf, bool_names[set_term_title], size);
993 }
994
set_set_term_title(void * data,const char * buf)995 static void set_set_term_title(void *data, const char *buf)
996 {
997 parse_bool(buf, &set_term_title);
998 }
999
toggle_set_term_title(void * data)1000 static void toggle_set_term_title(void *data)
1001 {
1002 set_term_title ^= 1;
1003 }
1004
get_shuffle(void * data,char * buf,size_t size)1005 static void get_shuffle(void *data, char *buf, size_t size)
1006 {
1007 strscpy(buf, bool_names[shuffle], size);
1008 }
1009
set_shuffle(void * data,const char * buf)1010 static void set_shuffle(void *data, const char *buf)
1011 {
1012 int old = shuffle;
1013 if (!parse_bool(buf, &shuffle))
1014 return;
1015 if (old != shuffle)
1016 mpris_shuffle_changed();
1017 update_statusline();
1018 }
1019
toggle_shuffle(void * data)1020 static void toggle_shuffle(void *data)
1021 {
1022 shuffle ^= 1;
1023 mpris_shuffle_changed();
1024 update_statusline();
1025 }
1026
get_softvol(void * data,char * buf,size_t size)1027 static void get_softvol(void *data, char *buf, size_t size)
1028 {
1029 strscpy(buf, bool_names[soft_vol], size);
1030 }
1031
do_set_softvol(int soft)1032 static void do_set_softvol(int soft)
1033 {
1034 if (!soft_vol)
1035 mixer_close();
1036 player_set_soft_vol(soft);
1037 if (!soft_vol)
1038 mixer_open();
1039 update_statusline();
1040 }
1041
set_softvol(void * data,const char * buf)1042 static void set_softvol(void *data, const char *buf)
1043 {
1044 int soft;
1045
1046 if (!parse_bool(buf, &soft))
1047 return;
1048 do_set_softvol(soft);
1049 }
1050
toggle_softvol(void * data)1051 static void toggle_softvol(void *data)
1052 {
1053 do_set_softvol(soft_vol ^ 1);
1054 }
1055
get_wrap_search(void * data,char * buf,size_t size)1056 static void get_wrap_search(void *data, char *buf, size_t size)
1057 {
1058 strscpy(buf, bool_names[wrap_search], size);
1059 }
1060
set_wrap_search(void * data,const char * buf)1061 static void set_wrap_search(void *data, const char *buf)
1062 {
1063 parse_bool(buf, &wrap_search);
1064 }
1065
toggle_wrap_search(void * data)1066 static void toggle_wrap_search(void *data)
1067 {
1068 wrap_search ^= 1;
1069 }
1070
get_skip_track_info(void * data,char * buf,size_t size)1071 static void get_skip_track_info(void *data, char *buf, size_t size)
1072 {
1073 strscpy(buf, bool_names[skip_track_info], size);
1074 }
1075
set_skip_track_info(void * data,const char * buf)1076 static void set_skip_track_info(void *data, const char *buf)
1077 {
1078 parse_bool(buf, &skip_track_info);
1079 }
1080
toggle_skip_track_info(void * data)1081 static void toggle_skip_track_info(void *data)
1082 {
1083 skip_track_info ^= 1;
1084 }
1085
update_mouse(void)1086 void update_mouse(void)
1087 {
1088 if (mouse) {
1089 mouseinterval(25);
1090 mousemask(BUTTON_CTRL | BUTTON_ALT
1091 | BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED
1092 | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
1093 | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED
1094 | BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED
1095 | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED
1096 | BUTTON4_PRESSED | BUTTON4_RELEASED | BUTTON4_CLICKED
1097 | BUTTON4_DOUBLE_CLICKED | BUTTON4_TRIPLE_CLICKED
1098 #if NCURSES_MOUSE_VERSION >= 2
1099 | BUTTON5_PRESSED | BUTTON5_RELEASED | BUTTON5_CLICKED
1100 | BUTTON5_DOUBLE_CLICKED | BUTTON5_TRIPLE_CLICKED
1101 #endif
1102 , NULL);
1103 } else {
1104 mousemask(0, NULL);
1105 }
1106 }
1107
get_mouse(void * data,char * buf,size_t size)1108 static void get_mouse(void *data, char *buf, size_t size)
1109 {
1110 strscpy(buf, bool_names[mouse], size);
1111 }
1112
set_mouse(void * data,const char * buf)1113 static void set_mouse(void *data, const char *buf)
1114 {
1115 parse_bool(buf, &mouse);
1116 update_mouse();
1117 }
1118
toggle_mouse(void * data)1119 static void toggle_mouse(void *data)
1120 {
1121 mouse ^= 1;
1122 update_mouse();
1123 }
1124
get_mpris(void * data,char * buf,size_t size)1125 static void get_mpris(void *data, char *buf, size_t size)
1126 {
1127 strscpy(buf, bool_names[mpris], size);
1128 }
1129
set_mpris(void * data,const char * buf)1130 static void set_mpris(void *data, const char *buf)
1131 {
1132 parse_bool(buf, &mpris);
1133 }
1134
toggle_mpris(void * data)1135 static void toggle_mpris(void *data)
1136 {
1137 mpris ^= 1;
1138 }
1139
get_time_show_leading_zero(void * data,char * buf,size_t size)1140 static void get_time_show_leading_zero(void *data, char *buf, size_t size)
1141 {
1142 strscpy(buf, bool_names[time_show_leading_zero], size);
1143 }
1144
set_time_show_leading_zero(void * data,const char * buf)1145 static void set_time_show_leading_zero(void *data, const char *buf)
1146 {
1147 if (!parse_bool(buf, &time_show_leading_zero))
1148 return;
1149 update_statusline();
1150 }
1151
toggle_time_show_leading_zero(void * data)1152 static void toggle_time_show_leading_zero(void *data)
1153 {
1154 time_show_leading_zero ^= 1;
1155 update_statusline();
1156 }
1157
get_lib_add_filter(void * data,char * buf,size_t size)1158 static void get_lib_add_filter(void *data, char *buf, size_t size)
1159 {
1160 strscpy(buf, lib_add_filter ? lib_add_filter : "", size);
1161 }
1162
set_lib_add_filter(void * data,const char * buf)1163 static void set_lib_add_filter(void *data, const char *buf)
1164 {
1165 struct expr *expr = NULL;
1166
1167 if (strlen(buf) != 0) {
1168 /* parse expression if non-empty string given */
1169 expr = expr_parse(buf);
1170
1171 if (!expr)
1172 return;
1173 }
1174
1175 free(lib_add_filter);
1176 lib_add_filter = xstrdup(buf);
1177
1178 lib_set_add_filter(expr);
1179 }
1180
get_stop_after_queue(void * data,char * buf,size_t size)1181 static void get_stop_after_queue(void *data, char *buf, size_t size)
1182 {
1183 strscpy(buf, bool_names[stop_after_queue], size);
1184 }
1185
set_stop_after_queue(void * data,const char * buf)1186 static void set_stop_after_queue(void *data, const char *buf)
1187 {
1188 parse_bool(buf, &stop_after_queue);
1189 }
1190
toggle_stop_after_queue(void * data)1191 static void toggle_stop_after_queue(void *data)
1192 {
1193 stop_after_queue ^= 1;
1194 }
1195
1196 /* }}} */
1197
1198 /* special callbacks (id set) {{{ */
1199
1200 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
1201 "default",
1202 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
1203 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
1204 NULL
1205 };
1206
get_color(void * data,char * buf,size_t size)1207 static void get_color(void *data, char *buf, size_t size)
1208 {
1209 int val = *(int *)data;
1210 if (val < 16) {
1211 strscpy(buf, color_enum_names[val + 1], size);
1212 } else {
1213 buf_int(buf, val, size);
1214 }
1215 }
1216
set_color(void * data,const char * buf)1217 static void set_color(void *data, const char *buf)
1218 {
1219 int color;
1220
1221 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
1222 return;
1223
1224 *(int *)data = color;
1225 update_colors();
1226 update_full();
1227 }
1228
get_start_view(void * data,char * buf,size_t size)1229 static void get_start_view(void *data, char *buf, size_t size)
1230 {
1231 strscpy(buf, view_names[start_view], size);
1232 }
1233
set_start_view(void * data,const char * buf)1234 static void set_start_view(void *data, const char *buf)
1235 {
1236 int view;
1237
1238 if (parse_enum(buf, 0, NR_VIEWS - 1, view_names, &view)) {
1239 start_view = view;
1240 }
1241 }
1242
get_attr(void * data,char * buf,size_t size)1243 static void get_attr(void *data, char *buf, size_t size)
1244 {
1245 int attr = *(int *)data;
1246
1247 if (attr == 0) {
1248 strscpy(buf, "default", size);
1249 return;
1250 }
1251
1252 const char *standout = "";
1253 const char *underline = "";
1254 const char *reverse = "";
1255 const char *blink = "";
1256 const char *bold = "";
1257
1258 if (attr & A_STANDOUT)
1259 standout = "standout|";
1260 if (attr & A_UNDERLINE)
1261 underline = "underline|";
1262 if (attr & A_REVERSE)
1263 reverse = "reverse|";
1264 if (attr & A_BLINK)
1265 blink = "blink|";
1266 if (attr & A_BOLD)
1267 bold = "bold|";
1268
1269 size_t len = snprintf(buf, size, "%s%s%s%s%s", standout, underline, reverse,
1270 blink, bold);
1271
1272 if (0 < len && len < size)
1273 buf[len - 1] = 0;
1274 }
1275
set_attr(void * data,const char * buf)1276 static void set_attr(void *data, const char *buf)
1277 {
1278 int attr = 0;
1279 size_t i = 0;
1280 size_t offset = 0;
1281 size_t length = 0;
1282 char* current;
1283
1284 do {
1285 if (buf[i] == '|' || buf[i] == '\0') {
1286 current = xstrndup(&buf[offset], length);
1287
1288 if (strcmp(current, "default") == 0)
1289 attr |= A_NORMAL;
1290 else if (strcmp(current, "standout") == 0)
1291 attr |= A_STANDOUT;
1292 else if (strcmp(current, "underline") == 0)
1293 attr |= A_UNDERLINE;
1294 else if (strcmp(current, "reverse") == 0)
1295 attr |= A_REVERSE;
1296 else if (strcmp(current, "blink") == 0)
1297 attr |= A_BLINK;
1298 else if (strcmp(current, "bold") == 0)
1299 attr |= A_BOLD;
1300
1301 free(current);
1302
1303 offset = i;
1304 length = -1;
1305 }
1306
1307 i++;
1308 length++;
1309 } while (buf[i - 1] != '\0');
1310
1311 *(int *)data = attr;
1312 update_colors();
1313 update_full();
1314 }
1315
id_to_fmt(enum format_id id)1316 static char **id_to_fmt(enum format_id id)
1317 {
1318 switch (id) {
1319 case FMT_CURRENT_ALT:
1320 return ¤t_alt_format;
1321 case FMT_PLAYLIST_ALT:
1322 return &list_win_alt_format;
1323 case FMT_TITLE_ALT:
1324 return &window_title_alt_format;
1325 case FMT_TRACKWIN_ALT:
1326 return &track_win_alt_format;
1327 case FMT_CURRENT:
1328 return ¤t_format;
1329 case FMT_PLAYLIST:
1330 return &list_win_format;
1331 case FMT_PLAYLIST_VA:
1332 return &list_win_format_va;
1333 case FMT_TITLE:
1334 return &window_title_format;
1335 case FMT_TRACKWIN:
1336 return &track_win_format;
1337 case FMT_TRACKWIN_ALBUM:
1338 return &track_win_album_format;
1339 case FMT_TRACKWIN_VA:
1340 return &track_win_format_va;
1341 case FMT_TREEWIN:
1342 return &tree_win_format;
1343 case FMT_TREEWIN_ARTIST:
1344 return &tree_win_artist_format;
1345 case FMT_STATUSLINE:
1346 return &statusline_format;
1347 default:
1348 die("unhandled format code: %d\n", id);
1349 }
1350 return NULL;
1351 }
1352
get_format(void * data,char * buf,size_t size)1353 static void get_format(void *data, char *buf, size_t size)
1354 {
1355 char **fmtp = data;
1356
1357 strscpy(buf, *fmtp, size);
1358 }
1359
set_format(void * data,const char * buf)1360 static void set_format(void *data, const char *buf)
1361 {
1362 char **fmtp = data;
1363
1364 if (!track_format_valid(buf)) {
1365 error_msg("invalid format string");
1366 return;
1367 }
1368 free(*fmtp);
1369 *fmtp = xstrdup(buf);
1370
1371 update_full();
1372 }
1373
1374 /* }}} */
1375
1376 #define DN(name) { #name, get_ ## name, set_ ## name, NULL, 0 },
1377 #define DN_FLAGS(name, flags) { #name, get_ ## name, set_ ## name, NULL, flags },
1378 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name, 0 },
1379
1380 static const struct {
1381 const char *name;
1382 opt_get_cb get;
1383 opt_set_cb set;
1384 opt_toggle_cb toggle;
1385 unsigned int flags;
1386 } simple_options[] = {
1387 DT(aaa_mode)
1388 DT(auto_reshuffle)
1389 DN_FLAGS(device, OPT_PROGRAM_PATH)
1390 DN(buffer_seconds)
1391 DN(scroll_offset)
1392 DN(rewind_offset)
1393 DT(confirm_run)
1394 DT(continue)
1395 DT(continue_album)
1396 DT(smart_artist_sort)
1397 DN(id3_default_charset)
1398 DN(icecast_default_charset)
1399 DN(lib_sort)
1400 DN(output_plugin)
1401 DN(passwd)
1402 DN(pl_sort)
1403 DT(play_library)
1404 DT(play_sorted)
1405 DT(display_artist_sort_name)
1406 DT(repeat)
1407 DT(repeat_current)
1408 DT(replaygain)
1409 DT(replaygain_limit)
1410 DN(replaygain_preamp)
1411 DT(resume)
1412 DT(show_hidden)
1413 DT(auto_expand_albums_follow)
1414 DT(auto_expand_albums_search)
1415 DT(auto_expand_albums_selcur)
1416 DT(show_all_tracks)
1417 DT(show_current_bitrate)
1418 DT(show_playback_position)
1419 DT(show_remaining_time)
1420 DT(set_term_title)
1421 DT(shuffle)
1422 DT(follow)
1423 DT(softvol)
1424 DN(softvol_state)
1425 DN_FLAGS(status_display_program, OPT_PROGRAM_PATH)
1426 DT(wrap_search)
1427 DT(skip_track_info)
1428 DT(mouse)
1429 DT(mpris)
1430 DT(time_show_leading_zero)
1431 DN(lib_add_filter)
1432 DN(start_view)
1433 DT(stop_after_queue)
1434 DN(tree_width_percent)
1435 DN(tree_width_max)
1436 { NULL, NULL, NULL, NULL, 0 }
1437 };
1438
1439 static const char * const color_names[NR_COLORS] = {
1440 "color_cmdline_bg",
1441 "color_cmdline_fg",
1442 "color_error",
1443 "color_info",
1444 "color_separator",
1445 "color_statusline_bg",
1446 "color_statusline_fg",
1447 "color_titleline_bg",
1448 "color_titleline_fg",
1449 "color_win_bg",
1450 "color_win_cur",
1451 "color_win_cur_sel_bg",
1452 "color_win_cur_sel_fg",
1453 "color_win_dir",
1454 "color_win_fg",
1455 "color_win_inactive_cur_sel_bg",
1456 "color_win_inactive_cur_sel_fg",
1457 "color_win_inactive_sel_bg",
1458 "color_win_inactive_sel_fg",
1459 "color_win_sel_bg",
1460 "color_win_sel_fg",
1461 "color_win_title_bg",
1462 "color_win_title_fg",
1463 "color_trackwin_album_bg",
1464 "color_trackwin_album_fg",
1465 };
1466
1467 static const char * const attr_names[NR_ATTRS] = {
1468 "color_cmdline_attr",
1469 "color_statusline_attr",
1470 "color_titleline_attr",
1471 "color_win_attr",
1472 "color_win_cur_sel_attr",
1473 "color_cur_sel_attr",
1474 "color_win_inactive_cur_sel_attr",
1475 "color_win_inactive_sel_attr",
1476 "color_win_sel_attr",
1477 "color_win_title_attr",
1478 "color_trackwin_album_attr",
1479 "color_win_cur_attr",
1480 };
1481
1482 LIST_HEAD(option_head);
1483 int nr_options = 0;
1484
option_add(const char * name,const void * data,opt_get_cb get,opt_set_cb set,opt_toggle_cb toggle,unsigned int flags)1485 void option_add(const char *name, const void *data, opt_get_cb get,
1486 opt_set_cb set, opt_toggle_cb toggle, unsigned int flags)
1487 {
1488 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
1489 struct list_head *item;
1490
1491 opt->name = name;
1492 opt->data = (void *)data;
1493 opt->get = get;
1494 opt->set = set;
1495 opt->toggle = toggle;
1496 opt->flags = flags;
1497
1498 item = option_head.next;
1499 while (item != &option_head) {
1500 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
1501
1502 if (strcmp(name, o->name) < 0)
1503 break;
1504 item = item->next;
1505 }
1506 /* add before item */
1507 list_add_tail(&opt->node, item);
1508 nr_options++;
1509 }
1510
option_find(const char * name)1511 struct cmus_opt *option_find(const char *name)
1512 {
1513 struct cmus_opt *opt = option_find_silent(name);
1514 if (opt == NULL)
1515 error_msg("no such option %s", name);
1516 return opt;
1517 }
1518
option_find_silent(const char * name)1519 struct cmus_opt *option_find_silent(const char *name)
1520 {
1521 struct cmus_opt *opt;
1522
1523 list_for_each_entry(opt, &option_head, node) {
1524 if (strcmp(name, opt->name) == 0)
1525 return opt;
1526 }
1527 return NULL;
1528 }
1529
option_set(const char * name,const char * value)1530 void option_set(const char *name, const char *value)
1531 {
1532 struct cmus_opt *opt = option_find(name);
1533
1534 if (opt)
1535 opt->set(opt->data, value);
1536 }
1537
options_add(void)1538 void options_add(void)
1539 {
1540 int i;
1541
1542 for (i = 0; simple_options[i].name; i++)
1543 option_add(simple_options[i].name, NULL, simple_options[i].get,
1544 simple_options[i].set, simple_options[i].toggle,
1545 simple_options[i].flags);
1546
1547 for (i = 0; i < NR_FMTS; i++)
1548 option_add(str_defaults[i].name, id_to_fmt(i), get_format,
1549 set_format, NULL, 0);
1550
1551 for (i = 0; i < NR_COLORS; i++)
1552 option_add(color_names[i], &colors[i], get_color, set_color,
1553 NULL, 0);
1554
1555 for (i = 0; i < NR_ATTRS; i++)
1556 option_add(attr_names[i], &attrs[i], get_attr, set_attr, NULL,
1557 0);
1558
1559 ip_add_options();
1560 op_add_options();
1561 }
1562
handle_line(void * data,const char * line)1563 static int handle_line(void *data, const char *line)
1564 {
1565 run_command(line);
1566 return 0;
1567 }
1568
source_file(const char * filename)1569 int source_file(const char *filename)
1570 {
1571 return file_for_each_line(filename, handle_line, NULL);
1572 }
1573
options_load(void)1574 void options_load(void)
1575 {
1576 char filename[512];
1577 int i;
1578
1579 /* initialize those that can't be statically initialized */
1580 cdda_device = get_default_cdda_device();
1581 for (i = 0; str_defaults[i].name; i++)
1582 option_set(str_defaults[i].name, str_defaults[i].value);
1583
1584 /* load autosave config */
1585 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
1586 if (source_file(filename) == -1) {
1587 char *def = xstrjoin(cmus_data_dir, "/rc");
1588
1589 if (errno != ENOENT)
1590 error_msg("loading %s: %s", filename, strerror(errno));
1591
1592 /* load defaults */
1593 if (source_file(def) == -1)
1594 die_errno("loading %s", def);
1595
1596 free(def);
1597 }
1598
1599 /* load optional static config */
1600 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
1601 if (source_file(filename) == -1) {
1602 if (errno != ENOENT)
1603 error_msg("loading %s: %s", filename, strerror(errno));
1604 }
1605 }
1606
options_exit(void)1607 void options_exit(void)
1608 {
1609 struct cmus_opt *opt;
1610 struct filter_entry *filt;
1611 char filename_tmp[512];
1612 char filename[512];
1613 FILE *f;
1614 int i;
1615
1616 snprintf(filename_tmp, sizeof(filename_tmp), "%s/autosave.tmp", cmus_config_dir);
1617 f = fopen(filename_tmp, "w");
1618 if (f == NULL) {
1619 warn_errno("creating %s", filename_tmp);
1620 return;
1621 }
1622
1623 /* save options */
1624 list_for_each_entry(opt, &option_head, node) {
1625 char buf[OPTION_MAX_SIZE];
1626
1627 buf[0] = 0;
1628 opt->get(opt->data, buf, OPTION_MAX_SIZE);
1629 fprintf(f, "set %s=%s\n", opt->name, buf);
1630 }
1631
1632 /* save key bindings */
1633 for (i = 0; i < NR_CTXS; i++) {
1634 struct binding *b = key_bindings[i];
1635
1636 while (b) {
1637 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
1638 b = b->next;
1639 }
1640 }
1641
1642 /* save filters */
1643 list_for_each_entry(filt, &filters_head, node)
1644 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
1645 fprintf(f, "factivate");
1646 list_for_each_entry(filt, &filters_head, node) {
1647 switch (filt->act_stat) {
1648 case FS_YES:
1649 fprintf(f, " %s", filt->name);
1650 break;
1651 case FS_NO:
1652 fprintf(f, " !%s", filt->name);
1653 break;
1654 }
1655 }
1656 fprintf(f, "\n");
1657
1658 fclose(f);
1659
1660 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
1661 i = rename(filename_tmp, filename);
1662 if (i)
1663 warn_errno("renaming %s to %s", filename_tmp, filename);
1664 }
1665
1666 struct resume {
1667 enum player_status status;
1668 char *filename;
1669 long int position;
1670 char *lib_filename;
1671 int view;
1672 char *live_filter;
1673 char *browser_dir;
1674 char *marked_pl;
1675 };
1676
handle_resume_line(void * data,const char * line)1677 static int handle_resume_line(void *data, const char *line)
1678 {
1679 struct resume *resume = data;
1680 char *cmd, *arg;
1681
1682 if (!parse_command(line, &cmd, &arg))
1683 return 0;
1684 if (!arg)
1685 goto out;
1686
1687 if (strcmp(cmd, "status") == 0) {
1688 parse_enum(arg, 0, NR_PLAYER_STATUS, player_status_names, (int *) &resume->status);
1689 } else if (strcmp(cmd, "file") == 0) {
1690 free(resume->filename);
1691 resume->filename = xstrdup(unescape(arg));
1692 } else if (strcmp(cmd, "position") == 0) {
1693 str_to_int(arg, &resume->position);
1694 } else if (strcmp(cmd, "lib_file") == 0) {
1695 free(resume->lib_filename);
1696 resume->lib_filename = xstrdup(unescape(arg));
1697 } else if (strcmp(cmd, "view") == 0) {
1698 parse_enum(arg, 0, NR_VIEWS, view_names, &resume->view);
1699 } else if (strcmp(cmd, "live-filter") == 0) {
1700 free(resume->live_filter);
1701 resume->live_filter = xstrdup(unescape(arg));
1702 } else if (strcmp(cmd, "browser-dir") == 0) {
1703 free(resume->browser_dir);
1704 resume->browser_dir = xstrdup(unescape(arg));
1705 } else if (strcmp(cmd, "marked-pl") == 0) {
1706 free(resume->marked_pl);
1707 resume->marked_pl = xstrdup(unescape(arg));
1708 }
1709
1710 free(arg);
1711 out:
1712 free(cmd);
1713 return 0;
1714 }
1715
resume_load(void)1716 void resume_load(void)
1717 {
1718 char filename[512];
1719 struct track_info *ti, *old;
1720 struct resume resume = { .status = PLAYER_STATUS_STOPPED, .view = -1 };
1721
1722 snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
1723 if (file_for_each_line(filename, handle_resume_line, &resume) == -1) {
1724 if (errno != ENOENT)
1725 error_msg("loading %s: %s", filename, strerror(errno));
1726 return;
1727 }
1728 if (resume.view >= 0 && resume.view != cur_view)
1729 set_view(resume.view);
1730 if (resume.lib_filename) {
1731 cache_lock();
1732 ti = old = cache_get_ti(resume.lib_filename, 0);
1733 cache_unlock();
1734 if (ti) {
1735 lib_add_track(ti, NULL);
1736 track_info_unref(ti);
1737 lib_store_cur_track(ti);
1738 track_info_unref(ti);
1739 ti = lib_set_track(lib_find_track(ti));
1740 if (ti) {
1741 BUG_ON(ti != old);
1742 track_info_unref(ti);
1743 tree_sel_current(auto_expand_albums_follow);
1744 sorted_sel_current();
1745 }
1746 }
1747 free(resume.lib_filename);
1748 }
1749 if (resume.filename) {
1750 cache_lock();
1751 ti = cache_get_ti(resume.filename, 0);
1752 cache_unlock();
1753 if (ti) {
1754 player_set_file(ti);
1755 if (resume.status != PLAYER_STATUS_STOPPED)
1756 player_seek(resume.position, 0, resume.status == PLAYER_STATUS_PLAYING);
1757 }
1758 free(resume.filename);
1759 }
1760 if (resume.live_filter) {
1761 filters_set_live(resume.live_filter);
1762 free(resume.live_filter);
1763 }
1764 if (resume.browser_dir) {
1765 browser_chdir(resume.browser_dir);
1766 free(resume.browser_dir);
1767 }
1768 if (resume.marked_pl) {
1769 pl_set_marked_pl_by_name(resume.marked_pl);
1770 free(resume.marked_pl);
1771 }
1772 }
1773
resume_exit(void)1774 void resume_exit(void)
1775 {
1776 char filename_tmp[512];
1777 char filename[512];
1778 struct track_info *ti;
1779 FILE *f;
1780 int rc;
1781
1782 snprintf(filename_tmp, sizeof(filename_tmp), "%s/resume.tmp", cmus_config_dir);
1783 f = fopen(filename_tmp, "w");
1784 if (!f) {
1785 warn_errno("creating %s", filename_tmp);
1786 return;
1787 }
1788
1789 fprintf(f, "status %s\n", player_status_names[player_info.status]);
1790 ti = player_info.ti;
1791 if (ti) {
1792 fprintf(f, "file %s\n", escape(ti->filename));
1793 fprintf(f, "position %d\n", player_info.pos);
1794 }
1795 if (lib_cur_track)
1796 ti = tree_track_info(lib_cur_track);
1797 else
1798 ti = lib_get_cur_stored_track();
1799 if (ti)
1800 fprintf(f, "lib_file %s\n", escape(ti->filename));
1801 fprintf(f, "view %s\n", view_names[cur_view]);
1802 if (lib_live_filter)
1803 fprintf(f, "live-filter %s\n", escape(lib_live_filter));
1804 fprintf(f, "browser-dir %s\n", escape(browser_dir));
1805
1806 fprintf(f, "marked-pl %s\n", escape(pl_marked_pl_name()));
1807
1808 fclose(f);
1809
1810 snprintf(filename, sizeof(filename), "%s/resume", cmus_config_dir);
1811 rc = rename(filename_tmp, filename);
1812 if (rc)
1813 warn_errno("renaming %s to %s", filename_tmp, filename);
1814 }
1815