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.

598 lines
10 KiB

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