applied fake full screen
[dwm.git] / dwm.c
diff --git a/dwm.c b/dwm.c
index a5ce993..32ce5e4 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 */
@@ -56,7 +58,6 @@
 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
 #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
 #define TEXTW(X)                (drw_fontset_getwidth(drw, (X)) + lrpad)
-#define ColBorder               2
 
 /* enums */
 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
@@ -120,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];
@@ -142,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);
@@ -170,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);
@@ -194,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);
@@ -217,15 +235,15 @@ static void toggleview(const Arg *arg);
 static void unfocus(Client *c, int setfocus);
 static void unmanage(Client *c, int destroyed);
 static void unmapnotify(XEvent *e);
-static int updategeom(void);
 static void updatebarpos(Monitor *m);
 static void updatebars(void);
 static void updateclientlist(void);
+static int updategeom(void);
 static void updatenumlockmask(void);
 static void updatesizehints(Client *c);
 static void updatestatus(void);
-static void updatewindowtype(Client *c);
 static void updatetitle(Client *c);
+static void updatewindowtype(Client *c);
 static void updatewmhints(Client *c);
 static void view(const Arg *arg);
 static Client *wintoclient(Window w);
@@ -234,14 +252,22 @@ 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 */
 static int bh, blw = 0;      /* bar geometry */
 static int lrpad;            /* sum of left and right padding for text */
+static int vp;               /* vertical padding for bar */
+static int sp;               /* side padding for bar */
 static int (*xerrorxlib)(Display *, XErrorEvent *);
 static unsigned int numlockmask = 0;
 static void (*handler[LASTEvent]) (XEvent *) = {
@@ -263,7 +289,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
 static Atom wmatom[WMLast], netatom[NetLast];
 static int running = 1;
 static Cur *cursor[CurLast];
-static Scm *scheme;
+static Clr **scheme;
 static Display *dpy;
 static Drw *drw;
 static Monitor *mons, *selmon;
@@ -522,7 +548,7 @@ clientmessage(XEvent *e)
                if (cme->data.l[1] == netatom[NetWMFullscreen]
                || cme->data.l[2] == netatom[NetWMFullscreen])
                        setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD    */
-                               || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
+                               || cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */));
        } else if (cme->message_type == netatom[NetActiveWindow]) {
                if (c != selmon->sel && !c->isurgent)
                        seturgent(c, 1);
@@ -552,7 +578,6 @@ void
 configurenotify(XEvent *e)
 {
        Monitor *m;
-       Client *c;
        XConfigureEvent *ev = &e->xconfigure;
        int dirty;
 
@@ -565,10 +590,7 @@ configurenotify(XEvent *e)
                        drw_resize(drw, sw, bh);
                        updatebars();
                        for (m = mons; m; m = m->next) {
-                               for (c = m->clients; c; c = c->next)
-                                       if (c->isfullscreen)
-                                               resizeclient(c, m->mx, m->my, m->mw, m->mh);
-                               XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+                               XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, m->ww -  2 * sp, bh);
                        }
                        focus(NULL);
                        arrange(NULL);
@@ -639,6 +661,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);
@@ -696,7 +719,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;
@@ -705,8 +728,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 - 2 * sp, 0, tw, bh, 0, stext, 0);
        }
 
        for (c = m->clients; c; c = c->next) {
@@ -729,15 +752,15 @@ 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);
+                       drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0);
                        if (m->sel->isfloating)
                                drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
                } else {
                        drw_setscheme(drw, scheme[SchemeNorm]);
-                       drw_rect(drw, x, 0, w, bh, 1, 1);
+                       drw_rect(drw, x, 0, w - 2 * sp, bh, 1, 1);
                }
        }
        drw_map(drw, m->barwin, 0, 0, m->ww, bh);
@@ -910,8 +933,7 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size)
        if (!text || size == 0)
                return 0;
        text[0] = '\0';
-       XGetTextProperty(dpy, w, &name, atom);
-       if (!name.nitems)
+       if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
                return 0;
        if (name.encoding == XA_STRING)
                strncpy(text, (char *)name.value, size - 1);
@@ -1145,8 +1167,6 @@ movemouse(const Arg *arg)
 
        if (!(c = selmon->sel))
                return;
-       if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
-               return;
        restack(selmon);
        ocx = c->x;
        ocy = c->y;
@@ -1300,8 +1320,6 @@ resizemouse(const Arg *arg)
 
        if (!(c = selmon->sel))
                return;
-       if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
-               return;
        restack(selmon);
        ocx = c->x;
        ocy = c->y;
@@ -1382,6 +1400,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)
 {
@@ -1478,27 +1573,23 @@ setfullscreen(Client *c, int fullscreen)
                XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
                        PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
                c->isfullscreen = 1;
-               c->oldstate = c->isfloating;
-               c->oldbw = c->bw;
-               c->bw = 0;
-               c->isfloating = 1;
-               resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
-               XRaiseWindow(dpy, c->win);
        } else if (!fullscreen && c->isfullscreen){
                XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
                        PropModeReplace, (unsigned char*)0, 0);
                c->isfullscreen = 0;
