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.

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