diff
Makefile | 11 +
README.md | 1 +
bar.c | 690 +++++++++++++++++++++++++++++++++++++++++++++++++++++
config.mk | 27 +++
xresources.example | 14 ++
5 files changed, 743 insertions(+)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..39672fe
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+include config.mk
+
+all: bar
+
+bar: bar.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o bar bar.c $(LDLIBS)
+
+clean:
+ rm -f bar
+
+.PHONY: all clean
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..89a1941
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+tiny x11 bar, to configure see xresources.example
diff --git a/bar.c b/bar.c
new file mode 100644
index 0000000..501438a
--- /dev/null
+++ b/bar.c
@@ -0,0 +1,690 @@
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#ifdef USE_XFT
+#include <X11/Xft/Xft.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_TEXT 512
+
+struct config {
+ char font[256];
+ char geometry[128];
+ char info_left[256];
+ char info_middle[256];
+ char info_right[256];
+ int padding_bottom;
+ int padding_left;
+ int padding_right;
+ int padding_top;
+ long rerun_ms;
+};
+
+struct fontset {
+ XFontStruct *core;
+#ifdef USE_XFT
+ XftColor color;
+ XftDraw *draw;
+ XftFont *xft;
+ int color_ready;
+#endif
+ int use_xft;
+};
+
+static long now_ms(void);
+static int get_resource_path(char *, size_t);
+static int file_mtime(const char *, long *);
+static int parse_int(const char *, int);
+static long parse_interval_ms(const char *, long);
+static int text_width(Display *, struct fontset *, const char *);
+static int font_ascent(struct fontset *);
+static int font_descent(struct fontset *);
+static int load_font(Display *, int, struct fontset *, const char *);
+static void bind_font(Display *, int, Window, GC, struct fontset *);
+static void free_font(Display *, struct fontset *);
+static void copystr(char *, size_t, const char *);
+static void trim(char *);
+static void resource_get_string(XrmDatabase, const char *, char *, size_t,
+ const char *);
+static int resource_get_int(XrmDatabase, const char *, int);
+static long resource_get_interval(XrmDatabase, const char *, long);
+static void load_config(Display *, struct config *, const char *);
+static void apply_geometry(Display *, int, const char *, int *, int *,
+ int *, int *);
+static void run_cmd(const char *, char *, size_t);
+static void refresh_texts(struct config *, char *, char *, char *);
+static void draw_text(Display *, Window, GC, int, int, const char *,
+ struct fontset *);
+static void draw_bar(Display *, Window, GC, struct fontset *, struct config *,
+ const char *, const char *, const char *, int);
+
+static long
+now_ms(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000L + tv.tv_usec / 1000L;
+}
+
+static int
+get_resource_path(char *out, size_t out_sz)
+{
+ const char *env;
+ const char *home;
+
+ out[0] = '\0';
+ env = getenv("XENVIRONMENT");
+ if (env != NULL && *env != '\0') {
+ copystr(out, out_sz, env);
+ return 1;
+ }
+
+ home = getenv("HOME");
+ if (home == NULL || *home == '\0')
+ return 0;
+
+ copystr(out, out_sz, home);
+ if (strlen(out) + sizeof("/.Xresources") > out_sz) {
+ out[0] = '\0';
+ return 0;
+ }
+ strcat(out, "/.Xresources");
+ return 1;
+}
+
+static int
+file_mtime(const char *path, long *mtime)
+{
+ struct stat st;
+
+ if (path == NULL || *path == '\0')
+ return 0;
+ if (stat(path, &st) != 0)
+ return 0;
+ *mtime = (long)st.st_mtime;
+ return 1;
+}
+
+static int
+parse_int(const char *s, int fallback)
+{
+ char *end;
+ long v;
+
+ errno = 0;
+ v = strtol(s, &end, 10);
+ if (errno != 0 || end == s)
+ return fallback;
+ while (*end != '\0') {
+ if (!isspace((unsigned char)*end))
+ return fallback;
+ end++;
+ }
+ return (int)v;
+}
+
+static long
+parse_interval_ms(const char *s, long fallback)
+{
+ char *end;
+ double v;
+
+ while (*s != '\0' && isspace((unsigned char)*s))
+ s++;
+
+ errno = 0;
+ v = strtod(s, &end);
+ if (errno != 0 || end == s || v < 0.0)
+ return fallback;
+
+ while (*end != '\0' && isspace((unsigned char)*end))
+ end++;
+ if (*end == '\0')
+ return (long)(v * 1000.0);
+ if (strcmp(end, "ms") == 0)
+ return (long)v;
+ if (strcmp(end, "s") == 0)
+ return (long)(v * 1000.0);
+ if (strcmp(end, "m") == 0)
+ return (long)(v * 60000.0);
+ return fallback;
+}
+
+static int
+text_width(Display *dpy, struct fontset *fs, const char *text)
+{
+#ifdef USE_XFT
+ if (fs->use_xft) {
+ XGlyphInfo ext;
+
+ XftTextExtentsUtf8(dpy, fs->xft, (FcChar8 *)text,
+ (int)strlen(text), &ext);
+ return ext.xOff;
+ }
+#endif
+ return XTextWidth(fs->core, text, (int)strlen(text));
+}
+
+static int
+font_ascent(struct fontset *fs)
+{
+#ifdef USE_XFT
+ if (fs->use_xft)
+ return fs->xft->ascent;
+#endif
+ return fs->core->ascent;
+}
+
+static int
+font_descent(struct fontset *fs)
+{
+#ifdef USE_XFT
+ if (fs->use_xft)
+ return fs->xft->descent;
+#endif
+ return fs->core->descent;
+}
+
+static int
+load_font(Display *dpy, int screen, struct fontset *fs, const char *name)
+{
+ memset(fs, 0, sizeof(*fs));
+ fs->core = XLoadQueryFont(dpy, name);
+ if (fs->core != NULL)
+ return 1;
+
+#ifdef USE_XFT
+ fs->xft = XftFontOpenName(dpy, screen, name);
+ if (fs->xft == NULL)
+ return 0;
+ fs->use_xft = 1;
+ return 1;
+#else
+ (void)screen;
+ return 0;
+#endif
+}
+
+static void
+bind_font(Display *dpy, int screen, Window win, GC gc, struct fontset *fs)
+{
+#ifdef USE_XFT
+ if (fs->use_xft) {
+ XRenderColor rc;
+
+ if (fs->draw == NULL) {
+ fs->draw = XftDrawCreate(dpy, win, DefaultVisual(dpy, screen),
+ DefaultColormap(dpy, screen));
+ }
+ if (!fs->color_ready) {
+ rc.red = 0;
+ rc.green = 0;
+ rc.blue = 0;
+ rc.alpha = 65535;
+ if (XftColorAllocValue(dpy, DefaultVisual(dpy, screen),
+ DefaultColormap(dpy, screen), &rc, &fs->color))
+ fs->color_ready = 1;
+ }
+ return;
+ }
+#endif
+ if (fs->core != NULL)
+ XSetFont(dpy, gc, fs->core->fid);
+}
+
+static void
+free_font(Display *dpy, struct fontset *fs)
+{
+ if (fs->core != NULL)
+ XFreeFont(dpy, fs->core);
+#ifdef USE_XFT
+ if (fs->xft != NULL)
+ XftFontClose(dpy, fs->xft);
+ if (fs->draw != NULL)
+ XftDrawDestroy(fs->draw);
+ if (fs->color_ready)
+ XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
+ DefaultColormap(dpy, DefaultScreen(dpy)), &fs->color);
+#endif
+ memset(fs, 0, sizeof(*fs));
+}
+
+static void
+copystr(char *dst, size_t dst_sz, const char *src)
+{
+ if (dst_sz == 0)
+ return;
+ strncpy(dst, src, dst_sz - 1);
+ dst[dst_sz - 1] = '\0';
+}
+
+static void
+trim(char *s)
+{
+ size_t len;
+ char *start;
+
+ start = s;
+ while (*start != '\0' && isspace((unsigned char)*start))
+ start++;
+ if (start != s)
+ memmove(s, start, strlen(start) + 1);
+
+ len = strlen(s);
+ while (len > 0 && isspace((unsigned char)s[len - 1])) {
+ s[len - 1] = '\0';
+ len--;
+ }
+ len = strlen(s);
+ if (len >= 2 && s[0] == '"' && s[len - 1] == '"') {
+ memmove(s, s + 1, len - 2);
+ s[len - 2] = '\0';
+ }
+}
+
+static void
+resource_get_string(XrmDatabase db, const char *name, char *out, size_t out_sz,
+ const char *fallback)
+{
+ char key[128];
+ char cls[128];
+ char *type;
+ XrmValue val;
+ size_t n;
+
+ strlcpy(key, "bar.", sizeof(key));
+ strlcat(key, name, sizeof(key));
+ strlcpy(cls, "Bar.", sizeof(cls));
+ strlcat(cls, name, sizeof(cls));
+ if (XrmGetResource(db, key, cls, &type, &val) && val.addr != NULL &&
+ val.size > 0) {
+ n = (size_t)val.size;
+ if (n >= out_sz)
+ n = out_sz - 1;
+ memcpy(out, val.addr, n);
+ out[n] = '\0';
+ trim(out);
+ return;
+ }
+
+ strlcpy(key, "*.", sizeof(key));
+ strlcat(key, name, sizeof(key));
+ strlcpy(cls, "*.", sizeof(cls));
+ strlcat(cls, name, sizeof(cls));
+ if (XrmGetResource(db, key, cls, &type, &val) && val.addr != NULL &&
+ val.size > 0) {
+ n = (size_t)val.size;
+ if (n >= out_sz)
+ n = out_sz - 1;
+ memcpy(out, val.addr, n);
+ out[n] = '\0';
+ trim(out);
+ return;
+ }
+
+ copystr(out, out_sz, fallback);
+}
+
+static int
+resource_get_int(XrmDatabase db, const char *name, int fallback)
+{
+ char buf[64];
+
+ resource_get_string(db, name, buf, sizeof(buf), "");
+ if (buf[0] == '\0')
+ return fallback;
+ return parse_int(buf, fallback);
+}
+
+static long
+resource_get_interval(XrmDatabase db, const char *name, long fallback)
+{
+ char buf[64];
+
+ resource_get_string(db, name, buf, sizeof(buf), "");
+ if (buf[0] == '\0')
+ return fallback;
+ return parse_interval_ms(buf, fallback);
+}
+
+static void
+load_config(Display *dpy, struct config *cfg, const char *resource_path)
+{
+ char *rm;
+ XrmDatabase db;
+
+ copystr(cfg->font, sizeof(cfg->font), "fixed");
+ cfg->geometry[0] = '\0';
+ cfg->info_left[0] = '\0';
+ cfg->info_middle[0] = '\0';
+ cfg->info_right[0] = '\0';
+ cfg->padding_bottom = 2;
+ cfg->padding_left = 8;
+ cfg->padding_right = 8;
+ cfg->padding_top = 2;
+ cfg->rerun_ms = 1000;
+
+ XrmInitialize();
+ db = NULL;
+ if (resource_path != NULL && *resource_path != '\0')
+ db = XrmGetFileDatabase(resource_path);
+ if (db == NULL) {
+ rm = XResourceManagerString(dpy);
+ if (rm != NULL)
+ db = XrmGetStringDatabase(rm);
+ }
+ if (db == NULL)
+ return;
+
+ resource_get_string(db, "font", cfg->font, sizeof(cfg->font), cfg->font);
+ resource_get_string(db, "geometry", cfg->geometry, sizeof(cfg->geometry),
+ "");
+ resource_get_string(db, "info_left", cfg->info_left, sizeof(cfg->info_left),
+ "");
+ resource_get_string(db, "info_middle", cfg->info_middle,
+ sizeof(cfg->info_middle), "");
+ resource_get_string(db, "info_right", cfg->info_right,
+ sizeof(cfg->info_right), "");
+ cfg->padding_bottom = resource_get_int(db, "padding_bottom",
+ cfg->padding_bottom);
+ cfg->padding_left = resource_get_int(db, "padding_left",
+ cfg->padding_left);
+ cfg->padding_right = resource_get_int(db, "padding_right",
+ cfg->padding_right);
+ cfg->padding_top = resource_get_int(db, "padding_top", cfg->padding_top);
+ cfg->rerun_ms = resource_get_interval(db, "rerun_interval", cfg->rerun_ms);
+ if (cfg->rerun_ms < 50)
+ cfg->rerun_ms = 50;
+
+ XrmDestroyDatabase(db);
+}
+
+static void
+apply_geometry(Display *dpy, int screen, const char *geom, int *x, int *y,
+ int *w, int *h)
+{
+ int gx, gy, mask, nx, ny, nw, nh, sw, sh;
+ unsigned int gw, gh;
+
+ if (geom == NULL || *geom == '\0')
+ return;
+
+ gx = 0;
+ gy = 0;
+ gw = 0;
+ gh = 0;
+ mask = XParseGeometry(geom, &gx, &gy, &gw, &gh);
+ if (mask == 0)
+ return;
+
+ nw = *w;
+ nh = *h;
+ if ((mask & WidthValue) && gw > 0)
+ nw = (int)gw;
+ if ((mask & HeightValue) && gh > 0)
+ nh = (int)gh;
+
+ sw = DisplayWidth(dpy, screen);
+ sh = DisplayHeight(dpy, screen);
+ if (nw < 1)
+ nw = 1;
+ if (nh < 1)
+ nh = 1;
+ if (nw > sw)
+ nw = sw;
+ if (nh > sh)
+ nh = sh;
+
+ nx = *x;
+ ny = *y;
+ if (mask & XValue) {
+ if (mask & XNegative)
+ nx = sw - nw + gx;
+ else
+ nx = gx;
+ }
+ if (mask & YValue) {
+ if (mask & YNegative)
+ ny = sh - nh + gy;
+ else
+ ny = gy;
+ }
+
+ if (nx < 0)
+ nx = 0;
+ if (ny < 0)
+ ny = 0;
+ if (nx + nw > sw)
+ nx = sw - nw;
+ if (ny + nh > sh)
+ ny = sh - nh;
+
+ *x = nx;
+ *y = ny;
+ *w = nw;
+ *h = nh;
+}
+
+static void
+run_cmd(const char *cmd, char *out, size_t out_sz)
+{
+ FILE *fp;
+ size_t n;
+
+ out[0] = '\0';
+ if (cmd == NULL || *cmd == '\0')
+ return;
+
+ fp = popen(cmd, "r");
+ if (fp == NULL)
+ return;
+ n = fread(out, 1, out_sz - 1, fp);
+ out[n] = '\0';
+ pclose(fp);
+ while (n > 0 && (out[n - 1] == '\n' || out[n - 1] == '\r' ||
+ out[n - 1] == ' ' || out[n - 1] == '\t')) {
+ out[n - 1] = '\0';
+ n--;
+ }
+}
+
+static void
+refresh_texts(struct config *cfg, char *left, char *middle, char *right)
+{
+ run_cmd(cfg->info_left, left, MAX_TEXT);
+ run_cmd(cfg->info_middle, middle, MAX_TEXT);
+ run_cmd(cfg->info_right, right, MAX_TEXT);
+}
+
+static void
+draw_text(Display *dpy, Window win, GC gc, int x, int y, const char *text,
+ struct fontset *fs)
+{
+#ifdef USE_XFT
+ if (fs->use_xft && fs->draw != NULL && fs->color_ready) {
+ XftDrawStringUtf8(fs->draw, &fs->color, fs->xft, x, y,
+ (FcChar8 *)text, (int)strlen(text));
+ return;
+ }
+#endif
+ XDrawString(dpy, win, gc, x, y, text, (int)strlen(text));
+}
+
+static void
+draw_bar(Display *dpy, Window win, GC gc, struct fontset *fs,
+ struct config *cfg, const char *left, const char *middle,
+ const char *right, int width)
+{
+ int y, lw, mw, rw, lx, mx, rx;
+
+ y = cfg->padding_top + font_ascent(fs);
+ lw = text_width(dpy, fs, left);
+ mw = text_width(dpy, fs, middle);
+ rw = text_width(dpy, fs, right);
+ lx = cfg->padding_left;
+ mx = (width - mw) / 2;
+ rx = width - cfg->padding_right - rw;
+ if (mx < lx + lw + 2)
+ mx = lx + lw + 2;
+ if (rx < mx + mw + 2)
+ rx = mx + mw + 2;
+
+ XClearWindow(dpy, win);
+ if (*right != '\0')
+ draw_text(dpy, win, gc, rx, y, right, fs);
+ if (*middle != '\0')
+ draw_text(dpy, win, gc, mx, y, middle, fs);
+ if (*left != '\0')
+ draw_text(dpy, win, gc, lx, y, left, fs);
+ XFlush(dpy);
+}
+
+int
+main(void)
+{
+ Display *dpy;
+ XEvent ev;
+ XGCValues gcv;
+ XSetWindowAttributes wa;
+ char left[MAX_TEXT], middle[MAX_TEXT], resource_path[512], right[MAX_TEXT];
+ struct config cfg;
+ struct fontset fs, newfs;
+ struct timeval tv;
+ fd_set fds;
+ GC gc;
+ Window root, win;
+ int have_mtime, height, pos_x, pos_y, screen, sel, watch_resources, width;
+ long last_mtime, mt, next_tick, next_watch, now, wait_ms, watch_interval;
+
+ dpy = XOpenDisplay(NULL);
+ if (dpy == NULL) {
+ fprintf(stderr, "bar: cannot open X display\n");
+ return 1;
+ }
+ screen = DefaultScreen(dpy);
+ root = RootWindow(dpy, screen);
+ width = DisplayWidth(dpy, screen);
+
+ watch_resources = get_resource_path(resource_path, sizeof(resource_path));
+ have_mtime = 0;
+ if (watch_resources && file_mtime(resource_path, &mt)) {
+ last_mtime = mt;
+ have_mtime = 1;
+ }
+
+ load_config(dpy, &cfg, watch_resources ? resource_path : NULL);
+ if (!load_font(dpy, screen, &fs, cfg.font) &&
+ !load_font(dpy, screen, &fs, "fixed")) {
+ fprintf(stderr, "bar: cannot load font\n");
+ XCloseDisplay(dpy);
+ return 1;
+ }
+ height = font_ascent(&fs) + font_descent(&fs) + cfg.padding_top +
+ cfg.padding_bottom;
+ if (height < 1)
+ height = 1;
+ pos_x = 0;
+ pos_y = 0;
+ apply_geometry(dpy, screen, cfg.geometry, &pos_x, &pos_y, &width, &height);
+
+ wa.override_redirect = True;
+ wa.background_pixel = WhitePixel(dpy, screen);
+ wa.border_pixel = BlackPixel(dpy, screen);
+ wa.event_mask = ExposureMask | StructureNotifyMask;
+ win = XCreateWindow(dpy, root, pos_x, pos_y, (unsigned int)width,
+ (unsigned int)height, 0, CopyFromParent, InputOutput, CopyFromParent,
+ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask, &wa);
+ XStoreName(dpy, win, "bar");
+ XMapRaised(dpy, win);
+
+ gcv.foreground = BlackPixel(dpy, screen);
+ gcv.background = WhitePixel(dpy, screen);
+ gc = XCreateGC(dpy, win, GCForeground | GCBackground, &gcv);
+ bind_font(dpy, screen, win, gc, &fs);
+
+ refresh_texts(&cfg, left, middle, right);
+ draw_bar(dpy, win, gc, &fs, &cfg, left, middle, right, width);
+
+ next_tick = now_ms() + cfg.rerun_ms;
+ watch_interval = 500;
+ next_watch = now_ms() + watch_interval;
+
+ for (;;) {
+ now = now_ms();
+ wait_ms = next_tick - now;
+ if (next_watch - now < wait_ms)
+ wait_ms = next_watch - now;
+ if (wait_ms < 0)
+ wait_ms = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(ConnectionNumber(dpy), &fds);
+ tv.tv_sec = wait_ms / 1000;
+ tv.tv_usec = (wait_ms % 1000) * 1000;
+
+ sel = select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv);
+ if (sel > 0 && FD_ISSET(ConnectionNumber(dpy), &fds)) {
+ while (XPending(dpy)) {
+ XNextEvent(dpy, &ev);
+ if (ev.type == ConfigureNotify)
+ width = ev.xconfigure.width;
+ if (ev.type == Expose || ev.type == ConfigureNotify)
+ draw_bar(dpy, win, gc, &fs, &cfg, left, middle, right,
+ width);
+ }
+ }
+
+ now = now_ms();
+ if (watch_resources && now >= next_watch) {
+ if (file_mtime(resource_path, &mt) &&
+ (!have_mtime || mt != last_mtime)) {
+ have_mtime = 1;
+ last_mtime = mt;
+
+ load_config(dpy, &cfg, resource_path);
+ if (load_font(dpy, screen, &newfs, cfg.font) ||
+ load_font(dpy, screen, &newfs, "fixed")) {
+ free_font(dpy, &fs);
+ fs = newfs;
+ }
+ bind_font(dpy, screen, win, gc, &fs);
+ width = DisplayWidth(dpy, screen);
+ height = font_ascent(&fs) + font_descent(&fs) +
+ cfg.padding_top + cfg.padding_bottom;
+ if (height < 1)
+ height = 1;
+ pos_x = 0;
+ pos_y = 0;
+ apply_geometry(dpy, screen, cfg.geometry, &pos_x, &pos_y,
+ &width, &height);
+ XMoveResizeWindow(dpy, win, pos_x, pos_y,
+ (unsigned int)width, (unsigned int)height);
+ refresh_texts(&cfg, left, middle, right);
+ draw_bar(dpy, win, gc, &fs, &cfg, left, middle, right,
+ width);
+ next_tick = now + cfg.rerun_ms;
+ } else if (!file_mtime(resource_path, &mt))
+ have_mtime = 0;
+ next_watch = now + watch_interval;
+ }
+
+ now = now_ms();
+ if (now >= next_tick) {
+ refresh_texts(&cfg, left, middle, right);
+ draw_bar(dpy, win, gc, &fs, &cfg, left, middle, right, width);
+ next_tick = now + cfg.rerun_ms;
+ }
+ }
+}
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..b85f412
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,27 @@
+OPENBSD = 0
+USE_XFT = 1
+
+CC = cc
+CFLAGS = -std=c89 -Wall -Wextra -pedantic -O2
+CPPFLAGS =
+LDFLAGS =
+LDLIBS = -lX11
+XFT_CPPFLAGS =
+XFT_LDFLAGS =
+
+.if ${OPENBSD} == 1
+CPPFLAGS += -I/usr/X11R6/include
+LDFLAGS += -L/usr/X11R6/lib
+.endif
+
+.if ${OPENBSD} == 1 && ${USE_XFT} == 1
+XFT_CPPFLAGS += -I/usr/X11R6/include
+XFT_LDFLAGS += -L/usr/X11R6/lib
+.endif
+
+.if ${USE_XFT} == 1
+CPPFLAGS += -DUSE_XFT
+CPPFLAGS += ${XFT_CPPFLAGS}
+LDFLAGS += ${XFT_LDFLAGS}
+LDLIBS += -lXft
+.endif
diff --git a/xresources.example b/xresources.example
new file mode 100644
index 0000000..4e75743
--- /dev/null
+++ b/xresources.example
@@ -0,0 +1,14 @@
+# runs string in "" as /bin/sh -c <string>
+bar.info_left: ""
+bar.info_middle: "date '+%Y-%m-%d %H:%M:%S'"
+bar.info_right: ""
+# you can use XLFD font names or fontconfig pattern
+bar.font: "lemon:size=12"
+bar.geometry: "1280x22+0+0"
+
+bar.padding_left: 10
+bar.padding_right: 10
+bar.padding_top: 2
+bar.padding_bottom: 2
+
+bar.rerun_interval: 10s