-               c->isfloating = c->oldstate;
-               c->bw = c->oldbw;
-               c->x = c->oldx;
-               c->y = c->oldy;
-               c->w = c->oldw;
-               c->h = c->oldh;
-               resizeclient(c, c->x, c->y, c->w, c->h);
-               arrange(c->mon);
        }
 }
 
+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)
 {
@@ -1522,7 +1613,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);
@@ -1549,6 +1640,9 @@ setup(void)
        lrpad = drw->fonts->h;
        bh = drw->fonts->h + 2;
        updategeom();
+       sp = sidepad;
+       vp = (topbar == 1) ? vertpad : - vertpad;
+
        /* init atoms */
        utf8string = XInternAtom(dpy, "UTF8_STRING", False);
        wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
@@ -1569,18 +1663,19 @@ setup(void)
        cursor[CurResize] = drw_cur_create(drw, XC_sizing);
        cursor[CurMove] = drw_cur_create(drw, XC_fleur);
        /* init appearance */
-       scheme = ecalloc(LENGTH(colors), sizeof(Scm));
+       scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
        for (i = 0; i < LENGTH(colors); i++)
                scheme[i] = drw_scm_create(drw, colors[i], 3);
        /* init bars */
        updatebars();
        updatestatus();
+       updatebarpos(selmon);
        /* supporting window for NetWMCheck */
        wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
        XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
                PropModeReplace, (unsigned char *) &wmcheckwin, 1);
        XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
-               PropModeReplace, (unsigned char *) "dwm", 4);
+               PropModeReplace, (unsigned char *) "dwm", 3);
        XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
                PropModeReplace, (unsigned char *) &wmcheckwin, 1);
        /* EWMH support per view */
@@ -1620,7 +1715,7 @@ showhide(Client *c)
        if (ISVISIBLE(c)) {
                /* show clients top down */
                XMoveWindow(dpy, c->win, c->x, c->y);
-               if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+               if (!c->mon->lt[c->mon->sellt]->arrange || c->isfloating)
                        resize(c, c->x, c->y, c->w, c->h, 0);
                showhide(c->snext);
        } else {
@@ -1685,16 +1780,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;
                }
 }
 
@@ -1703,7 +1800,7 @@ togglebar(const Arg *arg)
 {
        selmon->showbar = !selmon->showbar;
        updatebarpos(selmon);
-       XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
+       XMoveResizeWindow(dpy, selmon->barwin, selmon->wx + sp, selmon->by + vp, selmon->ww - 2 * sp, bh);
        arrange(selmon);
 }
 
@@ -1712,8 +1809,6 @@ togglefloating(const Arg *arg)
 {
        if (!selmon->sel)
                return;
-       if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
-               return;
        selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
        if (selmon->sel->isfloating)
                resize(selmon->sel, selmon->sel->x, selmon->sel->y,
@@ -1809,14 +1904,16 @@ updatebars(void)
                .background_pixmap = ParentRelative,
                .event_mask = ButtonPressMask|ExposureMask
        };
+       XClassHint ch = {"dwm", "dwm"};
        for (m = mons; m; m = m->next) {
                if (m->barwin)
                        continue;
-               m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
+               m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, m->ww - 2 * sp, bh, 0, DefaultDepth(dpy, screen),
                                CopyFromParent, DefaultVisual(dpy, screen),
                                CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
                XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
                XMapRaised(dpy, m->barwin);
+               XSetClassHint(dpy, m->barwin, &ch);
        }
 }
 
@@ -1826,11 +1923,11 @@ updatebarpos(Monitor *m)
        m->wy = m->my;
        m->wh = m->mh;
        if (m->showbar) {
-               m->wh -= bh;
-               m->by = m->topbar ? m->wy : m->wy + m->wh;
-               m->wy = m->topbar ? m->wy + bh : m->wy;
+               m->wh = m->wh - vertpad - bh;
+               m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad;
+               m->wy = m->topbar ? m->wy + bh + vp : m->wy;
        } else
-               m->by = -bh;
+               m->by = -bh - vp;
 }
 
 void
@@ -1981,8 +2078,15 @@ updatesizehints(Client *c)
                c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
        } else
                c->maxa = c->mina = 0.0;
-       c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
-               && c->maxw == c->minw && c->maxh == c->minh);
+       c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
+}
+
+void
+updatestatus(void)
+{
+       if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+               strcpy(stext, "dwm-"VERSION);
+       drawbar(selmon);
 }
 
 void
@@ -1994,14 +2098,6 @@ updatetitle(Client *c)
                strcpy(c->name, broken);
 }
 
-void
-updatestatus(void)
-{
-       if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
-               strcpy(stext, "dwm-"VERSION);
-       drawbar(selmon);
-}
-
 void
 updatewindowtype(Client *c)
 {
@@ -2125,6 +2221,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[])
 {
@@ -2137,8 +2287,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);