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.

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