Advertisement
Guest User

monsterwm.c

a guest
Aug 23rd, 2017
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 47.76 KB | None | 0 0
  1. /* see license for copyright and license */
  2.  
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <err.h>
  6. #include <stdarg.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <signal.h>
  10. #include <sys/wait.h>
  11. #include <X11/Xutil.h>
  12. #include <X11/XKBlib.h>
  13. #include <X11/Xproto.h>
  14. #include <X11/Xatom.h>
  15.  
  16. #define LENGTH(x) (sizeof(x)/sizeof(*x))
  17. #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
  18. #define BUTTONMASK ButtonPressMask|ButtonReleaseMask
  19. #define ISFFT(c) (c->isfull || c->isfloat || c->istrans)
  20. #define ROOTMASK SubstructureRedirectMask|ButtonPressMask|SubstructureNotifyMask|PropertyChangeMask
  21.  
  22. enum { RESIZE, MOVE };
  23. enum { TILE, MONOCLE, BSTACK, GRID, FLOAT, MODES };
  24. enum { WM_PROTOCOLS, WM_DELETE_WINDOW, WM_COUNT };
  25. enum { NET_SUPPORTED, NET_FULLSCREEN, NET_WM_STATE, NET_ACTIVE, NET_COUNT };
  26.  
  27. /**
  28. * argument structure to be passed to function by config.h
  29. * com - function pointer ~ the command to run
  30. * i - an integer to indicate different states
  31. * v - any type argument
  32. */
  33. typedef union {
  34. const char** com;
  35. const int i;
  36. const void *v;
  37. } Arg;
  38.  
  39. /**
  40. * a key struct represents a combination of
  41. * mod - a modifier mask
  42. * keysym - and the key pressed
  43. * func - the function to be triggered because of the above combo
  44. * arg - the argument to the function
  45. */
  46. typedef struct {
  47. unsigned int mod;
  48. KeySym keysym;
  49. void (*func)(const Arg *);
  50. const Arg arg;
  51. } Key;
  52.  
  53. /**
  54. * a button struct represents a combination of
  55. * mask - a modifier mask
  56. * button - and the mouse button pressed
  57. * func - the function to be triggered because of the above combo
  58. * arg - the argument to the function
  59. */
  60. typedef struct {
  61. unsigned int mask, button;
  62. void (*func)(const Arg *);
  63. const Arg arg;
  64. } Button;
  65.  
  66. /**
  67. * define behavior of certain applications
  68. * configured in config.h
  69. *
  70. * class - the class or name of the instance
  71. * desktop - what desktop it should be spawned at
  72. * follow - whether to change desktop focus to the specified desktop
  73. */
  74. typedef struct {
  75. const char *class;
  76. const int desktop;
  77. const Bool follow, floating;
  78. } AppRule;
  79.  
  80. /* exposed function prototypes sorted alphabetically */
  81. static void change_desktop(const Arg *arg);
  82. static void client_to_desktop(const Arg *arg);
  83. static void focusurgent();
  84. static void killclient();
  85. static void last_desktop();
  86. static void move_down();
  87. static void move_up();
  88. static void moveresize(const Arg *arg);
  89. static void mousemotion(const Arg *arg);
  90. static void next_win();
  91. static void prev_win();
  92. static void quit(const Arg *arg);
  93. static void resize_master(const Arg *arg);
  94. static void resize_stack(const Arg *arg);
  95. static void rotate(const Arg *arg);
  96. static void rotate_filled(const Arg *arg);
  97. static void spawn(const Arg *arg);
  98. static void swap_master();
  99. static void switch_mode(const Arg *arg);
  100. static void togglepanel();
  101.  
  102. #include "config.h"
  103.  
  104. /**
  105. * a client is a wrapper to a window that additionally
  106. * holds some properties for that window
  107. *
  108. * next - the client after this one, or NULL if the current is the last client
  109. * isurgn - set when the window received an urgent hint
  110. * isfull - set when the window is fullscreen
  111. * isfloat - set when the window is floating
  112. * istrans - set when the window is transient
  113. * win - the window this client is representing
  114. *
  115. * istrans is separate from isfloat as floating windows can be reset to
  116. * their tiling positions, while the transients will always be floating
  117. */
  118. typedef struct Client {
  119. struct Client *next;
  120. Bool isurgn, isfull, isfloat, istrans;
  121. Window win;
  122. } Client;
  123.  
  124. /**
  125. * properties of each desktop
  126. *
  127. * masz - the size of the master area
  128. * sasz - additional size of the first stack window area
  129. * mode - the desktop's tiling layout mode
  130. * head - the start of the client list
  131. * curr - the currently highlighted window
  132. * prev - the client that previously had focus
  133. * sbar - the visibility status of the panel/statusbar
  134. */
  135. typedef struct {
  136. int mode, masz, sasz;
  137. Client *head, *curr, *prev;
  138. Bool sbar;
  139. } Desktop;
  140.  
  141. /* hidden function prototypes sorted alphabetically */
  142. static Client* addwindow(Window w, Desktop *d);
  143. static void buttonpress(XEvent *e);
  144. static void cleanup(void);
  145. static void clientmessage(XEvent *e);
  146. static void configurerequest(XEvent *e);
  147. static void deletewindow(Window w);
  148. static void desktopinfo(void);
  149. static void destroynotify(XEvent *e);
  150. static void enternotify(XEvent *e);
  151. static void focus(Client *c, Desktop *d);
  152. static void focusin(XEvent *e);
  153. static unsigned long getcolor(const char* color, const int screen);
  154. static void grabbuttons(Client *c);
  155. static void grabkeys(void);
  156. static void grid(int x, int y, int w, int h, const Desktop *d);
  157. static void keypress(XEvent *e);
  158. static void maprequest(XEvent *e);
  159. static void monocle(int x, int y, int w, int h, const Desktop *d);
  160. static Client* prevclient(Client *c, Desktop *d);
  161. static void propertynotify(XEvent *e);
  162. static void removeclient(Client *c, Desktop *d);
  163. static void run(void);
  164. static void setfullscreen(Client *c, Desktop *d, Bool fullscrn);
  165. static void setup(void);
  166. static void sigchld(int sig);
  167. static void stack(int x, int y, int w, int h, const Desktop *d);
  168. static void tile(Desktop *d);
  169. static void unmapnotify(XEvent *e);
  170. static Bool wintoclient(Window w, Client **c, Desktop **d);
  171. static int xerror(Display *dis, XErrorEvent *ee);
  172. static int xerrorstart(Display *dis, XErrorEvent *ee);
  173.  
  174. /**
  175. * global variables
  176. *
  177. * running - whether the wm is accepting and processing more events
  178. * wh - screen height
  179. * ww - screen width
  180. * dis - the display aka dpy
  181. * root - the root window
  182. * wmatoms - array holding atoms for ICCCM support
  183. * netatoms - array holding atoms for EWMH support
  184. * desktops - array of managed desktops
  185. * currdeskidx - which desktop is currently active
  186. */
  187. static Bool running = True;
  188. static int wh, ww, currdeskidx, prevdeskidx, retval;
  189. static unsigned int numlockmask, win_unfocus, win_focus;
  190. static Display *dis;
  191. static Window root;
  192. static Atom wmatoms[WM_COUNT], netatoms[NET_COUNT];
  193. static Desktop desktops[DESKTOPS];
  194.  
  195. /**
  196. * array of event handlers
  197. *
  198. * when a new event is received,
  199. * call the appropriate handler function
  200. */
  201. static void (*events[LASTEvent])(XEvent *e) = {
  202. [KeyPress] = keypress, [EnterNotify] = enternotify,
  203. [MapRequest] = maprequest, [ClientMessage] = clientmessage,
  204. [ButtonPress] = buttonpress, [DestroyNotify] = destroynotify,
  205. [UnmapNotify] = unmapnotify, [PropertyNotify] = propertynotify,
  206. [ConfigureRequest] = configurerequest, [FocusIn] = focusin,
  207. };
  208.  
  209. /**
  210. * array of layout handlers
  211. *
  212. * x - the start position in the x axis to place clients
  213. * y - the start position in the y axis to place clients
  214. * w - available width that windows have to expand
  215. * h - available height that windows have to expand
  216. * d - the desktop to tile its clients
  217. */
  218. static void (*layout[MODES])(int x, int y, int w, int h, const Desktop *d) = {
  219. [TILE] = stack, [BSTACK] = stack, [GRID] = grid, [MONOCLE] = monocle,
  220. };
  221.  
  222. /**
  223. * add the given window to the given desktop
  224. *
  225. * create a new client to hold the new window
  226. *
  227. * if there is no head at the given desktop
  228. * add the window as the head
  229. * otherwise if ATTACH_ASIDE is not set,
  230. * add the window as the last client
  231. * otherwise add the window as head
  232. */
  233. Client* addwindow(Window w, Desktop *d) {
  234. Client *c = NULL, *t = prevclient(d->head, d);
  235. if (!(c = (Client *)calloc(1, sizeof(Client)))) err(EXIT_FAILURE, "cannot allocate client");
  236. if (!d->head) d->head = c;
  237. else if (!ATTACH_ASIDE) { c->next = d->head; d->head = c; }
  238. else if (t) t->next = c; else d->head->next = c;
  239.  
  240. XSelectInput(dis, (c->win = w), PropertyChangeMask|FocusChangeMask|(FOLLOW_MOUSE?EnterWindowMask:0));
  241. return c;
  242. }
  243.  
  244. /**
  245. * on the press of a key binding (see grabkeys)
  246. * call the appropriate handler
  247. */
  248. void buttonpress(XEvent *e) {
  249. Desktop *d = NULL; Client *c = NULL;
  250. Bool w = wintoclient(e->xbutton.window, &c, &d);
  251.  
  252. if (w && CLICK_TO_FOCUS && c != d->curr && e->xbutton.button == FOCUS_BUTTON) focus(c, d);
  253.  
  254. for (unsigned int i = 0; i < LENGTH(buttons); i++)
  255. if (CLEANMASK(buttons[i].mask) == CLEANMASK(e->xbutton.state) &&
  256. buttons[i].func && buttons[i].button == e->xbutton.button) {
  257. if (c && d->curr != c) focus(c, d);
  258. buttons[i].func(&(buttons[i].arg));
  259. }
  260. }
  261.  
  262. /**
  263. * focus another desktop
  264. *
  265. * to avoid flickering (esp. monocle mode):
  266. * first map the new windows
  267. * first the current window and then all other
  268. * then unmap the old windows
  269. * first all others then the current
  270. */
  271. void change_desktop(const Arg *arg) {
  272. if (arg->i == currdeskidx || arg->i < 0 || arg->i >= DESKTOPS) return;
  273. Desktop *d = &desktops[(prevdeskidx = currdeskidx)], *n = &desktops[(currdeskidx = arg->i)];
  274. if (n->curr) XMapWindow(dis, n->curr->win);
  275. for (Client *c = n->head; c; c = c->next) XMapWindow(dis, c->win);
  276. XChangeWindowAttributes(dis, root, CWEventMask, &(XSetWindowAttributes){.do_not_propagate_mask = SubstructureNotifyMask});
  277. for (Client *c = d->head; c; c = c->next) if (c != d->curr) XUnmapWindow(dis, c->win);
  278. if (d->curr) XUnmapWindow(dis, d->curr->win);
  279. XChangeWindowAttributes(dis, root, CWEventMask, &(XSetWindowAttributes){.event_mask = ROOTMASK});
  280. if (n->head) { tile(n); focus(n->curr, n); }
  281. desktopinfo();
  282. }
  283.  
  284. /**
  285. * remove all windows in all desktops by sending a delete window message
  286. */
  287. void cleanup(void) {
  288. Window root_return, parent_return, *children;
  289. unsigned int nchildren;
  290.  
  291. XUngrabKey(dis, AnyKey, AnyModifier, root);
  292. XQueryTree(dis, root, &root_return, &parent_return, &children, &nchildren);
  293. for (unsigned int i = 0; i < nchildren; i++) deletewindow(children[i]);
  294. if (children) XFree(children);
  295. XSync(dis, False);
  296. }
  297.  
  298. /**
  299. * move the current focused client to another desktop
  300. *
  301. * add the current client as the last on the new desktop
  302. * then remove it from the current desktop
  303. */
  304. void client_to_desktop(const Arg *arg) {
  305. if (arg->i == currdeskidx || arg->i < 0 || arg->i >= DESKTOPS || !desktops[currdeskidx].curr) return;
  306. Desktop *d = &desktops[currdeskidx], *n = &desktops[arg->i];
  307. Client *c = d->curr, *p = prevclient(d->curr, d), *l = prevclient(n->head, n);
  308.  
  309. /* unlink current client from current desktop */
  310. if (d->head == c || !p) d->head = c->next; else p->next = c->next;
  311. c->next = NULL;
  312. XChangeWindowAttributes(dis, root, CWEventMask, &(XSetWindowAttributes){.do_not_propagate_mask = SubstructureNotifyMask});
  313. if (XUnmapWindow(dis, c->win)) focus(d->prev, d);
  314. XChangeWindowAttributes(dis, root, CWEventMask, &(XSetWindowAttributes){.event_mask = ROOTMASK});
  315. if (!(c->isfloat || c->istrans) || (d->head && !d->head->next)) tile(d);
  316.  
  317. /* link client to new desktop and make it the current */
  318. focus(l ? (l->next = c):n->head ? (n->head->next = c):(n->head = c), n);
  319.  
  320. if (FOLLOW_WINDOW) change_desktop(arg); else desktopinfo();
  321. }
  322.  
  323. /**
  324. * receive and process client messages
  325. *
  326. * check if window wants to change its state to fullscreen,
  327. * or if the window want to become active/focused
  328. *
  329. * to change the state of a mapped window, a client MUST
  330. * send a _NET_WM_STATE client message to the root window
  331. * message_type must be _NET_WM_STATE
  332. * data.l[0] is the action to be taken
  333. * data.l[1] is the property to alter three actions:
  334. * - remove/unset _NET_WM_STATE_REMOVE=0
  335. * - add/set _NET_WM_STATE_ADD=1,
  336. * - toggle _NET_WM_STATE_TOGGLE=2
  337. *
  338. * to request to become active, a client should send a
  339. * message of _NET_ACTIVE_WINDOW type. when such a message
  340. * is received and a client holding that window exists,
  341. * the window becomes the current active focused window
  342. * on its desktop.
  343. */
  344. void clientmessage(XEvent *e) {
  345. Desktop *d = NULL; Client *c = NULL;
  346. if (!wintoclient(e->xclient.window, &c, &d)) return;
  347.  
  348. if (e->xclient.message_type == netatoms[NET_WM_STATE] && (
  349. (unsigned)e->xclient.data.l[1] == netatoms[NET_FULLSCREEN]
  350. || (unsigned)e->xclient.data.l[2] == netatoms[NET_FULLSCREEN])) {
  351. setfullscreen(c, d, (e->xclient.data.l[0] == 1 || (e->xclient.data.l[0] == 2 && !c->isfull)));
  352. if (!(c->isfloat || c->istrans) || !d->head->next) tile(d);
  353. } else if (e->xclient.message_type == netatoms[NET_ACTIVE]) focus(c, d);
  354. }
  355.  
  356. /**
  357. * configure a window's size, position, border width, and stacking order.
  358. *
  359. * windows usually have a prefered size (width, height) and position (x, y),
  360. * and sometimes borer with (border_width) and stacking order (above, detail).
  361. * a configure request attempts to reconfigure those properties for a window.
  362. *
  363. * we don't really care about those values, because a tiling wm will impose
  364. * its own values for those properties.
  365. * however the requested values must be set initially for some windows,
  366. * otherwise the window will misbehave or even crash (see gedit, geany, gvim).
  367. *
  368. * some windows depend on the number of columns and rows to set their
  369. * size, and not on pixels (terminals, consoles, some editors etc).
  370. * normally those clients when tiled and respecting the prefered size
  371. * will create gaps around them (window_hints).
  372. * however, clients are tiled to match the wm's prefered size,
  373. * not respecting those prefered values.
  374. *
  375. * some windows implement window manager functions themselves.
  376. * that is windows explicitly steal focus, or manage subwindows,
  377. * or move windows around w/o the window manager's help, etc..
  378. * to disallow this behavior, we 'tile()' the desktop to which
  379. * the window that sent the configure request belongs.
  380. */
  381. void configurerequest(XEvent *e) {
  382. XConfigureRequestEvent *ev = &e->xconfigurerequest;
  383. XWindowChanges wc = { ev->x, ev->y, ev->width, ev->height, ev->border_width, ev->above, ev->detail };
  384. if (XConfigureWindow(dis, ev->window, ev->value_mask, &wc)) XSync(dis, False);
  385. Desktop *d = NULL; Client *c = NULL;
  386. if (wintoclient(ev->window, &c, &d)) tile(d);
  387. }
  388.  
  389. /**
  390. * clients receiving a WM_DELETE_WINDOW message should behave as if
  391. * the user selected "delete window" from a hypothetical menu and
  392. * also perform any confirmation dialog with the user.
  393. */
  394. void deletewindow(Window w) {
  395. XEvent ev = { .type = ClientMessage };
  396. ev.xclient.window = w;
  397. ev.xclient.format = 32;
  398. ev.xclient.message_type = wmatoms[WM_PROTOCOLS];
  399. ev.xclient.data.l[0] = wmatoms[WM_DELETE_WINDOW];
  400. ev.xclient.data.l[1] = CurrentTime;
  401. XSendEvent(dis, w, False, NoEventMask, &ev);
  402. }
  403.  
  404. /**
  405. * output info about the desktops on standard output stream
  406. *
  407. * the information is formatted as a space separated line
  408. * where each token contains information about a desktop.
  409. * each token is a formatted as ':' separated string of values.
  410. * the values are:
  411. * - the desktop number/id
  412. * - the desktop's client count
  413. * - the desktop's tiling layout mode/id
  414. * - whether the desktop is the current focused (1) or not (0)
  415. * - whether any client in that desktop has received an urgent hint
  416. *
  417. * once the info is collected, immediately flush the stream
  418. */
  419. void desktopinfo(void) {
  420. Desktop *d = NULL;
  421. Client *c = NULL;
  422. Bool urgent = False;
  423.  
  424. for (int w = 0, i = 0; i < DESKTOPS; i++, w = 0, urgent = False) {
  425. for (d = &desktops[i], c = d->head; c; urgent |= c->isurgn, ++w, c = c->next);
  426. printf("%d:%d:%d:%d:%d%c", i, w, d->mode, i == currdeskidx, urgent, i == DESKTOPS-1 ? '\n':' ');
  427. }
  428. fflush(stdout);
  429. }
  430.  
  431. /**
  432. * generated whenever a client application destroys a window
  433. *
  434. * a destroy notification is received when a window is being closed
  435. * on receival, remove the client that held that window
  436. */
  437. void destroynotify(XEvent *e) {
  438. Desktop *d = NULL; Client *c = NULL;
  439. if (wintoclient(e->xdestroywindow.window, &c, &d)) removeclient(c, d);
  440. }
  441.  
  442. /**
  443. * when the mouse enters a window's borders, that window,
  444. * if has set notifications of such events (EnterWindowMask)
  445. * will notify that the pointer entered its region
  446. * and will get focus if FOLLOW_MOUSE is set in the config.
  447. */
  448. void enternotify(XEvent *e) {
  449. Desktop *d = NULL; Client *c = NULL, *p = NULL;
  450.  
  451. if (!FOLLOW_MOUSE || (e->xcrossing.mode != NotifyNormal && e->xcrossing.detail == NotifyInferior)
  452. || !wintoclient(e->xcrossing.window, &c, &d) || e->xcrossing.window == d->curr->win) return;
  453.  
  454. if ((p = d->prev))
  455. XChangeWindowAttributes(dis, p->win, CWEventMask, &(XSetWindowAttributes){.do_not_propagate_mask = EnterWindowMask});
  456. focus(c, d);
  457. if (p) XChangeWindowAttributes(dis, p->win, CWEventMask, &(XSetWindowAttributes){.event_mask = EnterWindowMask});
  458. }
  459.  
  460. /**
  461. * 1. set current/active/focused and previously focused client
  462. * in other words, manage curr and prev references
  463. * 2. restack clients
  464. * 3. highlight borders and set active window property
  465. * 4. give input focus to the current/active/focused client
  466. */
  467. void focus(Client *c, Desktop *d) {
  468. /* update references to prev and curr,
  469. * previously focused and currently focused clients.
  470. *
  471. * if there are no clients (!head) or the new client
  472. * is NULL, then delete the _NET_ACTIVE_WINDOW property
  473. *
  474. * if the new client is the prev client then
  475. * - either the current client was removed
  476. * and thus focus(prev) was called
  477. * - or the previous from current is prev
  478. * ie, two consecutive clients were focused
  479. * and then prev_win() was called, to focus
  480. * the previous from current client, which
  481. * happens to be prev (curr == c->next).
  482. * (below: h:head p:prev c:curr)
  483. *
  484. * [h]->[p]->[c]->NULL ===> [h|p]->[c]->NULL
  485. * ^ remove current
  486. *
  487. * [h]->[p]->[c]->NULL ===> [h]->[c]->[p]->NULL
  488. * ^ prev_win swaps prev and curr
  489. *
  490. * in the first case we need to update prev reference,
  491. * choice here is to set it to the previous from the
  492. * new current client.
  493. * the second case is handled as any other case, the
  494. * current client is now the previously focused (prev = curr)
  495. * and the new current client is now curr (curr = c)
  496. *
  497. * references should only change when the current
  498. * client is different from the one given to focus.
  499. *
  500. * the new client should never be NULL, except if,
  501. * there is no other client on the workspace (!head).
  502. * prev and curr always point to different clients.
  503. *
  504. * NOTICE: remove client can remove any client,
  505. * not just the current (curr). Thus, if prev is
  506. * removed, its reference needs to be updated.
  507. * That is handled by removeclient() function.
  508. * All other reference changes for curr and prev
  509. * should and are handled here.
  510. */
  511. if (!d->head || !c) { /* no clients - no active window - nothing to do */
  512. XDeleteProperty(dis, root, netatoms[NET_ACTIVE]);
  513. d->curr = d->prev = NULL;
  514. return;
  515. } else if (d->prev == c && d->curr != c->next) { d->prev = prevclient((d->curr = c), d);
  516. } else if (d->curr != c) { d->prev = d->curr; d->curr = c; }
  517.  
  518. /* restack clients
  519. *
  520. * stack order is based on client properties.
  521. * from top to bottom:
  522. * - current when floating or transient
  523. * - floating or trancient windows
  524. * - current when tiled
  525. * - current when fullscreen
  526. * - fullscreen windows
  527. * - tiled windows
  528. *
  529. * num of n:all fl:fullscreen ft:floating/transient windows
  530. */
  531. int n = 0, fl = 0, ft = 0;
  532. for (c = d->head; c; c = c->next, ++n) if (ISFFT(c)) { fl++; if (!c->isfull) ft++; }
  533. Window w[n];
  534. w[(d->curr->isfloat || d->curr->istrans) ? 0:ft] = d->curr->win;
  535. for (fl += !ISFFT(d->curr) ? 1:0, c = d->head; c; c = c->next) {
  536. XSetWindowBorder(dis, c->win, c == d->curr ? win_focus:win_unfocus);
  537. /*
  538. * a window should have borders in any case, except if
  539. * - the window is fullscreen
  540. * - the window is not floating or transient and
  541. * - the mode is MONOCLE or,
  542. * - it is the only window on screen
  543. */
  544. XSetWindowBorderWidth(dis, c->win, c->isfull || (!ISFFT(c) &&
  545. (d->mode == MONOCLE || !d->head->next)) ? 0:BORDER_WIDTH);
  546. if (c != d->curr) w[c->isfull ? --fl:ISFFT(c) ? --ft:--n] = c->win;
  547. if (CLICK_TO_FOCUS || c == d->curr) grabbuttons(c);
  548. }
  549. XRestackWindows(dis, w, LENGTH(w));
  550.  
  551. XSetInputFocus(dis, d->curr->win, RevertToPointerRoot, CurrentTime);
  552. XChangeProperty(dis, root, netatoms[NET_ACTIVE], XA_WINDOW, 32,
  553. PropModeReplace, (unsigned char *)&d->curr->win, 1);
  554.  
  555. XSync(dis, False);
  556. }
  557.  
  558. /**
  559. * dont give focus to any client except current.
  560. * some apps explicitly call XSetInputFocus (see
  561. * tabbed, chromium), resulting in loss of input
  562. * focuse (mouse/kbd) from the current focused
  563. * client.
  564. *
  565. * this gives focus back to the current selected
  566. * client, by the user, through the wm.
  567. */
  568. void focusin(XEvent *e) {
  569. Desktop *d = &desktops[currdeskidx];
  570. if (d->curr && d->curr->win != e->xfocus.window) focus(d->curr, d);
  571. }
  572.  
  573. /**
  574. * find and focus the first client that received an urgent hint
  575. * first look in the current desktop then on other desktops
  576. */
  577. void focusurgent(void) {
  578. Client *c = NULL;
  579. int d = -1;
  580. for (c = desktops[currdeskidx].head; c && !c->isurgn; c = c->next);
  581. while (!c && d < DESKTOPS-1) for (c = desktops[++d].head; c && !c->isurgn; c = c->next);
  582. if (c) { if (d != -1) change_desktop(&(Arg){.i = d}); focus(c, &desktops[currdeskidx]); }
  583. }
  584.  
  585. /**
  586. * get a pixel with the requested color to
  587. * fill some window area (such as borders)
  588. */
  589. unsigned long getcolor(const char* color, const int screen) {
  590. XColor c; Colormap map = DefaultColormap(dis, screen);
  591. if (!XAllocNamedColor(dis, map, color, &c, &c)) err(EXIT_FAILURE, "cannot allocate color");
  592. return c.pixel;
  593. }
  594.  
  595. /**
  596. * register button bindings to be notified of
  597. * when they occur.
  598. * the wm listens to those button bindings and
  599. * calls an appropriate handler when a binding
  600. * occurs (see buttonpress).
  601. */
  602. void grabbuttons(Client *c) {
  603. unsigned int b, m, modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
  604.  
  605. for (m = 0; CLICK_TO_FOCUS && m < LENGTH(modifiers); m++)
  606. if (c != desktops[currdeskidx].curr) XGrabButton(dis, FOCUS_BUTTON, modifiers[m],
  607. c->win, False, BUTTONMASK, GrabModeAsync, GrabModeAsync, None, None);
  608. else XUngrabButton(dis, FOCUS_BUTTON, modifiers[m], c->win);
  609.  
  610. for (b = 0, m = 0; b < LENGTH(buttons); b++, m = 0) while (m < LENGTH(modifiers))
  611. XGrabButton(dis, buttons[b].button, buttons[b].mask|modifiers[m++], c->win,
  612. False, BUTTONMASK, GrabModeAsync, GrabModeAsync, None, None);
  613. }
  614.  
  615. /**
  616. * register key bindings to be notified of
  617. * when they occur.
  618. * the wm listens to those key bindings and
  619. * calls an appropriate handler when a binding
  620. * occurs (see keypressed).
  621. */
  622. void grabkeys(void) {
  623. KeyCode code;
  624. XUngrabKey(dis, AnyKey, AnyModifier, root);
  625. unsigned int k, m, modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
  626.  
  627. for (k = 0, m = 0; k < LENGTH(keys); k++, m = 0)
  628. while ((code = XKeysymToKeycode(dis, keys[k].keysym)) && m < LENGTH(modifiers))
  629. XGrabKey(dis, code, keys[k].mod|modifiers[m++], root, True, GrabModeAsync, GrabModeAsync);
  630. }
  631.  
  632. /**
  633. * grid mode / grid layout
  634. * arrange windows in a grid aka fair
  635. */
  636. void grid(int x, int y, int w, int h, const Desktop *d) {
  637. int n = 0, cols = 0, cn = 0, rn = 0, i = -1;
  638. for (Client *c = d->head; c; c = c->next) if (!ISFFT(c)) ++n;
  639. for (cols = 0; cols <= n/2; cols++) if (cols*cols >= n) break; /* emulate square root */
  640. if (n == 0) return; else if (n == 5) cols = 2;
  641.  
  642. int rows = n/cols, ch = h - BORDER_WIDTH, cw = (w - BORDER_WIDTH)/(cols ? cols:1);
  643. for (Client *c = d->head; c; c = c->next) {
  644. if (ISFFT(c)) continue; else ++i;
  645. if (i/rows + 1 > cols - n%cols) rows = n/cols + 1;
  646. XMoveResizeWindow(dis, c->win, x + cn*cw, y + rn*ch/rows, cw - BORDER_WIDTH, ch/rows - BORDER_WIDTH);
  647. if (++rn >= rows) { rn = 0; cn++; }
  648. }
  649. }
  650.  
  651. /**
  652. * on the press of a key binding (see grabkeys)
  653. * call the appropriate handler
  654. */
  655. void keypress(XEvent *e) {
  656. KeySym keysym = XkbKeycodeToKeysym(dis, e->xkey.keycode, 0, 0);
  657. for (unsigned int i = 0; i < LENGTH(keys); i++)
  658. if (keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(e->xkey.state))
  659. if (keys[i].func) keys[i].func(&keys[i].arg);
  660. }
  661.  
  662. /**
  663. * explicitly kill the current client - close the highlighted window
  664. * if the client accepts WM_DELETE_WINDOW requests send a delete message
  665. * otherwise forcefully kill and remove the client
  666. */
  667. void killclient(void) {
  668. Desktop *d = &desktops[currdeskidx];
  669. if (!d->curr) return;
  670.  
  671. Atom *prot = NULL; int n = -1;
  672. if (XGetWMProtocols(dis, d->curr->win, &prot, &n))
  673. while(--n >= 0 && prot[n] != wmatoms[WM_DELETE_WINDOW]);
  674. if (n < 0) { XKillClient(dis, d->curr->win); removeclient(d->curr, d); }
  675. else deletewindow(d->curr->win);
  676. if (prot) XFree(prot);
  677. }
  678.  
  679. /**
  680. * focus the previously/last focused desktop
  681. */
  682. void last_desktop(void) {
  683. change_desktop(&(Arg){.i = prevdeskidx});
  684. }
  685.  
  686. /**
  687. * a map request is received when a window wants to display itself.
  688. * if the window has override_redirect flag set,
  689. * then it should not be handled by the wm.
  690. * if the window already has a client then there is nothing to do.
  691. *
  692. * match window class and/or install name against an app rule.
  693. * create a new client for the window and add it to the appropriate desktop.
  694. * set the floating, transient and fullscreen state of the client.
  695. * if the desktop in which the window is to be spawned is the current desktop
  696. * then display/map the window, else, if follow is set, focus the new desktop.
  697. */
  698. void maprequest(XEvent *e) {
  699. Desktop *d = NULL; Client *c = NULL;
  700. Window w = e->xmaprequest.window;
  701. XWindowAttributes wa = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  702. if (wintoclient(w, &c, &d) || (XGetWindowAttributes(dis, w, &wa) && wa.override_redirect)) return;
  703.  
  704. XClassHint ch = {0, 0};
  705. Bool follow = False, floating = False;
  706. int newdsk = currdeskidx;
  707.  
  708. if (XGetClassHint(dis, w, &ch)) for (unsigned int i = 0; i < LENGTH(rules); i++)
  709. if (strstr(ch.res_class, rules[i].class) || strstr(ch.res_name, rules[i].class)) {
  710. if (rules[i].desktop >= 0 && rules[i].desktop < DESKTOPS) newdsk = rules[i].desktop;
  711. follow = rules[i].follow, floating = rules[i].floating;
  712. break;
  713. }
  714. if (ch.res_class) XFree(ch.res_class);
  715. if (ch.res_name) XFree(ch.res_name);
  716.  
  717. c = addwindow(w, (d = &desktops[newdsk])); /* from now on, use c->win */
  718. c->istrans = XGetTransientForHint(dis, c->win, &w);
  719. if ((c->isfloat = (floating || d->mode == FLOAT)) && !c->istrans)
  720. XMoveWindow(dis, c->win, (ww - wa.width)/2, (wh - wa.height)/2);
  721.  
  722. int i; unsigned long l; unsigned char *state = NULL; Atom a;
  723. if (XGetWindowProperty(dis, c->win, netatoms[NET_WM_STATE], 0L, sizeof a,
  724. False, XA_ATOM, &a, &i, &l, &l, &state) == Success && state)
  725. setfullscreen(c, d, (*(Atom *)state == netatoms[NET_FULLSCREEN]));
  726. if (state) XFree(state);
  727.  
  728. if (currdeskidx == newdsk) { if (!ISFFT(c)) tile(d); XMapWindow(dis, c->win); }
  729. else if (follow) change_desktop(&(Arg){.i = newdsk});
  730. focus(c, d);
  731.  
  732. if (!follow) desktopinfo();
  733. }
  734.  
  735. /**
  736. * handle resize and positioning of a window with the pointer.
  737. *
  738. * grab the pointer and get it's current position.
  739. * now, all pointer movement events will be reported until it is ungrabbed.
  740. *
  741. * while the mouse is pressed, grab interesting events (see button press,
  742. * button release, pointer motion).
  743. * on on pointer movement resize or move the window under the curson.
  744. * also handle map requests and configure requests.
  745. *
  746. * finally, on ButtonRelease, ungrab the poitner.
  747. * event handling is passed back to run() function.
  748. *
  749. * once a window has been moved or resized, it's marked as floating.
  750. */
  751. void mousemotion(const Arg *arg) {
  752. Desktop *d = &desktops[currdeskidx];
  753. XWindowAttributes wa;
  754. XEvent ev;
  755.  
  756. if (!d->curr || !XGetWindowAttributes(dis, d->curr->win, &wa)) return;
  757.  
  758. if (arg->i == RESIZE) XWarpPointer(dis, d->curr->win, d->curr->win, 0, 0, 0, 0, --wa.width, --wa.height);
  759. int rx, ry, c, xw, yh; unsigned int v; Window w;
  760. if (!XQueryPointer(dis, root, &w, &w, &rx, &ry, &c, &c, &v) || w != d->curr->win) return;
  761.  
  762. if (XGrabPointer(dis, root, False, BUTTONMASK|PointerMotionMask, GrabModeAsync,
  763. GrabModeAsync, None, None, CurrentTime) != GrabSuccess) return;
  764.  
  765. if (!d->curr->isfloat && !d->curr->istrans) { d->curr->isfloat = True; tile(d); focus(d->curr, d); }
  766.  
  767. do {
  768. XMaskEvent(dis, BUTTONMASK|PointerMotionMask|SubstructureRedirectMask, &ev);
  769. if (ev.type == MotionNotify) {
  770. xw = (arg->i == MOVE ? wa.x:wa.width) + ev.xmotion.x - rx;
  771. yh = (arg->i == MOVE ? wa.y:wa.height) + ev.xmotion.y - ry;
  772. if (arg->i == RESIZE) XResizeWindow(dis, d->curr->win,
  773. xw > MINWSZ ? xw:wa.width, yh > MINWSZ ? yh:wa.height);
  774. else if (arg->i == MOVE) XMoveWindow(dis, d->curr->win, xw, yh);
  775. } else if (ev.type == ConfigureRequest || ev.type == MapRequest) events[ev.type](&ev);
  776. } while (ev.type != ButtonRelease);
  777.  
  778. XUngrabPointer(dis, CurrentTime);
  779. }
  780.  
  781. /**
  782. * monocle aka max aka fullscreen mode/layout
  783. * each window should cover all the available screen space
  784. */
  785. void monocle(int x, int y, int w, int h, const Desktop *d) {
  786. for (Client *c = d->head; c; c = c->next) if (!ISFFT(c)) XMoveResizeWindow(dis, c->win, x, y, w, h);
  787. }
  788.  
  789. /**
  790. * swap positions of current and next from current clients
  791. */
  792. void move_down(void) {
  793. Desktop *d = &desktops[currdeskidx];
  794. if (!d->curr || !d->head->next) return;
  795. /* p is previous, c is current, n is next, if current is head n is last */
  796. Client *p = prevclient(d->curr, d), *n = (d->curr->next) ? d->curr->next:d->head;
  797. /*
  798. * if c is head, swapping with n should update head to n
  799. * [c]->[n]->.. ==> [n]->[c]->..
  800. * ^head ^head
  801. *
  802. * else there is a previous client and p->next should be what's after c
  803. * ..->[p]->[c]->[n]->.. ==> ..->[p]->[n]->[c]->..
  804. */
  805. if (d->curr == d->head) d->head = n; else p->next = d->curr->next;
  806. /*
  807. * if c is the last client, c will be the current head
  808. * [n]->..->[p]->[c]->NULL ==> [c]->[n]->..->[p]->NULL
  809. * ^head ^head
  810. * else c will take the place of n, so c-next will be n->next
  811. * ..->[p]->[c]->[n]->.. ==> ..->[p]->[n]->[c]->..
  812. */
  813. d->curr->next = (d->curr->next) ? n->next:n;
  814. /*
  815. * if c was swapped with n then they now point to the same ->next. n->next should be c
  816. * ..->[p]->[c]->[n]->.. ==> ..->[p]->[n]->.. ==> ..->[p]->[n]->[c]->..
  817. * [c]-^
  818. *
  819. * else c is the last client and n is head,
  820. * so c will be move to be head, no need to update n->next
  821. * [n]->..->[p]->[c]->NULL ==> [c]->[n]->..->[p]->NULL
  822. * ^head ^head
  823. */
  824. if (d->curr->next == n->next) n->next = d->curr; else d->head = d->curr;
  825. if (!d->curr->isfloat && !d->curr->istrans) tile(d);
  826. }
  827.  
  828. /**
  829. * swap positions of current and previous from current clients
  830. */
  831. void move_up(void) {
  832. Desktop *d = &desktops[currdeskidx];
  833. if (!d->curr || !d->head->next) return;
  834. /* p is previous from current or last if current is head */
  835. Client *pp = NULL, *p = prevclient(d->curr, d);
  836. /* pp is previous from p, or null if current is head and thus p is last */
  837. if (p->next) for (pp = d->head; pp && pp->next != p; pp = pp->next);
  838. /*
  839. * if p has a previous client then the next client should be current (current is c)
  840. * ..->[pp]->[p]->[c]->.. ==> ..->[pp]->[c]->[p]->..
  841. *
  842. * if p doesn't have a previous client, then p might be head, so head must change to c
  843. * [p]->[c]->.. ==> [c]->[p]->..
  844. * ^head ^head
  845. * if p is not head, then c is head (and p is last), so the new head is next of c
  846. * [c]->[n]->..->[p]->NULL ==> [n]->..->[p]->[c]->NULL
  847. * ^head ^last ^head ^last
  848. */
  849. if (pp) pp->next = d->curr; else d->head = (d->curr == d->head) ? d->curr->next:d->curr;
  850. /*
  851. * next of p should be next of c
  852. * ..->[pp]->[p]->[c]->[n]->.. ==> ..->[pp]->[c]->[p]->[n]->..
  853. * except if c was head (now c->next is head), so next of p should be c
  854. * [c]->[n]->..->[p]->NULL ==> [n]->..->[p]->[c]->NULL
  855. * ^head ^last ^head ^last
  856. */
  857. p->next = (d->curr->next == d->head) ? d->curr:d->curr->next;
  858. /*
  859. * next of c should be p
  860. * ..->[pp]->[p]->[c]->[n]->.. ==> ..->[pp]->[c]->[p]->[n]->..
  861. * except if c was head (now c->next is head), so c is must be last
  862. * [c]->[n]->..->[p]->NULL ==> [n]->..->[p]->[c]->NULL
  863. * ^head ^last ^head ^last
  864. */
  865. d->curr->next = (d->curr->next == d->head) ? NULL:p;
  866. if (!d->curr->isfloat && !d->curr->istrans) tile(d);
  867. }
  868.  
  869. /**
  870. * move and resize a window with the keyboard
  871. */
  872. void moveresize(const Arg *arg) {
  873. Desktop *d = &desktops[currdeskidx];
  874. XWindowAttributes wa;
  875. if (!d->curr || !XGetWindowAttributes(dis, d->curr->win, &wa)) return;
  876. if (!d->curr->isfloat && !d->curr->istrans) { d->curr->isfloat = True; tile(d); focus(d->curr, d); }
  877. XMoveResizeWindow(dis, d->curr->win, wa.x + ((int *)arg->v)[0], wa.y + ((int *)arg->v)[1],
  878. wa.width + ((int *)arg->v)[2], wa.height + ((int *)arg->v)[3]);
  879. }
  880.  
  881. /**
  882. * cyclic focus the next window
  883. * if the window is the last on stack, focus head
  884. */
  885. void next_win(void) {
  886. Desktop *d = &desktops[currdeskidx];
  887. if (d->curr && d->head->next) focus(d->curr->next ? d->curr->next:d->head, d);
  888. }
  889.  
  890. /**
  891. * get the previous client from the given
  892. * if no such client, return NULL
  893. */
  894. Client* prevclient(Client *c, Desktop *d) {
  895. Client *p = NULL;
  896. if (c && d->head && d->head->next) for (p = d->head; p->next && p->next != c; p = p->next);
  897. return p;
  898. }
  899.  
  900. /**
  901. * cyclic focus the previous window
  902. * if the window is head, focus the last stack window
  903. */
  904. void prev_win(void) {
  905. Desktop *d = &desktops[currdeskidx];
  906. if (d->curr && d->head->next) focus(prevclient(d->curr, d), d);
  907. }
  908.  
  909. /**
  910. * set unrgent hint for a window
  911. */
  912. void propertynotify(XEvent *e) {
  913. Desktop *d = NULL; Client *c = NULL;
  914. if (e->xproperty.atom != XA_WM_HINTS || !wintoclient(e->xproperty.window, &c, &d)) return;
  915.  
  916. XWMHints *wmh = XGetWMHints(dis, c->win);
  917. c->isurgn = (c != desktops[currdeskidx].curr && wmh && (wmh->flags & XUrgencyHint));
  918.  
  919. if (wmh) XFree(wmh);
  920. desktopinfo();
  921. }
  922.  
  923. /**
  924. * to quit just stop receiving events
  925. * run is stopped and control is back to main
  926. */
  927. void quit(const Arg *arg) {
  928. retval = arg->i;
  929. running = False;
  930. }
  931.  
  932. /**
  933. * remove the specified client from the given desktop
  934. *
  935. * if c was the previous client, previous must be updated.
  936. * if c was the current client, current must be updated.
  937. */
  938. void removeclient(Client *c, Desktop *d) {
  939. Client **p = NULL;
  940. for (p = &d->head; *p && (*p != c); p = &(*p)->next);
  941. if (!*p) return; else *p = c->next;
  942. if (c == d->prev && !(d->prev = prevclient(d->curr, d))) d->prev = d->head;
  943. if (c == d->curr || (d->head && !d->head->next)) focus(d->prev, d);
  944. if (!(c->isfloat || c->istrans) || (d->head && !d->head->next)) tile(d);
  945. free(c);
  946. desktopinfo();
  947. }
  948.  
  949. /**
  950. * resize the master size
  951. * we should check for window size limits for both master and
  952. * stack clients. the size of a window can't be less than MINWSZ
  953. */
  954. void resize_master(const Arg *arg) {
  955. Desktop *d = &desktops[currdeskidx];
  956. int msz = (d->mode == BSTACK ? wh:ww) * MASTER_SIZE + (d->masz += arg->i);
  957. if (msz >= MINWSZ && (d->mode == BSTACK ? wh:ww) - msz >= MINWSZ) tile(d);
  958. else d->masz -= arg->i; /* reset master area size */
  959. }
  960.  
  961. /**
  962. * resize the first stack window
  963. */
  964. void resize_stack(const Arg *arg) {
  965. desktops[currdeskidx].sasz += arg->i;
  966. tile(&desktops[currdeskidx]);
  967. }
  968.  
  969. /**
  970. * jump and focus the next or previous desktop
  971. */
  972. void rotate(const Arg *arg) {
  973. change_desktop(&(Arg){.i = (DESKTOPS + currdeskidx + arg->i) % DESKTOPS});
  974. }
  975.  
  976. /**
  977. * jump and focus the next non-empty desktop
  978. */
  979. void rotate_filled(const Arg *arg) {
  980. int n = arg->i;
  981. while (n < DESKTOPS && !desktops[(DESKTOPS + currdeskidx + n) % DESKTOPS].head) (n += arg->i);
  982. change_desktop(&(Arg){.i = (DESKTOPS + currdeskidx + n) % DESKTOPS});
  983. }
  984.  
  985. /**
  986. * main event loop
  987. * on receival of an event call the appropriate handler
  988. */
  989. void run(void) {
  990. XEvent ev;
  991. while(running && !XNextEvent(dis, &ev)) if (events[ev.type]) events[ev.type](&ev);
  992. }
  993.  
  994. /**
  995. * set the fullscreen state of a client
  996. *
  997. * if a client gets fullscreen resize it
  998. * to cover all screen space.
  999. * the border should be zero (0).
  1000. *
  1001. * if a client is reset from fullscreen,
  1002. * the border should be BORDER_WIDTH,
  1003. * except if no other client is on that desktop.
  1004. */
  1005. void setfullscreen(Client *c, Desktop *d, Bool fullscrn) {
  1006. if (fullscrn != c->isfull) XChangeProperty(dis, c->win,
  1007. netatoms[NET_WM_STATE], XA_ATOM, 32, PropModeReplace, (unsigned char*)
  1008. ((c->isfull = fullscrn) ? &netatoms[NET_FULLSCREEN]:0), fullscrn);
  1009. if (fullscrn) XMoveResizeWindow(dis, c->win, 0, 0, ww, wh + PANEL_HEIGHT);
  1010. XSetWindowBorderWidth(dis, c->win, (c->isfull || !d->head->next ? 0:BORDER_WIDTH));
  1011. }
  1012.  
  1013. /**
  1014. * set initial values
  1015. */
  1016. void setup(void) {
  1017. sigchld(0);
  1018.  
  1019. /* screen and root window */
  1020. const int screen = DefaultScreen(dis);
  1021. root = RootWindow(dis, screen);
  1022.  
  1023. /* screen width and height */
  1024. ww = XDisplayWidth(dis, screen);
  1025. wh = XDisplayHeight(dis, screen) - PANEL_HEIGHT;
  1026.  
  1027. /* initialize mode and panel visibility for each desktop */
  1028. for (unsigned int d = 0; d < DESKTOPS; d++)
  1029. desktops[d] = (Desktop){ .mode = DEFAULT_MODE, .sbar = SHOW_PANEL };
  1030.  
  1031. /* get color for focused and unfocused client borders */
  1032. win_focus = getcolor(FOCUS, screen);
  1033. win_unfocus = getcolor(UNFOCUS, screen);
  1034.  
  1035. /* set numlockmask */
  1036. XModifierKeymap *modmap = XGetModifierMapping(dis);
  1037. for (int k = 0; k < 8; k++) for (int j = 0; j < modmap->max_keypermod; j++)
  1038. if (modmap->modifiermap[modmap->max_keypermod*k + j] == XKeysymToKeycode(dis, XK_Num_Lock))
  1039. numlockmask = (1 << k);
  1040. XFreeModifiermap(modmap);
  1041.  
  1042. /* set up atoms for dialog/notification windows */
  1043. wmatoms[WM_PROTOCOLS] = XInternAtom(dis, "WM_PROTOCOLS", False);
  1044. wmatoms[WM_DELETE_WINDOW] = XInternAtom(dis, "WM_DELETE_WINDOW", False);
  1045. netatoms[NET_SUPPORTED] = XInternAtom(dis, "_NET_SUPPORTED", False);
  1046. netatoms[NET_WM_STATE] = XInternAtom(dis, "_NET_WM_STATE", False);
  1047. netatoms[NET_ACTIVE] = XInternAtom(dis, "_NET_ACTIVE_WINDOW", False);
  1048. netatoms[NET_FULLSCREEN] = XInternAtom(dis, "_NET_WM_STATE_FULLSCREEN", False);
  1049.  
  1050. /* propagate EWMH support */
  1051. XChangeProperty(dis, root, netatoms[NET_SUPPORTED], XA_ATOM, 32,
  1052. PropModeReplace, (unsigned char *)netatoms, NET_COUNT);
  1053.  
  1054. /* set the appropriate error handler
  1055. * try an action that will cause an error if another wm is active
  1056. * wait until events are processed to process the error from the above action
  1057. * if all is good set the generic error handler */
  1058. XSetErrorHandler(xerrorstart);
  1059. /* set masks for reporting events handled by the wm */
  1060. XSelectInput(dis, root, ROOTMASK);
  1061. XSync(dis, False);
  1062. XSetErrorHandler(xerror);
  1063. XSync(dis, False);
  1064.  
  1065. grabkeys();
  1066. if (DEFAULT_DESKTOP >= 0 && DEFAULT_DESKTOP < DESKTOPS) change_desktop(&(Arg){.i = DEFAULT_DESKTOP});
  1067. }
  1068.  
  1069. void sigchld(__attribute__((unused)) int sig) {
  1070. if (signal(SIGCHLD, sigchld) != SIG_ERR) while(0 < waitpid(-1, NULL, WNOHANG));
  1071. else err(EXIT_FAILURE, "cannot install SIGCHLD handler");
  1072. }
  1073.  
  1074. /**
  1075. * execute a command
  1076. */
  1077. void spawn(const Arg *arg) {
  1078. if (fork()) return;
  1079. if (dis) close(ConnectionNumber(dis));
  1080. setsid();
  1081. execvp((char*)arg->com[0], (char**)arg->com);
  1082. err(EXIT_SUCCESS, "execvp %s", (char *)arg->com[0]);
  1083. }
  1084.  
  1085. /**
  1086. * tile or common tiling aka v-stack mode/layout
  1087. * bstack or bottom stack aka h-stack mode/layout
  1088. */
  1089. void stack(int x, int y, int w, int h, const Desktop *d) {
  1090. Client *c = NULL, *t = NULL; Bool b = (d->mode == BSTACK);
  1091. int n = 0, p = 0, z = (b ? w:h), ma = (b ? h:w) * MASTER_SIZE + d->masz;
  1092.  
  1093. /* count stack windows and grab first non-floating, non-fullscreen window */
  1094. for (t = d->head; t; t = t->next) if (!ISFFT(t)) { if (c) ++n; else c = t; }
  1095.  
  1096. /* if there is only one window (c && !n), it should cover the available screen space
  1097. * if there is only one stack window, then we don't care about growth
  1098. * if more than one stack windows (n > 1) adjustments may be needed.
  1099. *
  1100. * - p is the num of pixels than remain when spliting the
  1101. * available width/height to the number of windows
  1102. * - z is each client's height/width
  1103. *
  1104. * ---------- --. ----------------------.
  1105. * | |----| }--|--> sasz }--> first client will have
  1106. * | | 1s | | | z+p+sasz height/width.
  1107. * | M |----|-. }--> screen height (h) ---'
  1108. * | | 2s | }--|--> client height (z) two stack clients on tile mode
  1109. * -----------' -' ::: ascii art by c00kiemon5ter
  1110. *
  1111. * what we do is, remove the sasz from the screen height/width and then
  1112. * divide that space with the windows on the stack so all windows have
  1113. * equal height/width: z = (z - sasz)/n
  1114. *
  1115. * sasz was left out (subtrackted), to later be added to the first client
  1116. * height/width. before we do that, there will be cases when the num of
  1117. * windows cannot be perfectly divided with the available screen height/width.
  1118. * for example: 100px scr. height, and 3 stack windows: 100/3 = 33,3333..
  1119. * so we get that remaining space and merge it to sasz: p = (z - sasz) % n + sasz
  1120. *
  1121. * in the end, we know each client's height/width (z), and how many pixels
  1122. * should be added to the first stack client (p) so that it satisfies sasz,
  1123. * and also, does not result in gaps created on the bottom of the screen.
  1124. */
  1125. if (c && !n) XMoveResizeWindow(dis, c->win, x, y, w - 2*BORDER_WIDTH, h - 2*BORDER_WIDTH);
  1126. if (!c || !n) return; else if (n > 1) { p = (z - d->sasz)%n + d->sasz; z = (z - d->sasz)/n; }
  1127.  
  1128. /* tile the first non-floating, non-fullscreen window to cover the master area */
  1129. if (b) XMoveResizeWindow(dis, c->win, x, y, w - 2*BORDER_WIDTH, ma - BORDER_WIDTH);
  1130. else XMoveResizeWindow(dis, c->win, x, y, ma - BORDER_WIDTH, h - 2*BORDER_WIDTH);
  1131.  
  1132. /* tile the next non-floating, non-fullscreen (and first) stack window adding p */
  1133. for (c = c->next; c && ISFFT(c); c = c->next);
  1134. int cw = (b ? h:w) - 2*BORDER_WIDTH - ma, ch = z - BORDER_WIDTH;
  1135. if (b) XMoveResizeWindow(dis, c->win, x, y += ma, ch - BORDER_WIDTH + p, cw);
  1136. else XMoveResizeWindow(dis, c->win, x += ma, y, cw, ch - BORDER_WIDTH + p);
  1137.  
  1138. /* tile the rest of the non-floating, non-fullscreen stack windows */
  1139. for (b ? (x += ch+p):(y += ch+p), c = c->next; c; c = c->next) {
  1140. if (ISFFT(c)) continue;
  1141. if (b) { XMoveResizeWindow(dis, c->win, x, y, ch, cw); x += z; }
  1142. else { XMoveResizeWindow(dis, c->win, x, y, cw, ch); y += z; }
  1143. }
  1144. }
  1145.  
  1146. /**
  1147. * swap master window with current.
  1148. * if current is head swap with next
  1149. * if current is not head, then head
  1150. * is behind us, so move_up until we
  1151. * are the head
  1152. */
  1153. void swap_master(void) {
  1154. Desktop *d = &desktops[currdeskidx];
  1155. if (!d->curr || !d->head->next) return;
  1156. if (d->curr == d->head) move_down();
  1157. else while (d->curr != d->head) move_up();
  1158. focus(d->head, d);
  1159. }
  1160.  
  1161. /**
  1162. * switch tiling mode/layout
  1163. *
  1164. * if mode is reselected reset all floating clients
  1165. * if mode is FLOAT set all clients floating
  1166. */
  1167. void switch_mode(const Arg *arg) {
  1168. Desktop *d = &desktops[currdeskidx];
  1169. if (d->mode != arg->i) d->mode = arg->i;
  1170. else if (d->mode != FLOAT) for (Client *c = d->head; c; c = c->next) c->isfloat = False;
  1171. if (d->head) { tile(d); focus(d->curr, d); }
  1172. desktopinfo();
  1173. }
  1174.  
  1175. /**
  1176. * tile clients of the given desktop with the desktop's mode/layout
  1177. * call the tiling handler fucntion taking account the panel height
  1178. */
  1179. void tile(Desktop *d) {
  1180. if (!d->head || d->mode == FLOAT) return; /* nothing to arange */
  1181. layout[d->head->next ? d->mode:MONOCLE](0, TOP_PANEL && d->sbar ? PANEL_HEIGHT:0,
  1182. ww, wh + (d->sbar ? 0:PANEL_HEIGHT), d);
  1183. }
  1184.  
  1185. /**
  1186. * toggle visibility state of the panel/bar
  1187. */
  1188. void togglepanel(void) {
  1189. desktops[currdeskidx].sbar = !desktops[currdeskidx].sbar;
  1190. tile(&desktops[currdeskidx]);
  1191. }
  1192.  
  1193. /**
  1194. * windows that request to unmap should lose their client
  1195. * so invisible windows do not exist on screen
  1196. */
  1197. void unmapnotify(XEvent *e) {
  1198. Desktop *d = NULL; Client *c = NULL;
  1199. if (wintoclient(e->xunmap.window, &c, &d)) removeclient(c, d);
  1200. }
  1201.  
  1202. /**
  1203. * find to which client and desktop the given window belongs to
  1204. */
  1205. Bool wintoclient(Window w, Client **c, Desktop **d) {
  1206. for (int i = 0; i < DESKTOPS && !*c; i++)
  1207. for (*d = &desktops[i], *c = (*d)->head; *c && (*c)->win != w; *c = (*c)->next);
  1208. return (*c != NULL);
  1209. }
  1210.  
  1211. /**
  1212. * There's no way to check accesses to destroyed windows,
  1213. * thus those cases are ignored (especially on UnmapNotify's).
  1214. */
  1215. int xerror(__attribute__((unused)) Display *dis, XErrorEvent *ee) {
  1216. if ((ee->error_code == BadAccess && (ee->request_code == X_GrabKey
  1217. || ee->request_code == X_GrabButton))
  1218. || (ee->error_code == BadMatch && (ee->request_code == X_SetInputFocus
  1219. || ee->request_code == X_ConfigureWindow))
  1220. || (ee->error_code == BadDrawable && (ee->request_code == X_PolyFillRectangle
  1221. || ee->request_code == X_CopyArea || ee->request_code == X_PolySegment
  1222. || ee->request_code == X_PolyText8))
  1223. || ee->error_code == BadWindow) return 0;
  1224. err(EXIT_FAILURE, "xerror: request: %d code: %d", ee->request_code, ee->error_code);
  1225. }
  1226.  
  1227. /**
  1228. * error handler function to display an appropriate error message
  1229. * when the window manager initializes (see setup - XSetErrorHandler)
  1230. */
  1231. int xerrorstart(__attribute__((unused)) Display *dis, __attribute__((unused)) XErrorEvent *ee) {
  1232. errx(EXIT_FAILURE, "xerror: another window manager is already running");
  1233. }
  1234.  
  1235. int main(int argc, char *argv[]) {
  1236. if (argc == 2 && !strncmp(argv[1], "-v", 3))
  1237. errx(EXIT_SUCCESS, "version: %s - by c00kiemon5ter >:3 omnomnomnom", VERSION);
  1238. else if (argc != 1) errx(EXIT_FAILURE, "usage: man monsterwm");
  1239. if (!(dis = XOpenDisplay(NULL))) errx(EXIT_FAILURE, "cannot open display");
  1240. setup();
  1241. desktopinfo(); /* zero out every desktop on (re)start */
  1242. run();
  1243. cleanup();
  1244. XCloseDisplay(dis);
  1245. return retval;
  1246. }
  1247.  
  1248. /* vim: set expandtab ts=4 sts=4 sw=4 : */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement