[dwm] [PATCH] An experiment with X resources

From: Chris Webb <chris_AT_arachsys.com>
Date: Mon, 24 Sep 2007 14:30:23 +0100

This morning I've been thinking about run time configuration of dwm.

I've never especially liked the compile-time rules table in config.h. The
fact that one or two programs I use happen to be broken and need a rule
with isfloating = 1 doesn't seem like something that the dwm binary
should be cluttered with. Clearly this is a matter of personal taste and
your mileage will probably vary, but it definitely grates for me. I also
have a couple of different screens and I'd like to be able to reconfigure
fonts and colours (depending on the display dwm is managing) without
keeping multiple dwm binaries in my home directory.

As an experiment, I've added support for customisation with X resources
in a branch of my own tree. To avoid growing the code too much, I
completely replaced the hard-coded rules table with a dynamic one
populated from X resources, but all other settings use config.h values as
defaults. Key bindings remain a matter for config.h. As far as I can see,
configuring these at runtime would introduce a big, ugly symbol table
into dwm for little or no gain.

Doing all this grows dwm.c less than I was originally expecting, as can
be seen from the patch. Most of the extra code is involved with
constructing the tag and rule lists rather than just to add simpler
config options like fonts and colours.

I've ported my patch back to upstream dwm (hg tip) and attached it at the
end of this message in case anyone else on the list is interested in
experimenting along the same lines. A sample .Xresources file to merge
(use xrdb -merge/-load) might be:

  Dwm.bar: top
  Dwm.font: -*-terminus-medium-r-*-*-12-*-*-*-*-*-iso10646-*

  Dwm.normal.border: #cccccc
  Dwm.normal.background: #ffffff
  Dwm.normal.background: #000000
  Dwm.selected.border: #ff0000
  Dwm.selected.background: #0000ff
  Dwm.selected.foreground: #ffffff

  Dwm.tags: 1 2 3 4 5 6 7 8 9
  Dwm.rules: scratchpad browser

  Dwm.properties.scratchpad: URxvt:scratch
  Dwm.floating.scratchpad: True

  Dwm.properties.browser: Opera
  Dwm.tags.browser: 2

Cheers,

Chris.

diff -r 92c19c929a59 config.def.h
--- a/config.def.h Sun Sep 23 18:50:04 2007 +0200
+++ b/config.def.h Mon Sep 24 14:28:26 2007 +0100
@@ -1,4 +1,8 @@
 /* See LICENSE file for copyright and license details. */
+
+/* resources */
+#define RESNAME "dwm"
+#define RESCLASS "Dwm"
 
 /* appearance */
 #define BARPOS BarTop /* BarBot, BarOff */
@@ -12,14 +16,7 @@
 #define SELFGCOLOR "#ffffff"
 
 /* tagging */
-const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "www", NULL };
-Rule rules[] = {
- /* class:instance:title regex tags regex isfloating */
- { "Firefox", "www", False },
- { "Gimp", NULL, True },
- { "MPlayer", NULL, True },
- { "Acroread", NULL, True },
-};
+#define TAGS "1 2 3 4 5 6 7 8 9"
 
 /* layout(s) */
 #define ISTILE isarrange(tile) /* || isarrange(<custom>) */
diff -r 92c19c929a59 dwm.c
--- a/dwm.c Sun Sep 23 18:50:04 2007 +0200
+++ b/dwm.c Mon Sep 24 14:28:26 2007 +0100
@@ -40,6 +40,7 @@
 #include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <X11/Xproto.h>
+#include <X11/Xresource.h>
 #include <X11/Xutil.h>
 
 /* macros */
@@ -132,6 +133,8 @@ void *emallocz(unsigned int size);
 void *emallocz(unsigned int size);
 void enternotify(XEvent *e);
 void eprint(const char *errstr, ...);
+void *erealloc(void *res, unsigned int size);
+char *estrdup(const char *s);
 void expose(XEvent *e);
 void floating(void); /* default floating layout */
 void focus(Client *c);
@@ -139,7 +142,10 @@ void focusprev(const char *arg);
 void focusprev(const char *arg);
 Client *getclient(Window w);
 unsigned long getcolor(const char *colstr);
