git.strcat.st

/strcat/bar.git/ - summarytreelogarchive

subject
init
commit
2f6816f6636c6fecb27892065fabc6877da2cac1
date
2026-04-20T17:17:41Z
message
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