/* See LICENSE file for copyright and license details. * * dynamic window manager is designed like any other X client as well. It is * driven through handling X events. In contrast to other X clients, a window * manager selects for SubstructureRedirectMask on the root window, to receive * events about window (dis-)appearance. Only one X connection at a time is * allowed to select for this event mask. * * Calls to fetch an X event from the event queue are blocking. Due reading * status text from standard input, a select()-driven main loop has been * implemented which selects for reads on the X connection and STDIN_FILENO to * handle all data smoothly. The event handlers of dwm are organized in an * array which is accessed whenever a new event has been fetched. This allows * event dispatching in O(1) time. * * Each child of the root window is called a client, except windows which have * set the override_redirect flag. Clients are organized in a global * doubly-linked client list, the focus history is remembered through a global * stack list. Each client contains an array of Bools of the same size as the * global tags array to indicate the tags of a client. * * Keys and tagging rules are organized as arrays and defined in config.h. * * To understand everything else, start reading main(). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* macros */ #define MAX(a, b) ((a)>(b)?(a):(b)) #define MIN(a, b) ((a)<(b)?(a):(b)) #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask)) #define LENGTH(x) (sizeof x / sizeof x[0]) #define MAXTAGLEN 16 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define DEFGEOM(GEONAME,BX,BY,BW,WX,WY,WW,WH,MX,MY,MW,MH,TX,TY,TW,TH,MOX,MOY,MOW,MOH) \ void GEONAME(void) { \ bx = (BX); by = (BY); bw = (BW); \ wx = (WX); wy = (WY); ww = (WW); wh = (WH); \ mx = (MX); my = (MY); mw = (MW); mh = (MH); \ tx = (TX); ty = (TY); tw = (TW); th = (TH); \ mox = (MOX); moy = (MOY); mow = (MOW); moh = (MOH); \ } /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ /* typedefs */ typedef struct Client Client; struct Client { char name[256]; int x, y, w, h; int fx, fy, fw, fh; int basew, baseh, incw, inch, maxw, maxh, minw, minh; int minax, maxax, minay, maxay; long flags; unsigned int bw, oldbw; Bool isbanned, isfixed, isfloating, isurgent; Bool *tags; Client *next; Client *prev; Client *snext; Window win; }; typedef struct { int x, y, w, h; unsigned long norm[ColLast]; unsigned long sel[ColLast]; Drawable drawable; GC gc; struct { int ascent; int descent; int height; XFontSet set; XFontStruct *xfont; } font; } DC; /* draw context */ typedef struct { const char *symbol; void (*apply)(void); } Geom; typedef struct { unsigned long mod; KeySym keysym; void (*func)(const char *arg); const char *arg; } Key; typedef struct { const char *symbol; void (*arrange)(void); Bool isfloating; } Layout; typedef struct { const char *class; const char *instance; const char *title; const char *tag; Bool isfloating; } Rule; /* function declarations */ void applyrules(Client *c); void arrange(void); void attach(Client *c); void attachstack(Client *c); void ban(Client *c); void buttonpress(XEvent *e); void checkotherwm(void); void cleanup(void); void configure(Client *c); void configurenotify(XEvent *e); void configurerequest(XEvent *e); unsigned int counttiled(void); void destroynotify(XEvent *e); void detach(Client *c); void detachstack(Client *c); void drawbar(void); void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]); void drawtext(const char *text, unsigned long col[ColLast], Bool invert); void *emallocz(unsigned int size); void enternotify(XEvent *e); void eprint(const char *errstr, ...); void expose(XEvent *e); void focus(Client *c); void focusin(XEvent *e); void focusnext(const char *arg); void focusprev(const char *arg); Client *getclient(Window w); unsigned long getcolor(const char *colstr); long getstate(Window w); Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); void grabbuttons(Client *c, Bool focused); void grabkeys(void); unsigned int idxoftag(const char *t); void initfont(const char *fontstr); Bool isoccupied(unsigned int t); Bool isprotodel(Client *c); Bool isurgent(unsigned int t); Bool isvisible(Client *c); void keypress(XEvent *e); void killclient(const char *arg); void manage(Window w, XWindowAttributes *wa); void mappingnotify(XEvent *e); void maprequest(XEvent *e); void monocle(void); void movemouse(Client *c); Client *nexttiled(Client *c); void propertynotify(XEvent *e); void quit(const char *arg); void reapply(const char *arg); void resize(Client *c, int x, int y, int w, int h, Bool sizehints); void resizemouse(Client *c); void restack(void); void run(void); void scan(void); void setclientstate(Client *c, long state); void setgeom(const char *arg); void setlayout(const char *arg); void setmfact(const char *arg); void setup(void); void spawn(const char *arg); void tag(const char *arg); unsigned int textnw(const char *text, unsigned int len); unsigned int textw(const char *text); void tileh(void); void tilehstack(unsigned int n); Client *tilemaster(unsigned int n); void tileresize(Client *c, int x, int y, int w, int h); void tilev(void); void tilevstack(unsigned int n); void togglefloating(const char *arg); void toggletag(const char *arg); void toggleview(const char *arg); void unban(Client *c); void unmanage(Client *c); void unmapnotify(XEvent *e); void updatebarpos(void); void updatesizehints(Client *c); void updatetitle(Client *c); void updatewmhints(Client *c); void view(const char *arg); void viewprevtag(const char *arg); /* views previous selected tags */ int xerror(Display *dpy, XErrorEvent *ee); int xerrordummy(Display *dpy, XErrorEvent *ee); int xerrorstart(Display *dpy, XErrorEvent *ee); void zoom(const char *arg); /* variables */ char stext[256]; int screen, sx, sy, sw, sh; int (*xerrorxlib)(Display *, XErrorEvent *); int bx, by, bw, bh, blw, bgw, mx, my, mw, mh, mox, moy, mow, moh, tx, ty, tw, th, wx, wy, ww, wh; int seltags = 0; double mfact; unsigned int numlockmask = 0; void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, [ConfigureRequest] = configurerequest, [ConfigureNotify] = configurenotify, [DestroyNotify] = destroynotify, [EnterNotify] = enternotify, [Expose] = expose, [FocusIn] = focusin, [KeyPress] = keypress, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [PropertyNotify] = propertynotify, [UnmapNotify] = unmapnotify }; Atom wmatom[WMLast], netatom[NetLast]; Bool otherwm, readin; Bool running = True; Bool *tagset[2]; Client *clients = NULL; Client *sel = NULL; Client *stack = NULL; Cursor cursor[CurLast]; Display *dpy; DC dc = {0}; Window root, barwin; /* configuration, allows nested code to access above variables */ #include "config.h" #define TAGSZ (LENGTH(tags) * sizeof(Bool)) Layout *lt = layouts; Geom *geom = geoms; /* function implementations */ void applyrules(Client *c) { unsigned int i; Bool matched = False; Rule *r; XClassHint ch = { 0 }; /* rule matching */ XGetClassHint(dpy, c->win, &ch); for(i = 0; i < LENGTH(rules); i++) { r = &rules[i]; if((!r->title || strstr(c->name, r->title)) && (!r->class || (ch.res_class && strstr(ch.res_class, r->class))) && (!r->instance || (ch.res_name && strstr(ch.res_name, r->instance)))) { c->isfloating = r->isfloating; if(r->tag) { c->tags[idxoftag(r->tag)] = True; matched = True; } } } if(ch.res_class) XFree(ch.res_class); if(ch.res_name) XFree(ch.res_name); if(!matched) memcpy(c->tags, tagset[seltags], TAGSZ); } void arrange(void) { Client *c; for(c = clients; c; c = c->next) if(isvisible(c)) { unban(c); if(lt->isfloating || c->isfloating) resize(c, c->fx, c->fy, c->fw, c->fh, True); } else ban(c); focus(NULL); if(lt->arrange) lt->arrange(); restack(); } void attach(Client *c) { if(clients) clients->prev = c; c->next = clients; clients = c; } void attachstack(Client *c) { c->snext = stack; stack = c; } void ban(Client *c) { if(c->isbanned) return; XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); c->isbanned = True; } void buttonpress(XEvent *e) { unsigned int i, x; Client *c; XButtonPressedEvent *ev = &e->xbutton; if(ev->window == barwin) { if((ev->x < bgw) && ev->button == Button1) { setgeom(NULL); return; } x = bgw; for(i = 0; i < LENGTH(tags); i++) { x += textw(tags[i]); if(ev->x >= bgw && ev->x < x) { if(ev->button == Button1) { if(ev->state & MODKEY) tag(tags[i]); else view(tags[i]); } else if(ev->button == Button3) { if(ev->state & MODKEY) toggletag(tags[i]); else toggleview(tags[i]); } return; } } if((ev->x < x + blw) && ev->button == Button1) setlayout(NULL); } else if((c = getclient(ev->window))) { focus(c); if(CLEANMASK(ev->state) != MODKEY) return; if(ev->button == Button1) { restack(); movemouse(c); } else if(ev->button == Button2) { if(!lt->isfloating && c->isfloating) togglefloating(NULL); else zoom(NULL); } else if(ev->button == Button3 && !c->isfixed) { restack(); resizemouse(c); } } } void checkotherwm(void) { otherwm = False; XSetErrorHandler(xerrorstart); /* this causes an error if some other window manager is running */ XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); XSync(dpy, False); if(otherwm) eprint("dwm: another window manager is already running\n"); XSync(dpy, False); XSetErrorHandler(NULL); xerrorxlib = XSetErrorHandler(xerror); XSync(dpy, False); } void cleanup(void) { close(STDIN_FILENO); while(stack) { unban(stack); unmanage(stack); } if(dc.font.set) XFreeFontSet(dpy, dc.font.set); else XFreeFont(dpy, dc.font.xfont); XUngrabKey(dpy, AnyKey, AnyModifier, root); XFreePixmap(dpy, dc.drawable); XFreeGC(dpy, dc.gc); XFreeCursor(dpy, cursor[CurNormal]); XFreeCursor(dpy, cursor[CurResize]); XFreeCursor(dpy, cursor[CurMove]); XDestroyWindow(dpy, barwin); XSync(dpy, False); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); } void configure(Client *c) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.display = dpy; ce.event = c->win; ce.window = c->win; ce.x = c->x; ce.y = c->y; ce.width = c->w; ce.height = c->h; ce.border_width = c->bw; ce.above = None; ce.override_redirect = False; XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); } void configurenotify(XEvent *e) { XConfigureEvent *ev = &e->xconfigure; if(ev->window == root && (ev->width != sw || ev->height != sh)) { sw = ev->width; sh = ev->height; setgeom(geom->symbol); } } void configurerequest(XEvent *e) { Client *c; XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; if((c = getclient(ev->window))) { if(ev->value_mask & CWBorderWidth) c->bw = ev->border_width; if(c->isfixed || c->isfloating || lt->isfloating) { if(ev->value_mask & CWX) c->x = sx + ev->x; if(ev->value_mask & CWY) c->y = sy + ev->y; if(ev->value_mask & CWWidth) c->w = ev->width; if(ev->value_mask & CWHeight) c->h = ev->height; if((c->x - sx + c->w) > sw && c->isfloating) c->x = sx + (sw / 2 - c->w /
/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */
#include "dwm.h"
#include <stdio.h>

