oops
[dwm.git] / dwm.c
diff --git a/dwm.c b/dwm.c
index 4465af1..4edd7ce 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <X11/cursorfont.h>
 #include <X11/keysym.h>
@@ -36,6 +37,7 @@
 #include <X11/Xlib.h>
 #include <X11/Xproto.h>
 #include <X11/Xutil.h>
+#include <X11/Xresource.h>
 #ifdef XINERAMA
 #include <X11/extensions/Xinerama.h>
 #endif /* XINERAMA */
@@ -119,6 +121,7 @@ struct Monitor {
        int by;               /* bar geometry */
        int mx, my, mw, mh;   /* screen size */
        int wx, wy, ww, wh;   /* window area  */
+       int gappx;            /* gaps between windows */
        unsigned int seltags;
        unsigned int sellt;
        unsigned int tagset[2];
@@ -141,6 +144,19 @@ typedef struct {
        int monitor;
 } Rule;
 
+/* Xresources preferences */
+enum resource_type {
+       STRING = 0,
+       INTEGER = 1,
+       FLOAT = 2
+};
+
+typedef struct {
+       char *name;
+       enum resource_type type;
+       void *dst;
+} ResourcePref;
+
 /* function declarations */
 static void applyrules(Client *c);
 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@@ -169,6 +185,7 @@ static void focus(Client *c);
 static void focusin(XEvent *e);
 static void focusmon(const Arg *arg);
 static void focusstack(const Arg *arg);
+static Atom getatomprop(Client *c, Atom prop);
 static int getrootptr(int *x, int *y);
 static long getstate(Window w);
 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
@@ -193,12 +210,14 @@ static void resizeclient(Client *c, int x, int y, int w, int h);
 static void resizemouse(const Arg *arg);
 static void restack(Monitor *m);
 static void run(void);
+static void runautostart(void);
 static void scan(void);
 static int sendevent(Client *c, Atom proto);
 static void sendmon(Client *c, Monitor *m);
 static void setclientstate(Client *c, long state);
 static void setfocus(Client *c);
 static void setfullscreen(Client *c, int fullscreen);
+static void setgaps(const Arg *arg);
 static void setlayout(const Arg *arg);
 static void setmfact(const Arg *arg);
 static void setup(void);
@@ -233,9 +252,15 @@ static int xerror(Display *dpy, XErrorEvent *ee);
 static int xerrordummy(Display *dpy, XErrorEvent *ee);
 static int xerrorstart(Display *dpy, XErrorEvent *ee);
 static void zoom(const Arg *arg);
+static void load_xresources(void);
+static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst);
 
 /* variables */
+static const char autostartblocksh[] = "autostart_blocking.sh";
+static const char autostartsh[] = "autostart.sh";
 static const char broken[] = "broken";
+static const char dwmdir[] = "dwm";
+static const char localshare[] = ".local/share";
 static char stext[256];
 static int screen;
 static int sw, sh;           /* X display screen geometry width, height */
@@ -638,6 +663,7 @@ createmon(void)
        m->nmaster = nmaster;
        m->showbar = showbar;
        m->topbar = topbar;
+       m->gappx = gappx;
        m->lt[0] = &layouts[0];
        m->lt[1] = &layouts[1 % LENGTH(layouts)];
        strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
