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.

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