/* static */

static Client *
nexttiled(Client *c) {
	for(c = getnext(c); c && c->isfloat; c = getnext(c->next));
	return c;
}

static void
togglemax(Client *c) {
	XEvent ev;
		
	if(c->isfixed)
		return;

	if((c->ismax = !c->ismax)) {
		c->rx = c->x; c->x = wax;
		c->ry = c->y; c->y = way;
		c->rw = c->w; c->w = waw - 2 * BORDERPX;
		c->rh = c->h; c->h = wah - 2 * BORDERPX;
	}
	else {
		c->x = c->rx;
		c->y = c->ry;
		c->w = c->rw;
		c->h = c->rh;
	}
	resize(c, True);
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
}

/* extern */

void (*arrange)(void) = DEFMODE;

void
detach(Client *c) {
	if(c->prev)
		c->prev->next = c->next;
	if(c->next)
		c->next->prev = c->prev;
	if(c == clients)
		clients = c->next;
	c->next = c->prev = NULL;
}

void
dofloat(void) {
	Client *c;

	for(c = clients; c; c = c->next) {
		if(isvisible(c)) {
			resize(c, True);
		}
		else
			XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
	}
	if(!sel || !isvisible(sel)) {
		for(c = stack; c && !isvisible(c); c = c->snext);
		focus(c);
	}
	restack();
}

