2 * Copyright 2006 Timo Hirvonen
11 #include "ui_curses.h"
12 #include "format_print.h"
20 #include "command_mode.h"
24 #include "config/datadir.h"
35 /* initialized option variables */
37 char *output_plugin
= NULL
;
38 char *status_display_program
= NULL
;
39 char *server_password
;
40 int auto_reshuffle
= 0;
43 int show_remaining_time
= 0;
44 int set_term_title
= 1;
48 int pretty_artist_name
;
49 int fuzzy_artist_sort
;
51 int colors
[NR_COLORS
] = {
55 COLOR_YELLOW
| BRIGHT
,
64 COLOR_YELLOW
| BRIGHT
,
67 COLOR_YELLOW
| BRIGHT
,
72 COLOR_YELLOW
| BRIGHT
,
82 /* uninitialized option variables */
83 char *track_win_format
= NULL
;
84 char *track_win_alt_format
= NULL
;
85 char *list_win_format
= NULL
;
86 char *list_win_alt_format
= NULL
;
87 char *current_format
= NULL
;
88 char *current_alt_format
= NULL
;
89 char *window_title_format
= NULL
;
90 char *window_title_alt_format
= NULL
;
91 char *id3_default_charset
= NULL
;
93 static void buf_int(char *buf
, int val
)
95 snprintf(buf
, OPTION_MAX_SIZE
, "%d", val
);
98 static int parse_int(const char *buf
, int minval
, int maxval
, int *val
)
102 if (str_to_int(buf
, &tmp
) == -1 || tmp
< minval
|| tmp
> maxval
) {
103 error_msg("integer in range %d..%d expected", minval
, maxval
);
110 int parse_enum(const char *buf
, int minval
, int maxval
, const char * const names
[], int *val
)
115 if (str_to_int(buf
, &tmp
) == 0) {
116 if (tmp
< minval
|| tmp
> maxval
)
122 for (i
= 0; names
[i
]; i
++) {
123 if (strcasecmp(buf
, names
[i
]) == 0) {
129 error_msg("name or integer in range %d..%d expected", minval
, maxval
);
133 static const char * const bool_names
[] = {
134 "false", "true", NULL
137 static int parse_bool(const char *buf
, int *val
)
139 return parse_enum(buf
, 0, 1, bool_names
, val
);
142 /* this is used as id in struct cmus_opt */
155 /* callbacks for normal options {{{ */
157 #define SECOND_SIZE (44100 * 16 / 8 * 2)
158 static void get_buffer_seconds(unsigned int id
, char *buf
)
160 buf_int(buf
, (player_get_buffer_chunks() * CHUNK_SIZE
+ SECOND_SIZE
/ 2) / SECOND_SIZE
);
163 static void set_buffer_seconds(unsigned int id
, const char *buf
)
167 if (parse_int(buf
, 1, 20, &sec
))
168 player_set_buffer_chunks((sec
* SECOND_SIZE
+ CHUNK_SIZE
/ 2) / CHUNK_SIZE
);
171 static void get_id3_default_charset(unsigned int id
, char *buf
)
173 strcpy(buf
, id3_default_charset
);
176 static void set_id3_default_charset(unsigned int id
, const char *buf
)
178 free(id3_default_charset
);
179 id3_default_charset
= xstrdup(buf
);
182 static const char * const valid_sort_keys
[] = {
196 static const char **parse_sort_keys(const char *value
)
204 keys
= xnew(const char *, size
);
215 while (*e
&& *e
!= ' ')
229 if (valid_sort_keys
[i
] == NULL
) {
230 error_msg("invalid sort key '%s'", buf
);
235 if (strcmp(buf
, valid_sort_keys
[i
]) == 0)
239 if (pos
== size
- 1) {
241 keys
= xrenew(const char *, keys
, size
);
243 keys
[pos
++] = valid_sort_keys
[i
];
249 static void get_lib_sort(unsigned int id
, char *buf
)
251 strcpy(buf
, lib_editable
.sort_str
);
254 static void set_lib_sort(unsigned int id
, const char *buf
)
256 const char **keys
= parse_sort_keys(buf
);
259 editable_set_sort_keys(&lib_editable
, keys
);
262 static void get_pl_sort(unsigned int id
, char *buf
)
264 strcpy(buf
, pl_editable
.sort_str
);
267 static void set_pl_sort(unsigned int id
, const char *buf
)
269 const char **keys
= parse_sort_keys(buf
);
272 editable_set_sort_keys(&pl_editable
, keys
);
275 static void get_output_plugin(unsigned int id
, char *buf
)
277 const char *value
= op_get_current();
283 static void set_output_plugin(unsigned int id
, const char *buf
)
285 if (ui_initialized
) {
292 /* must set it later manually */
293 output_plugin
= xstrdup(buf
);
297 static void get_passwd(unsigned int id
, char *buf
)
300 strcpy(buf
, server_password
);
303 static void set_passwd(unsigned int id
, const char *buf
)
305 int len
= strlen(buf
);
308 free(server_password
);
309 server_password
= NULL
;
310 } else if (len
< 6) {
311 error_msg("unsafe password");
313 free(server_password
);
314 server_password
= xstrdup(buf
);
318 static void get_replaygain_preamp(unsigned int id
, char *buf
)
320 sprintf(buf
, "%f", replaygain_preamp
);
323 static void set_replaygain_preamp(unsigned int id
, const char *buf
)
328 val
= strtod(buf
, &end
);
330 error_msg("floating point number expected (dB)");
333 player_set_rg_preamp(val
);
336 static void get_softvol_state(unsigned int id
, char *buf
)
338 sprintf(buf
, "%d %d", soft_vol_l
, soft_vol_r
);
341 static void set_softvol_state(unsigned int id
, const char *buf
)
343 char buffer
[OPTION_MAX_SIZE
];
348 ptr
= strchr(buffer
, ' ');
354 if (str_to_int(buffer
, &l
) == -1 || l
< 0 || l
> 100)
356 if (str_to_int(ptr
, &r
) == -1 || r
< 0 || r
> 100)
359 player_set_soft_volume(l
, r
);
362 error_msg("two integers in range 0..100 expected");
365 static void get_status_display_program(unsigned int id
, char *buf
)
367 if (status_display_program
)
368 strcpy(buf
, status_display_program
);
371 static void set_status_display_program(unsigned int id
, const char *buf
)
373 free(status_display_program
);
374 status_display_program
= NULL
;
376 status_display_program
= xstrdup(buf
);
381 /* callbacks for toggle options {{{ */
383 static void get_auto_reshuffle(unsigned int id
, char *buf
)
385 strcpy(buf
, bool_names
[auto_reshuffle
]);
388 static void set_auto_reshuffle(unsigned int id
, const char *buf
)
390 parse_bool(buf
, &auto_reshuffle
);
393 static void toggle_auto_reshuffle(unsigned int id
)
398 static void get_continue(unsigned int id
, char *buf
)
400 strcpy(buf
, bool_names
[player_cont
]);
403 static void set_continue(unsigned int id
, const char *buf
)
405 if (!parse_bool(buf
, &player_cont
))
410 static void toggle_continue(unsigned int id
)
416 static void get_repeat_current(unsigned int id
, char *buf
)
418 strcpy(buf
, bool_names
[player_repeat_current
]);
421 static void set_repeat_current(unsigned int id
, const char *buf
)
423 if (!parse_bool(buf
, &player_repeat_current
))
428 static void toggle_repeat_current(unsigned int id
)
430 player_repeat_current
^= 1;
434 static void get_confirm_run(unsigned int id
, char *buf
)
436 strcpy(buf
, bool_names
[confirm_run
]);
439 static void set_confirm_run(unsigned int id
, const char *buf
)
441 parse_bool(buf
, &confirm_run
);
444 static void toggle_confirm_run(unsigned int id
)
449 const char * const view_names
[NR_VIEWS
+ 1] = {
450 "tree", "sorted", "playlist", "queue", "browser", "filters", "settings", NULL
453 static void get_play_library(unsigned int id
, char *buf
)
455 strcpy(buf
, bool_names
[play_library
]);
458 static void set_play_library(unsigned int id
, const char *buf
)
460 if (!parse_bool(buf
, &play_library
))
465 static void toggle_play_library(unsigned int id
)
471 static void get_play_sorted(unsigned int id
, char *buf
)
473 strcpy(buf
, bool_names
[play_sorted
]);
476 static void set_play_sorted(unsigned int id
, const char *buf
)
480 if (!parse_bool(buf
, &tmp
))
487 static void toggle_play_sorted(unsigned int id
)
490 play_sorted
= play_sorted
^ 1;
492 /* shuffle would override play_sorted... */
494 /* play_sorted makes no sense in playlist */
503 static void get_fuzzy_artist_sort(unsigned int id
, char *buf
)
505 strcpy(buf
, bool_names
[fuzzy_artist_sort
]);
508 static void set_fuzzy_artist_sort(unsigned int id
, const char *buf
)
510 if (parse_bool(buf
, &fuzzy_artist_sort
))
514 static void toggle_fuzzy_artist_sort(unsigned int id
)
516 fuzzy_artist_sort
^= 1;
520 static void get_pretty_artist_name(unsigned int id
, char *buf
)
522 strcpy(buf
, bool_names
[pretty_artist_name
]);
525 static void set_pretty_artist_name(unsigned int id
, const char *buf
)
527 parse_bool(buf
, &pretty_artist_name
);
528 lib_tree_win
->changed
= 1;
531 static void toggle_pretty_artist_name(unsigned int id
)
533 pretty_artist_name
^= 1;
534 lib_tree_win
->changed
= 1;
537 const char * const aaa_mode_names
[] = {
538 "all", "artist", "album", NULL
541 static void get_aaa_mode(unsigned int id
, char *buf
)
543 strcpy(buf
, aaa_mode_names
[aaa_mode
]);
546 static void set_aaa_mode(unsigned int id
, const char *buf
)
550 if (!parse_enum(buf
, 0, 2, aaa_mode_names
, &tmp
))
557 static void toggle_aaa_mode(unsigned int id
)
561 /* aaa mode makes no sense in playlist */
570 static void get_repeat(unsigned int id
, char *buf
)
572 strcpy(buf
, bool_names
[repeat
]);
575 static void set_repeat(unsigned int id
, const char *buf
)
577 if (!parse_bool(buf
, &repeat
))
582 static void toggle_repeat(unsigned int id
)
588 static const char * const replaygain_names
[] = {
589 "disabled", "track", "album", NULL
592 static void get_replaygain(unsigned int id
, char *buf
)
594 strcpy(buf
, replaygain_names
[replaygain
]);
597 static void set_replaygain(unsigned int id
, const char *buf
)
601 if (!parse_enum(buf
, 0, 2, replaygain_names
, &tmp
))
606 static void toggle_replaygain(unsigned int id
)
608 player_set_rg((replaygain
+ 1) % 3);
611 static void get_replaygain_limit(unsigned int id
, char *buf
)
613 strcpy(buf
, bool_names
[replaygain_limit
]);
616 static void set_replaygain_limit(unsigned int id
, const char *buf
)
620 if (!parse_bool(buf
, &tmp
))
622 player_set_rg_limit(tmp
);
625 static void toggle_replaygain_limit(unsigned int id
)
627 player_set_rg_limit(replaygain_limit
^ 1);
630 static void get_show_hidden(unsigned int id
, char *buf
)
632 strcpy(buf
, bool_names
[show_hidden
]);
635 static void set_show_hidden(unsigned int id
, const char *buf
)
637 if (!parse_bool(buf
, &show_hidden
))
642 static void toggle_show_hidden(unsigned int id
)
648 static void get_show_remaining_time(unsigned int id
, char *buf
)
650 strcpy(buf
, bool_names
[show_remaining_time
]);
653 static void set_show_remaining_time(unsigned int id
, const char *buf
)
655 if (!parse_bool(buf
, &show_remaining_time
))
660 static void toggle_show_remaining_time(unsigned int id
)
662 show_remaining_time
^= 1;
666 static void get_set_term_title(unsigned int id
, char *buf
)
668 strcpy(buf
, bool_names
[set_term_title
]);
671 static void set_set_term_title(unsigned int id
, const char *buf
)
673 parse_bool(buf
, &set_term_title
);
676 static void toggle_set_term_title(unsigned int id
)
681 static void get_shuffle(unsigned int id
, char *buf
)
683 strcpy(buf
, bool_names
[shuffle
]);
686 static void set_shuffle(unsigned int id
, const char *buf
)
688 if (!parse_bool(buf
, &shuffle
))
693 static void toggle_shuffle(unsigned int id
)
699 static void get_softvol(unsigned int id
, char *buf
)
701 strcpy(buf
, bool_names
[soft_vol
]);
704 static void do_set_softvol(int soft
)
708 player_set_soft_vol(soft
);
714 static void set_softvol(unsigned int id
, const char *buf
)
718 if (!parse_bool(buf
, &soft
))
720 do_set_softvol(soft
);
723 static void toggle_softvol(unsigned int id
)
725 do_set_softvol(soft_vol
^ 1);
730 /* special callbacks (id set) {{{ */
732 static const char * const color_enum_names
[1 + 8 * 2 + 1] = {
734 "black", "red", "green", "yellow", "blue", "magenta", "cyan", "gray",
735 "darkgray", "lightred", "lightgreen", "lightyellow", "lightblue", "lightmagenta", "lightcyan", "white",
739 static void get_color(unsigned int id
, char *buf
)
745 strcpy(buf
, color_enum_names
[val
+ 1]);
751 static void set_color(unsigned int id
, const char *buf
)
755 if (!parse_enum(buf
, -1, 255, color_enum_names
, &color
))
763 static char **id_to_fmt(enum format_id id
)
766 case FMT_CURRENT_ALT
:
767 return ¤t_alt_format
;
768 case FMT_PLAYLIST_ALT
:
769 return &list_win_alt_format
;
771 return &window_title_alt_format
;
772 case FMT_TRACKWIN_ALT
:
773 return &track_win_alt_format
;
775 return ¤t_format
;
777 return &list_win_format
;
779 return &window_title_format
;
781 return &track_win_format
;
786 static void get_format(unsigned int id
, char *buf
)
788 char **fmtp
= id_to_fmt(id
);
793 static void set_format(unsigned int id
, const char *buf
)
795 char **fmtp
= id_to_fmt(id
);
797 if (!format_valid(buf
)) {
798 error_msg("invalid format string");
802 *fmtp
= xstrdup(buf
);
809 #define DN(name) { #name, get_ ## name, set_ ## name, NULL },
810 #define DT(name) { #name, get_ ## name, set_ ## name, toggle_ ## name },
812 static const struct {
816 opt_toggle_cb toggle
;
817 } simple_options
[] = {
823 DT(fuzzy_artist_sort
)
824 DN(id3_default_charset
)
831 DT(pretty_artist_name
)
836 DN(replaygain_preamp
)
838 DT(show_remaining_time
)
843 DN(status_display_program
)
844 { NULL
, NULL
, NULL
, NULL
}
847 static const char * const color_names
[NR_COLORS
] = {
853 "color_statusline_bg",
854 "color_statusline_fg",
855 "color_titleline_bg",
856 "color_titleline_fg",
859 "color_win_cur_sel_bg",
860 "color_win_cur_sel_fg",
863 "color_win_inactive_cur_sel_bg",
864 "color_win_inactive_cur_sel_fg",
865 "color_win_inactive_sel_bg",
866 "color_win_inactive_sel_fg",
869 "color_win_title_bg",
873 /* default values for the variables which we must initialize but
874 * can't do it statically */
875 static const struct {
879 { "altformat_current", " %F " },
880 { "altformat_playlist", " %f%= %d " },
881 { "altformat_title", "%f" },
882 { "altformat_trackwin", " %f%= %d " },
883 { "format_current", " %a - %l - %02n. %t%= %y " },
884 { "format_playlist", " %-20a %02n. %t%= %y %d " },
885 { "format_title", "%a - %l - %t (%y)" },
886 { "format_trackwin", " %02n. %t%= %y %d " },
888 { "lib_sort" , "artist album discnumber tracknumber title filename" },
890 { "id3_default_charset","ISO-8859-1" },
894 LIST_HEAD(option_head
);
897 void option_add(const char *name
, unsigned int id
, opt_get_cb get
,
898 opt_set_cb set
, opt_toggle_cb toggle
)
900 struct cmus_opt
*opt
= xnew(struct cmus_opt
, 1);
901 struct list_head
*item
;
907 opt
->toggle
= toggle
;
909 item
= option_head
.next
;
910 while (item
!= &option_head
) {
911 struct cmus_opt
*o
= container_of(item
, struct cmus_opt
, node
);
913 if (strcmp(name
, o
->name
) < 0)
917 /* add before item */
918 list_add_tail(&opt
->node
, item
);
922 struct cmus_opt
*option_find(const char *name
)
924 struct cmus_opt
*opt
;
926 list_for_each_entry(opt
, &option_head
, node
) {
927 if (strcmp(name
, opt
->name
) == 0)
930 error_msg("no such option %s", name
);
934 void option_set(const char *name
, const char *value
)
936 struct cmus_opt
*opt
= option_find(name
);
939 opt
->set(opt
->id
, value
);
942 void options_add(void)
946 for (i
= 0; simple_options
[i
].name
; i
++)
947 option_add(simple_options
[i
].name
, 0, simple_options
[i
].get
,
948 simple_options
[i
].set
, simple_options
[i
].toggle
);
950 for (i
= 0; i
< NR_FMTS
; i
++)
951 option_add(str_defaults
[i
].name
, i
, get_format
, set_format
, NULL
);
953 for (i
= 0; i
< NR_COLORS
; i
++)
954 option_add(color_names
[i
], i
, get_color
, set_color
, NULL
);
959 static int handle_line(void *data
, const char *line
)
965 int source_file(const char *filename
)
967 return file_for_each_line(filename
, handle_line
, NULL
);
970 void options_load(void)
975 /* initialize those that can't be statically initialized */
976 for (i
= 0; str_defaults
[i
].name
; i
++)
977 option_set(str_defaults
[i
].name
, str_defaults
[i
].value
);
979 /* load autosave config */
980 snprintf(filename
, sizeof(filename
), "%s/autosave", cmus_config_dir
);
981 if (source_file(filename
) == -1) {
982 const char *def
= DATADIR
"/cmus/rc";
985 error_msg("loading %s: %s", filename
, strerror(errno
));
988 if (source_file(def
) == -1)
989 die_errno("loading %s", def
);
992 /* load optional static config */
993 snprintf(filename
, sizeof(filename
), "%s/rc", cmus_config_dir
);
994 if (source_file(filename
) == -1) {
996 error_msg("loading %s: %s", filename
, strerror(errno
));
1000 void options_exit(void)
1002 struct cmus_opt
*opt
;
1003 struct filter_entry
*filt
;
1008 snprintf(filename
, sizeof(filename
), "%s/autosave", cmus_config_dir
);
1009 f
= fopen(filename
, "w");
1011 warn_errno("creating %s", filename
);
1016 list_for_each_entry(opt
, &option_head
, node
) {
1017 char buf
[OPTION_MAX_SIZE
];
1020 opt
->get(opt
->id
, buf
);
1021 fprintf(f
, "set %s=%s\n", opt
->name
, buf
);
1024 /* save key bindings */
1025 for (i
= 0; i
< NR_CTXS
; i
++) {
1026 struct binding
*b
= key_bindings
[i
];
1029 fprintf(f
, "bind %s %s %s\n", key_context_names
[i
], b
->key
->name
, b
->cmd
);
1035 list_for_each_entry(filt
, &filters_head
, node
)
1036 fprintf(f
, "fset %s=%s\n", filt
->name
, filt
->filter
);
1037 fprintf(f
, "factivate");
1038 list_for_each_entry(filt
, &filters_head
, node
) {
1039 switch (filt
->act_stat
) {
1041 fprintf(f
, " %s", filt
->name
);
1044 fprintf(f
, " !%s", filt
->name
);