+char *getresource(const char *resource, char *defval);
+void getrules(void);
 long getstate(Window w);
+void gettags(void);
 Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 void grabbuttons(Client *c, Bool focused);
 unsigned int idxoftag(const char *tag);
@@ -190,11 +196,11 @@ void zoom(const char *arg);
 void zoom(const char *arg);
 
 /* variables */
-char stext[256];
+char stext[256], **tags;
 double mwfact;
 int screen, sx, sy, sw, sh, wax, way, waw, wah;
 int (*xerrorxlib)(Display *, XErrorEvent *);
-unsigned int bh, bpos, ntags;
+unsigned int bh, bpos, ntags = 0;
 unsigned int blw = 0;
 unsigned int ltidx = 0; /* default */
 unsigned int nlayouts = 0;
@@ -226,7 +232,9 @@ Display *dpy;
 Display *dpy;
 DC dc = {0};
 Window barwin, root;
+Rule *rules;
 Regs *regs = NULL;
+XrmDatabase xrdb;
 
 /* configuration, allows nested code to access above variables */
 #include "config.h"
@@ -402,7 +410,6 @@ compileregs(void) {
 
         if(regs)
                 return;
- nrules = sizeof rules / sizeof rules[0];
         regs = emallocz(nrules * sizeof(Regs));
         for(i = 0; i < nrules; i++) {
                 if(rules[i].prop) {
@@ -638,6 +645,13 @@ emallocz(unsigned int size) {
         return res;
 }
 
+void *
+erealloc(void *res, unsigned int size) {
+ if (!(res = res ? realloc(res, size) : malloc(size)))
+ eprint("fatal: could not realloc() %u bytes\n", size);
+ return res;
+}
+
 void
 enternotify(XEvent *e) {
         Client *c;
@@ -661,6 +675,15 @@ eprint(const char *errstr, ...) {
         vfprintf(stderr, errstr, ap);
         va_end(ap);
         exit(EXIT_FAILURE);
+}
+
+char *
+estrdup(const char *s) {
+ char *t;
+ t = strdup(s);
+ if (!t)
+ eprint("fatal: strdup failed\n");
+ return t;
 }
 
 void
@@ -755,6 +778,53 @@ getcolor(const char *colstr) {
         if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
                 eprint("error, cannot allocate color '%s'\n", colstr);
         return color.pixel;
+}
+
+char *
+getresource(const char *resource, char *defval) {
+ static char name[256], class[256], *type;
+ XrmValue value;
+ snprintf(name, sizeof(name), "%s.%s", RESNAME, resource);
+ snprintf(class, sizeof(class), "%s.%s", RESCLASS, resource);
+ XrmGetResource(xrdb, name, class, &type, &value);
+ if(value.addr)
+ return value.addr;
+ return defval;
+}
+
+void
+getrules(void) {
+ char *line, *p, *rule, *prop, *tags, *floating;
+ static char name[256], class[256], *type;
+ XrmValue value;
+ line = p = estrdup(getresource("rules", ""));
+ while(rule = strsep(&p, " \t\n")) {
+ if(!rule[0])
+ continue;
+ snprintf(name, sizeof(name), "%s.properties.%s", RESNAME, rule);
+ snprintf(class, sizeof(class), "%s.properties.%s", RESCLASS, rule);
+ XrmGetResource(xrdb, name, class, &type, &value);
+ if (!value.addr || !value.addr[0])
+ continue;
+ rules = erealloc(rules, sizeof(Rule) * (++nrules));
+ rules[nrules - 1].prop = estrdup(value.addr);
+ snprintf(name, sizeof(name), "%s.tags.%s", RESNAME, rule);
+ snprintf(class, sizeof(class), "%s.tags.%s", RESCLASS, rule);
+ XrmGetResource(xrdb, name, class, &type, &value);
+ if (value.addr && value.addr[0] && strcasecmp(value.addr, "null"))
+ rules[nrules - 1].tags = estrdup(value.addr);
+ else
+ rules[nrules - 1].tags = NULL;
+ snprintf(name, sizeof(name), "%s.floating.%s", RESNAME, rule);
+ snprintf(class, sizeof(class), "%s.floating.%s", RESCLASS, rule);
+ XrmGetResource(xrdb, name, class, &type, &value);
+ rules[nrules - 1].isfloating = (value.addr && value.addr[0]
+ && strcasecmp(value.addr, "false")
+ && strcasecmp(value.addr, "no")
+ && strcasecmp(value.addr, "off")
+ && strcmp(value.addr, "0"));
+ }
+ free(line);
 }
 
 long
@@ -773,6 +843,20 @@ getstate(Window w) {
                 result = *p;
         XFree(p);
         return result;
+}
+
+void
+gettags(void) {
+ char *line, *tag;
+ line = estrdup(getresource("tags", TAGS));
+ while(tag = strsep(&line, " \t\n")) {
+ if(!tag[0])
+ continue;
+ tags = erealloc(tags, sizeof(char *) * ++ntags);
+ tags[ntags - 1] = tag;
+ }
+ if (ntags == 0)
+ eprint("dwm: no tags defined\n");
 }
 
 Bool
@@ -1420,6 +1504,7 @@ setup(void) {
 setup(void) {
         int d;
         unsigned int i, j, mask;
+ char *s;
         Window w;
         XModifierKeymap *modmap;
         XSetWindowAttributes wa;
@@ -1461,23 +1546,32 @@ setup(void) {
         XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
         XSelectInput(dpy, root, wa.event_mask);
 
+ /* init resource database */
+ XrmInitialize();
+ s = XResourceManagerString(dpy);
+ xrdb = XrmGetStringDatabase(s);
+ free(s);
+
+ /* init rules */
+ getrules();
+
+ /* init tags */
+ gettags();
+ compileregs();
+ seltags = emallocz(sizeof(Bool) * ntags);
+ seltags[0] = True;
+
         /* grab keys */
         keypress(NULL);
 
- /* init tags */
- compileregs();
- for(ntags = 0; tags[ntags]; ntags++);
- seltags = emallocz(sizeof(Bool) * ntags);
- seltags[0] = True;
-
         /* init appearance */
- dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR);
- dc.norm[ColBG] = getcolor(NORMBGCOLOR);
- dc.norm[ColFG] = getcolor(NORMFGCOLOR);
- dc.sel[ColBorder] = getcolor(SELBORDERCOLOR);
- dc.sel[ColBG] = getcolor(SELBGCOLOR);
- dc.sel[ColFG] = getcolor(SELFGCOLOR);
- initfont(FONT);
+ dc.norm[ColBorder] = getcolor(getresource("normal.border", NORMBORDERCOLOR));
+ dc.norm[ColBG] = getcolor(getresource("normal.background", NORMBGCOLOR));
+ dc.norm[ColFG] = getcolor(getresource("normal.foreground", NORMFGCOLOR));
+ dc.sel[ColBorder] = getcolor(getresource("selected.border", SELBORDERCOLOR));
+ dc.sel[ColBG] = getcolor(getresource("selected.background", SELBGCOLOR));
+ dc.sel[ColFG] = getcolor(getresource("selected.foreground", SELFGCOLOR));
+ initfont(getresource("font", FONT));
         dc.h = bh = dc.font.height + 2;
 
         /* init layouts */
@@ -1490,7 +1584,15 @@ setup(void) {
         }
 
         /* init bar */
- bpos = BARPOS;
+ s = getresource("bar", NULL);
+ if(!s || !s[0] || strcasecmp(s, "default"))
+ bpos = BARPOS;
+ else if(strcasecmp(s, "top") == 0)
+ bpos = BarTop;
+ else if(strcasecmp(s, "bottom") == 0)
+ bpos = BarBot;
+ else
+ bpos = BarOff;
         wa.override_redirect = 1;
         wa.background_pixmap = ParentRelative;
         wa.event_mask = ButtonPressMask | ExposureMask;
@@ -1506,6 +1608,9 @@ setup(void) {
         XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
         if(!dc.font.set)
                 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
+
+ /* free resource database */
+ XrmDestroyDatabase(xrdb);
 
         /* multihead support */
         selscreen = XQueryPointer(dpy, root, &w, &w, &d, &d, &d, &d, &mask);
Received on Mon Sep 24 2007 - 15:30:26 UTC

This archive was generated by hypermail 2.2.0 : Sun Jul 13 2008 - 14:55:14 UTC