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.

450 lines
10 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
18 years ago
17 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. XTextProperty name;
  165. /* check if window has set a property */
  166. name.nitems = 0;
  167. XGetTextProperty(dpy, c->win, &name, dwmprops);
  168. if(name.nitems && name.encoding == XA_STRING) {
  169. strncpy(prop, (char *)name.value, sizeof prop - 1);
  170. prop[sizeof prop - 1] = '\0';
  171. XFree(name.value);
  172. for(i = 0; i < ntags && i < sizeof prop - 1 && prop[i] != '\0'; i++)
  173. if((c->tags[i] = prop[i] == '1'))
  174. result = True;
  175. if(i < sizeof prop - 1 && prop[i] != '\0')
  176. c->isfloating = prop[i] == '1';
  177. }
  178. return result;
  179. }
  180. void
  181. manage(Window w, XWindowAttributes *wa) {
  182. unsigned int i;
  183. Client *c, *t = NULL;
  184. Window trans;
  185. Status rettrans;
  186. XWindowChanges wc;
  187. c = emallocz(sizeof(Client));
  188. c->tags = emallocz(ntags * sizeof(Bool));
  189. c->win = w;
  190. c->x = wa->x;
  191. c->y = wa->y;
  192. c->w = wa->width;
  193. c->h = wa->height;
  194. c->oldborder = wa->border_width;
  195. if(c->w == sw && c->h == sh) {
  196. c->x = sx;
  197. c->y = sy;
  198. c->border = wa->border_width;
  199. }
  200. else {
  201. if(c->x + c->w + 2 * c->border > wax + waw)
  202. c->x = wax + waw - c->w - 2 * c->border;
  203. if(c->y + c->h + 2 * c->border > way + wah)
  204. c->y = way + wah - c->h - 2 * c->border;
  205. if(c->x < wax)
  206. c->x = wax;
  207. if(c->y < way)
  208. c->y = way;
  209. c->border = BORDERPX;
  210. }
  211. wc.border_width = c->border;
  212. XConfigureWindow(dpy, w, CWBorderWidth, &wc);
  213. XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
  214. configure(c); /* propagates border_width, if size doesn't change */
  215. updatesizehints(c);
  216. XSelectInput(dpy, w,
  217. StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
  218. grabbuttons(c, False);
  219. updatetitle(c);
  220. if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
  221. for(t = clients; t && t->win != trans; t = t->next);
  222. if(t)
  223. for(i = 0; i < ntags; i++)
  224. c->tags[i] = t->tags[i];
  225. if(!loadprops(c))
  226. applyrules(c);
  227. if(!c->isfloating)
  228. c->isfloating = (rettrans == Success) || c->isfixed;
  229. saveprops(c);
  230. attach(c);
  231. attachstack(c);
  232. XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */
  233. ban(c);
  234. arrange();
  235. }
  236. void
  237. resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
  238. double dx, dy, max, min, ratio;
  239. XWindowChanges wc;
  240. if(sizehints) {
  241. if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
  242. dx = (double)(w - c->basew);
  243. dy = (double)(h - c->baseh);
  244. min = (double)(c->minax) / (double)(c->minay);
  245. max = (double)(c->maxax) / (double)(c->maxay);
  246. ratio = dx / dy;
  247. if(max > 0 && min > 0 && ratio > 0) {
  248. if(ratio < min) {
  249. dy = (dx * min + dy) / (min * min + 1);
  250. dx = dy * min;
  251. w = (int)dx + c->basew;
  252. h = (int)dy + c->baseh;
  253. }
  254. else if(ratio > max) {
  255. dy = (dx * min + dy) / (max * max + 1);
  256. dx = dy * min;
  257. w = (int)dx + c->basew;
  258. h = (int)dy + c->baseh;
  259. }
  260. }
  261. }
  262. if(c->minw && w < c->minw)
  263. w = c->minw;
  264. if(c->minh && h < c->minh)
  265. h = c->minh;
  266. if(c->maxw && w > c->maxw)
  267. w = c->maxw;
  268. if(c->maxh && h > c->maxh)
  269. h = c->maxh;
  270. if(c->incw)
  271. w -= (w - c->basew) % c->incw;
  272. if(c->inch)
  273. h -= (h - c->baseh) % c->inch;
  274. }
  275. if(w <= 0 || h <= 0)
  276. return;
  277. /* offscreen appearance fixes */
  278. if(x > sw)
  279. x = sw - w - 2 * c->border;
  280. if(y > sh)
  281. y = sh - h - 2 * c->border;
  282. if(x + w + 2 * c->border < sx)
  283. x = sx;
  284. if(y + h + 2 * c->border < sy)
  285. y = sy;
  286. if(c->x != x || c->y != y || c->w != w || c->h != h) {
  287. c->x = wc.x = x;
  288. c->y = wc.y = y;
  289. c->w = wc.width = w;
  290. c->h = wc.height = h;
  291. wc.border_width = c->border;
  292. XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
  293. configure(c);
  294. XSync(dpy, False);
  295. }
  296. }
  297. void
  298. saveprops(Client *c) {
  299. unsigned int i;
  300. for(i = 0; i < ntags && i < sizeof prop - 1; i++)
  301. prop[i] = c->tags[i] ? '1' : '0';
  302. if(i < sizeof prop - 1)
  303. prop[i++] = c->isfloating ? '1' : '0';
  304. prop[i] = '\0';
  305. XChangeProperty(dpy, c->win, dwmprops, XA_STRING, 8,
  306. PropModeReplace, (unsigned char *)prop, i);
  307. }
  308. void
  309. unban(Client *c) {
  310. if(!c->isbanned)
  311. return;
  312. XMapWindow(dpy, c->win);
  313. setclientstate(c, NormalState);
  314. c->isbanned = False;
  315. }
  316. void
  317. unmanage(Client *c, long state) {
  318. XWindowChanges wc;
  319. wc.border_width = c->oldborder;
  320. /* The server grab construct avoids race conditions. */
  321. XGrabServer(dpy);
  322. XSetErrorHandler(xerrordummy);
  323. XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
  324. detach(c);
  325. detachstack(c);
  326. if(sel == c)
  327. focus(NULL);
  328. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  329. setclientstate(c, state);
  330. free(c->tags);
  331. free(c);
  332. XSync(dpy, False);
  333. XSetErrorHandler(xerror);
  334. XUngrabServer(dpy);
  335. if(state != NormalState)
  336. arrange();
  337. }
  338. void
  339. updatesizehints(Client *c) {
  340. long msize;
  341. XSizeHints size;
  342. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  343. size.flags = PSize;
  344. c->flags = size.flags;
  345. if(c->flags & PBaseSize) {
  346. c->basew = size.base_width;
  347. c->baseh = size.base_height;
  348. }
  349. else if(c->flags & PMinSize) {
  350. c->basew = size.min_width;
  351. c->baseh = size.min_height;
  352. }
  353. else
  354. c->basew = c->baseh = 0;
  355. if(c->flags & PResizeInc) {
  356. c->incw = size.width_inc;
  357. c->inch = size.height_inc;
  358. }
  359. else
  360. c->incw = c->inch = 0;
  361. if(c->flags & PMaxSize) {
  362. c->maxw = size.max_width;
  363. c->maxh = size.max_height;
  364. }
  365. else
  366. c->maxw = c->maxh = 0;
  367. if(c->flags & PMinSize) {
  368. c->minw = size.min_width;
  369. c->minh = size.min_height;
  370. }
  371. else if(c->flags & PBaseSize) {
  372. c->minw = size.base_width;
  373. c->minh = size.base_height;
  374. }
  375. else
  376. c->minw = c->minh = 0;
  377. if(c->flags & PAspect) {
  378. c->minax = size.min_aspect.x;
  379. c->maxax = size.max_aspect.x;
  380. c->minay = size.min_aspect.y;
  381. c->maxay = size.max_aspect.y;
  382. }
  383. else
  384. c->minax = c->maxax = c->minay = c->maxay = 0;
  385. c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
  386. && c->maxw == c->minw && c->maxh == c->minh);
  387. }
  388. void
  389. updatetitle(Client *c) {
  390. char **list = NULL;
  391. int n;
  392. XTextProperty name;
  393. name.nitems = 0;
  394. c->name[0] = 0;
  395. XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
  396. if(!name.nitems)
  397. XGetWMName(dpy, c->win, &name);
  398. if(!name.nitems)
  399. return;
  400. if(name.encoding == XA_STRING)
  401. strncpy(c->name, (char *)name.value, sizeof c->name - 1);
  402. else {
  403. if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
  404. && n > 0 && *list)
  405. {
  406. strncpy(c->name, *list, sizeof c->name - 1);
  407. XFreeStringList(list);
  408. }
  409. }
  410. c->name[sizeof c->name - 1] = '\0';
  411. XFree(name.value);
  412. }