From dcd0518e25d7aa84a720780cb70b3f8fca867972 Mon Sep 17 00:00:00 2001 From: Kenneth Lyons Date: Mon, 5 Oct 2015 01:10:01 -0700 Subject: [PATCH] Added support for Pango markup. --- i3status.c | 14 +++++++++++-- include/i3status.h | 9 +++++++- man/i3status.man | 19 +++++++++++++++++ src/output.c | 43 +++++++++++++++++++++++++++++++++++++++ src/print_time.c | 25 ++++++++++++++++++++--- src/print_wireless_info.c | 2 +- 6 files changed, 105 insertions(+), 7 deletions(-) diff --git a/i3status.c b/i3status.c index 462721b..3781425 100644 --- a/i3status.c +++ b/i3status.c @@ -295,6 +295,7 @@ int main(int argc, char *argv[]) { CFG_STR("color_separator", "#333333", CFGF_NONE), CFG_INT("interval", 1, CFGF_NONE), CFG_COLOR_OPTS("#00FF00", "#FFFF00", "#FF0000"), + CFG_STR("markup", "none", CFGF_NONE), CFG_END()}; cfg_opt_t run_watch_opts[] = { @@ -365,6 +366,7 @@ int main(int argc, char *argv[]) { cfg_opt_t tztime_opts[] = { CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE), CFG_STR("timezone", "", CFGF_NONE), + CFG_STR("format_time", NULL, CFGF_NONE), CFG_CUSTOM_ALIGN_OPT, CFG_CUSTOM_MIN_WIDTH_OPT, CFG_END()}; @@ -532,6 +534,14 @@ int main(int argc, char *argv[]) { 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"))) die("Bad color format"); + char *markup_str = cfg_getstr(cfg_general, "markup"); + if (strcasecmp(markup_str, "pango") == 0) + markup_format = M_PANGO; + else if (strcasecmp(markup_str, "none") == 0) + markup_format = M_NONE; + else + die("Unknown markup format: \"%s\"\n", markup_str); + #if YAJL_MAJOR >= 2 yajl_gen json_gen = yajl_gen_alloc(NULL); #else @@ -648,13 +658,13 @@ int main(int argc, char *argv[]) { CASE_SEC("time") { SEC_OPEN_MAP("time"); - print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, tv.tv_sec); + print_time(json_gen, buffer, NULL, cfg_getstr(sec, "format"), NULL, NULL, tv.tv_sec); SEC_CLOSE_MAP; } CASE_SEC_TITLE("tztime") { SEC_OPEN_MAP("tztime"); - print_time(json_gen, buffer, title, cfg_getstr(sec, "format"), cfg_getstr(sec, "timezone"), tv.tv_sec); + print_time(json_gen, buffer, title, cfg_getstr(sec, "format"), cfg_getstr(sec, "timezone"), cfg_getstr(sec, "format_time"), tv.tv_sec); SEC_CLOSE_MAP; } diff --git a/include/i3status.h b/include/i3status.h index 037e154..5f65c5e 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -8,6 +8,9 @@ enum { O_DZEN2, O_TERM, O_NONE } output_format; +enum { M_PANGO, + M_NONE } markup_format; + char *pct_mark; #include @@ -78,6 +81,9 @@ char *pct_mark; * not forgotten in the module */ \ *outwalk = '\0'; \ if (output_format == O_I3BAR) { \ + 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 { \ @@ -176,6 +182,7 @@ void print_separator(const char *separator); char *color(const char *colorstr); char *endcolor() __attribute__((pure)); void reset_cursor(void); +void maybe_escape_markup(char *text, char **buffer); /* src/auto_detect_format.c */ char *auto_detect_format(); @@ -193,7 +200,7 @@ const char *first_eth_interface(const net_type_t type); void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down); void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const char *format, const char *format_not_mounted, const char *prefix_type, const char *threshold_type, const double low_threshold); 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_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds); -void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, time_t t); +void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *format_time, time_t t); void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t); const char *get_ip_addr(const char *interface); void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down); diff --git a/man/i3status.man b/man/i3status.man index d550b81..0b5b367 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -184,6 +184,13 @@ format as the separator is drawn by i3bar directly otherwise. For the other output formats, the provided non-empty string will be automatically enclosed with the necessary coloring bits if color support is enabled. +i3bar supports Pango markup, allowing your format strings to specify font +color, size, etc. by setting the +markup+ directive to "pango". Note that the +ampersand ("&"), less-than ("<"), greater-than (">"), single-quote ("'"), and +double-quote (""") characters need to be replaced with "`&`", "`<`", +"`>`", "`'`", and "`"`" respectively. This is done automatically +for generated content (e.g. wireless ESSID, time). + *Example configuration*: ------------------------------------------------------------- general { @@ -417,6 +424,18 @@ in the +tztime+ module. *Example timezone*: +Europe/Berlin+ +If you would like to use markup in this section, there is a separate ++format_time+ option that is automatically escaped. Its output then replaces +%time in the format string. + +*Example configuration (markup)*: +------------------------------------------------------------- +tztime time { + format = "time: %time" + format_time = "%H:%M %Z" +} +------------------------------------------------------------- + === DDate Outputs the current discordian date in user-specified format. See +ddate(1)+ for diff --git a/src/output.c b/src/output.c index f7a8888..1c8c415 100644 --- a/src/output.c +++ b/src/output.c @@ -78,3 +78,46 @@ void print_separator(const char *separator) { void reset_cursor(void) { printf("\033[?25h"); } + +/* + * Escapes ampersand, less-than, greater-than, single-quote, and double-quote + * characters with the corresponding Pango markup strings if markup is enabled. + * See the glib implementation: + * https://git.gnome.org/browse/glib/tree/glib/gmarkup.c?id=03db1f455b4265654e237d2ad55464b4113cba8a#n2142 + * + */ +void maybe_escape_markup(char *text, char **buffer) { + if (markup_format == M_NONE) { + *buffer += sprintf(*buffer, "%s", text); + return; + } + for (; *text != '\0'; text++) { + switch (*text) { + case '&': + *buffer += sprintf(*buffer, "%s", "&"); + break; + case '<': + *buffer += sprintf(*buffer, "%s", "<"); + break; + case '>': + *buffer += sprintf(*buffer, "%s", ">"); + break; + case '\'': + *buffer += sprintf(*buffer, "%s", "'"); + break; + case '"': + *buffer += sprintf(*buffer, "%s", """); + break; + default: + if ((0x1 <= *text && *text <= 0x8) || + (0xb <= *text && *text <= 0xc) || + (0xe <= *text && *text <= 0x1f) || + (0x7f <= *text && *text <= 0x84) || + (0x86 <= *text && *text <= 0x9f)) + *buffer += sprintf(*buffer, "&#x%x;", *text); + else + *(*buffer)++ = *text; + break; + } + } +} diff --git a/src/print_time.c b/src/print_time.c index c70a09c..9fa6642 100644 --- a/src/print_time.c +++ b/src/print_time.c @@ -33,17 +33,36 @@ void set_timezone(const char *tz) { } } -void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, time_t t) { +void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *format_time, time_t t) { + const char *walk; char *outwalk = buffer; struct tm tm; + char timebuf[1024]; if (title != NULL) INSTANCE(title); - /* Convert time and format output. */ set_timezone(tz); localtime_r(&t, &tm); - outwalk += strftime(outwalk, 4095, format, &tm); + + if (format_time == NULL) { + strftime(timebuf, sizeof(timebuf), format, &tm); + maybe_escape_markup(timebuf, &outwalk); + } else { + for (walk = format; *walk != '\0'; walk++) { + if (*walk != '%') { + *(outwalk++) = *walk; + continue; + } + + if (BEGINS_WITH(walk + 1, "time")) { + strftime(timebuf, sizeof(timebuf), format_time, &tm); + maybe_escape_markup(timebuf, &outwalk); + walk += strlen("time"); + } + } + } + *outwalk = '\0'; OUTPUT_FULL_TEXT(buffer); } diff --git a/src/print_wireless_info.c b/src/print_wireless_info.c index aff0438..4f92507 100644 --- a/src/print_wireless_info.c +++ b/src/print_wireless_info.c @@ -519,7 +519,7 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, if (BEGINS_WITH(walk + 1, "essid")) { if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID) - outwalk += sprintf(outwalk, "%s", info.essid); + maybe_escape_markup(info.essid, &outwalk); else *(outwalk++) = '?'; walk += strlen("essid"); -- 2.39.5