Handle streams separately in tree_add_track()
[cmus.git] / options.c
blobdfc44c2a01d9562fc62790f7d21e2dc32f600be8
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "options.h"
6 #include "list.h"
7 #include "utils.h"
8 #include "xmalloc.h"
9 #include "player.h"
10 #include "buffer.h"
11 #include "ui_curses.h"
12 #include "format_print.h"
13 #include "cmus.h"
14 #include "misc.h"
15 #include "lib.h"
16 #include "pl.h"
17 #include "browser.h"
18 #include "keys.h"
19 #include "filters.h"
20 #include "command_mode.h"
21 #include "file.h"
22 #include "prog.h"
23 #include "output.h"
24 #include "config/datadir.h"
26 #include <stdio.h>
27 #include <errno.h>
29 #if defined(__sun__)
30 #include <ncurses.h>
31 #else
32 #include <curses.h>
33 #endif
35 /* initialized option variables */
37 char *output_plugin = NULL;
38 char *status_display_program = NULL;
39 char *server_password;
40 int auto_reshuffle = 0;
41 int confirm_run = 1;
42 int show_hidden = 0;
43 int show_remaining_time = 0;
44 int set_term_title = 1;
45 int play_library = 1;
46 int repeat = 0;
47 int shuffle = 0;
49 int colors[NR_COLORS] = {
50 -1,
51 -1,
52 COLOR_RED | BRIGHT,
53 COLOR_YELLOW | BRIGHT,
55 COLOR_BLUE,
56 COLOR_WHITE,
57 COLOR_BLACK,
58 COLOR_BLUE,
60 COLOR_WHITE | BRIGHT,
61 -1,
62 COLOR_YELLOW | BRIGHT,
63 COLOR_BLUE,
65 COLOR_YELLOW | BRIGHT,
66 COLOR_BLUE | BRIGHT,
67 -1,
68 COLOR_WHITE,
70 COLOR_YELLOW | BRIGHT,
71 COLOR_WHITE,
72 COLOR_BLACK,
73 COLOR_BLUE,
75 COLOR_WHITE | BRIGHT,
76 COLOR_BLUE,
77 COLOR_WHITE | BRIGHT
80 /* uninitialized option variables */
81 char *track_win_format = NULL;
82 char *track_win_alt_format = NULL;
83 char *list_win_format = NULL;
84 char *list_win_alt_format = NULL;
85 char *current_format = NULL;
86 char *current_alt_format = NULL;
87 char *window_title_format = NULL;
88 char *window_title_alt_format = NULL;
89 char *id3_default_charset = NULL;
91 static void buf_int(char *buf, int val)
93 snprintf(buf, OPTION_MAX_SIZE, "%d", val);
96 static int parse_int(const char *buf, int minval, int maxval, int *val)
98 long int tmp;
100 if (str_to_int(buf, &tmp) == -1 || tmp < minval || tmp > maxval) {
101 error_msg("integer in range %d..%d expected", minval, maxval);
102 return 0;
104 *val = tmp;
105 return 1;
108 int parse_enum(const char *buf, int minval, int maxval, const char * const names[], int *val)
110 long int tmp;
111 int i;
113 if (str_to_int(buf, &tmp) == 0) {
114 if (tmp < minval || tmp > maxval)
115 goto err;
116 *val = tmp;
117 return 1;
120 for (i = 0; names[i]; i++) {
121 if (strcasecmp(buf, names[i]) == 0) {
122 *val = i + minval;
123 return 1;
126 err:
127 error_msg("name or integer in range %d..%d expected", minval, maxval);
128 return 0;
131 static const char * const bool_names[] = {
132 "false", "true", NULL
135 static int parse_bool(const char *buf, int *val)
137 return parse_enum(buf, 0, 1, bool_names, val);
140 /* this is used as id in struct cmus_opt */
141 enum format_id {
142 FMT_CURRENT_ALT,
143 FMT_PLAYLIST_ALT,
144 FMT_TITLE_ALT,
145 FMT_TRACKWIN_ALT,
146 FMT_CURRENT,
147 FMT_PLAYLIST,
148 FMT_TITLE,
149 FMT_TRACKWIN
151 #define NR_FMTS 8
153 /* callbacks for normal options {{{ */
155 #define SECOND_SIZE (44100 * 16 / 8 * 2)
156 static void get_buffer_seconds(unsigned int id, char *buf)
158 buf_int(buf, (player_get_buffer_chunks() * CHUNK_SIZE + SECOND_SIZE / 2) / SECOND_SIZE);
161 static void set_buffer_seconds(unsigned int id, const char *buf)
163 int sec;
165 if (parse_int(buf, 1, 20, &sec))
166 player_set_buffer_chunks((sec * SECOND_SIZE + CHUNK_SIZE / 2) / CHUNK_SIZE);
169 static void get_id3_default_charset(unsigned int id, char *buf)
171 strcpy(buf, id3_default_charset);
174 static void set_id3_default_charset(unsigned int id, const char *buf)
176 free(id3_default_charset);
177 id3_default_charset = xstrdup(buf);
180 static const char * const valid_sort_keys[] = {
181 "artist",
182 "album",
183 "title",
184 "tracknumber",
185 "discnumber",
186 "date",
187 "genre",
188 "comment",
189 "filename",
190 "albumartist",
191 NULL
194 static const char **parse_sort_keys(const char *value)
196 const char **keys;
197 const char *s, *e;
198 int size = 4;
199 int pos = 0;
201 size = 4;
202 keys = xnew(const char *, size);
204 s = value;
205 while (1) {
206 char buf[32];
207 int i, len;
209 while (*s == ' ')
210 s++;
212 e = s;
213 while (*e && *e != ' ')
214 e++;
216 len = e - s;
217 if (len == 0)
218 break;
219 if (len > 31)
220 len = 31;
222 memcpy(buf, s, len);
223 buf[len] = 0;
224 s = e;
226 for (i = 0; ; i++) {
227 if (valid_sort_keys[i] == NULL) {
228 error_msg("invalid sort key '%s'", buf);
229 free(keys);
230 return NULL;
233 if (strcmp(buf, valid_sort_keys[i]) == 0)
234 break;
237 if (pos == size - 1) {
238 size *= 2;
239 keys = xrenew(const char *, keys, size);
241 keys[pos++] = valid_sort_keys[i];
243 keys[pos] = NULL;
244 return keys;
247 static void get_lib_sort(unsigned int id, char *buf)
249 strcpy(buf, lib_editable.sort_str);
252 static void set_lib_sort(unsigned int id, const char *buf)
254 const char **keys = parse_sort_keys(buf);
256 if (keys)
257 editable_set_sort_keys(&lib_editable, keys);
260 static void get_pl_sort(unsigned int id, char *buf)
262 strcpy(buf, pl_editable.sort_str);
265 static void set_pl_sort(unsigned int id, const char *buf)
267 const char **keys = parse_sort_keys(buf);
269 if (keys)
270 editable_set_sort_keys(&pl_editable, keys);
273 static void get_output_plugin(unsigned int id, char *buf)
275 const char *value = op_get_current();
277 if (value)
278 strcpy(buf, value);
281 static void set_output_plugin(unsigned int id, const char *buf)
283 if (ui_initialized) {
284 if (!soft_vol)
285 mixer_close();
286 player_set_op(buf);
287 if (!soft_vol)
288 mixer_open();
289 } else {
290 /* must set it later manually */
291 output_plugin = xstrdup(buf);
295 static void get_passwd(unsigned int id, char *buf)
297 if (server_password)
298 strcpy(buf, server_password);
301 static void set_passwd(unsigned int id, const char *buf)
303 int len = strlen(buf);
305 if (len == 0) {
306 free(server_password);
307 server_password = NULL;
308 } else if (len < 6) {
309 error_msg("unsafe password");
310 } else {
311 free(server_password);
312 server_password = xstrdup(buf);
316 static void get_replaygain_preamp(unsigned int id, char *buf)
318 sprintf(buf, "%f", replaygain_preamp);
321 static void set_replaygain_preamp(unsigned int id, const char *buf)
323 double val;
324 char *end;
326 val = strtod(buf, &end);
327 if (end == buf) {
328 error_msg("floating point number expected (dB)");
329 return;
331 player_set_rg_preamp(val);
334 static void get_softvol_state(unsigned int id, char *buf)
336 sprintf(buf, "%d %d", soft_vol_l, soft_vol_r);
339 static void set_softvol_state(unsigned int id, const char *buf)
341 char buffer[OPTION_MAX_SIZE];
342 char *ptr;
343 long int l, r;
345 strcpy(buffer, buf);
346 ptr = strchr(buffer, ' ');
347 if (!ptr)
348 goto err;
349 while (*ptr == ' ')
350 *ptr++ = 0;
352 if (str_to_int(buffer, &l) == -1 || l < 0 || l > 100)
353 goto err;
354 if (str_to_int(ptr, &r) == -1 || r < 0 || r > 100)
355 goto err;
357 player_set_soft_volume(l, r);
358 return;
359 err:
360 error_msg("two integers in range 0..100 expected");
363 static void get_status_display_program(unsigned int id, char *buf)
365 if (status_display_program)
366 strcpy(buf, status_display_program);
369 static void set_status_display_program(unsigned int id, const char *buf)
371 free(status_display_program);
372 status_display_program = NULL;
373 if (buf[0])
374 status_display_program = xstrdup(buf);
377 /* }}} */
379 /* callbacks for toggle options {{{ */
381 static void get_auto_reshuffle(unsigned int id, char *buf)
383 strcpy(buf, bool_names[auto_reshuffle]);
386 static void set_auto_reshuffle(unsigned int id, const char *buf)
388 parse_bool(buf, &auto_reshuffle);
391 static void toggle_auto_reshuffle(unsigned int id)
393 auto_reshuffle ^= 1;
396 static void get_continue(unsigned int id, char *buf)
398 strcpy(buf, bool_names[player_cont]);
401 static void set_continue(unsigned int id, const char *buf)
403 if (!parse_bool(buf, &player_cont))
404 return;
405 update_statusline();
408 static void toggle_continue(unsigned int id)
410 player_cont ^= 1;
411 update_statusline();
414 static void get_repeat_current(unsigned int id, char *buf)
416 strcpy(buf, bool_names[player_repeat_current]);
419 static void set_repeat_current(unsigned int id, const char *buf)
421 if (!parse_bool(buf, &player_repeat_current))
422 return;
423 update_statusline();
426 static void toggle_repeat_current(unsigned int id)
428 player_repeat_current ^= 1;
429 update_statusline();
432 static void get_confirm_run(unsigned int id, char *buf)
434 strcpy(buf, bool_names[confirm_run]);
437 static void set_confirm_run(unsigned int id, const char *buf)
439 parse_bool(buf, &confirm_run);
442 static void toggle_confirm_run(unsigned int id)
444 confirm_run ^= 1;
447 const char * const view_names[NR_VIEWS + 1] = {
448 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
451 static void get_play_library(unsigned int id, char *buf)
453 strcpy(buf, bool_names[play_library]);
456 static void set_play_library(unsigned int id, const char *buf)
458 if (!parse_bool(buf, &play_library))
459 return;
460 update_statusline();
463 static void toggle_play_library(unsigned int id)
465 play_library ^= 1;
466 update_statusline();
469 static void get_play_sorted(unsigned int id, char *buf)
471 strcpy(buf, bool_names[play_sorted]);
474 static void set_play_sorted(unsigned int id, const char *buf)
476 int tmp;
478 if (!parse_bool(buf, &tmp))
479 return;
481 play_sorted = tmp;
482 update_statusline();
485 static void toggle_play_sorted(unsigned int id)
487 editable_lock();
488 play_sorted = play_sorted ^ 1;
490 /* shuffle would override play_sorted... */
491 if (play_sorted) {
492 /* play_sorted makes no sense in playlist */
493 play_library = 1;
494 shuffle = 0;
497 editable_unlock();
498 update_statusline();
501 const char * const aaa_mode_names[] = {
502 "all", "artist", "album", NULL
505 static void get_aaa_mode(unsigned int id, char *buf)
507 strcpy(buf, aaa_mode_names[aaa_mode]);
510 static void set_aaa_mode(unsigned int id, const char *buf)
512 int tmp;
514 if (!parse_enum(buf, 0, 2, aaa_mode_names, &tmp))
515 return;
517 aaa_mode = tmp;
518 update_statusline();
521 static void toggle_aaa_mode(unsigned int id)
523 editable_lock();
525 /* aaa mode makes no sense in playlist */
526 play_library = 1;
528 aaa_mode++;
529 aaa_mode %= 3;
530 editable_unlock();
531 update_statusline();
534 static void get_repeat(unsigned int id, char *buf)
536 strcpy(buf, bool_names[repeat]);
539 static void set_repeat(unsigned int id, const char *buf)
541 if (!parse_bool(buf, &repeat))
542 return;
543 update_statusline();
546 static void toggle_repeat(unsigned int id)
548 repeat ^= 1;
549 update_statusline();
552 static const char * const replaygain_names[] = {
553 "disabled", "track", "album", NULL
556 static void get_replaygain(unsigned int id, char *buf)
558 strcpy(buf, replaygain_names[replaygain]);
561 static void set_replaygain(unsigned int id, const char *buf)
563 int tmp;
565 if (!parse_enum(buf, 0, 2, replaygain_names, &tmp))
566 return;
567 player_set_rg(tmp);
570 static void toggle_replaygain(unsigned int id)
572 player_set_rg((replaygain + 1) % 3);
575 static void get_replaygain_limit(unsigned int id, char *buf)
577 strcpy(buf, bool_names[replaygain_limit]);
580 static void set_replaygain_limit(unsigned int id, const char *buf)
582 int tmp;
584 if (!parse_bool(buf, &tmp))
585 return;
586 player_set_rg_limit(tmp);
589 static void toggle_replaygain_limit(unsigned int id)
591 player_set_rg_limit(replaygain_limit ^ 1);
594 static void get_show_hidden(unsigned int id, char *buf)
596 strcpy(buf, bool_names[show_hidden]);
599 static void set_show_hidden(unsigned int id, const char *buf)
601 if (!parse_bool(buf, &show_hidden))
602 return;
603 browser_reload();
606 static void toggle_show_hidden(unsigned int id)
608 show_hidden ^= 1;
609 browser_reload();
612 static void get_show_remaining_time(unsigned int id, char *buf)
614 strcpy(buf, bool_names[show_remaining_time]);
617 static void set_show_remaining_time(unsigned int id, const char *buf)
619 if (!parse_bool(buf, &show_remaining_time))
620 return;
621 update_statusline();
624 static void toggle_show_remaining_time(unsigned int id)
626 show_remaining_time ^= 1;
627 update_statusline();
630 static void get_set_term_title(unsigned int id, char *buf)
632 strcpy(buf, bool_names[set_term_title]);
635 static void set_set_term_title(unsigned int id, const char *buf)
637 parse_bool(buf, &set_term_title);
640 static void toggle_set_term_title(unsigned int id)
642 set_term_title ^= 1;
645 static void get_shuffle(unsigned int id, char *buf)
647 strcpy(buf, bool_names[shuffle]);
650 static void set_shuffle(unsigned int id, const char *buf)
652 if (!parse_bool(buf, &shuffle))
653 return;
654 update_statusline();
657 static void toggle_shuffle(unsigned int id)
659 shuffle ^= 1;
660 update_statusline();
663 static void get_softvol(unsigned int id, char *buf)
665 strcpy(buf, bool_names[soft_vol]);
668 static void do_set_softvol(int soft)
670 if (!soft_vol)
671 mixer_close();
672 player_set_soft_vol(soft);
673 if (!soft_vol)
674 mixer_open();
675 update_statusline();
678 static void set_softvol(unsigned int id, const char *buf)
680 int soft;
682 if (!parse_bool(buf, &soft))
683 return;
684 do_set_softvol(soft);
687 static void toggle_softvol(unsigned int id)
689 do_set_softvol(soft_vol ^ 1);
692 /* }}} */
694 /* special callbacks (id set) {{{ */
696 static const char * const color_enum_names[1 + 8 * 2 + 1] = {
697 "default",
698 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
699 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
700 NULL
703 static void get_color(unsigned int id, char *buf)
705 int val;
707 val = colors[id];
708 if (val < 16) {
709 strcpy(buf, color_enum_names[val + 1]);
710 } else {
711 buf_int(buf, val);
715 static void set_color(unsigned int id, const char *buf)
717 int color;
719 if (!parse_enum(buf, -1, 255, color_enum_names, &color))
720 return;
722 colors[id] = color;
723 update_colors();
724 update_full();
727 static char **id_to_fmt(enum format_id id)
729 switch (id) {
730 case FMT_CURRENT_ALT:
731 return &current_alt_format;
732 case FMT_PLAYLIST_ALT:
733 return &list_win_alt_format;
734 case FMT_TITLE_ALT:
735 return &window_title_alt_format;
736 case FMT_TRACKWIN_ALT:
737 return &track_win_alt_format;
738 case FMT_CURRENT:
739 return &current_format;
740 case FMT_PLAYLIST:
741 return &list_win_format;
742 case FMT_TITLE:
743 return &window_title_format;
744 case FMT_TRACKWIN:
745 return &track_win_format;
747 return NULL;
750 static void get_format(unsigned int id, char *buf)
752 char **fmtp = id_to_fmt(id);
754 strcpy(buf, *fmtp);
757 static void set_format(unsigned int id, const char *buf)
759 char **fmtp = id_to_fmt(id);
761 if (!format_valid(buf)) {
762 error_msg("invalid format string");
763 return;
765 free(*fmtp);
766 *fmtp = xstrdup(buf);
768 update_full();
771 /* }}} */
773 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
774 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
776 static const struct {
777 const char *name;
778 opt_get_cb get;
779 opt_set_cb set;
780 opt_toggle_cb toggle;
781 } simple_options[] = {
782 DT(aaa_mode)
783 DT(auto_reshuffle)
784 DN(buffer_seconds)
785 DT(confirm_run)
786 DT(continue)
787 DN(id3_default_charset)
788 DN(lib_sort)
789 DN(output_plugin)
790 DN(passwd)
791 DN(pl_sort)
792 DT(play_library)
793 DT(play_sorted)
794 DT(repeat)
795 DT(repeat_current)
796 DT(replaygain)
797 DT(replaygain_limit)
798 DN(replaygain_preamp)
799 DT(show_hidden)
800 DT(show_remaining_time)
801 DT(set_term_title)
802 DT(shuffle)
803 DT(softvol)
804 DN(softvol_state)
805 DN(status_display_program)
806 { NULL, NULL, NULL, NULL }
809 static const char * const color_names[NR_COLORS] = {
810 "color_cmdline_bg",
811 "color_cmdline_fg",
812 "color_error",
813 "color_info",
814 "color_separator",
815 "color_statusline_bg",
816 "color_statusline_fg",
817 "color_titleline_bg",
818 "color_titleline_fg",
819 "color_win_bg",
820 "color_win_cur",
821 "color_win_cur_sel_bg",
822 "color_win_cur_sel_fg",
823 "color_win_dir",
824 "color_win_fg",
825 "color_win_inactive_cur_sel_bg",
826 "color_win_inactive_cur_sel_fg",
827 "color_win_inactive_sel_bg",
828 "color_win_inactive_sel_fg",
829 "color_win_sel_bg",
830 "color_win_sel_fg",
831 "color_win_title_bg",
832 "color_win_title_fg"
835 /* default values for the variables which we must initialize but
836 * can't do it statically */
837 static const struct {
838 const char *name;
839 const char *value;
840 } str_defaults[] = {
841 { "altformat_current", " %F " },
842 { "altformat_playlist", " %f%= %d " },
843 { "altformat_title", "%f" },
844 { "altformat_trackwin", " %f%= %d " },
845 { "format_current", " %a - %l - %02n. %t%= %y " },
846 { "format_playlist", " %-20a %02n. %t%= %y %d " },
847 { "format_title", "%a - %l - %t (%y)" },
848 { "format_trackwin", " %02n. %t%= %y %d " },
850 { "lib_sort" , "artist album discnumber tracknumber title filename" },
851 { "pl_sort", "" },
852 { "id3_default_charset","ISO-8859-1" },
853 { NULL, NULL }
856 LIST_HEAD(option_head);
857 int nr_options = 0;
859 void option_add(const char *name, unsigned int id, opt_get_cb get,
860 opt_set_cb set, opt_toggle_cb toggle)
862 struct cmus_opt *opt = xnew(struct cmus_opt, 1);
863 struct list_head *item;
865 opt->name = name;
866 opt->id = id;
867 opt->get = get;
868 opt->set = set;
869 opt->toggle = toggle;
871 item = option_head.next;
872 while (item != &option_head) {
873 struct cmus_opt *o = container_of(item, struct cmus_opt, node);
875 if (strcmp(name, o->name) < 0)
876 break;
877 item = item->next;
879 /* add before item */
880 list_add_tail(&opt->node, item);
881 nr_options++;
884 struct cmus_opt *option_find(const char *name)
886 struct cmus_opt *opt;
888 list_for_each_entry(opt, &option_head, node) {
889 if (strcmp(name, opt->name) == 0)
890 return opt;
892 error_msg("no such option %s", name);
893 return NULL;
896 void option_set(const char *name, const char *value)
898 struct cmus_opt *opt = option_find(name);
900 if (opt)
901 opt->set(opt->id, value);
904 void options_add(void)
906 int i;
908 for (i = 0; simple_options[i].name; i++)
909 option_add(simple_options[i].name, 0, simple_options[i].get,
910 simple_options[i].set, simple_options[i].toggle);
912 for (i = 0; i < NR_FMTS; i++)
913 option_add(str_defaults[i].name, i, get_format, set_format, NULL);
915 for (i = 0; i < NR_COLORS; i++)
916 option_add(color_names[i], i, get_color, set_color, NULL);
918 op_add_options();
921 static int handle_line(void *data, const char *line)
923 run_command(line);
924 return 0;
927 int source_file(const char *filename)
929 return file_for_each_line(filename, handle_line, NULL);
932 void options_load(void)
934 char filename[512];
935 int i;
937 /* initialize those that can't be statically initialized */
938 for (i = 0; str_defaults[i].name; i++)
939 option_set(str_defaults[i].name, str_defaults[i].value);
941 /* load autosave config */
942 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
943 if (source_file(filename) == -1) {
944 const char *def = DATADIR "/cmus/rc";
946 if (errno != ENOENT)
947 error_msg("loading %s: %s", filename, strerror(errno));
949 /* load defaults */
950 if (source_file(def) == -1)
951 die_errno("loading %s", def);
954 /* load optional static config */
955 snprintf(filename, sizeof(filename), "%s/rc", cmus_config_dir);
956 if (source_file(filename) == -1) {
957 if (errno != ENOENT)
958 error_msg("loading %s: %s", filename, strerror(errno));
962 void options_exit(void)
964 struct cmus_opt *opt;
965 struct filter_entry *filt;
966 char filename[512];
967 FILE *f;
968 int i;
970 snprintf(filename, sizeof(filename), "%s/autosave", cmus_config_dir);
971 f = fopen(filename, "w");
972 if (f == NULL) {
973 warn_errno("creating %s", filename);
974 return;
977 /* save options */
978 list_for_each_entry(opt, &option_head, node) {
979 char buf[OPTION_MAX_SIZE];
981 buf[0] = 0;
982 opt->get(opt->id, buf);
983 fprintf(f, "set %s=%s\n", opt->name, buf);
986 /* save key bindings */
987 for (i = 0; i < NR_CTXS; i++) {
988 struct binding *b = key_bindings[i];
990 while (b) {
991 fprintf(f, "bind %s %s %s\n", key_context_names[i], b->key->name, b->cmd);
992 b = b->next;
996 /* save filters */
997 list_for_each_entry(filt, &filters_head, node)
998 fprintf(f, "fset %s=%s\n", filt->name, filt->filter);
999 fprintf(f, "factivate");
1000 list_for_each_entry(filt, &filters_head, node) {
1001 switch (filt->act_stat) {
1002 case FS_YES:
1003 fprintf(f, " %s", filt->name);
1004 break;
1005 case FS_NO:
1006 fprintf(f, " !%s", filt->name);
1007 break;
1010 fprintf(f, "\n");
1012 fclose(f);