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.

406 lines
9.7 KiB

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