dwm.c
changeset 1129 43d862bda73e
parent 1128 538657548b5d
child 1130 b661ad410646
equal deleted inserted replaced
1128:538657548b5d 1129:43d862bda73e
     1 /**
     1 /**
     2  * TODO
       
     3  * - treat monocle as floating layout, actually otherwise certain monocled windows don't get raised
       
     4  * - use WX, WY, WW, WH for window snapping/resizing/mouse
       
     5  * - MOX, MOY, MOW, MOH should only be used in the case of monocle layout and of n==1 in tiled
       
     6  * - simplify tile()
       
     7  * - allow for vstack
     2  * - allow for vstack
     8  */
     3  */
     9 /* See LICENSE file for copyright and license details.
     4 /* See LICENSE file for copyright and license details.
    10  *
     5  *
    11  * dynamic window manager is designed like any other X client as well. It is
     6  * dynamic window manager is designed like any other X client as well. It is
    65 /* typedefs */
    60 /* typedefs */
    66 typedef struct Client Client;
    61 typedef struct Client Client;
    67 struct Client {
    62 struct Client {
    68 	char name[256];
    63 	char name[256];
    69 	int x, y, w, h;
    64 	int x, y, w, h;
    70 	int rx, ry, rw, rh;
       
    71 	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
    65 	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
    72 	int minax, maxax, minay, maxay;
    66 	int minax, maxax, minay, maxay;
    73 	long flags;
    67 	long flags;
    74 	unsigned int border, oldborder;
    68 	unsigned int border, oldborder;
    75 	Bool isbanned, isfixed, isfloating, isurgent;
    69 	Bool isbanned, isfixed, isfloating, isurgent;
   103 } Key;
    97 } Key;
   104 
    98 
   105 typedef struct {
    99 typedef struct {
   106 	const char *symbol;
   100 	const char *symbol;
   107 	void (*arrange)(void);
   101 	void (*arrange)(void);
       
   102 	Bool isfloating;
   108 } Layout;
   103 } Layout;
   109 
   104 
   110 typedef struct {
   105 typedef struct {
   111 	const char *prop;
   106 	const char *prop;
   112 	const char *tag;
   107 	const char *tag;
   174 void spawn(const char *arg);
   169 void spawn(const char *arg);
   175 void tag(const char *arg);
   170 void tag(const char *arg);
   176 unsigned int textnw(const char *text, unsigned int len);
   171 unsigned int textnw(const char *text, unsigned int len);
   177 unsigned int textw(const char *text);
   172 unsigned int textw(const char *text);
   178 void tile(void);
   173 void tile(void);
       
   174 unsigned int tilemaster(void);
       
   175 void tilevstack(unsigned int n);
   179 void togglefloating(const char *arg);
   176 void togglefloating(const char *arg);
   180 void toggletag(const char *arg);
   177 void toggletag(const char *arg);
   181 void toggleview(const char *arg);
   178 void toggleview(const char *arg);
   182 void unban(Client *c);
   179 void unban(Client *c);
   183 void unmanage(Client *c);
   180 void unmanage(Client *c);
   211 	[MapRequest] = maprequest,
   208 	[MapRequest] = maprequest,
   212 	[PropertyNotify] = propertynotify,
   209 	[PropertyNotify] = propertynotify,
   213 	[UnmapNotify] = unmapnotify
   210 	[UnmapNotify] = unmapnotify
   214 };
   211 };
   215 Atom wmatom[WMLast], netatom[NetLast];
   212 Atom wmatom[WMLast], netatom[NetLast];
   216 Bool dozoom = True;
       
   217 Bool otherwm, readin;
   213 Bool otherwm, readin;
   218 Bool running = True;
   214 Bool running = True;
   219 Bool *prevtags;
   215 Bool *prevtags;
   220 Bool *seltags;
   216 Bool *seltags;
   221 Client *clients = NULL;
   217 Client *clients = NULL;
   295 
   291 
   296 void
   292 void
   297 ban(Client *c) {
   293 ban(Client *c) {
   298 	if(c->isbanned)
   294 	if(c->isbanned)
   299 		return;
   295 		return;
   300 	XMoveWindow(dpy, c->win, c->x + 3 * sw, c->y);
   296 	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
   301 	c->isbanned = True;
   297 	c->isbanned = True;
   302 }
   298 }
   303 
   299 
   304 void
   300 void
   305 buttonpress(XEvent *e) {
   301 buttonpress(XEvent *e) {
   426 	XWindowChanges wc;
   422 	XWindowChanges wc;
   427 
   423 
   428 	if((c = getclient(ev->window))) {
   424 	if((c = getclient(ev->window))) {
   429 		if(ev->value_mask & CWBorderWidth)
   425 		if(ev->value_mask & CWBorderWidth)
   430 			c->border = ev->border_width;
   426 			c->border = ev->border_width;
   431 		if(c->isfixed || c->isfloating || (floating == lt->arrange)) {
   427 		if(c->isfixed || c->isfloating || lt->isfloating) {
   432 			if(ev->value_mask & CWX)
   428 			if(ev->value_mask & CWX)
   433 				c->x = sx + ev->x;
   429 				c->x = sx + ev->x;
   434 			if(ev->value_mask & CWY)
   430 			if(ev->value_mask & CWY)
   435 				c->y = sy + ev->y;
   431 				c->y = sy + ev->y;
   436 			if(ev->value_mask & CWWidth)
   432 			if(ev->value_mask & CWWidth)
   635 
   631 
   636 void
   632 void
   637 floating(void) { /* default floating layout */
   633 floating(void) { /* default floating layout */
   638 	Client *c;
   634 	Client *c;
   639 
   635 
   640 	dozoom = False;
       
   641 	for(c = clients; c; c = c->next)
   636 	for(c = clients; c; c = c->next)
   642 		if(isvisible(c))
   637 		if(isvisible(c))
   643 			resize(c, c->x, c->y, c->w, c->h, True);
   638 			resize(c, c->x, c->y, c->w, c->h, True);
   644 }
   639 }
   645 
   640 
   648 	if(!c || (c && !isvisible(c)))
   643 	if(!c || (c && !isvisible(c)))
   649 		for(c = stack; c && !isvisible(c); c = c->snext);
   644 		for(c = stack; c && !isvisible(c); c = c->snext);
   650 	if(sel && sel != c) {
   645 	if(sel && sel != c) {
   651 		grabbuttons(sel, False);
   646 		grabbuttons(sel, False);
   652 		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
   647 		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
   653 		if(lt->arrange == monocle)
       
   654 			resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
       
   655 	}
   648 	}
   656 	if(c) {
   649 	if(c) {
   657 		detachstack(c);
   650 		detachstack(c);
   658 		attachstack(c);
   651 		attachstack(c);
   659 		grabbuttons(c, True);
   652 		grabbuttons(c, True);
   660 		if(lt->arrange == monocle) {
       
   661 			if(sel != c) {
       
   662 				c->rx = c->x;
       
   663 				c->ry = c->y;
       
   664 				c->rw = c->w;
       
   665 				c->rh = c->h;
       
   666 			}
       
   667 			resize(c, MOX, MOY, MOW, MOH, RESIZEHINTS);
       
   668 		}
       
   669 	}
   653 	}
   670 	sel = c;
   654 	sel = c;
   671 	if(c) {
   655 	if(c) {
   672 		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
   656 		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
   673 		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
   657 		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
   985 
   969 
   986 	c = emallocz(sizeof(Client));
   970 	c = emallocz(sizeof(Client));
   987 	c->tags = emallocz(TAGSZ);
   971 	c->tags = emallocz(TAGSZ);
   988 	c->win = w;
   972 	c->win = w;
   989 
   973 
   990 	c->x = c->rx = wa->x + sx;
   974 	/* geometry */
   991 	c->y = c->ry = wa->y + sy;
   975 	c->x = wa->x;
   992 	c->w = c->rw = wa->width;
   976 	c->y = wa->y;
   993 	c->h = c->rh = wa->height;
   977 	c->w = wa->width;
       
   978 	c->h = wa->height;
   994 	c->oldborder = wa->border_width;
   979 	c->oldborder = wa->border_width;
   995 
       
   996 	if(c->w == sw && c->h == sh) {
   980 	if(c->w == sw && c->h == sh) {
   997 		c->x = sx;
   981 		c->x = sx;
   998 		c->y = sy;
   982 		c->y = sy;
   999 		c->border = wa->border_width;
   983 		c->border = wa->border_width;
  1000 	}
   984 	}
  1001 	else {
   985 	else {
  1002 		if(c->x + c->w + 2 * c->border > sx + sw)
   986 		if(c->x + c->w + 2 * c->border > WX + WW)
  1003 			c->x = sx + sw - c->w - 2 * c->border;
   987 			c->x = WX + WW - c->w - 2 * c->border;
  1004 		if(c->y + c->h + 2 * c->border > sy + sh)
   988 		if(c->y + c->h + 2 * c->border > WY + WH)
  1005 			c->y = sy + sh - c->h - 2 * c->border;
   989 			c->y = WY + WH - c->h - 2 * c->border;
  1006 		if(c->x < sx)
   990 		if(c->x < WX)
  1007 			c->x = sx;
   991 			c->x = WX;
  1008 		if(c->y < sy)
   992 		if(c->y < WY)
  1009 			c->y = sy;
   993 			c->y = WY;
  1010 		c->border = BORDERPX;
   994 		c->border = BORDERPX;
  1011 	}
   995 	}
       
   996 
  1012 	wc.border_width = c->border;
   997 	wc.border_width = c->border;
  1013 	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
   998 	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
  1014 	XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
   999 	XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
  1015 	configure(c); /* propagates border_width, if size doesn't change */
  1000 	configure(c); /* propagates border_width, if size doesn't change */
  1016 	updatesizehints(c);
  1001 	updatesizehints(c);
  1055 	if(!getclient(ev->window))
  1040 	if(!getclient(ev->window))
  1056 		manage(ev->window, &wa);
  1041 		manage(ev->window, &wa);
  1057 }
  1042 }
  1058 
  1043 
  1059 void
  1044 void
  1060 monocle(void) {
  1045 monocle(void) { 
  1061 	dozoom = False;
  1046 	Client *c;
       
  1047 
       
  1048 	for(c = clients; c; c = c->next)
       
  1049 		if(isvisible(c))
       
  1050 			resize(c, MOX, MOY, MOW, MOH, RESIZEHINTS);
  1062 }
  1051 }
  1063 
  1052 
  1064 void
  1053 void
  1065 movemouse(Client *c) {
  1054 movemouse(Client *c) {
  1066 	int x1, y1, ocx, ocy, di, nx, ny;
  1055 	int x1, y1, ocx, ocy, di, nx, ny;
  1087 			break;
  1076 			break;
  1088 		case MotionNotify:
  1077 		case MotionNotify:
  1089 			XSync(dpy, False);
  1078 			XSync(dpy, False);
  1090 			nx = ocx + (ev.xmotion.x - x1);
  1079 			nx = ocx + (ev.xmotion.x - x1);
  1091 			ny = ocy + (ev.xmotion.y - y1);
  1080 			ny = ocy + (ev.xmotion.y - y1);
  1092 			if(abs(sx - nx) < SNAP)
  1081 			if(abs(WX - nx) < SNAP)
  1093 				nx = sx;
  1082 				nx = WX;
  1094 			else if(abs((sx + sw) - (nx + c->w + 2 * c->border)) < SNAP)
  1083 			else if(abs((WX + WW) - (nx + c->w + 2 * c->border)) < SNAP)
  1095 				nx = sx + sw - c->w - 2 * c->border;
  1084 				nx = WX + WW - c->w - 2 * c->border;
  1096 			if(abs(sy - ny) < SNAP)
  1085 			if(abs(WY - ny) < SNAP)
  1097 				ny = sy;
  1086 				ny = WY;
  1098 			else if(abs((sy + sh) - (ny + c->h + 2 * c->border)) < SNAP)
  1087 			else if(abs((WY + WH) - (ny + c->h + 2 * c->border)) < SNAP)
  1099 				ny = sy + sh - c->h - 2 * c->border;
  1088 				ny = WY + WH - c->h - 2 * c->border;
  1100 			if(!c->isfloating && (lt->arrange != floating) && (abs(nx - c->x) > SNAP || abs(ny - c->y) > SNAP))
  1089 			if(!c->isfloating && !lt->isfloating && (abs(nx - c->x) > SNAP || abs(ny - c->y) > SNAP))
  1101 				togglefloating(NULL);
  1090 				togglefloating(NULL);
  1102 			if((lt->arrange == floating) || c->isfloating)
  1091 			if((lt->isfloating) || c->isfloating)
  1103 				resize(c, nx, ny, c->w, c->h, False);
  1092 				resize(c, nx, ny, c->w, c->h, False);
  1104 			break;
  1093 			break;
  1105 		}
  1094 		}
  1106 	}
  1095 	}
  1107 }
  1096 }
  1256 			XSync(dpy, False);
  1245 			XSync(dpy, False);
  1257 			if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
  1246 			if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
  1258 				nw = 1;
  1247 				nw = 1;
  1259 			if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
  1248 			if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
  1260 				nh = 1;
  1249 				nh = 1;
  1261 			if(!c->isfloating && (lt->arrange != floating) && (abs(nw - c->w) > SNAP || abs(nh - c->h) > SNAP))
  1250 			if(!c->isfloating && !lt->isfloating && (abs(nw - c->w) > SNAP || abs(nh - c->h) > SNAP))
  1262 				togglefloating(NULL);
  1251 				togglefloating(NULL);
  1263 			if((lt->arrange == floating) || c->isfloating)
  1252 			if((lt->isfloating) || c->isfloating)
  1264 				resize(c, c->x, c->y, nw, nh, True);
  1253 				resize(c, c->x, c->y, nw, nh, True);
  1265 			break;
  1254 			break;
  1266 		}
  1255 		}
  1267 	}
  1256 	}
  1268 }
  1257 }
  1274 	XWindowChanges wc;
  1263 	XWindowChanges wc;
  1275 
  1264 
  1276 	drawbar();
  1265 	drawbar();
  1277 	if(!sel)
  1266 	if(!sel)
  1278 		return;
  1267 		return;
  1279 	if(sel->isfloating || (lt->arrange == floating))
  1268 	if(sel->isfloating || lt->isfloating)
  1280 		XRaiseWindow(dpy, sel->win);
  1269 		XRaiseWindow(dpy, sel->win);
  1281 	if(lt->arrange != floating) {
  1270 	if(!lt->isfloating) {
  1282 		wc.stack_mode = Below;
  1271 		wc.stack_mode = Below;
  1283 		wc.sibling = barwin;
  1272 		wc.sibling = barwin;
  1284 		if(!sel->isfloating) {
  1273 		if(!sel->isfloating) {
  1285 			XConfigureWindow(dpy, sel->win, CWSibling|CWStackMode, &wc);
  1274 			XConfigureWindow(dpy, sel->win, CWSibling|CWStackMode, &wc);
  1286 			wc.sibling = sel->win;
  1275 			wc.sibling = sel->win;
  1391 			PropModeReplace, (unsigned char *)data, 2);
  1380 			PropModeReplace, (unsigned char *)data, 2);
  1392 }
  1381 }
  1393 
  1382 
  1394 void
  1383 void
  1395 setlayout(const char *arg) {
  1384 setlayout(const char *arg) {
       
  1385 	static Layout *revert = 0;
  1396 	unsigned int i;
  1386 	unsigned int i;
  1397 
  1387 
  1398 	if(!arg)
  1388 	if(!arg)
  1399 		return;
  1389 		return;
  1400 	for(i = 0; i < LENGTH(layouts); i++)
  1390 	for(i = 0; i < LENGTH(layouts); i++)
  1401 		if(!strcmp(arg, layouts[i].symbol))
  1391 		if(!strcmp(arg, layouts[i].symbol))
  1402 			break;
  1392 			break;
  1403 	if(i == LENGTH(layouts))
  1393 	if(i == LENGTH(layouts))
  1404 		return;
  1394 		return;
  1405 	lt = &layouts[i];
  1395 	if(revert && &layouts[i] == lt)
       
  1396 		lt = revert;
       
  1397 	else {
       
  1398 		revert = lt;
       
  1399 		lt = &layouts[i];
       
  1400 	}
  1406 	if(sel)
  1401 	if(sel)
  1407 		arrange();
  1402 		arrange();
  1408 	else
  1403 	else
  1409 		drawbar();
  1404 		drawbar();
  1410 }
  1405 }
  1543 textw(const char *text) {
  1538 textw(const char *text) {
  1544 	return textnw(text, strlen(text)) + dc.font.height;
  1539 	return textnw(text, strlen(text)) + dc.font.height;
  1545 }
  1540 }
  1546 
  1541 
  1547 void
  1542 void
       
  1543 tileresize(Client *c, int x, int y, int w, int h) {
       
  1544 	resize(c, x, y, w, h, RESIZEHINTS);
       
  1545 	if((RESIZEHINTS) && ((c->h < bh) || (c->h > h) || (c->w < bh) || (c->w > w)))
       
  1546 		/* client doesn't accept size constraints */
       
  1547 		resize(c, x, y, w, h, False);
       
  1548 }
       
  1549 
       
  1550 unsigned int
       
  1551 tilemaster(void) {
       
  1552 	unsigned int n;
       
  1553 	Client *c, *mc;
       
  1554 
       
  1555 	for(n = 0, mc = c = nexttiled(clients); c; c = nexttiled(c->next))
       
  1556 		n++;
       
  1557 	if(n == 0)
       
  1558 		return 0;
       
  1559 	if(n == 1)
       
  1560 		tileresize(mc, MOX, MOY, (MOW) - 2 * mc->border, (MOH) - 2 * mc->border);
       
  1561 	else
       
  1562 		tileresize(mc, MX, MY, (MW) - 2 * mc->border, (MH) - 2 * mc->border);
       
  1563 	return n - 1;
       
  1564 }
       
  1565 
       
  1566 void
       
  1567 tilevstack(unsigned int n) {
       
  1568 	int i, y, h;
       
  1569 	Client *c;
       
  1570 
       
  1571 	if(n == 0)
       
  1572 		return;
       
  1573 
       
  1574 	y = TY;
       
  1575 	h = (TH) / n;
       
  1576 	if(h < bh)
       
  1577 		h = TH;
       
  1578 
       
  1579 	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++)
       
  1580 		if(i > 0) {
       
  1581 			if(i > 1 && i == n) /* remainder */
       
  1582 				tileresize(c, TX, y, (TW) - 2 * c->border,
       
  1583 				              ((TY) + (TH)) - y - 2 * c->border);
       
  1584 			else
       
  1585 				tileresize(c, TX, y, (TW) - 2 * c->border,
       
  1586 				              h - 2 * c->border);
       
  1587 			if(h != TH)
       
  1588 				y = c->y + c->h + 2 * c->border;
       
  1589 		}
       
  1590 }
       
  1591 
       
  1592 void
  1548 tile(void) {
  1593 tile(void) {
  1549 	unsigned int i, n, nx, ny, nw, nh, mw, th;
  1594 	tilevstack(tilemaster());
  1550 	Client *c, *mc;
       
  1551 
       
  1552 	dozoom = True;
       
  1553 	nx = MX;
       
  1554 	ny = MY;
       
  1555 	nw = 0;
       
  1556 	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
       
  1557 		n++;
       
  1558 
       
  1559 	/* window geoms */
       
  1560 	mw = (n == 1) ? MOW : MW;
       
  1561 	th = (n > 1) ? TH / (n - 1) : 0;
       
  1562 	if(n > 1 && th < bh)
       
  1563 		th = TH;
       
  1564 
       
  1565 	for(i = 0, c = mc = nexttiled(clients); c; c = nexttiled(c->next)) {
       
  1566 		if(i == 0) { /* master */
       
  1567 			nw = mw - 2 * c->border;
       
  1568 			nh = MH - 2 * c->border;
       
  1569 		}
       
  1570 		else {  /* tile window */
       
  1571 			if(i == 1) {
       
  1572 				ny = TY;
       
  1573 				nx = TX;
       
  1574 				nw = TW - 2 * c->border;
       
  1575 			}
       
  1576 			if(i + 1 == n) /* remainder */
       
  1577 				nh = (TY + TH) - ny - 2 * c->border;
       
  1578 			else
       
  1579 				nh = th - 2 * c->border;
       
  1580 		}
       
  1581 		resize(c, nx, ny, nw, nh, RESIZEHINTS);
       
  1582 		if((RESIZEHINTS) && ((c->h < bh) || (c->h > nh) || (c->w < bh) || (c->w > nw)))
       
  1583 			/* client doesn't accept size constraints */
       
  1584 			resize(c, nx, ny, nw, nh, False);
       
  1585 		if(n > 1 && th != TH)
       
  1586 			ny = c->y + c->h + 2 * c->border;
       
  1587 		i++;
       
  1588 	}
       
  1589 }
  1595 }
  1590 
  1596 
  1591 void
  1597 void
  1592 togglefloating(const char *arg) {
  1598 togglefloating(const char *arg) {
  1593 	if(!sel)
  1599 	if(!sel)
  1794 
  1800 
  1795 void
  1801 void
  1796 zoom(const char *arg) {
  1802 zoom(const char *arg) {
  1797 	Client *c = sel;
  1803 	Client *c = sel;
  1798 
  1804 
  1799 	if(!sel || !dozoom || sel->isfloating)
  1805 	if(!sel || lt->isfloating || sel->isfloating)
  1800 		return;
  1806 		return;
  1801 	if(c == nexttiled(clients))
  1807 	if(c == nexttiled(clients))
  1802 		if(!(c = nexttiled(c->next)))
  1808 		if(!(c = nexttiled(c->next)))
  1803 			return;
  1809 			return;
  1804 	detach(c);
  1810 	detach(c);