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.

386 lines
8.9 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
  1. /* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
  2. * See LICENSE file for license details.
  3. */
  4. #include "dwm.h"
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <X11/Xatom.h>
  8. #include <X11/Xutil.h>
  9. /* static */
  10. static void
  11. attachstack(Client *c) {
  12. c->snext = stack;
  13. stack = c;
  14. }
  15. static void
  16. detachstack(Client *c) {
  17. Client **tc;
  18. for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
  19. *tc = c->snext;
  20. }
  21. static void
  22. grabbuttons(Client *c, Bool focused) {
  23. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  24. if(focused) {
  25. XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
  26. GrabModeAsync, GrabModeSync, None, None);
  27. XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
  28. GrabModeAsync, GrabModeSync, None, None);
  29. XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  30. GrabModeAsync, GrabModeSync, None, None);
  31. XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  32. GrabModeAsync, GrabModeSync, None, None);
  33. XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
  34. GrabModeAsync, GrabModeSync, None, None);
  35. XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
  36. GrabModeAsync, GrabModeSync, None, None);
  37. XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  38. GrabModeAsync, GrabModeSync, None, None);
  39. XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  40. GrabModeAsync, GrabModeSync, None, None);
  41. XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
  42. GrabModeAsync, GrabModeSync, None, None);
  43. XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
  44. GrabModeAsync, GrabModeSync, None, None);
  45. XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
  46. GrabModeAsync, GrabModeSync, None, None);
  47. XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
  48. GrabModeAsync, GrabModeSync, None, None);
  49. }
  50. else
  51. XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
  52. GrabModeAsync, GrabModeSync, None, None);
  53. }
  54. static Bool
  55. isprotodel(Client *c) {
  56. int i, n;
  57. Atom *protocols;
  58. Bool ret = False;
  59. if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
  60. for(i = 0; !ret && i < n; i++)
  61. if(protocols[i] == wmatom[WMDelete])
  62. ret = True;
  63. XFree(protocols);
  64. }
  65. return ret;
  66. }
  67. static void
  68. setclientstate(Client *c, long state) {
  69. long data[] = {state, None};
  70. XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
  71. PropModeReplace, (unsigned char *)data, 2);
  72. }
  73. static int
  74. xerrordummy(Display *dsply, XErrorEvent *ee) {
  75. return 0;
  76. }
  77. /* extern */
  78. void
  79. attach(Client *c) {
  80. if(clients)
  81. clients->prev = c;
  82. c->next = clients;
  83. clients = c;
  84. }
  85. void
  86. configure(Client *c) {
  87. XConfigureEvent ce;
  88. ce.type = ConfigureNotify;
  89. ce.display = dpy;
  90. ce.event = c->win;
  91. ce.window = c->win;
  92. ce.x = c->x;
  93. ce.y = c->y;
  94. ce.width = c->w;
  95. ce.height = c->h;
  96. ce.border_width = c->border;
  97. ce.above = None;
  98. ce.override_redirect = False;
  99. XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
  100. }
  101. void
  102. detach(Client *c) {
  103. if(c->prev)
  104. c->prev->next = c->next;
  105. if(c->next)
  106. c->next->prev = c->prev;
  107. if(c == clients)
  108. clients = c->next;
  109. c->next = c->prev = NULL;
  110. }
  111. void
  112. focus(Client *c) {
  113. if(c && !isvisible(c))
  114. return;
  115. if(sel && sel != c) {
  116. grabbuttons(sel, False);
  117. XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
  118. }
  119. if(c) {
  120. detachstack(c);
  121. attachstack(c);
  122. grabbuttons(c, True);
  123. }
  124. sel = c;
  125. drawstatus();
  126. if(!selscreen)
  127. return;
  128. if(c) {
  129. XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
  130. XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
  131. }
  132. else
  133. XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
  134. }
  135. void
  136. killclient(const char *arg) {
  137. if(!sel)
  138. return;
  139. if(isprotodel(sel))
  140. sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
  141. else
  142. XKillClient(dpy, sel->win);
  143. }
  144. void
  145. manage(Window w, XWindowAttributes *wa) {
  146. Client *c, *t;
  147. Window trans;
  148. XWindowChanges wc;
  149. c = emallocz(sizeof(Client));
  150. c->tags = emallocz(ntags * sizeof(Bool));
  151. c->win = w;
  152. c->x = wa->x;
  153. c->y = wa->y;
  154. c->w = wa->width;
  155. c->h = wa->height;
  156. if(c->w == sw && c->h == sh) {
  157. c->border = 0;
  158. c->x = sx;
  159. c->y = sy;
  160. }
  161. else {
  162. c->border = BORDERPX;
  163. if(c->x + c->w + 2 * c->border > wax + waw)
  164. c->x = wax + waw - c->w - 2 * c->border;
  165. if(c->y + c->h + 2 * c->border > way + wah)
  166. c->y = way + wah - c->h - 2 * c->border;
  167. if(c->x < wax)
  168. c->x = wax;
  169. if(c->y < way)
  170. c->y = way;
  171. }
  172. updatesizehints(c);
  173. XSelectInput(dpy, w,
  174. StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
  175. XGetTransientForHint(dpy, w, &trans);
  176. grabbuttons(c, False);
  177. wc.border_width = c->border;
  178. XConfigureWindow(dpy, w, CWBorderWidth, &wc);
  179. XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
  180. configure(c); /* propagates border_width, if size doesn't change */
  181. updatetitle(c);
  182. for(t = clients; t && t->win != trans; t = t->next);
  183. settags(c, t);
  184. if(!c->isuntiled)
  185. c->isuntiled = (t != NULL) || c->isfixed;
  186. attach(c);
  187. attachstack(c);
  188. c->isbanned = True;
  189. XMoveWindow(dpy, w, c->x + 2 * sw, c->y);
  190. XMapWindow(dpy, w);
  191. setclientstate(c, NormalState);
  192. if(isvisible(c))
  193. focus(c);
  194. lt->arrange();
  195. }
  196. void
  197. resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
  198. float actual, dx, dy, max, min;
  199. XWindowChanges wc;
  200. if(w <= 0 || h <= 0)
  201. return;
  202. if(sizehints) {
  203. if(c->minw && w < c->minw)
  204. w = c->minw;
  205. if(c->minh && h < c->minh)
  206. h = c->minh;
  207. if(c->maxw && w > c->maxw)
  208. w = c->maxw;
  209. if(c->maxh && h > c->maxh)
  210. h = c->maxh;
  211. /* inspired by algorithm from fluxbox */
  212. if(c->minay > 0 && c->maxay && (h - c->baseh) > 0) {
  213. dx = (float)(w - c->basew);
  214. dy = (float)(h - c->baseh);
  215. min = (float)(c->minax) / (float)(c->minay);
  216. max = (float)(c->maxax) / (float)(c->maxay);
  217. actual = dx / dy;
  218. if(max > 0 && min > 0 && actual > 0) {
  219. if(actual < min) {
  220. dy = (dx * min + dy) / (min * min + 1);
  221. dx = dy * min;
  222. w = (int)dx + c->basew;
  223. h = (int)dy + c->baseh;
  224. }
  225. else if(actual > max) {
  226. dy = (dx * min + dy) / (max * max + 1);
  227. dx = dy * min;
  228. w = (int)dx + c->basew;
  229. h = (int)dy + c->baseh;
  230. }
  231. }
  232. }
  233. if(c->incw)
  234. w -= (w - c->basew) % c->incw;
  235. if(c->inch)
  236. h -= (h - c->baseh) % c->inch;
  237. }
  238. if(w == sw && h == sh)
  239. c->border = 0;
  240. else
  241. c->border = BORDERPX;
  242. /* offscreen appearance fixes */
  243. if(x > sw)
  244. x = sw - w - 2 * c->border;
  245. if(y > sh)
  246. y = sh - h - 2 * c->border;
  247. if(x + w + 2 * c->border < sx)
  248. x = sx;
  249. if(y + h + 2 * c->border < sy)
  250. y = sy;
  251. if(c->x != x || c->y != y || c->w != w || c->h != h) {
  252. c->x = wc.x = x;
  253. c->y = wc.y = y;
  254. c->w = wc.width = w;
  255. c->h = wc.height = h;
  256. wc.border_width = c->border;
  257. XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
  258. configure(c);
  259. XSync(dpy, False);
  260. }
  261. }
  262. void
  263. toggletiled(const char *arg) {
  264. if(!sel || lt->arrange == untile)
  265. return;
  266. sel->isuntiled = !sel->isuntiled;
  267. lt->arrange();
  268. }
  269. void
  270. updatesizehints(Client *c) {
  271. long msize;
  272. XSizeHints size;
  273. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  274. size.flags = PSize;
  275. c->flags = size.flags;
  276. if(c->flags & PBaseSize) {
  277. c->basew = size.base_width;
  278. c->baseh = size.base_height;
  279. }
  280. else
  281. c->basew = c->baseh = 0;
  282. if(c->flags & PResizeInc) {
  283. c->incw = size.width_inc;
  284. c->inch = size.height_inc;
  285. }
  286. else
  287. c->incw = c->inch = 0;
  288. if(c->flags & PMaxSize) {
  289. c->maxw = size.max_width;
  290. c->maxh = size.max_height;
  291. }
  292. else
  293. c->maxw = c->maxh = 0;
  294. if(c->flags & PMinSize) {
  295. c->minw = size.min_width;
  296. c->minh = size.min_height;
  297. }
  298. else
  299. c->minw = c->minh = 0;
  300. if(c->flags & PAspect) {
  301. c->minax = size.min_aspect.x;
  302. c->minay = size.min_aspect.y;
  303. c->maxax = size.max_aspect.x;
  304. c->maxay = size.max_aspect.y;
  305. }
  306. else
  307. c->minax = c->minay = c->maxax = c->maxay = 0;
  308. c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
  309. && c->maxw == c->minw && c->maxh == c->minh);
  310. }
  311. void
  312. updatetitle(Client *c) {
  313. char **list = NULL;
  314. int n;
  315. XTextProperty name;
  316. name.nitems = 0;
  317. c->name[0] = 0;
  318. XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
  319. if(!name.nitems)
  320. XGetWMName(dpy, c->win, &name);
  321. if(!name.nitems)
  322. return;
  323. if(name.encoding == XA_STRING)
  324. strncpy(c->name, (char *)name.value, sizeof c->name);
  325. else {
  326. if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
  327. && n > 0 && *list)
  328. {
  329. strncpy(c->name, *list, sizeof c->name);
  330. XFreeStringList(list);
  331. }
  332. }
  333. XFree(name.value);
  334. }
  335. void
  336. unmanage(Client *c) {
  337. Client *nc;
  338. /* The server grab construct avoids race conditions. */
  339. XGrabServer(dpy);
  340. XSetErrorHandler(xerrordummy);
  341. detach(c);
  342. detachstack(c);
  343. if(sel == c) {
  344. for(nc = stack; nc && !isvisible(nc); nc = nc->snext);
  345. focus(nc);
  346. }
  347. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  348. setclientstate(c, WithdrawnState);
  349. free(c->tags);
  350. free(c);
  351. XSync(dpy, False);
  352. XSetErrorHandler(xerror);
  353. XUngrabServer(dpy);
  354. lt->arrange();
  355. }