void
dotile(void) {
	unsigned int i, n, mw, mh, tw, th;
	Client *c;

	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
		n++;
	/* window geoms */
	mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1);
	mw = (n > nmaster) ? (waw * master) / 1000 : waw;
	th = (n > nmaster) ? wah / (n - nmaster) : 0;
	tw = waw - mw;

	for(i = 0, c = clients; c; c = c->next)
		if(isvisible(c)) {
			if(c->isfloat) {
				resize(c, True);
				continue;
			}
			c->ismax = False;
			c->x = wax;
			c->y = way;
			if(i < nmaster) {
				c->y += i * mh;
				c->w = mw - 2 * BORDERPX;
				c->h = mh - 2 * BORDERPX;
			}
			else {  /* tile window */
				c->x += mw;
				c->w = tw - 2 * BORDERPX;
				if(th > 2 * BORDERPX) {
					c->y += (i - nmaster) * th;
					c->h = th - 2 * BORDERPX;
				}
				else /* fallback if th <= 2 * BORDERPX */
					c->h = wah - 2 * BORDERPX;
			}
			resize(c, False);
			i++;
		}
		else
			XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
	if(!sel || !isvisible(sel)) {
		for(c = stack; c && !isvisible(c); c = c->snext);
		focus(c);
	}
	restack();
}