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
This commit is contained in:
Marco Hunsicker 2014-03-05 20:53:07 +01:00 committed by Michael Stapelberg
parent 0c2b518b8a
commit 1de12e7b20
3 changed files with 136 additions and 0 deletions

View File

@ -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 its 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()
};

View File

@ -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, ...);

View File

@ -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