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.

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