Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a general and per-module default color configuration #284

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions i3status.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
#define CFG_CUSTOM_ALIGN_OPT \
CFG_STR_CB("align", NULL, CFGF_NONE, parse_align)

#define CFG_CUSTOM_DEFAULT_COLOR_OPT \
CFG_STR("color_default", NULL, CFGF_NONE)

#define CFG_COLOR_OPTS(good, degraded, bad) \
CFG_STR("color_good", good, CFGF_NONE) \
, \
CFG_CUSTOM_DEFAULT_COLOR_OPT, \
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 @@ -71,6 +74,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 @@ -153,10 +158,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 @@ -339,6 +348,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_DEFAULT_COLOR_OPT,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand All @@ -351,6 +361,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_DEFAULT_COLOR_OPT,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand All @@ -359,6 +370,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_DEFAULT_COLOR_OPT,
CFG_CUSTOM_MIN_WIDTH_OPT,
CFG_CUSTOM_SEPARATOR_OPT,
CFG_CUSTOM_SEP_BLOCK_WIDTH_OPT,
Expand Down Expand Up @@ -584,7 +596,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 @@ -87,14 +95,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(ctx->json_gen, (const unsigned char *)"color", strlen("color")); \
yajl_gen_string(ctx->json_gen, (const unsigned char *)colorstr, strlen(colorstr)); \
} \
char *_markup = cfg_getstr(cfg_general, "markup"); \
yajl_gen_string(ctx->json_gen, (const unsigned char *)"markup", strlen("markup")); \
yajl_gen_string(ctx->json_gen, (const unsigned char *)_markup, strlen(_markup)); \
yajl_gen_string(ctx->json_gen, (const unsigned char *)"full_text", strlen("full_text")); \
yajl_gen_string(ctx->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 @@ -143,30 +159,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(ctx->json_gen, (const unsigned char *)"color", strlen("color")); \
yajl_gen_string(ctx->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 Down Expand Up @@ -201,8 +193,8 @@ char *sstrdup(const char *str);

/* 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 @@ -445,6 +437,8 @@ void print_file_contents(file_contents_ctx_t *ctx);
/* 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 @@ -144,12 +144,13 @@ read_file uptime {
=== 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 @@ -193,10 +194,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
74 changes: 51 additions & 23 deletions src/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,68 @@
#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). Returns
* NULL if the color is disabled.
*
*/
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 @@ -49,7 +83,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 @@ -61,16 +95,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
14 changes: 5 additions & 9 deletions src/print_battery_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static void add_battery_info(struct battery_info *acc, const struct battery_info

static bool slurp_battery_info(battery_info_ctx_t *ctx, 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 @@ -529,6 +530,7 @@ static bool slurp_battery_info(battery_info_ctx_t *ctx, struct battery_info *bat
static bool slurp_all_batteries(battery_info_ctx_t *ctx, 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 @@ -586,6 +588,7 @@ static bool slurp_all_batteries(battery_info_ctx_t *ctx, struct battery_info *ba

void print_battery_info(battery_info_ctx_t *ctx) {
char *outwalk = ctx->buf;
output_color_t outcolor = COLOR_DEFAULT;
struct battery_info batt_info = {
.full_design = -1,
.full_last = -1,
Expand All @@ -595,7 +598,6 @@ void print_battery_info(battery_info_ctx_t *ctx) {
.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 @@ -658,11 +660,9 @@ void print_battery_info(battery_info_ctx_t *ctx) {

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

Expand Down Expand Up @@ -732,9 +732,5 @@ void print_battery_info(battery_info_ctx_t *ctx) {
free(formatted);
free(untrimmed);

if (colorful_output) {
END_COLOR;
}

OUTPUT_FULL_TEXT(ctx->buf);
}
Loading
Loading