Skip to content

Commit

Permalink
Add default color and minor color enhancements
Browse files Browse the repository at this point in the history
- Add "color_default" config in general and modules: used as the
  default/fallback color when specified (by default disabled, same as
  before).

- Allow any color to be set to an empty string to disable it (overrides
  the default color, no color will be emitted).

- (code-only) Replace `START_COLOR` and `END_COLOR` by an enum
  `output_color_t` and a variable `outcolor`, only used in
  `OUTPUT_FULL_TEXT` to emit the color over the full text. It makes
  color management in modules a bit easier and better enforces a single
  color emission per-module.

- (code-only) Cache `general.colors` in global variable `enable_colors`.

- Fix per-module color overrides for outputs other than i3bar.
  • Loading branch information
Gravemind committed Nov 25, 2018
1 parent a84ad18 commit 086554d
Show file tree
Hide file tree
Showing 18 changed files with 148 additions and 142 deletions.
22 changes: 19 additions & 3 deletions i3status.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
CFG_STR_CB("align", NULL, CFGF_NONE, parse_align)

#define CFG_COLOR_OPTS(good, degraded, bad) \
CFG_STR("color_good", good, CFGF_NONE) \
CFG_STR("color_default", NULL, CFGF_NONE) \
, \
CFG_STR("color_good", good, CFGF_NONE), \
CFG_STR("color_degraded", degraded, CFGF_NONE), \
CFG_STR("color_bad", bad, CFGF_NONE)

Expand Down Expand Up @@ -77,6 +78,8 @@ output_format_t output_format;

char *pct_mark;

bool enable_colors;

