You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1847 lines
43 KiB

  1. /* See LICENSE file for copyright and license details. */
  2. #include <errno.h>
  3. #include <locale.h>
  4. #include <regex.h>
  5. #include <stdarg.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <unistd.h>
  10. #include <sys/select.h>
  11. #include <sys/wait.h>
  12. #include <X11/cursorfont.h>
  13. #include <X11/keysym.h>
  14. #include <X11/Xatom.h>
  15. #include <X11/Xproto.h>
  16. #include <X11/Xutil.h>
  17. /* macros */
  18. #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
  19. #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
  20. #define MOUSEMASK (BUTTONMASK | PointerMotionMask)
  21. /* enums */
  22. enum { BarTop, BarBot, BarOff }; /* bar position */
  23. enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
  24. enum { ColBorder, ColFG, ColBG, ColLast }; /* color */
  25. enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */
  26. enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */
  27. /* typedefs */
  28. typedef struct Client Client;
  29. struct Client {
  30. char name[256];
  31. int x, y, w, h;
  32. int rx, ry, rw, rh; /* revert geometry */
  33. int basew, baseh, incw, inch, maxw, maxh, minw, minh;
  34. int minax, maxax, minay, maxay;
  35. long flags;
  36. unsigned int border, oldborder;
  37. Bool isbanned, isfixed, ismax, isfloating;
  38. Bool *tags;
  39. Client *next;
  40. Client *prev;
  41. Client *snext;
  42. Window win;
  43. };
  44. typedef struct {
  45. int x, y, w, h;
  46. unsigned long norm[ColLast];
  47. unsigned long sel[ColLast];
  48. Drawable drawable;
  49. GC gc;
  50. struct {
  51. int ascent;
  52. int descent;
  53. int height;
  54. XFontSet set;
  55. XFontStruct *xfont;
  56. } font;
  57. } DC; /* draw context */
  58. typedef struct {
  59. unsigned long mod;
  60. KeySym keysym;
  61. void (*func)(const char *arg);
  62. const char *arg;
  63. } Key;
  64. typedef struct {
  65. const char *symbol;
  66. void (*arrange)(void);
  67. } Layout;
  68. typedef struct {
  69. const char *prop;
  70. const char *tags;
  71. Bool isfloating;
  72. } Rule;
  73. typedef struct {
  74. regex_t *propregex;
  75. regex_t *tagregex;
  76. } Regs;
  77. /* functions */
  78. static void eprint(const char *errstr, ...);
  79. static void *emallocz(unsigned int size);
  80. static void spawn(const char *arg);
  81. static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]);
  82. static unsigned long initcolor(const char *colstr);
  83. static void initfont(const char *fontstr);
  84. static Bool isoccupied(unsigned int t);
  85. static unsigned int textnw(const char *text, unsigned int len);
  86. static void drawtext(const char *text, unsigned long col[ColLast]);
  87. static void drawbar(void);
  88. static void initstyle(void);
  89. static void initbar(void);
  90. static unsigned int textw(const char *text);
  91. static void togglebar(const char *arg);
  92. static void updatebarpos(void);
  93. static void attachstack(Client *c);
  94. static void detachstack(Client *c);
  95. static void grabbuttons(Client *c, Bool focused);
  96. static Bool isprotodel(Client *c);
  97. static void setclientstate(Client *c, long state);
  98. static int xerrordummy(Display *dsply, XErrorEvent *ee);
  99. static void ban(Client *c);
  100. static void configure(Client *c);
  101. static void killclient(const char *arg);
  102. static void manage(Window w, XWindowAttributes *wa);
  103. static void resize(Client *c, int x, int y, int w, int h, Bool sizehints);
  104. static void unban(Client *c);
  105. static void unmanage(Client *c);
  106. static void updatesizehints(Client *c);
  107. static void updatetitle(Client *c);
  108. static Client *getclient(Window w);
  109. static void movemouse(Client *c);
  110. static void resizemouse(Client *c);
  111. static void buttonpress(XEvent *e);
  112. static void configurerequest(XEvent *e);
  113. static void configurenotify(XEvent *e);
  114. static void destroynotify(XEvent *e);
  115. static void enternotify(XEvent *e);
  116. static void expose(XEvent *e);
  117. static void keypress(XEvent *e);
  118. static void leavenotify(XEvent *e);
  119. static void mappingnotify(XEvent *e);
  120. static void maprequest(XEvent *e);
  121. static void propertynotify(XEvent *e);
  122. static void unmapnotify(XEvent *e);
  123. static void grabkeys(void);
  124. static unsigned int idxoftag(const char *tag);
  125. static void floating(void); /* default floating layout */
  126. static void applyrules(Client *c);
  127. static void compileregs(void);
  128. static void focusnext(const char *arg);
  129. static void focusprev(const char *arg);
  130. static void initlayouts(void);
  131. static Bool isfloating(void);
  132. static Bool isvisible(Client *c);
  133. static void restack(void);
  134. static void setlayout(const char *arg);
  135. static void tag(const char *arg);
  136. static void togglefloating(const char *arg);
  137. static void togglemax(const char *arg);
  138. static void toggletag(const char *arg);
  139. static void toggleview(const char *arg);
  140. static void view(const char *arg);
  141. static void cleanup(void);
  142. static long getstate(Window w);
  143. static void scan(void);
  144. static void setup(void);
  145. static int xerrorstart(Display *dsply, XErrorEvent *ee);
  146. static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
  147. static void quit(const char *arg);
  148. static int xerror(Display *dpy, XErrorEvent *ee);
  149. static void arrange(void);
  150. static void attach(Client *c);
  151. static void detach(Client *c);
  152. static void focus(Client *c);
  153. static Bool isarrange(void (*func)());
  154. static Client *nexttiled(Client *c);
  155. static void setmwfact(const char *arg);
  156. static void tile(void);
  157. static void zoom(const char *arg);
  158. #include "config.h"
  159. /* variables */
  160. static char stext[256];
  161. static double mwfact = MWFACT;
  162. static int screen, sx, sy, sw, sh, wax, way, waw, wah;
  163. static int (*xerrorxlib)(Display *, XErrorEvent *);
  164. static unsigned int bh;
  165. static unsigned int blw = 0;
  166. static unsigned int bpos = BARPOS;
  167. static unsigned int ltidx = 0; /* default */
  168. static unsigned int nlayouts = 0;
  169. static unsigned int nrules = 0;
  170. static unsigned int ntags;
  171. static unsigned int numlockmask = 0;
  172. static void (*handler[LASTEvent]) (XEvent *) = {
  173. [ButtonPress] = buttonpress,
  174. [ConfigureRequest] = configurerequest,
  175. [ConfigureNotify] = configurenotify,
  176. [DestroyNotify] = destroynotify,
  177. [EnterNotify] = enternotify,
  178. [LeaveNotify] = leavenotify,
  179. [Expose] = expose,
  180. [KeyPress] = keypress,
  181. [MappingNotify] = mappingnotify,
  182. [MapRequest] = maprequest,
  183. [PropertyNotify] = propertynotify,
  184. [UnmapNotify] = unmapnotify
  185. };
  186. static Atom wmatom[WMLast], netatom[NetLast];
  187. static Bool otherwm, readin;
  188. static Bool running = True;
  189. static Bool *seltags;
  190. static Bool selscreen = True;
  191. static Client *clients = NULL;
  192. static Client *sel = NULL;
  193. static Client *stack = NULL;
  194. static Cursor cursor[CurLast];
  195. static Display *dpy;
  196. static DC dc = {0};
  197. static Window barwin, root;
  198. static Regs *regs = NULL;
  199. static void
  200. eprint(const char *errstr, ...) {
  201. va_list ap;
  202. va_start(ap, errstr);
  203. vfprintf(stderr, errstr, ap);
  204. va_end(ap);
  205. exit(EXIT_FAILURE);
  206. }
  207. static void *
  208. emallocz(unsigned int size) {
  209. void *res = calloc(1, size);
  210. if(!res)
  211. eprint("fatal: could not malloc() %u bytes\n", size);
  212. return res;
  213. }
  214. static void
  215. spawn(const char *arg) {
  216. static char *shell = NULL;
  217. if(!shell && !(shell = getenv("SHELL")))
  218. shell = "/bin/sh";
  219. if(!arg)
  220. return;
  221. /* The double-fork construct avoids zombie processes and keeps the code
  222. * clean from stupid signal handlers. */
  223. if(fork() == 0) {
  224. if(fork() == 0) {
  225. if(dpy)
  226. close(ConnectionNumber(dpy));
  227. setsid();
  228. execl(shell, shell, "-c", arg, (char *)NULL);
  229. fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
  230. perror(" failed");
  231. }
  232. exit(0);
  233. }
  234. wait(0);
  235. }
  236. static void
  237. drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
  238. int x;
  239. XGCValues gcv;
  240. XRectangle r = { dc.x, dc.y, dc.w, dc.h };
  241. gcv.foreground = col[ColFG];
  242. XChangeGC(dpy, dc.gc, GCForeground, &gcv);
  243. x = (dc.font.ascent + dc.font.descent + 2) / 4;
  244. r.x = dc.x + 1;
  245. r.y = dc.y + 1;
  246. if(filled) {
  247. r.width = r.height = x + 1;
  248. XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
  249. }
  250. else if(empty) {
  251. r.width = r.height = x;
  252. XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
  253. }
  254. }
  255. static unsigned long
  256. initcolor(const char *colstr) {
  257. Colormap cmap = DefaultColormap(dpy, screen);
  258. XColor color;
  259. if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
  260. eprint("error, cannot allocate color '%s'\n", colstr);
  261. return color.pixel;
  262. }
  263. static void
  264. initfont(const char *fontstr) {
  265. char *def, **missing;
  266. int i, n;
  267. missing = NULL;
  268. if(dc.font.set)
  269. XFreeFontSet(dpy, dc.font.set);
  270. dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
  271. if(missing) {
  272. while(n--)
  273. fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
  274. XFreeStringList(missing);
  275. }
  276. if(dc.font.set) {
  277. XFontSetExtents *font_extents;
  278. XFontStruct **xfonts;
  279. char **font_names;
  280. dc.font.ascent = dc.font.descent = 0;
  281. font_extents = XExtentsOfFontSet(dc.font.set);
  282. n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
  283. for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
  284. if(dc.font.ascent < (*xfonts)->ascent)
  285. dc.font.ascent = (*xfonts)->ascent;
  286. if(dc.font.descent < (*xfonts)->descent)
  287. dc.font.descent = (*xfonts)->descent;
  288. xfonts++;
  289. }
  290. }
  291. else {
  292. if(dc.font.xfont)
  293. XFreeFont(dpy, dc.font.xfont);
  294. dc.font.xfont = NULL;
  295. if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
  296. || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
  297. eprint("error, cannot load font: '%s'\n", fontstr);
  298. dc.font.ascent = dc.font.xfont->ascent;
  299. dc.font.descent = dc.font.xfont->descent;
  300. }
  301. dc.font.height = dc.font.ascent + dc.font.descent;
  302. }
  303. static Bool
  304. isoccupied(unsigned int t) {
  305. Client *c;
  306. for(c = clients; c; c = c->next)
  307. if(c->tags[t])
  308. return True;
  309. return False;
  310. }
  311. static unsigned int
  312. textnw(const char *text, unsigned int len) {
  313. XRectangle r;
  314. if(dc.font.set) {
  315. XmbTextExtents(dc.font.set, text, len, NULL, &r);
  316. return r.width;
  317. }
  318. return XTextWidth(dc.font.xfont, text, len);
  319. }
  320. static void
  321. drawtext(const char *text, unsigned long col[ColLast]) {
  322. int x, y, w, h;
  323. static char buf[256];
  324. unsigned int len, olen;
  325. XRectangle r = { dc.x, dc.y, dc.w, dc.h };
  326. XSetForeground(dpy, dc.gc, col[ColBG]);
  327. XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
  328. if(!text)
  329. return;
  330. w = 0;
  331. olen = len = strlen(text);
  332. if(len >= sizeof buf)
  333. len = sizeof buf - 1;
  334. memcpy(buf, text, len);
  335. buf[len] = 0;
  336. h = dc.font.ascent + dc.font.descent;
  337. y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
  338. x = dc.x + (h / 2);
  339. /* shorten text if necessary */
  340. while(len && (w = textnw(buf, len)) > dc.w - h)
  341. buf[--len] = 0;
  342. if(len < olen) {
  343. if(len > 1)
  344. buf[len - 1] = '.';
  345. if(len > 2)
  346. buf[len - 2] = '.';
  347. if(len > 3)
  348. buf[len - 3] = '.';
  349. }
  350. if(w > dc.w)
  351. return; /* too long */
  352. XSetForeground(dpy, dc.gc, col[ColFG]);
  353. if(dc.font.set)
  354. XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
  355. else
  356. XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
  357. }
  358. static void
  359. drawbar(void) {
  360. int i, x;
  361. dc.x = dc.y = 0;
  362. for(i = 0; i < ntags; i++) {
  363. dc.w = textw(tags[i]);
  364. if(seltags[i]) {
  365. drawtext(tags[i], dc.sel);
  366. drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
  367. }
  368. else {
  369. drawtext(tags[i], dc.norm);
  370. drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
  371. }
  372. dc.x += dc.w;
  373. }
  374. dc.w = blw;
  375. drawtext(layouts[ltidx].symbol, dc.norm);
  376. x = dc.x + dc.w;
  377. dc.w = textw(stext);
  378. dc.x = sw - dc.w;
  379. if(dc.x < x) {
  380. dc.x = x;
  381. dc.w = sw - x;
  382. }
  383. drawtext(stext, dc.norm);
  384. if((dc.w = dc.x - x) > bh) {
  385. dc.x = x;
  386. if(sel) {
  387. drawtext(sel->name, dc.sel);
  388. drawsquare(sel->ismax, sel->isfloating, dc.sel);
  389. }
  390. else
  391. drawtext(NULL, dc.norm);
  392. }
  393. XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
  394. XSync(dpy, False);
  395. }
  396. static void
  397. initstyle(void) {
  398. dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
  399. dc.norm[ColBG] = initcolor(NORMBGCOLOR);
  400. dc.norm[ColFG] = initcolor(NORMFGCOLOR);
  401. dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
  402. dc.sel[ColBG] = initcolor(SELBGCOLOR);
  403. dc.sel[ColFG] = initcolor(SELFGCOLOR);
  404. initfont(FONT);
  405. dc.h = bh = dc.font.height + 2;
  406. }
  407. static void
  408. initbar(void) {
  409. XSetWindowAttributes wa;
  410. wa.override_redirect = 1;
  411. wa.background_pixmap = ParentRelative;
  412. wa.event_mask = ButtonPressMask | ExposureMask;
  413. barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
  414. DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
  415. CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
  416. XDefineCursor(dpy, barwin, cursor[CurNormal]);
  417. updatebarpos();
  418. XMapRaised(dpy, barwin);
  419. strcpy(stext, "dwm-"VERSION);
  420. dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
  421. dc.gc = XCreateGC(dpy, root, 0, 0);
  422. XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
  423. if(!dc.font.set)
  424. XSetFont(dpy, dc.gc, dc.font.xfont->fid);
  425. }
  426. static unsigned int
  427. textw(const char *text) {
  428. return textnw(text, strlen(text)) + dc.font.height;
  429. }
  430. static void
  431. togglebar(const char *arg) {
  432. if(bpos == BarOff)
  433. bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
  434. else
  435. bpos = BarOff;
  436. updatebarpos();
  437. arrange();
  438. }
  439. static void
  440. updatebarpos(void) {
  441. XEvent ev;
  442. wax = sx;
  443. way = sy;
  444. wah = sh;
  445. waw = sw;
  446. switch(bpos) {
  447. default:
  448. wah -= bh;
  449. way += bh;
  450. XMoveWindow(dpy, barwin, sx, sy);
  451. break;
  452. case BarBot:
  453. wah -= bh;
  454. XMoveWindow(dpy, barwin, sx, sy + wah);
  455. break;
  456. case BarOff:
  457. XMoveWindow(dpy, barwin, sx, sy - bh);
  458. break;
  459. }
  460. XSync(dpy, False);
  461. while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  462. }
  463. static void
  464. attachstack(Client *c) {
  465. c->snext = stack;
  466. stack = c;
  467. }
  468. static void
  469. detachstack(Client *c) {
  470. Client **tc;
  471. for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
  472. *tc = c->snext;
  473. }
  474. static void
  475. grabbuttons(Client *c, Bool focused) {
  476. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  477. if(focused) {
  478. XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
  479. GrabModeAsync, GrabModeSync, None, None);
  480. XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
  481. GrabModeAsync, GrabModeSync, None, None);
  482. XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  483. GrabModeAsync, GrabModeSync, None, None);
  484. XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  485. GrabModeAsync, GrabModeSync, None, None);
  486. XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
  487. GrabModeAsync, GrabModeSync, None, None);
  488. XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
  489. GrabModeAsync, GrabModeSync, None, None);
  490. XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  491. GrabModeAsync, GrabModeSync, None, None);
  492. XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  493. GrabModeAsync, GrabModeSync, None, None);
  494. XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
  495. GrabModeAsync, GrabModeSync, None, None);
  496. XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
  497. GrabModeAsync, GrabModeSync, None, None);
  498. XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  499. GrabModeAsync, GrabModeSync, None, None);
  500. XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  501. GrabModeAsync, GrabModeSync, None, None);
  502. }
  503. else
  504. XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
  505. GrabModeAsync, GrabModeSync, None, None);
  506. }
  507. static Bool
  508. isprotodel(Client *c) {
  509. int i, n;
  510. Atom *protocols;
  511. Bool ret = False;
  512. if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
  513. for(i = 0; !ret && i < n; i++)
  514. if(protocols[i] == wmatom[WMDelete])
  515. ret = True;
  516. XFree(protocols);
  517. }
  518. return ret;
  519. }
  520. static void
  521. setclientstate(Client *c, long state) {
  522. long data[] = {state, None};
  523. XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
  524. PropModeReplace, (unsigned char *)data, 2);
  525. }
  526. static int
  527. xerrordummy(Display *dsply, XErrorEvent *ee) {
  528. return 0;
  529. }
  530. static void
  531. ban(Client *c) {
  532. if(c->isbanned)
  533. return;
  534. XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
  535. c->isbanned = True;
  536. }
  537. static void
  538. configure(Client *c) {
  539. XConfigureEvent ce;
  540. ce.type = ConfigureNotify;
  541. ce.display = dpy;
  542. ce.event = c->win;
  543. ce.window = c->win;
  544. ce.x = c->x;
  545. ce.y = c->y;
  546. ce.width = c->w;
  547. ce.height = c->h;
  548. ce.border_width = c->border;
  549. ce.above = None;
  550. ce.override_redirect = False;
  551. XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
  552. }
  553. static void
  554. killclient(const char *arg) {
  555. XEvent ev;
  556. if(!sel)
  557. return;
  558. if(isprotodel(sel)) {
  559. ev.type = ClientMessage;
  560. ev.xclient.window = sel->win;
  561. ev.xclient.message_type = wmatom[WMProtocols];
  562. ev.xclient.format = 32;
  563. ev.xclient.data.l[0] = wmatom[WMDelete];
  564. ev.xclient.data.l[1] = CurrentTime;
  565. XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
  566. }
  567. else
  568. XKillClient(dpy, sel->win);
  569. }
  570. static void
  571. manage(Window w, XWindowAttributes *wa) {
  572. unsigned int i;
  573. Client *c, *t = NULL;
  574. Window trans;
  575. Status rettrans;
  576. XWindowChanges wc;
  577. c = emallocz(sizeof(Client));
  578. c->tags = emallocz(ntags * sizeof(Bool));
  579. c->win = w;
  580. c->x = wa->x;
  581. c->y = wa->y;
  582. c->w = wa->width;
  583. c->h = wa->height;
  584. c->oldborder = wa->border_width;
  585. if(c->w == sw && c->h == sh) {
  586. c->x = sx;
  587. c->y = sy;
  588. c->border = wa->border_width;
  589. }
  590. else {
  591. if(c->x + c->w + 2 * c->border > wax + waw)
  592. c->x = wax + waw - c->w - 2 * c->border;
  593. if(c->y + c->h + 2 * c->border > way + wah)
  594. c->y = way + wah - c->h - 2 * c->border;
  595. if(c->x < wax)
  596. c->x = wax;
  597. if(c->y < way)
  598. c->y = way;
  599. c->border = BORDERPX;
  600. }
  601. wc.border_width = c->border;
  602. XConfigureWindow(dpy, w, CWBorderWidth, &wc);
  603. XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
  604. configure(c); /* propagates border_width, if size doesn't change */
  605. updatesizehints(c);
  606. XSelectInput(dpy, w,
  607. StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
  608. grabbuttons(c, False);
  609. updatetitle(c);
  610. if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
  611. for(t = clients; t && t->win != trans; t = t->next);
  612. if(t)
  613. for(i = 0; i < ntags; i++)
  614. c->tags[i] = t->tags[i];
  615. applyrules(c);
  616. if(!c->isfloating)
  617. c->isfloating = (rettrans == Success) || c->isfixed;
  618. attach(c);
  619. attachstack(c);
  620. XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */
  621. ban(c);
  622. XMapWindow(dpy, c->win);
  623. setclientstate(c, NormalState);
  624. arrange();
  625. }
  626. static void
  627. resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
  628. double dx, dy, max, min, ratio;
  629. XWindowChanges wc;
  630. if(sizehints) {
  631. if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
  632. dx = (double)(w - c->basew);
  633. dy = (double)(h - c->baseh);
  634. min = (double)(c->minax) / (double)(c->minay);
  635. max = (double)(c->maxax) / (double)(c->maxay);
  636. ratio = dx / dy;
  637. if(max > 0 && min > 0 && ratio > 0) {
  638. if(ratio < min) {
  639. dy = (dx * min + dy) / (min * min + 1);
  640. dx = dy * min;
  641. w = (int)dx + c->basew;
  642. h = (int)dy + c->baseh;
  643. }
  644. else if(ratio > max) {
  645. dy = (dx * min + dy) / (max * max + 1);
  646. dx = dy * min;
  647. w = (int)dx + c->basew;
  648. h = (int)dy + c->baseh;
  649. }
  650. }
  651. }
  652. if(c->minw && w < c->minw)
  653. w = c->minw;
  654. if(c->minh && h < c->minh)
  655. h = c->minh;
  656. if(c->maxw && w > c->maxw)
  657. w = c->maxw;
  658. if(c->maxh && h > c->maxh)
  659. h = c->maxh;
  660. if(c->incw)
  661. w -= (w - c->basew) % c->incw;
  662. if(c->inch)
  663. h -= (h - c->baseh) % c->inch;
  664. }
  665. if(w <= 0 || h <= 0)
  666. return;
  667. /* offscreen appearance fixes */
  668. if(x > sw)
  669. x = sw - w - 2 * c->border;
  670. if(y > sh)
  671. y = sh - h - 2 * c->border;
  672. if(x + w + 2 * c->border < sx)
  673. x = sx;
  674. if(y + h + 2 * c->border < sy)
  675. y = sy;
  676. if(c->x != x || c->y != y || c->w != w || c->h != h) {
  677. c->x = wc.x = x;
  678. c->y = wc.y = y;
  679. c->w = wc.width = w;
  680. c->h = wc.height = h;
  681. wc.border_width = c->border;
  682. XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
  683. configure(c);
  684. XSync(dpy, False);
  685. }
  686. }
  687. static void
  688. unban(Client *c) {
  689. if(!c->isbanned)
  690. return;
  691. XMoveWindow(dpy, c->win, c->x, c->y);
  692. c->isbanned = False;
  693. }
  694. static void
  695. unmanage(Client *c) {
  696. XWindowChanges wc;
  697. wc.border_width = c->oldborder;
  698. /* The server grab construct avoids race conditions. */
  699. XGrabServer(dpy);
  700. XSetErrorHandler(xerrordummy);
  701. XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
  702. detach(c);
  703. detachstack(c);
  704. if(sel == c)
  705. focus(NULL);
  706. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  707. setclientstate(c, WithdrawnState);
  708. free(c->tags);
  709. free(c);
  710. XSync(dpy, False);
  711. XSetErrorHandler(xerror);
  712. XUngrabServer(dpy);
  713. arrange();
  714. }
  715. static void
  716. updatesizehints(Client *c) {
  717. long msize;
  718. XSizeHints size;
  719. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  720. size.flags = PSize;
  721. c->flags = size.flags;
  722. if(c->flags & PBaseSize) {
  723. c->basew = size.base_width;
  724. c->baseh = size.base_height;
  725. }
  726. else if(c->flags & PMinSize) {
  727. c->basew = size.min_width;
  728. c->baseh = size.min_height;
  729. }
  730. else
  731. c->basew = c->baseh = 0;
  732. if(c->flags & PResizeInc) {
  733. c->incw = size.width_inc;
  734. c->inch = size.height_inc;
  735. }
  736. else
  737. c->incw = c->inch = 0;
  738. if(c->flags & PMaxSize) {
  739. c->maxw = size.max_width;
  740. c->maxh = size.max_height;
  741. }
  742. else
  743. c->maxw = c->maxh = 0;
  744. if(c->flags & PMinSize) {
  745. c->minw = size.min_width;
  746. c->minh = size.min_height;
  747. }
  748. else if(c->flags & PBaseSize) {
  749. c->minw = size.base_width;
  750. c->minh = size.base_height;
  751. }
  752. else
  753. c->minw = c->minh = 0;
  754. if(c->flags & PAspect) {
  755. c->minax = size.min_aspect.x;
  756. c->maxax = size.max_aspect.x;
  757. c->minay = size.min_aspect.y;
  758. c->maxay = size.max_aspect.y;
  759. }
  760. else
  761. c->minax = c->maxax = c->minay = c->maxay = 0;
  762. c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
  763. && c->maxw == c->minw && c->maxh == c->minh);
  764. }
  765. static void
  766. updatetitle(Client *c) {
  767. if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
  768. gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
  769. }
  770. static Client *
  771. getclient(Window w) {
  772. Client *c;
  773. for(c = clients; c && c->win != w; c = c->next);
  774. return c;
  775. }
  776. static void
  777. movemouse(Client *c) {
  778. int x1, y1, ocx, ocy, di, nx, ny;
  779. unsigned int dui;
  780. Window dummy;
  781. XEvent ev;
  782. ocx = nx = c->x;
  783. ocy = ny = c->y;
  784. if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
  785. None, cursor[CurMove], CurrentTime) != GrabSuccess)
  786. return;
  787. c->ismax = False;
  788. XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
  789. for(;;) {
  790. XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
  791. switch (ev.type) {
  792. case ButtonRelease:
  793. XUngrabPointer(dpy, CurrentTime);
  794. return;
  795. case ConfigureRequest:
  796. case Expose:
  797. case MapRequest:
  798. handler[ev.type](&ev);
  799. break;
  800. case MotionNotify:
  801. XSync(dpy, False);
  802. nx = ocx + (ev.xmotion.x - x1);
  803. ny = ocy + (ev.xmotion.y - y1);
  804. if(abs(wax + nx) < SNAP)
  805. nx = wax;
  806. else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
  807. nx = wax + waw - c->w - 2 * c->border;
  808. if(abs(way - ny) < SNAP)
  809. ny = way;
  810. else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
  811. ny = way + wah - c->h - 2 * c->border;
  812. resize(c, nx, ny, c->w, c->h, False);
  813. break;
  814. }
  815. }
  816. }
  817. static void
  818. resizemouse(Client *c) {
  819. int ocx, ocy;
  820. int nw, nh;
  821. XEvent ev;
  822. ocx = c->x;
  823. ocy = c->y;
  824. if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
  825. None, cursor[CurResize], CurrentTime) != GrabSuccess)
  826. return;
  827. c->ismax = False;
  828. XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
  829. for(;;) {
  830. XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
  831. switch(ev.type) {
  832. case ButtonRelease:
  833. XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
  834. c->w + c->border - 1, c->h + c->border - 1);
  835. XUngrabPointer(dpy, CurrentTime);
  836. while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  837. return;
  838. case ConfigureRequest:
  839. case Expose:
  840. case MapRequest:
  841. handler[ev.type](&ev);
  842. break;
  843. case MotionNotify:
  844. XSync(dpy, False);
  845. if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
  846. nw = 1;
  847. if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
  848. nh = 1;
  849. resize(c, c->x, c->y, nw, nh, True);
  850. break;
  851. }
  852. }
  853. }
  854. static void
  855. buttonpress(XEvent *e) {
  856. unsigned int i, x;
  857. Client *c;
  858. XButtonPressedEvent *ev = &e->xbutton;
  859. if(barwin == ev->window) {
  860. x = 0;
  861. for(i = 0; i < ntags; i++) {
  862. x += textw(tags[i]);
  863. if(ev->x < x) {
  864. if(ev->button == Button1) {
  865. if(ev->state & MODKEY)
  866. tag(tags[i]);
  867. else
  868. view(tags[i]);
  869. }
  870. else if(ev->button == Button3) {
  871. if(ev->state & MODKEY)
  872. toggletag(tags[i]);
  873. else
  874. toggleview(tags[i]);
  875. }
  876. return;
  877. }
  878. }
  879. if((ev->x < x + blw) && ev->button == Button1)
  880. setlayout(NULL);
  881. }
  882. else if((c = getclient(ev->window))) {
  883. focus(c);
  884. if(CLEANMASK(ev->state) != MODKEY)
  885. return;
  886. if(ev->button == Button1 && (isfloating() || c->isfloating)) {
  887. restack();
  888. movemouse(c);
  889. }
  890. else if(ev->button == Button2)
  891. zoom(NULL);
  892. else if(ev->button == Button3
  893. && (isfloating() || c->isfloating) && !c->isfixed)
  894. {
  895. restack();
  896. resizemouse(c);
  897. }
  898. }
  899. }
  900. static void
  901. configurerequest(XEvent *e) {
  902. Client *c;
  903. XConfigureRequestEvent *ev = &e->xconfigurerequest;
  904. XWindowChanges wc;
  905. if((c = getclient(ev->window))) {
  906. c->ismax = False;
  907. if(ev->value_mask & CWBorderWidth)
  908. c->border = ev->border_width;
  909. if(c->isfixed || c->isfloating || isfloating()) {
  910. if(ev->value_mask & CWX)
  911. c->x = ev->x;
  912. if(ev->value_mask & CWY)
  913. c->y = ev->y;
  914. if(ev->value_mask & CWWidth)
  915. c->w = ev->width;
  916. if(ev->value_mask & CWHeight)
  917. c->h = ev->height;
  918. if((c->x + c->w) > sw && c->isfloating)
  919. c->x = sw / 2 - c->w / 2; /* center in x direction */
  920. if((c->y + c->h) > sh && c->isfloating)
  921. c->y = sh / 2 - c->h / 2; /* center in y direction */
  922. if((ev->value_mask & (CWX | CWY))
  923. && !(ev->value_mask & (CWWidth | CWHeight)))
  924. configure(c);
  925. if(isvisible(c))
  926. XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
  927. }
  928. else
  929. configure(c);
  930. }
  931. else {
  932. wc.x = ev->x;
  933. wc.y = ev->y;
  934. wc.width = ev->width;
  935. wc.height = ev->height;
  936. wc.border_width = ev->border_width;
  937. wc.sibling = ev->above;
  938. wc.stack_mode = ev->detail;
  939. XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
  940. }
  941. XSync(dpy, False);
  942. }
  943. static void
  944. configurenotify(XEvent *e) {
  945. XConfigureEvent *ev = &e->xconfigure;
  946. if (ev->window == root && (ev->width != sw || ev->height != sh)) {
  947. sw = ev->width;
  948. sh = ev->height;
  949. XFreePixmap(dpy, dc.drawable);
  950. dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
  951. XResizeWindow(dpy, barwin, sw, bh);
  952. updatebarpos();
  953. arrange();
  954. }
  955. }
  956. static void
  957. destroynotify(XEvent *e) {
  958. Client *c;
  959. XDestroyWindowEvent *ev = &e->xdestroywindow;
  960. if((c = getclient(ev->window)))
  961. unmanage(c);
  962. }
  963. static void
  964. enternotify(XEvent *e) {
  965. Client *c;
  966. XCrossingEvent *ev = &e->xcrossing;
  967. if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
  968. return;
  969. if((c = getclient(ev->window)))
  970. focus(c);
  971. else if(ev->window == root) {
  972. selscreen = True;
  973. focus(NULL);
  974. }
  975. }
  976. static void
  977. expose(XEvent *e) {
  978. XExposeEvent *ev = &e->xexpose;
  979. if(ev->count == 0) {
  980. if(barwin == ev->window)
  981. drawbar();
  982. }
  983. }
  984. static void
  985. keypress(XEvent *e) {
  986. KEYS
  987. unsigned int len = sizeof keys / sizeof keys[0];
  988. unsigned int i;
  989. KeySym keysym;
  990. XKeyEvent *ev = &e->xkey;
  991. keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
  992. for(i = 0; i < len; i++)
  993. if(keysym == keys[i].keysym
  994. && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
  995. {
  996. if(keys[i].func)
  997. keys[i].func(keys[i].arg);
  998. }
  999. }
  1000. static void
  1001. leavenotify(XEvent *e) {
  1002. XCrossingEvent *ev = &e->xcrossing;
  1003. if((ev->window == root) && !ev->same_screen) {
  1004. selscreen = False;
  1005. focus(NULL);
  1006. }
  1007. }
  1008. static void
  1009. mappingnotify(XEvent *e) {
  1010. XMappingEvent *ev = &e->xmapping;
  1011. XRefreshKeyboardMapping(ev);
  1012. if(ev->request == MappingKeyboard)
  1013. grabkeys();
  1014. }
  1015. static void
  1016. maprequest(XEvent *e) {
  1017. static XWindowAttributes wa;
  1018. XMapRequestEvent *ev = &e->xmaprequest;
  1019. if(!XGetWindowAttributes(dpy, ev->window, &wa))
  1020. return;
  1021. if(wa.override_redirect)
  1022. return;
  1023. if(!getclient(ev->window))
  1024. manage(ev->window, &wa);
  1025. }
  1026. static void
  1027. propertynotify(XEvent *e) {
  1028. Client *c;
  1029. Window trans;
  1030. XPropertyEvent *ev = &e->xproperty;
  1031. if(ev->state == PropertyDelete)
  1032. return; /* ignore */
  1033. if((c = getclient(ev->window))) {
  1034. switch (ev->atom) {
  1035. default: break;
  1036. case XA_WM_TRANSIENT_FOR:
  1037. XGetTransientForHint(dpy, c->win, &trans);
  1038. if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
  1039. arrange();
  1040. break;
  1041. case XA_WM_NORMAL_HINTS:
  1042. updatesizehints(c);
  1043. break;
  1044. }
  1045. if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
  1046. updatetitle(c);
  1047. if(c == sel)
  1048. drawbar();
  1049. }
  1050. }
  1051. }
  1052. static void
  1053. unmapnotify(XEvent *e) {
  1054. Client *c;
  1055. XUnmapEvent *ev = &e->xunmap;
  1056. if((c = getclient(ev->window)))
  1057. unmanage(c);
  1058. }
  1059. static void
  1060. grabkeys(void) {
  1061. KEYS
  1062. unsigned int len = sizeof keys / sizeof keys[0];
  1063. unsigned int i;
  1064. KeyCode code;
  1065. XUngrabKey(dpy, AnyKey, AnyModifier, root);
  1066. for(i = 0; i < len; i++) {
  1067. code = XKeysymToKeycode(dpy, keys[i].keysym);
  1068. XGrabKey(dpy, code, keys[i].mod, root, True,
  1069. GrabModeAsync, GrabModeAsync);
  1070. XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
  1071. GrabModeAsync, GrabModeAsync);
  1072. XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
  1073. GrabModeAsync, GrabModeAsync);
  1074. XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
  1075. GrabModeAsync, GrabModeAsync);
  1076. }
  1077. }
  1078. static unsigned int
  1079. idxoftag(const char *tag) {
  1080. unsigned int i;
  1081. for(i = 0; i < ntags; i++)
  1082. if(tags[i] == tag)
  1083. return i;
  1084. return 0;
  1085. }
  1086. static void
  1087. floating(void) { /* default floating layout */
  1088. Client *c;
  1089. for(c = clients; c; c = c->next)
  1090. if(isvisible(c))
  1091. resize(c, c->x, c->y, c->w, c->h, True);
  1092. }
  1093. static void
  1094. applyrules(Client *c) {
  1095. static char buf[512];
  1096. unsigned int i, j;
  1097. regmatch_t tmp;
  1098. Bool matched = False;
  1099. XClassHint ch = { 0 };
  1100. /* rule matching */
  1101. XGetClassHint(dpy, c->win, &ch);
  1102. snprintf(buf, sizeof buf, "%s:%s:%s",
  1103. ch.res_class ? ch.res_class : "",
  1104. ch.res_name ? ch.res_name : "", c->name);
  1105. for(i = 0; i < nrules; i++)
  1106. if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
  1107. c->isfloating = rules[i].isfloating;
  1108. for(j = 0; regs[i].tagregex && j < ntags; j++) {
  1109. if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
  1110. matched = True;
  1111. c->tags[j] = True;
  1112. }
  1113. }
  1114. }
  1115. if(ch.res_class)
  1116. XFree(ch.res_class);
  1117. if(ch.res_name)
  1118. XFree(ch.res_name);
  1119. if(!matched)
  1120. for(i = 0; i < ntags; i++)
  1121. c->tags[i] = seltags[i];
  1122. }
  1123. static void
  1124. compileregs(void) {
  1125. unsigned int i;
  1126. regex_t *reg;
  1127. if(regs)
  1128. return;
  1129. nrules = sizeof rules / sizeof rules[0];
  1130. regs = emallocz(nrules * sizeof(Regs));
  1131. for(i = 0; i < nrules; i++) {
  1132. if(rules[i].prop) {
  1133. reg = emallocz(sizeof(regex_t));
  1134. if(regcomp(reg, rules[i].prop, REG_EXTENDED))
  1135. free(reg);
  1136. else
  1137. regs[i].propregex = reg;
  1138. }
  1139. if(rules[i].tags) {
  1140. reg = emallocz(sizeof(regex_t));
  1141. if(regcomp(reg, rules[i].tags, REG_EXTENDED))
  1142. free(reg);
  1143. else
  1144. regs[i].tagregex = reg;
  1145. }
  1146. }
  1147. }
  1148. static void
  1149. focusnext(const char *arg) {
  1150. Client *c;
  1151. if(!sel)
  1152. return;
  1153. for(c = sel->next; c && !isvisible(c); c = c->next);
  1154. if(!c)
  1155. for(c = clients; c && !isvisible(c); c = c->next);
  1156. if(c) {
  1157. focus(c);
  1158. restack();
  1159. }
  1160. }
  1161. static void
  1162. focusprev(const char *arg) {
  1163. Client *c;
  1164. if(!sel)
  1165. return;
  1166. for(c = sel->prev; c && !isvisible(c); c = c->prev);
  1167. if(!c) {
  1168. for(c = clients; c && c->next; c = c->next);
  1169. for(; c && !isvisible(c); c = c->prev);
  1170. }
  1171. if(c) {
  1172. focus(c);
  1173. restack();
  1174. }
  1175. }
  1176. static void
  1177. initlayouts(void) {
  1178. unsigned int i, w;
  1179. nlayouts = sizeof layouts / sizeof layouts[0];
  1180. for(blw = i = 0; i < nlayouts; i++) {
  1181. w = textw(layouts[i].symbol);
  1182. if(w > blw)
  1183. blw = w;
  1184. }
  1185. }
  1186. static Bool
  1187. isfloating(void) {
  1188. return layouts[ltidx].arrange == floating;
  1189. }
  1190. static Bool
  1191. isvisible(Client *c) {
  1192. unsigned int i;
  1193. for(i = 0; i < ntags; i++)
  1194. if(c->tags[i] && seltags[i])
  1195. return True;
  1196. return False;
  1197. }
  1198. static void
  1199. restack(void) {
  1200. Client *c;
  1201. XEvent ev;
  1202. XWindowChanges wc;
  1203. drawbar();
  1204. if(!sel)
  1205. return;
  1206. if(sel->isfloating || isfloating())
  1207. XRaiseWindow(dpy, sel->win);
  1208. if(!isfloating()) {
  1209. wc.stack_mode = Below;
  1210. wc.sibling = barwin;
  1211. if(!sel->isfloating) {
  1212. XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
  1213. wc.sibling = sel->win;
  1214. }
  1215. for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
  1216. if(c == sel)
  1217. continue;
  1218. XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
  1219. wc.sibling = c->win;
  1220. }
  1221. }
  1222. XSync(dpy, False);
  1223. while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  1224. }
  1225. static void
  1226. setlayout(const char *arg) {
  1227. unsigned int i;
  1228. if(!arg) {
  1229. if(++ltidx == nlayouts)
  1230. ltidx = 0;;
  1231. }
  1232. else {
  1233. for(i = 0; i < nlayouts; i++)
  1234. if(!strcmp(arg, layouts[i].symbol))
  1235. break;
  1236. if(i == nlayouts)
  1237. return;
  1238. ltidx = i;
  1239. }
  1240. if(sel)
  1241. arrange();
  1242. else
  1243. drawbar();
  1244. }
  1245. static void
  1246. tag(const char *arg) {
  1247. unsigned int i;
  1248. if(!sel)
  1249. return;
  1250. for(i = 0; i < ntags; i++)
  1251. sel->tags[i] = arg == NULL;
  1252. i = idxoftag(arg);
  1253. if(i >= 0 && i < ntags)
  1254. sel->tags[i] = True;
  1255. arrange();
  1256. }
  1257. static void
  1258. togglefloating(const char *arg) {
  1259. if(!sel)
  1260. return;
  1261. sel->isfloating = !sel->isfloating;
  1262. if(sel->isfloating)
  1263. resize(sel, sel->x, sel->y, sel->w, sel->h, True);
  1264. arrange();
  1265. }
  1266. static void
  1267. togglemax(const char *arg) {
  1268. XEvent ev;
  1269. if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed)
  1270. return;
  1271. if((sel->ismax = !sel->ismax)) {
  1272. sel->rx = sel->x;
  1273. sel->ry = sel->y;
  1274. sel->rw = sel->w;
  1275. sel->rh = sel->h;
  1276. resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
  1277. }
  1278. else
  1279. resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
  1280. drawbar();
  1281. while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  1282. }
  1283. static void
  1284. toggletag(const char *arg) {
  1285. unsigned int i, j;
  1286. if(!sel)
  1287. return;
  1288. i = idxoftag(arg);
  1289. sel->tags[i] = !sel->tags[i];
  1290. for(j = 0; j < ntags && !sel->tags[j]; j++);
  1291. if(j == ntags)
  1292. sel->tags[i] = True;
  1293. arrange();
  1294. }
  1295. static void
  1296. toggleview(const char *arg) {
  1297. unsigned int i, j;
  1298. i = idxoftag(arg);
  1299. seltags[i] = !seltags[i];
  1300. for(j = 0; j < ntags && !seltags[j]; j++);
  1301. if(j == ntags)
  1302. seltags[i] = True; /* cannot toggle last view */
  1303. arrange();
  1304. }
  1305. static void
  1306. view(const char *arg) {
  1307. unsigned int i;
  1308. for(i = 0; i < ntags; i++)
  1309. seltags[i] = arg == NULL;
  1310. i = idxoftag(arg);
  1311. if(i >= 0 && i < ntags)
  1312. seltags[i] = True;
  1313. arrange();
  1314. }
  1315. static void
  1316. cleanup(void) {
  1317. close(STDIN_FILENO);
  1318. while(stack) {
  1319. unban(stack);
  1320. unmanage(stack);
  1321. }
  1322. if(dc.font.set)
  1323. XFreeFontSet(dpy, dc.font.set);
  1324. else
  1325. XFreeFont(dpy, dc.font.xfont);
  1326. XUngrabKey(dpy, AnyKey, AnyModifier, root);
  1327. XFreePixmap(dpy, dc.drawable);
  1328. XFreeGC(dpy, dc.gc);
  1329. XDestroyWindow(dpy, barwin);
  1330. XFreeCursor(dpy, cursor[CurNormal]);
  1331. XFreeCursor(dpy, cursor[CurResize]);
  1332. XFreeCursor(dpy, cursor[CurMove]);
  1333. XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
  1334. XSync(dpy, False);
  1335. free(seltags);
  1336. }
  1337. static long
  1338. getstate(Window w) {
  1339. int format, status;
  1340. long result = -1;
  1341. unsigned char *p = NULL;
  1342. unsigned long n, extra;
  1343. Atom real;
  1344. status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
  1345. &real, &format, &n, &extra, (unsigned char **)&p);
  1346. if(status != Success)
  1347. return -1;
  1348. if(n != 0)
  1349. result = *p;
  1350. XFree(p);
  1351. return result;
  1352. }
  1353. static void
  1354. scan(void) {
  1355. unsigned int i, num;
  1356. Window *wins, d1, d2;
  1357. XWindowAttributes wa;
  1358. wins = NULL;
  1359. if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
  1360. for(i = 0; i < num; i++) {
  1361. if(!XGetWindowAttributes(dpy, wins[i], &wa)
  1362. || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
  1363. continue;
  1364. if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
  1365. manage(wins[i], &wa);
  1366. }
  1367. for(i = 0; i < num; i++) { /* now the transients */
  1368. if(!XGetWindowAttributes(dpy, wins[i], &wa))
  1369. continue;
  1370. if(XGetTransientForHint(dpy, wins[i], &d1)
  1371. && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
  1372. manage(wins[i], &wa);
  1373. }
  1374. }
  1375. if(wins)
  1376. XFree(wins);
  1377. }
  1378. static void
  1379. setup(void) {
  1380. int i, j;
  1381. unsigned int mask;
  1382. Window w;
  1383. XModifierKeymap *modmap;
  1384. XSetWindowAttributes wa;
  1385. /* init atoms */
  1386. wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
  1387. wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  1388. wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
  1389. wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
  1390. netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
  1391. netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
  1392. XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
  1393. PropModeReplace, (unsigned char *) netatom, NetLast);
  1394. /* init cursors */
  1395. cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
  1396. cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
  1397. cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
  1398. /* init modifier map */
  1399. modmap = XGetModifierMapping(dpy);
  1400. for (i = 0; i < 8; i++)
  1401. for (j = 0; j < modmap->max_keypermod; j++) {
  1402. if(modmap->modifiermap[i * modmap->max_keypermod + j]
  1403. == XKeysymToKeycode(dpy, XK_Num_Lock))
  1404. numlockmask = (1 << i);
  1405. }
  1406. XFreeModifiermap(modmap);
  1407. /* select for events */
  1408. wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
  1409. | EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
  1410. wa.cursor = cursor[CurNormal];
  1411. XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
  1412. XSelectInput(dpy, root, wa.event_mask);
  1413. grabkeys();
  1414. compileregs();
  1415. for(ntags = 0; tags[ntags]; ntags++);
  1416. seltags = emallocz(sizeof(Bool) * ntags);
  1417. seltags[0] = True;
  1418. /* geometry */
  1419. sx = sy = 0;
  1420. sw = DisplayWidth(dpy, screen);
  1421. sh = DisplayHeight(dpy, screen);
  1422. initstyle();
  1423. initlayouts();
  1424. initbar();
  1425. /* multihead support */
  1426. selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
  1427. }
  1428. /*
  1429. * Startup Error handler to check if another window manager
  1430. * is already running.
  1431. */
  1432. static int
  1433. xerrorstart(Display *dsply, XErrorEvent *ee) {
  1434. otherwm = True;
  1435. return -1;
  1436. }
  1437. static Bool
  1438. gettextprop(Window w, Atom atom, char *text, unsigned int size) {
  1439. char **list = NULL;
  1440. int n;
  1441. XTextProperty name;
  1442. if(!text || size == 0)
  1443. return False;
  1444. text[0] = '\0';
  1445. XGetTextProperty(dpy, w, &name, atom);
  1446. if(!name.nitems)
  1447. return False;
  1448. if(name.encoding == XA_STRING)
  1449. strncpy(text, (char *)name.value, size - 1);
  1450. else {
  1451. if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
  1452. && n > 0 && *list)
  1453. {
  1454. strncpy(text, *list, size - 1);
  1455. XFreeStringList(list);
  1456. }
  1457. }
  1458. text[size - 1] = '\0';
  1459. XFree(name.value);
  1460. return True;
  1461. }
  1462. static void
  1463. quit(const char *arg) {
  1464. readin = running = False;
  1465. }
  1466. /* There's no way to check accesses to destroyed windows, thus those cases are
  1467. * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
  1468. * default error handler, which may call exit.
  1469. */
  1470. static int
  1471. xerror(Display *dpy, XErrorEvent *ee) {
  1472. if(ee->error_code == BadWindow
  1473. || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
  1474. || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
  1475. || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
  1476. || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
  1477. || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
  1478. || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
  1479. || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
  1480. return 0;
  1481. fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
  1482. ee->request_code, ee->error_code);
  1483. return xerrorxlib(dpy, ee); /* may call exit */
  1484. }
  1485. static void
  1486. arrange(void) {
  1487. Client *c;
  1488. for(c = clients; c; c = c->next)
  1489. if(isvisible(c))
  1490. unban(c);
  1491. else
  1492. ban(c);
  1493. layouts[ltidx].arrange();
  1494. focus(NULL);
  1495. restack();
  1496. }
  1497. static void
  1498. attach(Client *c) {
  1499. if(clients)
  1500. clients->prev = c;
  1501. c->next = clients;
  1502. clients = c;
  1503. }
  1504. static void
  1505. detach(Client *c) {
  1506. if(c->prev)
  1507. c->prev->next = c->next;
  1508. if(c->next)
  1509. c->next->prev = c->prev;
  1510. if(c == clients)
  1511. clients = c->next;
  1512. c->next = c->prev = NULL;
  1513. }
  1514. static void
  1515. focus(Client *c) {
  1516. if((!c && selscreen) || (c && !isvisible(c)))
  1517. for(c = stack; c && !isvisible(c); c = c->snext);
  1518. if(sel && sel != c) {
  1519. grabbuttons(sel, False);
  1520. XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
  1521. }
  1522. if(c) {
  1523. detachstack(c);
  1524. attachstack(c);
  1525. grabbuttons(c, True);
  1526. }
  1527. sel = c;
  1528. drawbar();
  1529. if(!selscreen)
  1530. return;
  1531. if(c) {
  1532. XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
  1533. XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
  1534. }
  1535. else
  1536. XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
  1537. }
  1538. static Bool
  1539. isarrange(void (*func)())
  1540. {
  1541. return func == layouts[ltidx].arrange;
  1542. }
  1543. static Client *
  1544. nexttiled(Client *c) {
  1545. for(; c && (c->isfloating || !isvisible(c)); c = c->next);
  1546. return c;
  1547. }
  1548. static void
  1549. setmwfact(const char *arg) {
  1550. double delta;
  1551. if(!isarrange(tile))
  1552. return;
  1553. /* arg handling, manipulate mwfact */
  1554. if(arg == NULL)
  1555. mwfact = MWFACT;
  1556. else if(1 == sscanf(arg, "%lf", &delta)) {
  1557. if(arg[0] != '+' && arg[0] != '-')
  1558. mwfact = delta;
  1559. else
  1560. mwfact += delta;
  1561. if(mwfact < 0.1)
  1562. mwfact = 0.1;
  1563. else if(mwfact > 0.9)
  1564. mwfact = 0.9;
  1565. }
  1566. arrange();
  1567. }
  1568. static void
  1569. tile(void) {
  1570. unsigned int i, n, nx, ny, nw, nh, mw, th;
  1571. Client *c;
  1572. for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
  1573. n++;
  1574. /* window geoms */
  1575. mw = (n == 1) ? waw : mwfact * waw;
  1576. th = (n > 1) ? wah / (n - 1) : 0;
  1577. if(n > 1 && th < bh)
  1578. th = wah;
  1579. nx = wax;
  1580. ny = way;
  1581. for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) {
  1582. c->ismax = False;
  1583. if(i == 0) { /* master */
  1584. nw = mw - 2 * c->border;
  1585. nh = wah - 2 * c->border;
  1586. }
  1587. else { /* tile window */
  1588. if(i == 1) {
  1589. ny = way;
  1590. nx += mw;
  1591. }
  1592. nw = waw - mw - 2 * c->border;
  1593. if(i + 1 == n) /* remainder */
  1594. nh = (way + wah) - ny - 2 * c->border;
  1595. else
  1596. nh = th - 2 * c->border;
  1597. }
  1598. resize(c, nx, ny, nw, nh, RESIZEHINTS);
  1599. if(n > 1 && th != wah)
  1600. ny += nh + 2 * c->border;
  1601. }
  1602. }
  1603. static void
  1604. zoom(const char *arg) {
  1605. Client *c;
  1606. if(!sel || !isarrange(tile) || sel->isfloating)
  1607. return;
  1608. if((c = sel) == nexttiled(clients))
  1609. if(!(c = nexttiled(c->next)))
  1610. return;
  1611. detach(c);
  1612. attach(c);
  1613. focus(c);
  1614. arrange();
  1615. }
  1616. int
  1617. main(int argc, char *argv[]) {
  1618. char *p;
  1619. int r, xfd;
  1620. fd_set rd;
  1621. XEvent ev;
  1622. if(argc == 2 && !strcmp("-v", argv[1]))
  1623. eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n");
  1624. else if(argc != 1)
  1625. eprint("usage: dwm [-v]\n");
  1626. setlocale(LC_CTYPE, "");
  1627. if(!(dpy = XOpenDisplay(0)))
  1628. eprint("dwm: cannot open display\n");
  1629. xfd = ConnectionNumber(dpy);
  1630. screen = DefaultScreen(dpy);
  1631. root = RootWindow(dpy, screen);
  1632. otherwm = False;
  1633. XSetErrorHandler(xerrorstart);
  1634. /* this causes an error if some other window manager is running */
  1635. XSelectInput(dpy, root, SubstructureRedirectMask);
  1636. XSync(dpy, False);
  1637. if(otherwm)
  1638. eprint("dwm: another window manager is already running\n");
  1639. XSync(dpy, False);
  1640. XSetErrorHandler(NULL);
  1641. xerrorxlib = XSetErrorHandler(xerror);
  1642. XSync(dpy, False);
  1643. setup();
  1644. drawbar();
  1645. scan();
  1646. /* main event loop, also reads status text from stdin */
  1647. XSync(dpy, False);
  1648. readin = True;
  1649. while(running) {
  1650. FD_ZERO(&rd);
  1651. if(readin)
  1652. FD_SET(STDIN_FILENO, &rd);
  1653. FD_SET(xfd, &rd);
  1654. if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) {
  1655. if(errno == EINTR)
  1656. continue;
  1657. eprint("select failed\n");
  1658. }
  1659. if(FD_ISSET(STDIN_FILENO, &rd)) {
  1660. switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) {
  1661. case -1:
  1662. strncpy(stext, strerror(errno), sizeof stext - 1);
  1663. stext[sizeof stext - 1] = '\0';
  1664. readin = False;
  1665. break;
  1666. case 0:
  1667. strncpy(stext, "EOF", 4);
  1668. readin = False;
  1669. break;
  1670. default:
  1671. for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0');
  1672. for(; p >= stext && *p != '\n'; --p);
  1673. if(p > stext)
  1674. strncpy(stext, p + 1, sizeof stext);
  1675. }
  1676. drawbar();
  1677. }
  1678. while(XPending(dpy)) {
  1679. XNextEvent(dpy, &ev);
  1680. if(handler[ev.type])
  1681. (handler[ev.type])(&ev); /* call handler */
  1682. }
  1683. }
  1684. cleanup();
  1685. XCloseDisplay(dpy);
  1686. return 0;
  1687. }