@@ -695,7 +721,7 @@ dirtomon(int dir)
 void
 drawbar(Monitor *m)
 {
-       int x, w, sw = 0;
+       int x, w, tw = 0;
        int boxs = drw->fonts->h / 9;
        int boxw = drw->fonts->h / 6 + 2;
        unsigned int i, occ = 0, urg = 0;
@@ -704,8 +730,8 @@ drawbar(Monitor *m)
        /* draw status first so it can be overdrawn by tags later */
        if (m == selmon) { /* status is only drawn on selected monitor */
                drw_setscheme(drw, scheme[SchemeNorm]);
-               sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
-               drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
+               tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
+               drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
        }
 
        for (c = m->clients; c; c = c->next) {
@@ -728,7 +754,7 @@ drawbar(Monitor *m)
        drw_setscheme(drw, scheme[SchemeNorm]);
        x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
 
-       if ((w = m->ww - sw - x) > bh) {
+       if ((w = m->ww - tw - x) > bh) {
                if (m->sel) {
                        drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
                        drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
@@ -1380,6 +1406,83 @@ run(void)
                        handler[ev.type](&ev); /* call handler */
 }
 
+void
+runautostart(void)
+{
+       char *pathpfx;
+       char *path;
+       char *xdgdatahome;
+       char *home;
+       struct stat sb;
+
+       if ((home = getenv("HOME")) == NULL)
+               /* this is almost impossible */
+               return;
+
+       /* if $XDG_DATA_HOME is set and not empty, use $XDG_DATA_HOME/dwm,
+        * otherwise use ~/.local/share/dwm as autostart script directory
+        */
+       xdgdatahome = getenv("XDG_DATA_HOME");
+       if (xdgdatahome != NULL && *xdgdatahome != '\0') {
+               /* space for path segments, separators and nul */
+               pathpfx = ecalloc(1, strlen(xdgdatahome) + strlen(dwmdir) + 2);
+
+               if (sprintf(pathpfx, "%s/%s", xdgdatahome, dwmdir) <= 0) {
+                       free(pathpfx);
+                       return;
+               }
+       } else {
+               /* space for path segments, separators and nul */
+               pathpfx = ecalloc(1, strlen(home) + strlen(localshare)
+                                    + strlen(dwmdir) + 3);
+
+               if (sprintf(pathpfx, "%s/%s/%s", home, localshare, dwmdir) < 0) {
+                       free(pathpfx);
+                       return;
+               }
+       }
+
+       /* check if the autostart script directory exists */
+       if (! (stat(pathpfx, &sb) == 0 && S_ISDIR(sb.st_mode))) {
+               /* the XDG conformant path does not exist or is no directory
+                * so we try ~/.dwm instead
+                */
+               char *pathpfx_new = realloc(pathpfx, strlen(home) + strlen(dwmdir) + 3);
+               if(pathpfx_new == NULL) {
+                       free(pathpfx);
+                       return;
+               }
+   pathpfx = pathpfx_new;
+
+               if (sprintf(pathpfx, "%s/.%s", home, dwmdir) <= 0) {
+                       free(pathpfx);
+                       return;
+               }
+       }
+
+       /* try the blocking script first */
+       path = ecalloc(1, strlen(pathpfx) + strlen(autostartblocksh) + 2);
+       if (sprintf(path, "%s/%s", pathpfx, autostartblocksh) <= 0) {
+               free(path);
+               free(pathpfx);
+       }
+
+       if (access(path, X_OK) == 0)
+               system(path);
+
+       /* now the non-blocking script */
+       if (sprintf(path, "%s/%s", pathpfx, autostartsh) <= 0) {
+               free(path);
+               free(pathpfx);
+       }
+
+       if (access(path, X_OK) == 0)
+               system(strcat(path, " &"));
+
+       free(pathpfx);
+       free(path);
+}
+
 void
 scan(void)
 {
@@ -1497,6 +1600,16 @@ setfullscreen(Client *c, int fullscreen)
        }
 }
 
+void
+setgaps(const Arg *arg)
+{
+       if ((arg->i == 0) || (selmon->gappx + arg->i < 0))
+               selmon->gappx = 0;
+       else
+               selmon->gappx += arg->i;
+       arrange(selmon);
+}
+
 void
 setlayout(const Arg *arg)
 {
@@ -1520,7 +1633,7 @@ setmfact(const Arg *arg)
        if (!arg || !selmon->lt[selmon->sellt]->arrange)
                return;
        f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
-       if (f < 0.1 || f > 0.9)
+       if (f < 0.05 || f > 0.95)
                return;
        selmon->mfact = f;
        arrange(selmon);
@@ -1683,16 +1796,18 @@ tile(Monitor *m)
        if (n > m->nmaster)
                mw = m->nmaster ? m->ww * m->mfact : 0;
        else
-               mw = m->ww;
-       for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+               mw = m->ww - m->gappx;
+       for (i = 0, my = ty = m->gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
                if (i < m->nmaster) {
-                       h = (m->wh - my) / (MIN(n, m->nmaster) - i);
-                       resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
-                       my += HEIGHT(c);
+                       h = (m->wh - my) / (MIN(n, m->nmaster) - i) - m->gappx;
+                       resize(c, m->wx + m->gappx, m->wy + my, mw - (2*c->bw) - m->gappx, h - (2*c->bw), 0);
+                       if (my + HEIGHT(c) + m->gappx < m->wh)
+                               my += HEIGHT(c) + m->gappx;
                } else {
-                       h = (m->wh - ty) / (n - i);
-                       resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
-                       ty += HEIGHT(c);
+                       h = (m->wh - ty) / (n - i) - m->gappx;
+                       resize(c, m->wx + mw + m->gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*m->gappx, h - (2*c->bw), 0);
+                       if (ty + HEIGHT(c) + m->gappx < m->wh)
+                               ty += HEIGHT(c) + m->gappx;
                }
 }
 
@@ -2124,6 +2239,60 @@ zoom(const Arg *arg)
        pop(c);
 }
 
+void
+resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst)
+{
+       char *sdst = NULL;
+       int *idst = NULL;
+       float *fdst = NULL;
+
+       sdst = dst;
+       idst = dst;
+       fdst = dst;
+
+       char fullname[256];
+       char *type;
+       XrmValue ret;
+
+       snprintf(fullname, sizeof(fullname), "%s.%s", "dwm", name);
+       fullname[sizeof(fullname) - 1] = '\0';
+
+       XrmGetResource(db, fullname, "*", &type, &ret);
+       if (!(ret.addr == NULL || strncmp("String", type, 64)))
+       {
+               switch (rtype) {
+               case STRING:
+                       strcpy(sdst, ret.addr);
+                       break;
+               case INTEGER:
+                       *idst = strtoul(ret.addr, NULL, 10);
+                       break;
+               case FLOAT:
+                       *fdst = strtof(ret.addr, NULL);
+                       break;
+               }
+       }
+}
+
+void
+load_xresources(void)
+{
+       Display *display;
+       char *resm;
+       XrmDatabase db;
+       ResourcePref *p;
+
+       display = XOpenDisplay(NULL);
+       resm = XResourceManagerString(display);
+       if (!resm)
+               return;
+
+       db = XrmGetStringDatabase(resm);
+       for (p = resources; p < resources + LENGTH(resources); p++)
+               resource_load(db, p->name, p->type, p->dst);
+       XCloseDisplay(display);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -2136,12 +2305,15 @@ main(int argc, char *argv[])
        if (!(dpy = XOpenDisplay(NULL)))
                die("dwm: cannot open display");
        checkotherwm();
+       XrmInitialize();
+       load_xresources();
        setup();
 #ifdef __OpenBSD__
        if (pledge("stdio rpath proc exec", NULL) == -1)
                die("pledge");
 #endif /* __OpenBSD__ */
        scan();
+       runautostart();
        run();
        cleanup();
        XCloseDisplay(dpy);