diff options
Diffstat (limited to 'dwm.c')
-rw-r--r-- | dwm.c | 440 |
1 files changed, 368 insertions, 72 deletions
@@ -35,6 +35,7 @@ #include <X11/Xatom.h> #include <X11/Xlib.h> #include <X11/Xproto.h> +#include <X11/Xresource.h> #include <X11/Xutil.h> #ifdef XINERAMA #include <X11/extensions/Xinerama.h> @@ -47,15 +48,36 @@ /* macros */ #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define GETINC(X) ((X) - 2000) +#define INC(X) ((X) + 2000) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define ISINC(X) ((X) > 1000 && (X) < 3000) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#define PREVSEL 3000 #define LENGTH(X) (sizeof X / sizeof X[0]) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define MOD(N,M) ((N)%(M) < 0 ? (N)%(M) + (M) : (N)%(M)) #define WIDTH(X) ((X)->w + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw) #define TAGMASK ((1 << LENGTH(tags)) - 1) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define TRUNC(X,A,B) (MAX((A), MIN((X), (B)))) +#define XRDB_LOAD_COLOR(R,V) if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) { \ + if (value.addr != NULL && strnlen(value.addr, 8) == 7 && value.addr[0] == '#') { \ + int i = 1; \ + for (; i <= 6; i++) { \ + if (value.addr[i] < 48) break; \ + if (value.addr[i] > 57 && value.addr[i] < 65) break; \ + if (value.addr[i] > 70 && value.addr[i] < 97) break; \ + if (value.addr[i] > 102) break; \ + } \ + if (i == 7) { \ + strncpy(V, value.addr, 7); \ + V[7] = '\0'; \ + } \ + } \ + } /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ @@ -92,7 +114,7 @@ struct Client { int basew, baseh, incw, inch, maxw, maxh, minw, minh; int bw, oldbw; unsigned int tags; - int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, issticky; Client *next; Client *snext; Monitor *mon; @@ -107,6 +129,11 @@ typedef struct { } Key; typedef struct { + const char * sig; + void (*func)(const Arg *); +} Signal; + +typedef struct { const char *symbol; void (*arrange)(Monitor *); } Layout; @@ -119,6 +146,10 @@ struct Monitor { int by; /* bar geometry */ int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ unsigned int seltags; unsigned int sellt; unsigned int tagset[2]; @@ -148,6 +179,7 @@ static void arrange(Monitor *m); static void arrangemon(Monitor *m); static void attach(Client *c); static void attachstack(Client *c); +static int fake_signal(void); static void buttonpress(XEvent *e); static void checkotherwm(void); static void cleanup(void); @@ -156,6 +188,7 @@ static void clientmessage(XEvent *e); static void configure(Client *c); static void configurenotify(XEvent *e); static void configurerequest(XEvent *e); +static void copyvalidchars(char *text, char *rawtext); static Monitor *createmon(void); static void destroynotify(XEvent *e); static void detach(Client *c); @@ -169,6 +202,7 @@ static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static int getdwmblockspid(); static int getrootptr(int *x, int *y); static long getstate(Window w); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); @@ -177,6 +211,7 @@ static void grabkeys(void); static void incnmaster(const Arg *arg); static void keypress(XEvent *e); static void killclient(const Arg *arg); +static void loadxrdb(void); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); @@ -186,6 +221,7 @@ static void movemouse(const Arg *arg); static Client *nexttiled(Client *c); static void pop(Client *); static void propertynotify(XEvent *e); +static void pushstack(const Arg *arg); static void quit(const Arg *arg); static Monitor *recttomon(int x, int y, int w, int h); static void resize(Client *c, int x, int y, int w, int h, int interact); @@ -193,6 +229,7 @@ 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); @@ -205,12 +242,18 @@ static void setup(void); static void seturgent(Client *c, int urg); static void showhide(Client *c); static void sigchld(int unused); +static void sigdwmblocks(const Arg *arg); +static void sighup(int unused); +static void sigterm(int unused); static void spawn(const Arg *arg); +static int stackpos(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); -static void tile(Monitor *); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); +static void togglesticky(const Arg *arg); +static void togglefullscr(const Arg *arg); +static void togglescratch(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); @@ -232,11 +275,15 @@ static Monitor *wintomon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void xrdb(const Arg *arg); static void zoom(const Arg *arg); /* variables */ static const char broken[] = "broken"; static char stext[256]; +static char rawstext[256]; +static int dwmblockssig; +pid_t dwmblockspid = 0; static int screen; static int sw, sh; /* X display screen geometry width, height */ static int bh, blw = 0; /* bar geometry */ @@ -260,6 +307,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { [UnmapNotify] = unmapnotify }; static Atom wmatom[WMLast], netatom[NetLast]; +static int restart = 0; static int running = 1; static Cur *cursor[CurLast]; static Clr **scheme; @@ -271,6 +319,8 @@ static Window root, wmcheckwin; /* configuration, allows nested code to access above variables */ #include "config.h" +static unsigned int scratchtag = 1 << LENGTH(tags); + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -416,7 +466,7 @@ attachstack(Client *c) void buttonpress(XEvent *e) { - unsigned int i, x, click; + unsigned int i, x, click, occ = 0; Arg arg = {0}; Client *c; Monitor *m; @@ -431,17 +481,39 @@ buttonpress(XEvent *e) } if (ev->window == selmon->barwin) { i = x = 0; - do + for (c = m->clients; c; c = c->next) + occ |= c->tags == 255 ? 0 : c->tags; + do { + /* do not reserve space for vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; x += TEXTW(tags[i]); - while (ev->x >= x && ++i < LENGTH(tags)); + } while (ev->x >= x && ++i < LENGTH(tags)); if (i < LENGTH(tags)) { click = ClkTagBar; arg.ui = 1 << i; } else if (ev->x < x + blw) click = ClkLtSymbol; - else if (ev->x > selmon->ww - TEXTW(stext)) + else if (ev->x > (x = selmon->ww - TEXTW(stext) + lrpad)) { click = ClkStatusText; - else + + char *text = rawstext; + int i = -1; + char ch; + dwmblockssig = 0; + while (text[++i]) { + if ((unsigned char)text[i] < ' ') { + ch = text[i]; + text[i] = '\0'; + x += TEXTW(text) - lrpad; + text[i] = ch; + text += i+1; + i = -1; + if (x >= ev->x) break; + dwmblockssig = ch; + } + } + } else click = ClkWinTitle; } else if ((c = wintoclient(ev->window))) { focus(c); @@ -627,6 +699,19 @@ configurerequest(XEvent *e) XSync(dpy, False); } +void +copyvalidchars(char *text, char *rawtext) +{ + int i = -1, j = 0; + + while(rawtext[++i]) { + if ((unsigned char)rawtext[i] >= ' ') { + text[j++] = rawtext[i]; + } + } + text[j] = '\0'; +} + Monitor * createmon(void) { @@ -638,6 +723,10 @@ createmon(void) m->nmaster = nmaster; m->showbar = showbar; m->topbar = topbar; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); @@ -709,19 +798,19 @@ drawbar(Monitor *m) } for (c = m->clients; c; c = c->next) { - occ |= c->tags; + occ |= c->tags == 255 ? 0 : c->tags; if (c->isurgent) urg |= c->tags; } x = 0; for (i = 0; i < LENGTH(tags); i++) { + /* do not draw vacant tags */ + if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + w = TEXTW(tags[i]); drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); - if (occ & 1 << i) - drw_rect(drw, x + boxs, boxs, boxw, boxw, - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, - urg & 1 << i); x += w; } w = blw = TEXTW(m->ltsymbol); @@ -832,27 +921,16 @@ focusmon(const Arg *arg) void focusstack(const Arg *arg) { - Client *c = NULL, *i; + int i = stackpos(arg); + Client *c, *p; - if (!selmon->sel) + if (i < 0 || selmon->sel->isfullscreen) return; - if (arg->i > 0) { - for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); - if (!c) - for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); - } else { - for (i = selmon->clients; i != selmon->sel; i = i->next) - if (ISVISIBLE(i)) - c = i; - if (!c) - for (; i; i = i->next) - if (ISVISIBLE(i)) - c = i; - } - if (c) { - focus(c); - restack(selmon); - } + + for(p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c)); + i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next); + focus(c ? c : p); + restack(selmon); } Atom @@ -872,6 +950,18 @@ getatomprop(Client *c, Atom prop) } int +getdwmblockspid() +{ + char buf[16]; + FILE *fp = popen("pidof -s dwmblocks", "r"); + fgets(buf, sizeof(buf), fp); + pid_t pid = strtoul(buf, NULL, 10); + pclose(fp); + dwmblockspid = pid; + return pid != 0 ? 0 : -1; +} + +int getrootptr(int *x, int *y) { int di; @@ -998,6 +1088,49 @@ keypress(XEvent *e) keys[i].func(&(keys[i].arg)); } +int +fake_signal(void) +{ + char fsignal[256]; + char indicator[9] = "fsignal:"; + char str_sig[50]; + char param[16]; + int i, len_str_sig, n, paramn; + size_t len_fsignal, len_indicator = strlen(indicator); + Arg arg; + + // Get root name property + if (gettextprop(root, XA_WM_NAME, fsignal, sizeof(fsignal))) { + len_fsignal = strlen(fsignal); + + // Check if this is indeed a fake signal + if (len_indicator > len_fsignal ? 0 : strncmp(indicator, fsignal, len_indicator) == 0) { + paramn = sscanf(fsignal+len_indicator, "%s%n%s%n", str_sig, &len_str_sig, param, &n); + + if (paramn == 1) arg = (Arg) {0}; + else if (paramn > 2) return 1; + else if (strncmp(param, "i", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%i", &(arg.i)); + else if (strncmp(param, "ui", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%u", &(arg.ui)); + else if (strncmp(param, "f", n - len_str_sig) == 0) + sscanf(fsignal + len_indicator + n, "%f", &(arg.f)); + else return 1; + + // Check if a signal was found, and if so handle it + for (i = 0; i < LENGTH(signals); i++) + if (strncmp(str_sig, signals[i].sig, len_str_sig) == 0 && signals[i].func) + signals[i].func(&(arg)); + + // A fake signal was sent + return 1; + } + } + + // No fake signal was sent, so proceed with update + return 0; +} + void killclient(const Arg *arg) { @@ -1015,6 +1148,37 @@ killclient(const Arg *arg) } void +loadxrdb() +{ + Display *display; + char * resm; + XrmDatabase xrdb; + char *type; + XrmValue value; + + display = XOpenDisplay(NULL); + + if (display != NULL) { + resm = XResourceManagerString(display); + + if (resm != NULL) { + xrdb = XrmGetStringDatabase(resm); + + if (xrdb != NULL) { + XRDB_LOAD_COLOR("dwm.color0", normbordercolor); + XRDB_LOAD_COLOR("dwm.color8", selbordercolor); + XRDB_LOAD_COLOR("dwm.color0", normbgcolor); + XRDB_LOAD_COLOR("dwm.color6", normfgcolor); + XRDB_LOAD_COLOR("dwm.color0", selfgcolor); + XRDB_LOAD_COLOR("dwm.color14", selbgcolor); + } + } + } + + XCloseDisplay(display); +} + +void manage(Window w, XWindowAttributes *wa) { Client *c, *t = NULL; @@ -1049,6 +1213,14 @@ manage(Window w, XWindowAttributes *wa) && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); c->bw = borderpx; + selmon->tagset[selmon->seltags] &= ~scratchtag; + if (!strcmp(c->name, scratchpadname)) { + c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag; + c->isfloating = True; + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + wc.border_width = c->bw; XConfigureWindow(dpy, w, CWBorderWidth, &wc); XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); @@ -1103,16 +1275,16 @@ maprequest(XEvent *e) void monocle(Monitor *m) { - unsigned int n = 0; - Client *c; + unsigned int n = 0; + Client *c; - for (c = m->clients; c; c = c->next) - if (ISVISIBLE(c)) - n++; - if (n > 0) /* override layout symbol */ - snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); - for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) - resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); } void @@ -1209,17 +1381,41 @@ pop(Client *c) } void +pushstack(const Arg *arg) { + int i = stackpos(arg); + Client *sel = selmon->sel, *c, *p; + + if(i < 0) + return; + else if(i == 0) { + detach(sel); + attach(sel); + } + else { + for(p = NULL, c = selmon->clients; c; p = c, c = c->next) + if(!(i -= (ISVISIBLE(c) && c != sel))) + break; + c = c ? c : p; + detach(sel); + sel->next = c->next; + c->next = sel; + } + arrange(selmon); +} + +void propertynotify(XEvent *e) { Client *c; Window trans; XPropertyEvent *ev = &e->xproperty; - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) - updatestatus(); - else if (ev->state == PropertyDelete) + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { + if (!fake_signal()) + updatestatus(); + } else if (ev->state == PropertyDelete) { return; /* ignore */ - else if ((c = wintoclient(ev->window))) { + } else if ((c = wintoclient(ev->window))) { switch(ev->atom) { default: break; case XA_WM_TRANSIENT_FOR: @@ -1248,6 +1444,7 @@ propertynotify(XEvent *e) void quit(const Arg *arg) { + if(arg->i) restart = 1; running = 0; } @@ -1381,6 +1578,11 @@ run(void) } void +runAutostart(void) { + system("killall dwmblocks 2>/dev/null ; dwmblocks &"); +} + +void scan(void) { unsigned int i, num; @@ -1497,6 +1699,36 @@ setfullscreen(Client *c, int fullscreen) } } +int +stackpos(const Arg *arg) { + int n, i; + Client *c, *l; + + if(!selmon->clients) + return -1; + + if(arg->i == PREVSEL) { + for(l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel); l = l->snext); + if(!l) + return -1; + for(i = 0, c = selmon->clients; c != l; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + return i; + } + else if(ISINC(arg->i)) { + if(!selmon->sel) + return -1; + for(i = 0, c = selmon->clients; c != selmon->sel; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + for(n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next); + return MOD(i + GETINC(arg->i), n); + } + else if(arg->i < 0) { + for(i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next); + return MAX(i + arg->i, 0); + } + else + return arg->i; +} + void setlayout(const Arg *arg) { @@ -1536,6 +1768,9 @@ setup(void) /* clean up any zombies immediately */ sigchld(0); + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + /* init screen */ screen = DefaultScreen(dpy); sw = DisplayWidth(dpy, screen); @@ -1637,10 +1872,42 @@ sigchld(int unused) } void +sighup(int unused) +{ + Arg a = {.i = 1}; + quit(&a); +} + +void +sigterm(int unused) +{ + Arg a = {.i = 0}; + quit(&a); +} + +void +sigdwmblocks(const Arg *arg) +{ + union sigval sv; + sv.sival_int = 0 | (dwmblockssig << 8) | arg->i; + if (!dwmblockspid) + if (getdwmblockspid() == -1) + return; + + if (sigqueue(dwmblockspid, SIGUSR1, sv) == -1) { + if (errno == ESRCH) { + if (!getdwmblockspid()) + sigqueue(dwmblockspid, SIGUSR1, sv); + } + } +} + +void spawn(const Arg *arg) { if (arg->v == dmenucmd) dmenumon[0] = '0' + selmon->num; + selmon->tagset[selmon->seltags] &= ~scratchtag; if (fork() == 0) { if (dpy) close(ConnectionNumber(dpy)); @@ -1671,32 +1938,6 @@ tagmon(const Arg *arg) } void -tile(Monitor *m) -{ - unsigned int i, n, h, mw, my, ty; - Client *c; - - for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); - if (n == 0) - return; - - 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++) - 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); - } 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); - } -} - -void togglebar(const Arg *arg) { selmon->showbar = !selmon->showbar; @@ -1720,6 +1961,44 @@ togglefloating(const Arg *arg) } void +togglescratch(const Arg *arg) +{ + Client *c; + unsigned int found = 0; + + for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next); + if (found) { + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag; + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(NULL); + arrange(selmon); + } + if (ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else + spawn(arg); +} + +void +togglefullscr(const Arg *arg) +{ + if(selmon->sel) + setfullscreen(selmon->sel, !selmon->sel->isfullscreen); +} + +void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + selmon->sel->issticky = !selmon->sel->issticky; + arrange(selmon); +} + +void toggletag(const Arg *arg) { unsigned int newtags; @@ -1987,8 +2266,10 @@ updatesizehints(Client *c) void updatestatus(void) { - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); drawbar(selmon); } @@ -2111,6 +2392,17 @@ xerrorstart(Display *dpy, XErrorEvent *ee) } void +xrdb(const Arg *arg) +{ + loadxrdb(); + int i; + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + focus(NULL); + arrange(NULL); +} + +void zoom(const Arg *arg) { Client *c = selmon->sel; @@ -2136,13 +2428,17 @@ main(int argc, char *argv[]) if (!(dpy = XOpenDisplay(NULL))) die("dwm: cannot open display"); checkotherwm(); + XrmInitialize(); + loadxrdb(); setup(); #ifdef __OpenBSD__ if (pledge("stdio rpath proc exec", NULL) == -1) die("pledge"); #endif /* __OpenBSD__ */ scan(); + runAutostart(); run(); + if(restart) execvp(argv[0], argv); cleanup(); XCloseDisplay(dpy); return EXIT_SUCCESS; |