/*
* Set the exit_upon_signal flag, because one cannot do anything in a safe
* manner in a signal handler (e.g. fprintf, which we really want to do for
Expand Down Expand Up @@ -174,10 +177,14 @@ static int parse_min_width(cfg_t *context, cfg_opt_t *option, const char *value,
}

/*
* Validates a color in "#RRGGBB" format
* Validates a color in "#RRGGBB" format, or empty string
*
*/
static int valid_color(const char *value) {
/* allow empty */
if (value == NULL || value[0] == '\0')
return 1;

const int len = strlen(value);

if (output_format == O_LEMONBAR) {
Expand Down Expand Up @@ -389,6 +396,7 @@ int main(int argc, char *argv[]) {
cfg_opt_t time_opts[] = {
CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE),
CFG_CUSTOM_ALIGN_OPT,
CFG_CUSTOM_COLOR_OPTS,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand All @@ -401,6 +409,7 @@ int main(int argc, char *argv[]) {
CFG_STR("format_time", NULL, CFGF_NONE),
CFG_BOOL("hide_if_equals_localtime", false, CFGF_NONE),
CFG_CUSTOM_ALIGN_OPT,
CFG_CUSTOM_COLOR_OPTS,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand All @@ -409,6 +418,7 @@ int main(int argc, char *argv[]) {
cfg_opt_t ddate_opts[] = {
CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE),
CFG_CUSTOM_ALIGN_OPT,
CFG_CUSTOM_COLOR_OPTS,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand Down Expand Up @@ -613,7 +623,13 @@ int main(int argc, char *argv[]) {
if (strcasecmp(separator, "default") == 0)
separator = get_default_separator();

if (!valid_color(cfg_getstr(cfg_general, "color_good")) || !valid_color(cfg_getstr(cfg_general, "color_degraded")) || !valid_color(cfg_getstr(cfg_general, "color_bad")) || !valid_color(cfg_getstr(cfg_general, "color_separator")))
enable_colors = cfg_getbool(cfg_general, "colors");

if (!valid_color(cfg_getstr(cfg_general, "color_default")) ||
!valid_color(cfg_getstr(cfg_general, "color_good")) ||
!valid_color(cfg_getstr(cfg_general, "color_degraded")) ||
!valid_color(cfg_getstr(cfg_general, "color_bad")) ||
!valid_color(cfg_getstr(cfg_general, "color_separator")))
die("Bad color format");

char *markup_str = cfg_getstr(cfg_general, "markup");
Expand Down
48 changes: 21 additions & 27 deletions include/i3status.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ typedef enum {
} markup_format_t;
extern markup_format_t markup_format;

typedef enum {
COLOR_DEFAULT,
COLOR_GOOD,
COLOR_BAD,
COLOR_DEGRADED,
COLOR_SEPARATOR,
} output_color_t;

extern char *pct_mark;

#include <stdbool.h>
Expand Down Expand Up @@ -86,14 +94,22 @@ extern char *pct_mark;
/* Terminate the output buffer here in any case, so that it’s \
* not forgotten in the module */ \
*outwalk = '\0'; \
const char *colorstr = begin_color_str(outcolor, true); \
if (output_format == O_I3BAR) { \
if (colorstr) { \
yajl_gen_string(json_gen, (const unsigned char *)"color", strlen("color")); \
yajl_gen_string(json_gen, (const unsigned char *)colorstr, strlen(colorstr)); \
} \
char *_markup = cfg_getstr(cfg_general, "markup"); \
yajl_gen_string(json_gen, (const unsigned char *)"markup", strlen("markup")); \
yajl_gen_string(json_gen, (const unsigned char *)_markup, strlen(_markup)); \
yajl_gen_string(json_gen, (const unsigned char *)"full_text", strlen("full_text")); \
yajl_gen_string(json_gen, (const unsigned char *)text, strlen(text)); \
} else { \
printf("%s", text); \
if (colorstr) \
printf("%s%s%s", colorstr, text, end_color_str()); \
else \
printf("%s", text); \
} \
} while (0)

Expand Down Expand Up @@ -142,30 +158,6 @@ extern char *pct_mark;
} \
} while (0)

#define START_COLOR(colorstr) \
do { \
if (cfg_getbool(cfg_general, "colors")) { \
const char *_val = NULL; \
if (cfg_section) \
_val = cfg_getstr(cfg_section, colorstr); \
if (!_val) \
_val = cfg_getstr(cfg_general, colorstr); \
if (output_format == O_I3BAR) { \
yajl_gen_string(json_gen, (const unsigned char *)"color", strlen("color")); \
yajl_gen_string(json_gen, (const unsigned char *)_val, strlen(_val)); \
} else { \
outwalk += sprintf(outwalk, "%s", color(colorstr)); \
} \
} \
} while (0)

#define END_COLOR \
do { \
if (cfg_getbool(cfg_general, "colors") && output_format != O_I3BAR) { \
outwalk += sprintf(outwalk, "%s", endcolor()); \
} \
} while (0)

#define INSTANCE(instance) \
do { \
if (output_format == O_I3BAR) { \
Expand All @@ -192,8 +184,8 @@ bool slurp(const char *filename, char *destination, int size);

/* src/output.c */
void print_separator(const char *separator);
char *color(const char *colorstr);
char *endcolor() __attribute__((pure));
const char *begin_color_str(output_color_t outcolor, bool try_cfg_section);
const char *end_color_str() __attribute__((pure));
void reset_cursor(void);
void maybe_escape_markup(char *text, char **buffer);

Expand Down Expand Up @@ -233,6 +225,8 @@ bool pulse_initialize(void);
/* socket file descriptor for general purposes */
extern int general_socket;

extern bool enable_colors;

extern cfg_t *cfg, *cfg_general, *cfg_section;

extern void **cur_instance;
Expand Down
21 changes: 11 additions & 10 deletions man/i3status.man
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ disk "/" {
=== General

The +colors+ directive will disable all colors if you set it to +false+. You can
also specify the colors that will be used to display "good", "degraded" or "bad"
values using the +color_good+, +color_degraded+ or +color_bad+ directives,
respectively. Those directives are only used if color support is not disabled by
the +colors+ directive. The input format for color values is the canonical RGB
hexadecimal triplet (with no separators between the colors), prefixed by a hash
character ("#").
also specify the colors that will be used to display "default", "good",
"degraded" or "bad" values using the +color_default+, +color_good+,
+color_degraded+ or +color_bad+ directives, respectively. Those directives are
only used if color support is not disabled by the +colors+ directive. The input
format for color values is the canonical RGB hexadecimal triplet (with no
separators between the colors) prefixed by a hash character ("#"), or an empty
string to disable the color.

*Example configuration*:
-------------------------------------------------------------
Expand Down Expand Up @@ -177,10 +178,10 @@ none::
Does not use any color codes. Separates values by the pipe symbol by default.
This should be used with i3bar and can be used for custom scripts.

It's also possible to use the color_good, color_degraded, color_bad directives
to define specific colors per module. If one of these directives is defined
in a module section its value will override the value defined in the general
section just for this module.
It's also possible to use the color_default, color_good, color_degraded,
color_bad directives to define specific colors per module. If one of these
directives is defined in a module section its value will override the value
defined in the general section just for this module.

If you don't fancy the vertical separators between modules i3status/i3bar
uses by default, you can employ the +separator+ directive to configure how
Expand Down
73 changes: 50 additions & 23 deletions src/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,67 @@
#include "i3status.h"

/*
* Returns the correct color format for dzen (^fg(color)), xmobar (<fc=color>)
* or lemonbar (%{Fcolor})
* Returns the correct color format for i3bar (color), dzen (^fg(color)), xmobar
* (<fc=color>), lemonbar (%{Fcolor}) or terminal (escape-sequence)
*
*/
char *color(const char *colorstr) {
static char colorbuf[32];
if (!cfg_getbool(cfg_general, "colors")) {
colorbuf[0] = '\0';
return colorbuf;
const char *begin_color_str(output_color_t outcolor, bool try_cfg_section) {
if (!enable_colors || output_format == O_NONE) {
return NULL;
}

const char *color_key = NULL;
switch (outcolor) {
case COLOR_DEFAULT:
color_key = "color_default";
break;
case COLOR_GOOD:
color_key = "color_good";
break;
case COLOR_BAD:
color_key = "color_bad";
break;
case COLOR_DEGRADED:
color_key = "color_degraded";
break;
case COLOR_SEPARATOR:
color_key = "color_separator";
break;
}
if (!color_key)
return NULL;

const char *color = NULL;
if (try_cfg_section && cfg_section)
color = cfg_getstr(cfg_section, color_key);
if (!color)
color = cfg_getstr(cfg_general, color_key);
if (!color || color[0] == '\0')
return NULL;

if (output_format == O_I3BAR)
return color;

static char colorbuf[32];
if (output_format == O_DZEN2)
(void)snprintf(colorbuf, sizeof(colorbuf), "^fg(%s)", cfg_getstr(cfg_general, colorstr));
(void)snprintf(colorbuf, sizeof(colorbuf), "^fg(%s)", color);
else if (output_format == O_XMOBAR)
(void)snprintf(colorbuf, sizeof(colorbuf), "<fc=%s>", cfg_getstr(cfg_general, colorstr));
(void)snprintf(colorbuf, sizeof(colorbuf), "<fc=%s>", color);
else if (output_format == O_LEMONBAR)
(void)snprintf(colorbuf, sizeof(colorbuf), "%%{F%s}", cfg_getstr(cfg_general, colorstr));
(void)snprintf(colorbuf, sizeof(colorbuf), "%%{F%s}", color);
else if (output_format == O_TERM) {
/* The escape-sequence for color is <CSI><col>;1m (bright/bold
* output), where col is a 3-bit rgb-value with b in the
* least-significant bit. We round the given color to the
* nearist 3-bit-depth color and output the escape-sequence */
char *str = cfg_getstr(cfg_general, colorstr);
int col = strtol(str + 1, NULL, 16);
int col = strtol(color + 1, NULL, 16);
int r = (col & (0xFF << 0)) / 0x80;
int g = (col & (0xFF << 8)) / 0x8000;
int b = (col & (0xFF << 16)) / 0x800000;
col = (r << 2) | (g << 1) | b;
(void)snprintf(colorbuf, sizeof(colorbuf), "\033[3%d;1m", col);
} else {
return NULL;
}
return colorbuf;
}
Expand All @@ -47,7 +80,7 @@ char *color(const char *colorstr) {
* Some color formats (xmobar) require to terminate colors again
*
*/
char *endcolor(void) {
const char *end_color_str(void) {
if (output_format == O_XMOBAR)
return "</fc>";
else if (output_format == O_TERM)
Expand All @@ -59,16 +92,10 @@ char *endcolor(void) {
void print_separator(const char *separator) {
if (output_format == O_I3BAR || strlen(separator) == 0)
return;

if (output_format == O_DZEN2)
printf("^fg(%s)%s^fg()", cfg_getstr(cfg_general, "color_separator"), separator);
else if (output_format == O_XMOBAR)
printf("<fc=%s>%s</fc>", cfg_getstr(cfg_general, "color_separator"), separator);
else if (output_format == O_LEMONBAR)
printf("%%{F%s}%s%%{F-}", cfg_getstr(cfg_general, "color_separator"), separator);
else if (output_format == O_TERM)
printf("%s%s%s", color("color_separator"), separator, endcolor());
else if (output_format == O_NONE)
const char *colorstr = begin_color_str(COLOR_SEPARATOR, false);
if (colorstr)
printf("%s%s%s", colorstr, separator, end_color_str());
else
printf("%s", separator);
}

Expand Down
13 changes: 5 additions & 8 deletions src/print_battery_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ static void add_battery_info(struct battery_info *acc, const struct battery_info

static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, int number, const char *path, const char *format_down) {
char *outwalk = buffer;
output_color_t outcolor = COLOR_DEFAULT;

#if defined(LINUX)
char buf[1024];
Expand Down Expand Up @@ -471,6 +472,7 @@ static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen
static bool slurp_all_batteries(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, const char *path, const char *format_down) {
#if defined(LINUX)
char *outwalk = buffer;
output_color_t outcolor = COLOR_DEFAULT;
bool is_found = false;

char *placeholder;
Expand Down Expand Up @@ -529,6 +531,7 @@ static bool slurp_all_batteries(struct battery_info *batt_info, yajl_gen json_ge
void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_unk, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds) {
const char *walk;
char *outwalk = buffer;
output_color_t outcolor = COLOR_DEFAULT;
struct battery_info batt_info = {
.full_design = -1,
.full_last = -1,
Expand All @@ -538,7 +541,6 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
.percentage_remaining = -1,
.status = CS_UNKNOWN,
};
bool colorful_output = false;

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__OpenBSD__)
/* These OSes report battery stats in whole percent. */
Expand Down Expand Up @@ -599,11 +601,9 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char

if (batt_info.status == CS_DISCHARGING && low_threshold > 0) {
if (batt_info.percentage_remaining >= 0 && strcasecmp(threshold_type, "percentage") == 0 && batt_info.percentage_remaining < low_threshold) {
START_COLOR("color_bad");
colorful_output = true;
outcolor = COLOR_BAD;
} else if (batt_info.seconds_remaining >= 0 && strcasecmp(threshold_type, "time") == 0 && batt_info.seconds_remaining < 60 * low_threshold) {
START_COLOR("color_bad");
colorful_output = true;
outcolor = COLOR_BAD;
}
}

Expand Down Expand Up @@ -697,8 +697,5 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
}
}

if (colorful_output)
END_COLOR;

OUTPUT_FULL_TEXT(buffer);
}
10 changes: 2 additions & 8 deletions src/print_cpu_temperature.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ static int read_temperature(char *thermal_zone, temperature_t *temperature) {
*/
void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, const char *format_above_threshold, int max_threshold) {
char *outwalk = buffer;
output_color_t outcolor = COLOR_DEFAULT;
#ifdef THERMAL_ZONE
const char *selected_format = format;
const char *walk;
bool colorful_output = false;
char *thermal_zone;
temperature_t temperature;
temperature.raw_value = 0;
Expand Down Expand Up @@ -241,8 +241,7 @@ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const
goto error;

if (temperature.raw_value >= max_threshold) {
START_COLOR("color_bad");
colorful_output = true;
outcolor = COLOR_BAD;
if (format_above_threshold != NULL)
selected_format = format_above_threshold;
}
Expand All @@ -260,11 +259,6 @@ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const
}
}

if (colorful_output) {
END_COLOR;
colorful_output = false;
}

free(thermal_zone);

OUTPUT_FULL_TEXT(buffer);
Expand Down
Loading

0 comments on commit 086554d

Please sign in to comment.