1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2019 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "BasicColors.hxx"
22 #include "CustomColors.hxx"
24 #include "util/RuntimeError.hxx"
25 #include "util/StringStrip.hxx"
26 #include "util/Compiler.h"
29 #include "Options.hxx"
38 * Use the terminal's default color.
40 * @see init_pair(3ncurses)
42 static constexpr short COLOR_NONE = -1;
45 * A non-standad magic value which means "inherit this color from the
48 static constexpr short COLOR_INHERIT = -2;
51 * A non-standad magic value which means "inherit attributes from the
54 static constexpr attr_t A_INHERIT = ~attr_t(0);
58 * A name which can be used to address this style from the
61 const char *const name;
65 * Inherit unspecified values from this style. The special
66 * value #Style::DEFAULT means "don't inherit".
71 * The foreground (text) color in "color" mode.
76 * The background (fill) color in "color" mode.
81 * The attributes in "color" mode.
87 * The attributes in "mono" mode.
92 constexpr StyleData(const char *_name, Style,
93 short, short, attr_t, attr_t _mono) noexcept
94 :name(_name), mono(_mono) {}
101 static StyleData styles[size_t(Style::END)] = {
102 /* color pair = field name, color, mono */
104 nullptr, Style::DEFAULT,
105 COLOR_NONE, COLOR_NONE, A_NORMAL,
109 "title", Style::BACKGROUND,
110 COLOR_WHITE, COLOR_BLUE, A_NORMAL,
114 "title-bold", Style::TITLE,
115 COLOR_YELLOW, COLOR_INHERIT, A_BOLD,
119 "line", Style::TITLE,
120 COLOR_WHITE, COLOR_INHERIT, A_NORMAL,
124 "line-bold", Style::LINE,
125 COLOR_INHERIT, COLOR_INHERIT, A_BOLD,
129 "line-flags", Style::LINE,
130 COLOR_GREEN, COLOR_INHERIT, A_BOLD,
134 "list", Style::BACKGROUND,
135 COLOR_WHITE, COLOR_INHERIT, A_NORMAL,
139 "list-bold", Style::LIST,
140 COLOR_INHERIT, COLOR_INHERIT, A_BOLD,
144 "progressbar", Style::STATUS,
145 COLOR_WHITE, COLOR_INHERIT, A_BOLD,
149 "progressbar-background", Style::PROGRESSBAR,
150 COLOR_BLACK, COLOR_INHERIT, A_BOLD,
154 "status-song", Style::BACKGROUND,
155 COLOR_WHITE, COLOR_BLUE, A_NORMAL,
159 "status-state", Style::STATUS,
160 COLOR_GREEN, COLOR_INHERIT, A_BOLD,
164 "status-time", Style::STATUS_BOLD,
165 COLOR_INHERIT, COLOR_INHERIT, A_NORMAL,
169 "alert", Style::STATUS,
170 COLOR_RED, COLOR_INHERIT, A_BOLD,
174 "browser-directory", Style::LIST,
175 COLOR_YELLOW, COLOR_INHERIT, A_INHERIT,
179 "browser-playlist", Style::LIST,
180 COLOR_RED, COLOR_INHERIT, A_INHERIT,
184 "background", Style::DEFAULT,
185 COLOR_NONE, COLOR_BLACK, A_NORMAL,
190 static constexpr auto &
191 GetStyle(Style style) noexcept
193 return styles[size_t(style)];
200 StyleByName(const char *name) noexcept
202 for (size_t i = 1; i < size_t(Style::END); ++i)
203 if (!strcasecmp(styles[i].name, name))
210 colors_update_pair(Style style) noexcept
212 auto &data = GetStyle(style);
214 int fg = data.fg_color;
215 for (Style i = style; fg == COLOR_INHERIT;) {
216 i = GetStyle(i).inherit;
217 assert(i != Style::DEFAULT);
218 fg = GetStyle(i).fg_color;
221 int bg = data.bg_color;
222 for (Style i = style; bg == COLOR_INHERIT;) {
223 i = GetStyle(i).inherit;
224 assert(i != Style::DEFAULT);
225 bg = GetStyle(i).bg_color;
228 /* apply A_INHERIT (modifies the "attr" value, which is
230 for (Style i = style; data.attr == A_INHERIT;) {
231 i = GetStyle(i).inherit;
232 assert(i != Style::DEFAULT);
233 data.attr = GetStyle(i).attr;
236 init_pair(short(style), fg, bg);
243 ParseBackgroundColor(const char *s)
245 short color = ParseColorNameOrNumber(s);
249 if (!strcasecmp(s, "none"))
252 throw FormatRuntimeError("%s: %s", _("Unknown color"), s);
259 ParseStyle(StyleData &d, const char *str)
261 std::string copy(str);
263 for (char *cur = strtok(©.front(), ","); cur != nullptr;
264 cur = strtok(nullptr, ",")) {
266 char *slash = strchr(cur, '/');
267 if (slash != nullptr) {
268 const char *name = slash + 1;
269 d.bg_color = ParseBackgroundColor(name);
277 /* Legacy colors (brightblue,etc) */
278 if (!strncasecmp(cur, "bright", 6)) {
284 short b = ParseColorNameOrNumber(cur);
290 if (!strcasecmp(cur, "none"))
291 d.fg_color = COLOR_NONE;
292 else if (!strcasecmp(cur, "grey") ||
293 !strcasecmp(cur, "gray")) {
294 d.fg_color = COLOR_BLACK;
299 else if (!strcasecmp(cur, "standout"))
300 d.attr |= A_STANDOUT;
301 else if (!strcasecmp(cur, "underline"))
302 d.attr |= A_UNDERLINE;
303 else if (!strcasecmp(cur, "reverse"))
305 else if (!strcasecmp(cur, "blink"))
307 else if (!strcasecmp(cur, "dim"))
309 else if (!strcasecmp(cur, "bold"))
312 throw FormatRuntimeError("%s: %s",
313 _("Unknown color"), str);
318 ModifyStyle(const char *name, const char *value)
320 const auto style = StyleByName(name);
321 if (style == Style::END)
322 throw FormatRuntimeError("%s: %s",
323 _("Unknown color field"), name);
325 auto &data = GetStyle(style);
327 if (style == Style::BACKGROUND) {
328 /* "background" is a special style which all other
329 styles inherit their background color from; if the
330 user configures a color, it will be the background
331 color, but no attributes */
332 data.bg_color = ParseBackgroundColor(value);
336 return ParseStyle(data, value);
340 ApplyStyles() noexcept
343 /* initialize color support */
345 use_default_colors();
346 /* define any custom colors defined in the configuration file */
349 if (options.enable_colors) {
350 for (size_t i = 1; i < size_t(Style::END); ++i)
351 /* update the color pairs */
352 colors_update_pair(Style(i));
354 } else if (options.enable_colors) {
355 fprintf(stderr, "%s\n",
356 _("Terminal lacks color capabilities"));
357 options.enable_colors = false;
363 SelectStyle(WINDOW *w, Style style) noexcept
365 const auto &data = GetStyle(style);
368 if (options.enable_colors) {
370 wattr_set(w, data.attr, short(style), nullptr);
374 (void)wattrset(w, data.mono);