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.

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