diff options
| author | Marco Hunsicker <i3@hunsicker.de> | 2014-03-05 20:53:07 +0100 | 
|---|---|---|
| committer | Michael Stapelberg <michael@stapelberg.de> | 2014-03-07 08:46:44 +0100 | 
| commit | 1de12e7b20e7ce38e2777218f1d922b3255599e1 (patch) | |
| tree | 237106d7c44437c2e477a8fb842828f4ca7bbb87 | |
| parent | 0c2b518b8a30c08a69c07ab6ffeea98eb2d58424 (diff) | |
Support align and min_width module options
This patch enables users to define "align" and "min_width" options
right in the i3status module config sections.
Specifically this patch:
 * Adds macros for the two new options that are used in the option
   definitions. As the min_width option can take either a string or a
   number, a custom type has been added along with a corresponding callback
   function that parses the provided value (and provides input validation).
   The align option also uses a callback for input validation
 * Expands all module config option definitions to include the new
   options
 * Extends the SEC_CLOSE_MAP() macro to generate the JSON for the new
   options as necessary
 * Updates the manpage to explain the new options
| -rw-r--r-- | i3status.c | 83 | ||||
| -rw-r--r-- | include/i3status.h | 24 | ||||
| -rw-r--r-- | man/i3status.man | 29 | 
3 files changed, 136 insertions, 0 deletions
| @@ -11,6 +11,7 @@   * See file LICENSE for license information.   *   */ +#include <limits.h>  #include <string.h>  #include <stdio.h>  #include <stdbool.h> @@ -35,6 +36,9 @@  #define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); } +#define CFG_CUSTOM_ALIGN_OPT \ +    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_degraded", degraded, CFGF_NONE), \ @@ -42,6 +46,9 @@  #define CFG_CUSTOM_COLOR_OPTS CFG_COLOR_OPTS(NULL, NULL, NULL) +#define CFG_CUSTOM_MIN_WIDTH_OPT \ +    CFG_PTR_CB("min_width", NULL, CFGF_NONE, parse_min_width, free) +  /* socket file descriptor for general purposes */  int general_socket; @@ -89,6 +96,54 @@ static char *sstrdup(const char *str) {          return result;  } +/* + * Parses the "align" module option (to validate input). + */ +static int parse_align(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { +        if (strcasecmp(value, "left") != 0 && strcasecmp(value,"right") != 0 && strcasecmp(value, "center") != 0) +                die("Invalid alignment attribute found in section %s, line %d: \"%s\"\n" +                    "Valid attributes are: left, center, right\n", context->name, context->line, value); + +        char **cresult = result; +        *cresult = sstrdup(value); + +        return 0; +} + +/* + * Parses the "min_width" module option whose value can either be a string or an integer. + */ +static int parse_min_width(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { +        char *end; +        long num = strtol(value, &end, 10); + +        if (num < 0) +                die("Invalid min_width attribute found in section %s, line %d: %d\n" +                    "Expected positive integer or string\n", context->name, context->line, num); +        else if (num == LONG_MIN || num == LONG_MAX || (end && *end != '\0')) +                num = 0; + +        if (strlen(value) == 0) +                die("Empty min_width attribute found in section %s, line %d\n" +                    "Expected positive integer or non-empty string\n", context->name, context->line); + +        if (strcmp(value, "0") == 0) +                die("Invalid min_width attribute found in section %s, line %d: \"%s\"\n" +                    "Expected positive integer or string\n", context->name, context->line, value); + +        struct min_width *parsed = scalloc(sizeof(struct min_width)); +        parsed->num = num; + +        /* num is preferred, but if it’s 0 (i.e. not valid), store and use +         * the raw string value */ +        if (num == 0) +                parsed->str = sstrdup(value); + +        struct min_width **cresult = result; +        *cresult = parsed; + +        return 0; +}  /*   * Validates a color in "#RRGGBB" format @@ -219,35 +274,45 @@ int main(int argc, char *argv[]) {          cfg_opt_t run_watch_opts[] = {                  CFG_STR("pidfile", NULL, CFGF_NONE),                  CFG_STR("format", "%title: %status", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t path_exists_opts[] = {                  CFG_STR("path", NULL, CFGF_NONE),                  CFG_STR("format", "%title: %status", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t wireless_opts[] = {                  CFG_STR("format_up", "W: (%quality at %essid, %bitrate) %ip", CFGF_NONE),                  CFG_STR("format_down", "W: down", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t ethernet_opts[] = {                  CFG_STR("format_up", "E: %ip (%speed)", CFGF_NONE),                  CFG_STR("format_down", "E: down", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t ipv6_opts[] = {                  CFG_STR("format_up", "%ip", CFGF_NONE),                  CFG_STR("format_down", "no IPv6", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          }; @@ -260,35 +325,47 @@ int main(int argc, char *argv[]) {                  CFG_BOOL("last_full_capacity", false, CFGF_NONE),                  CFG_BOOL("integer_battery_capacity", false, CFGF_NONE),                  CFG_BOOL("hide_seconds", false, CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t time_opts[] = {                  CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t tztime_opts[] = {                  CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE),                  CFG_STR("timezone", "", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t ddate_opts[] = {                  CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t load_opts[] = {                  CFG_STR("format", "%1min %5min %15min", CFGF_NONE),                  CFG_FLOAT("max_threshold", 5, CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t usage_opts[] = {                  CFG_STR("format", "%usage", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          }; @@ -296,13 +373,17 @@ int main(int argc, char *argv[]) {                  CFG_STR("format", "%degrees C", CFGF_NONE),                  CFG_STR("path", NULL, CFGF_NONE),                  CFG_INT("max_threshold", 75, CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          };          cfg_opt_t disk_opts[] = {                  CFG_STR("format", "%free", CFGF_NONE),                  CFG_STR("prefix_type", "binary", CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          }; @@ -312,7 +393,9 @@ int main(int argc, char *argv[]) {                  CFG_STR("device", "default", CFGF_NONE),                  CFG_STR("mixer", "Master", CFGF_NONE),                  CFG_INT("mixer_idx", 0, CFGF_NONE), +                CFG_CUSTOM_ALIGN_OPT,                  CFG_CUSTOM_COLOR_OPTS, +                CFG_CUSTOM_MIN_WIDTH_OPT,                  CFG_END()          }; diff --git a/include/i3status.h b/include/i3status.h index 95da091..8c64586 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -88,6 +88,22 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format;  #define SEC_CLOSE_MAP \  	do { \  		if (output_format == O_I3BAR) { \ +			char *_align = cfg_getstr(sec, "align"); \ +			if (_align) { \ +				yajl_gen_string(json_gen, (const unsigned char *)"align", strlen("align")); \ +				yajl_gen_string(json_gen, (const unsigned char *)_align, strlen(_align)); \ +			} \ +			struct min_width *_width = cfg_getptr(sec, "min_width"); \ +			if (_width) { \ +				/* if the value can be parsed as a number, we use the numerical value */ \ +				if (_width->num > 0) { \ +					yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \ +					yajl_gen_integer(json_gen, _width->num); \ +				} else { \ +					yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \ +					yajl_gen_string(json_gen, (const unsigned char *)_width->str, strlen(_width->str)); \ +				} \ +			} \  			const char *_sep = cfg_getstr(cfg_general, "separator"); \  			if (strlen(_sep) == 0) {\  				yajl_gen_string(json_gen, (const unsigned char *)"separator", strlen("separator")); \ @@ -132,6 +148,14 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format;  typedef enum { CS_DISCHARGING, CS_CHARGING, CS_FULL } charging_status_t; +/* + * The "min_width" module option may either be defined as a string or a number. + */ +struct min_width { +    long num; +    const char *str; +}; +  /* src/general.c */  char *skip_character(char *input, char character, int amount);  void die(const char *fmt, ...); diff --git a/man/i3status.man b/man/i3status.man index 7ba4422..656a8bf 100644 --- a/man/i3status.man +++ b/man/i3status.man @@ -404,6 +404,35 @@ volume master {  }  ------------------------------------------------------------- +== Universal module options + +When using the i3bar output format, there are a few additional options that +can be used with all modules to customize their appearance: + +align:: +	The alignment policy to use when the minimum width (see below) is not +	reached. Either +center+ (default), +right+ or +left+. +min_width:: +	The minimum width (in pixels) the module should occupy. If the module takes +	less space than the specified size, the block will be padded to the left +	and/or the right side, according to the defined alignment policy. This is +	useful when you want to prevent the whole status line from shifting when +	values take more or less space between each iteration. +	The option can also be a string. In this case, the width of the given text +	determines the minimum width of the block. This is useful when you want to +	set a sensible minimum width regardless of which font you are using, and at +	what particular size. Please note that a number enclosed with quotes will +	still be treated as a number. + +*Example configuration*: +------------------------------------------------------------- +disk "/" { +    format = "%avail" +    align = "left" +    min_width = 100 +} +------------------------------------------------------------- +  == Using i3status with dzen2  After installing dzen2, you can directly use it with i3status. Just ensure that | 
