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.

543 lines
9.2 KiB

19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
  1. /*
  2. * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
  3. * See LICENSE file for license details.
  4. */
  5. #include <math.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <X11/Xatom.h>
  9. #include <X11/Xutil.h>
  10. #include "dwm.h"
  11. static void (*arrange)(Arg *) = floating;
  12. static void
  13. center(Client *c)
  14. {
  15. XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
  16. }
  17. static Client *
  18. next(Client *c)
  19. {
  20. for(; c && !c->tags[tsel]; c = c->next);
  21. return c;
  22. }
  23. void
  24. zoom(Arg *arg)
  25. {
  26. Client **l;
  27. if(!sel)
  28. return;
  29. for(l = &clients; *l && *l != sel; l = &(*l)->next);
  30. *l = sel->next;
  31. sel->next = clients; /* pop */
  32. clients = sel;
  33. arrange(NULL);
  34. center(sel);
  35. focus(sel);
  36. }
  37. void
  38. max(Arg *arg)
  39. {
  40. if(!sel)
  41. return;
  42. sel->x = sx;
  43. sel->y = sy;
  44. sel->w = sw - 2 * sel->border;
  45. sel->h = sh - 2 * sel->border;
  46. craise(sel);
  47. resize(sel);
  48. discard_events(EnterWindowMask);
  49. }
  50. void
  51. view(Arg *arg)
  52. {
  53. tsel = arg->i;
  54. arrange(NULL);
  55. }
  56. void
  57. tag(Arg *arg)
  58. {
  59. int i, n;
  60. if(!sel)
  61. return;
  62. if(arg->i == tsel) {
  63. for(n = i = 0; i < TLast; i++)
  64. if(sel->tags[i])
  65. n++;
  66. if(n < 2)
  67. return;
  68. }
  69. if(sel->tags[arg->i])
  70. sel->tags[arg->i] = NULL; /* toggle tag */
  71. else
  72. sel->tags[arg->i] = tags[arg->i];
  73. arrange(NULL);
  74. }
  75. static void
  76. ban_client(Client *c)
  77. {
  78. XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
  79. XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty);
  80. }
  81. void
  82. floating(Arg *arg)
  83. {
  84. Client *c;
  85. arrange = floating;
  86. for(c = clients; c; c = c->next) {
  87. if(c->tags[tsel])
  88. resize(c);
  89. else
  90. ban_client(c);
  91. }
  92. if(sel && !sel->tags[tsel]) {
  93. if((sel = next(clients))) {
  94. craise(sel);
  95. focus(sel);
  96. }
  97. }
  98. discard_events(EnterWindowMask);
  99. }
  100. void
  101. tiling(Arg *arg)
  102. {
  103. Client *c;
  104. int n, cols, rows, gw, gh, i, j;
  105. float rt, fd;
  106. arrange = tiling;
  107. for(n = 0, c = clients; c; c = next(c->next), n++);
  108. if(n) {
  109. rt = sqrt(n);
  110. if(modff(rt, &fd) < 0.5)
  111. rows = floor(rt);
  112. else
  113. rows = ceil(rt);
  114. if(rows * rows < n)
  115. cols = rows + 1;
  116. else
  117. cols = rows;
  118. gw = (sw - 2) / cols;
  119. gh = (sh - 2) / rows;
  120. }
  121. else
  122. cols = rows = gw = gh = 0;
  123. for(i = j = 0, c = clients; c; c = c->next) {
  124. if(c->tags[tsel]) {
  125. c->x = i * gw;
  126. c->y = j * gh;
  127. c->w = gw;
  128. c->h = gh;
  129. resize(c);
  130. if(++i == cols) {
  131. j++;
  132. i = 0;
  133. }
  134. }
  135. else
  136. ban_client(c);
  137. }
  138. if(sel && !sel->tags[tsel]) {
  139. if((sel = next(clients))) {
  140. craise(sel);
  141. focus(sel);
  142. }
  143. }
  144. discard_events(EnterWindowMask);
  145. }
  146. void
  147. prevc(Arg *arg)
  148. {
  149. Client *c;
  150. if(!sel)
  151. return;
  152. if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) {
  153. craise(c);
  154. center(c);
  155. focus(c);
  156. }
  157. }
  158. void
  159. nextc(Arg *arg)
  160. {
  161. Client *c;
  162. if(!sel)
  163. return;
  164. if(!(c = next(sel->next)))
  165. c = next(clients);
  166. if(c) {
  167. craise(c);
  168. center(c);
  169. c->revert = sel;
  170. focus(c);
  171. }
  172. }
  173. void
  174. ckill(Arg *arg)
  175. {
  176. if(!sel)
  177. return;
  178. if(sel->proto & WM_PROTOCOL_DELWIN)
  179. send_message(sel->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
  180. else
  181. XKillClient(dpy, sel->win);
  182. }
  183. static void
  184. resize_title(Client *c)
  185. {
  186. int i;
  187. c->tw = 0;
  188. for(i = 0; i < TLast; i++)
  189. if(c->tags[i])
  190. c->tw += textw(c->tags[i]) + dc.font.height;
  191. c->tw += textw(c->name) + dc.font.height;
  192. if(c->tw > c->w)
  193. c->tw = c->w + 2;
  194. c->tx = c->x + c->w - c->tw + 2;
  195. c->ty = c->y;
  196. XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
  197. }
  198. void
  199. update_name(Client *c)
  200. {
  201. XTextProperty name;
  202. int n;
  203. char **list = NULL;
  204. name.nitems = 0;
  205. c->name[0] = 0;
  206. XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]);
  207. if(!name.nitems)
  208. XGetWMName(dpy, c->win, &name);
  209. if(!name.nitems)
  210. return;
  211. if(name.encoding == XA_STRING)
  212. strncpy(c->name, (char *)name.value, sizeof(c->name));
  213. else {
  214. if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
  215. && n > 0 && *list)
  216. {
  217. strncpy(c->name, *list, sizeof(c->name));
  218. XFreeStringList(list);
  219. }
  220. }
  221. XFree(name.value);
  222. resize_title(c);
  223. }
  224. void
  225. update_size(Client *c)
  226. {
  227. XSizeHints size;
  228. long msize;
  229. if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
  230. size.flags = PSize;
  231. c->flags = size.flags;
  232. if(c->flags & PBaseSize) {
  233. c->basew = size.base_width;
  234. c->baseh = size.base_height;
  235. }
  236. else
  237. c->basew = c->baseh = 0;
  238. if(c->flags & PResizeInc) {
  239. c->incw = size.width_inc;
  240. c->inch = size.height_inc;
  241. }
  242. else
  243. c->incw = c->inch = 0;
  244. if(c->flags & PMaxSize) {
  245. c->maxw = size.max_width;
  246. c->maxh = size.max_height;
  247. }
  248. else
  249. c->maxw = c->maxh = 0;
  250. if(c->flags & PMinSize) {
  251. c->minw = size.min_width;
  252. c->minh = size.min_height;
  253. }
  254. else
  255. c->minw = c->minh = 0;
  256. if(c->flags & PWinGravity)
  257. c->grav = size.win_gravity;
  258. else
  259. c->grav = NorthWestGravity;
  260. }
  261. void
  262. craise(Client *c)
  263. {
  264. XRaiseWindow(dpy, c->win);
  265. XRaiseWindow(dpy, c->title);
  266. }
  267. void
  268. lower(Client *c)
  269. {
  270. XLowerWindow(dpy, c->title);
  271. XLowerWindow(dpy, c->win);
  272. }
  273. void
  274. focus(Client *c)
  275. {
  276. if(sel && sel != c) {
  277. XSetWindowBorder(dpy, sel->win, dc.bg);
  278. XMapWindow(dpy, sel->title);
  279. draw_client(sel);
  280. }
  281. sel = c;
  282. XUnmapWindow(dpy, c->title);
  283. XSetWindowBorder(dpy, c->win, dc.fg);
  284. draw_client(c);
  285. XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
  286. XFlush(dpy);
  287. discard_events(EnterWindowMask);
  288. }
  289. void
  290. manage(Window w, XWindowAttributes *wa)
  291. {
  292. Client *c, **l;
  293. XSetWindowAttributes twa;
  294. c = emallocz(sizeof(Client));
  295. c->win = w;
  296. c->tx = c->x = wa->x;
  297. c->ty = c->y = wa->y;
  298. c->tw = c->w = wa->width;
  299. c->h = wa->height;
  300. c->th = th;
  301. c->border = 1;
  302. c->proto = win_proto(c->win);
  303. update_size(c);
  304. XSelectInput(dpy, c->win,
  305. StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
  306. XGetTransientForHint(dpy, c->win, &c->trans);
  307. twa.override_redirect = 1;
  308. twa.background_pixmap = ParentRelative;
  309. twa.event_mask = ExposureMask;
  310. c->tags[tsel] = tags[tsel];
  311. c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
  312. 0, DefaultDepth(dpy, screen), CopyFromParent,
  313. DefaultVisual(dpy, screen),
  314. CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
  315. update_name(c);
  316. for(l = &clients; *l; l = &(*l)->next);
  317. c->next = *l; /* *l == nil */
  318. *l = c;
  319. XSetWindowBorderWidth(dpy, c->win, 1);
  320. XMapRaised(dpy, c->win);
  321. XMapRaised(dpy, c->title);
  322. XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask,
  323. GrabModeAsync, GrabModeSync, None, None);
  324. XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask,
  325. GrabModeAsync, GrabModeSync, None, None);
  326. XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask,
  327. GrabModeAsync, GrabModeSync, None, None);
  328. arrange(NULL);
  329. center(c);
  330. focus(c);
  331. }
  332. void
  333. gravitate(Client *c, Bool invert)
  334. {
  335. int dx = 0, dy = 0;
  336. switch(c->grav) {
  337. case StaticGravity:
  338. case NorthWestGravity:
  339. case NorthGravity:
  340. case NorthEastGravity:
  341. dy = c->border;
  342. break;
  343. case EastGravity:
  344. case CenterGravity:
  345. case WestGravity:
  346. dy = -(c->h / 2) + c->border;
  347. break;
  348. case SouthEastGravity:
  349. case SouthGravity:
  350. case SouthWestGravity:
  351. dy = -c->h;
  352. break;
  353. default:
  354. break;
  355. }
  356. switch (c->grav) {
  357. case StaticGravity:
  358. case NorthWestGravity:
  359. case WestGravity:
  360. case SouthWestGravity:
  361. dx = c->border;
  362. break;
  363. case NorthGravity:
  364. case CenterGravity:
  365. case SouthGravity:
  366. dx = -(c->w / 2) + c->border;
  367. break;
  368. case NorthEastGravity:
  369. case EastGravity:
  370. case SouthEastGravity:
  371. dx = -(c->w + c->border);
  372. break;
  373. default:
  374. break;
  375. }
  376. if(invert) {
  377. dx = -dx;
  378. dy = -dy;
  379. }
  380. c->x += dx;
  381. c->y += dy;
  382. }
  383. void
  384. resize(Client *c)
  385. {
  386. XConfigureEvent e;
  387. if(c->incw)
  388. c->w -= (c->w - c->basew) % c->incw;
  389. if(c->inch)
  390. c->h -= (c->h - c->baseh) % c->inch;
  391. if(c->minw && c->w < c->minw)
  392. c->w = c->minw;
  393. if(c->minh && c->h < c->minh)
  394. c->h = c->minh;
  395. if(c->maxw && c->w > c->maxw)
  396. c->w = c->maxw;
  397. if(c->maxh && c->h > c->maxh)
  398. c->h = c->maxh;
  399. resize_title(c);
  400. XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
  401. e.type = ConfigureNotify;
  402. e.event = c->win;
  403. e.window = c->win;
  404. e.x = c->x;
  405. e.y = c->y;
  406. e.width = c->w;
  407. e.height = c->h;
  408. e.border_width = c->border;
  409. e.above = None;
  410. e.override_redirect = False;
  411. XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
  412. XFlush(dpy);
  413. }
  414. static int
  415. dummy_error_handler(Display *dsply, XErrorEvent *err)
  416. {
  417. return 0;
  418. }
  419. void
  420. unmanage(Client *c)
  421. {
  422. Client **l;
  423. XGrabServer(dpy);
  424. XSetErrorHandler(dummy_error_handler);
  425. XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
  426. XDestroyWindow(dpy, c->title);
  427. for(l = &clients; *l && *l != c; l = &(*l)->next);
  428. *l = c->next;
  429. for(l = &clients; *l; l = &(*l)->next)
  430. if((*l)->revert == c)
  431. (*l)->revert = NULL;
  432. if(sel == c)
  433. sel = sel->revert ? sel->revert : clients;
  434. free(c);
  435. XFlush(dpy);
  436. XSetErrorHandler(error_handler);
  437. XUngrabServer(dpy);
  438. arrange(NULL);
  439. if(sel)
  440. focus(sel);
  441. }
  442. Client *
  443. gettitle(Window w)
  444. {
  445. Client *c;
  446. for(c = clients; c; c = c->next)
  447. if(c->title == w)
  448. return c;
  449. return NULL;
  450. }
  451. Client *
  452. getclient(Window w)
  453. {
  454. Client *c;
  455. for(c = clients; c; c = c->next)
  456. if(c->win == w)
  457. return c;
  458. return NULL;
  459. }
  460. void
  461. draw_client(Client *c)
  462. {
  463. int i;
  464. if(c == sel)
  465. return;
  466. dc.x = dc.y = 0;
  467. dc.h = c->th;
  468. dc.w = 0;
  469. for(i = 0; i < TLast; i++) {
  470. if(c->tags[i]) {
  471. dc.x += dc.w;
  472. dc.w = textw(c->tags[i]) + dc.font.height;
  473. draw(True, c->tags[i]);
  474. }
  475. }
  476. dc.x += dc.w;
  477. dc.w = textw(c->name) + dc.font.height;
  478. draw(True, c->name);
  479. XCopyArea(dpy, dc.drawable, c->title, dc.gc,
  480. 0, 0, c->tw, c->th, 0, 0);
  481. XFlush(dpy);
  482. }