client.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. /*
  2. * calmwm - the calm window manager
  3. *
  4. * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. *
  18. * $OpenBSD: client.c,v 1.198 2015/07/01 14:36:42 okan Exp $
  19. */
  20. #include <sys/types.h>
  21. #include <sys/queue.h>
  22. #include <err.h>
  23. #include <errno.h>
  24. #include <limits.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. #include "calmwm.h"
  30. static struct client_ctx *client_next(struct client_ctx *);
  31. static struct client_ctx *client_prev(struct client_ctx *);
  32. static void client_mtf(struct client_ctx *);
  33. static void client_none(struct screen_ctx *);
  34. static void client_placecalc(struct client_ctx *);
  35. static void client_wm_protocols(struct client_ctx *);
  36. static void client_mwm_hints(struct client_ctx *);
  37. static int client_inbound(struct client_ctx *, int, int);
  38. struct client_ctx *curcc = NULL;
  39. struct client_ctx *
  40. client_init(Window win, struct screen_ctx *sc)
  41. {
  42. struct client_ctx *cc;
  43. XWindowAttributes wattr;
  44. int mapped;
  45. if (win == None)
  46. return(NULL);
  47. if (!XGetWindowAttributes(X_Dpy, win, &wattr))
  48. return(NULL);
  49. if (sc == NULL) {
  50. if ((sc = screen_find(wattr.root)) == NULL)
  51. return(NULL);
  52. mapped = 1;
  53. } else {
  54. if (wattr.override_redirect || wattr.map_state != IsViewable)
  55. return(NULL);
  56. mapped = wattr.map_state != IsUnmapped;
  57. }
  58. cc = xcalloc(1, sizeof(*cc));
  59. XGrabServer(X_Dpy);
  60. cc->sc = sc;
  61. cc->win = win;
  62. TAILQ_INIT(&cc->nameq);
  63. client_setname(cc);
  64. conf_client(cc);
  65. XGetClassHint(X_Dpy, cc->win, &cc->ch);
  66. client_wm_hints(cc);
  67. client_wm_protocols(cc);
  68. client_getsizehints(cc);
  69. client_mwm_hints(cc);
  70. /* Saved pointer position */
  71. cc->ptr.x = -1;
  72. cc->ptr.y = -1;
  73. cc->geom.x = wattr.x;
  74. cc->geom.y = wattr.y;
  75. cc->geom.w = wattr.width;
  76. cc->geom.h = wattr.height;
  77. cc->colormap = wattr.colormap;
  78. if (wattr.map_state != IsViewable) {
  79. client_placecalc(cc);
  80. client_move(cc);
  81. if ((cc->wmh) && (cc->wmh->flags & StateHint))
  82. client_set_wm_state(cc, cc->wmh->initial_state);
  83. }
  84. XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask |
  85. PropertyChangeMask | KeyReleaseMask);
  86. XAddToSaveSet(X_Dpy, cc->win);
  87. client_transient(cc);
  88. /* Notify client of its configuration. */
  89. client_config(cc);
  90. TAILQ_INSERT_TAIL(&sc->clientq, cc, entry);
  91. xu_ewmh_net_client_list(sc);
  92. xu_ewmh_restore_net_wm_state(cc);
  93. if (client_get_wm_state(cc) == IconicState)
  94. client_hide(cc);
  95. else
  96. client_unhide(cc);
  97. if (mapped)
  98. group_autogroup(cc);
  99. XSync(X_Dpy, False);
  100. XUngrabServer(X_Dpy);
  101. return(cc);
  102. }
  103. struct client_ctx *
  104. client_find(Window win)
  105. {
  106. struct screen_ctx *sc;
  107. struct client_ctx *cc;
  108. TAILQ_FOREACH(sc, &Screenq, entry) {
  109. TAILQ_FOREACH(cc, &sc->clientq, entry) {
  110. if (cc->win == win)
  111. return(cc);
  112. }
  113. }
  114. return(NULL);
  115. }
  116. void
  117. client_delete(struct client_ctx *cc)
  118. {
  119. struct screen_ctx *sc = cc->sc;
  120. struct winname *wn;
  121. TAILQ_REMOVE(&sc->clientq, cc, entry);
  122. xu_ewmh_net_client_list(sc);
  123. if (cc->group != NULL)
  124. TAILQ_REMOVE(&cc->group->clientq, cc, group_entry);
  125. if (cc == client_current())
  126. client_none(sc);
  127. while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
  128. TAILQ_REMOVE(&cc->nameq, wn, entry);
  129. free(wn->name);
  130. free(wn);
  131. }
  132. if (cc->ch.res_class)
  133. XFree(cc->ch.res_class);
  134. if (cc->ch.res_name)
  135. XFree(cc->ch.res_name);
  136. if (cc->wmh)
  137. XFree(cc->wmh);
  138. free(cc);
  139. }
  140. void
  141. client_setactive(struct client_ctx *cc)
  142. {
  143. struct screen_ctx *sc = cc->sc;
  144. struct client_ctx *oldcc;
  145. if (cc->flags & CLIENT_HIDDEN)
  146. return;
  147. XInstallColormap(X_Dpy, cc->colormap);
  148. if ((cc->flags & CLIENT_INPUT) ||
  149. (!(cc->flags & CLIENT_WM_TAKE_FOCUS))) {
  150. XSetInputFocus(X_Dpy, cc->win,
  151. RevertToPointerRoot, CurrentTime);
  152. }
  153. if (cc->flags & CLIENT_WM_TAKE_FOCUS)
  154. client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
  155. if ((oldcc = client_current())) {
  156. oldcc->flags &= ~CLIENT_ACTIVE;
  157. client_draw_border(oldcc);
  158. }
  159. /* If we're in the middle of cycing, don't change the order. */
  160. if (!sc->cycling)
  161. client_mtf(cc);
  162. curcc = cc;
  163. cc->flags |= CLIENT_ACTIVE;
  164. cc->flags &= ~CLIENT_URGENCY;
  165. client_draw_border(cc);
  166. conf_grab_mouse(cc->win);
  167. xu_ewmh_net_active_window(sc, cc->win);
  168. }
  169. /*
  170. * set when there is no active client
  171. */
  172. static void
  173. client_none(struct screen_ctx *sc)
  174. {
  175. Window none = None;
  176. xu_ewmh_net_active_window(sc, none);
  177. curcc = NULL;
  178. }
  179. struct client_ctx *
  180. client_current(void)
  181. {
  182. return(curcc);
  183. }
  184. void
  185. client_toggle_freeze(struct client_ctx *cc)
  186. {
  187. if (cc->flags & CLIENT_FREEZE)
  188. cc->flags &= ~CLIENT_FREEZE;
  189. else
  190. cc->flags |= CLIENT_FREEZE;
  191. }
  192. void
  193. client_toggle_hidden(struct client_ctx *cc)
  194. {
  195. if (cc->flags & CLIENT_HIDDEN)
  196. cc->flags &= ~CLIENT_HIDDEN;
  197. else
  198. cc->flags |= CLIENT_HIDDEN;
  199. xu_ewmh_set_net_wm_state(cc);
  200. }
  201. void
  202. client_toggle_sticky(struct client_ctx *cc)
  203. {
  204. if (cc->flags & CLIENT_STICKY)
  205. cc->flags &= ~CLIENT_STICKY;
  206. else
  207. cc->flags |= CLIENT_STICKY;
  208. xu_ewmh_set_net_wm_state(cc);
  209. }
  210. void
  211. client_toggle_fullscreen(struct client_ctx *cc)
  212. {
  213. struct screen_ctx *sc = cc->sc;
  214. struct geom area;
  215. if ((cc->flags & CLIENT_FREEZE) &&
  216. !(cc->flags & CLIENT_FULLSCREEN))
  217. return;
  218. if (cc->flags & CLIENT_FULLSCREEN) {
  219. cc->bwidth = Conf.bwidth;
  220. cc->geom = cc->fullgeom;
  221. cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE);
  222. goto resize;
  223. }
  224. cc->fullgeom = cc->geom;
  225. area = screen_area(sc,
  226. cc->geom.x + cc->geom.w / 2,
  227. cc->geom.y + cc->geom.h / 2, CWM_NOGAP);
  228. cc->bwidth = 0;
  229. cc->geom = area;
  230. cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE);
  231. resize:
  232. client_resize(cc, 0);
  233. xu_ewmh_set_net_wm_state(cc);
  234. }
  235. void
  236. client_toggle_maximize(struct client_ctx *cc)
  237. {
  238. struct screen_ctx *sc = cc->sc;
  239. struct geom area;
  240. if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
  241. return;
  242. if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_MAXIMIZED) {
  243. cc->geom = cc->savegeom;
  244. cc->flags &= ~CLIENT_MAXIMIZED;
  245. goto resize;
  246. }
  247. if (!(cc->flags & CLIENT_VMAXIMIZED)) {
  248. cc->savegeom.h = cc->geom.h;
  249. cc->savegeom.y = cc->geom.y;
  250. }
  251. if (!(cc->flags & CLIENT_HMAXIMIZED)) {
  252. cc->savegeom.w = cc->geom.w;
  253. cc->savegeom.x = cc->geom.x;
  254. }
  255. /*
  256. * pick screen that the middle of the window is on.
  257. * that's probably more fair than if just the origin of
  258. * a window is poking over a boundary
  259. */
  260. area = screen_area(sc,
  261. cc->geom.x + cc->geom.w / 2,
  262. cc->geom.y + cc->geom.h / 2, CWM_GAP);
  263. cc->geom.x = area.x;
  264. cc->geom.y = area.y;
  265. cc->geom.w = area.w - (cc->bwidth * 2);
  266. cc->geom.h = area.h - (cc->bwidth * 2);
  267. cc->flags |= CLIENT_MAXIMIZED;
  268. resize:
  269. client_resize(cc, 0);
  270. xu_ewmh_set_net_wm_state(cc);
  271. }
  272. void
  273. client_toggle_vmaximize(struct client_ctx *cc)
  274. {
  275. struct screen_ctx *sc = cc->sc;
  276. struct geom area;
  277. if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
  278. return;
  279. if (cc->flags & CLIENT_VMAXIMIZED) {
  280. cc->geom.y = cc->savegeom.y;
  281. cc->geom.h = cc->savegeom.h;
  282. cc->flags &= ~CLIENT_VMAXIMIZED;
  283. goto resize;
  284. }
  285. cc->savegeom.y = cc->geom.y;
  286. cc->savegeom.h = cc->geom.h;
  287. area = screen_area(sc,
  288. cc->geom.x + cc->geom.w / 2,
  289. cc->geom.y + cc->geom.h / 2, CWM_GAP);
  290. cc->geom.y = area.y;
  291. cc->geom.h = area.h - (cc->bwidth * 2);
  292. cc->flags |= CLIENT_VMAXIMIZED;
  293. resize:
  294. client_resize(cc, 0);
  295. xu_ewmh_set_net_wm_state(cc);
  296. }
  297. void
  298. client_toggle_hmaximize(struct client_ctx *cc)
  299. {
  300. struct screen_ctx *sc = cc->sc;
  301. struct geom area;
  302. if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
  303. return;
  304. if (cc->flags & CLIENT_HMAXIMIZED) {
  305. cc->geom.x = cc->savegeom.x;
  306. cc->geom.w = cc->savegeom.w;
  307. cc->flags &= ~CLIENT_HMAXIMIZED;
  308. goto resize;
  309. }
  310. cc->savegeom.x = cc->geom.x;
  311. cc->savegeom.w = cc->geom.w;
  312. area = screen_area(sc,
  313. cc->geom.x + cc->geom.w / 2,
  314. cc->geom.y + cc->geom.h / 2, CWM_GAP);
  315. cc->geom.x = area.x;
  316. cc->geom.w = area.w - (cc->bwidth * 2);
  317. cc->flags |= CLIENT_HMAXIMIZED;
  318. resize:
  319. client_resize(cc, 0);
  320. xu_ewmh_set_net_wm_state(cc);
  321. }
  322. void
  323. client_resize(struct client_ctx *cc, int reset)
  324. {
  325. if (reset) {
  326. cc->flags &= ~CLIENT_MAXIMIZED;
  327. xu_ewmh_set_net_wm_state(cc);
  328. }
  329. client_draw_border(cc);
  330. XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x,
  331. cc->geom.y, cc->geom.w, cc->geom.h);
  332. client_config(cc);
  333. }
  334. void
  335. client_move(struct client_ctx *cc)
  336. {
  337. XMoveWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y);
  338. client_config(cc);
  339. }
  340. void
  341. client_lower(struct client_ctx *cc)
  342. {
  343. XLowerWindow(X_Dpy, cc->win);
  344. }
  345. void
  346. client_raise(struct client_ctx *cc)
  347. {
  348. XRaiseWindow(X_Dpy, cc->win);
  349. }
  350. void
  351. client_config(struct client_ctx *cc)
  352. {
  353. XConfigureEvent cn;
  354. (void)memset(&cn, 0, sizeof(cn));
  355. cn.type = ConfigureNotify;
  356. cn.event = cc->win;
  357. cn.window = cc->win;
  358. cn.x = cc->geom.x;
  359. cn.y = cc->geom.y;
  360. cn.width = cc->geom.w;
  361. cn.height = cc->geom.h;
  362. cn.border_width = cc->bwidth;
  363. cn.above = None;
  364. cn.override_redirect = 0;
  365. XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&cn);
  366. }
  367. void
  368. client_ptrwarp(struct client_ctx *cc)
  369. {
  370. int x = cc->ptr.x, y = cc->ptr.y;
  371. if (x == -1 || y == -1) {
  372. x = cc->geom.w / 2;
  373. y = cc->geom.h / 2;
  374. }
  375. if (cc->flags & CLIENT_HIDDEN)
  376. client_unhide(cc);
  377. else
  378. client_raise(cc);
  379. xu_ptr_setpos(cc->win, x, y);
  380. }
  381. void
  382. client_ptrsave(struct client_ctx *cc)
  383. {
  384. int x, y;
  385. xu_ptr_getpos(cc->win, &x, &y);
  386. if (client_inbound(cc, x, y)) {
  387. cc->ptr.x = x;
  388. cc->ptr.y = y;
  389. } else {
  390. cc->ptr.x = -1;
  391. cc->ptr.y = -1;
  392. }
  393. }
  394. void
  395. client_hide(struct client_ctx *cc)
  396. {
  397. if (cc->flags & CLIENT_STICKY)
  398. return;
  399. XUnmapWindow(X_Dpy, cc->win);
  400. cc->flags &= ~CLIENT_ACTIVE;
  401. cc->flags |= CLIENT_HIDDEN;
  402. client_set_wm_state(cc, IconicState);
  403. if (cc == client_current())
  404. client_none(cc->sc);
  405. }
  406. void
  407. client_unhide(struct client_ctx *cc)
  408. {
  409. if (cc->flags & CLIENT_STICKY)
  410. return;
  411. XMapRaised(X_Dpy, cc->win);
  412. cc->flags &= ~CLIENT_HIDDEN;
  413. client_set_wm_state(cc, NormalState);
  414. client_draw_border(cc);
  415. }
  416. void
  417. client_urgency(struct client_ctx *cc)
  418. {
  419. if (!(cc->flags & CLIENT_ACTIVE))
  420. cc->flags |= CLIENT_URGENCY;
  421. }
  422. void
  423. client_draw_border(struct client_ctx *cc)
  424. {
  425. struct screen_ctx *sc = cc->sc;
  426. unsigned long pixel;
  427. if (cc->flags & CLIENT_ACTIVE)
  428. switch (cc->flags & CLIENT_HIGHLIGHT) {
  429. case CLIENT_GROUP:
  430. pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel;
  431. break;
  432. case CLIENT_UNGROUP:
  433. pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel;
  434. break;
  435. default:
  436. pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel;
  437. break;
  438. }
  439. else
  440. pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel;
  441. if (cc->flags & CLIENT_URGENCY)
  442. pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel;
  443. XSetWindowBorderWidth(X_Dpy, cc->win, cc->bwidth);
  444. XSetWindowBorder(X_Dpy, cc->win, pixel);
  445. }
  446. static void
  447. client_wm_protocols(struct client_ctx *cc)
  448. {
  449. Atom *p;
  450. int i, j;
  451. if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) {
  452. for (i = 0; i < j; i++) {
  453. if (p[i] == cwmh[WM_DELETE_WINDOW])
  454. cc->flags |= CLIENT_WM_DELETE_WINDOW;
  455. else if (p[i] == cwmh[WM_TAKE_FOCUS])
  456. cc->flags |= CLIENT_WM_TAKE_FOCUS;
  457. }
  458. XFree(p);
  459. }
  460. }
  461. void
  462. client_wm_hints(struct client_ctx *cc)
  463. {
  464. if ((cc->wmh = XGetWMHints(X_Dpy, cc->win)) == NULL)
  465. return;
  466. if ((cc->wmh->flags & InputHint) && (cc->wmh->input))
  467. cc->flags |= CLIENT_INPUT;
  468. if ((cc->wmh->flags & XUrgencyHint))
  469. client_urgency(cc);
  470. }
  471. void
  472. client_msg(struct client_ctx *cc, Atom proto, Time ts)
  473. {
  474. XClientMessageEvent cm;
  475. (void)memset(&cm, 0, sizeof(cm));
  476. cm.type = ClientMessage;
  477. cm.window = cc->win;
  478. cm.message_type = cwmh[WM_PROTOCOLS];
  479. cm.format = 32;
  480. cm.data.l[0] = proto;
  481. cm.data.l[1] = ts;
  482. XSendEvent(X_Dpy, cc->win, False, NoEventMask, (XEvent *)&cm);
  483. }
  484. void
  485. client_send_delete(struct client_ctx *cc)
  486. {
  487. if (cc->flags & CLIENT_WM_DELETE_WINDOW)
  488. client_msg(cc, cwmh[WM_DELETE_WINDOW], CurrentTime);
  489. else
  490. XKillClient(X_Dpy, cc->win);
  491. }
  492. void
  493. client_setname(struct client_ctx *cc)
  494. {
  495. struct winname *wn;
  496. char *newname;
  497. if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME], &newname))
  498. if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname))
  499. newname = xstrdup("");
  500. TAILQ_FOREACH(wn, &cc->nameq, entry) {
  501. if (strcmp(wn->name, newname) == 0) {
  502. /* Move to the last since we got a hit. */
  503. TAILQ_REMOVE(&cc->nameq, wn, entry);
  504. TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
  505. goto match;
  506. }
  507. }
  508. wn = xmalloc(sizeof(*wn));
  509. wn->name = newname;
  510. TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
  511. cc->nameqlen++;
  512. match:
  513. cc->name = wn->name;
  514. /* Now, do some garbage collection. */
  515. if (cc->nameqlen > CLIENT_MAXNAMEQLEN) {
  516. if ((wn = TAILQ_FIRST(&cc->nameq)) == NULL)
  517. errx(1, "client_setname: window name queue empty");
  518. TAILQ_REMOVE(&cc->nameq, wn, entry);
  519. free(wn->name);
  520. free(wn);
  521. cc->nameqlen--;
  522. }
  523. }
  524. void
  525. client_cycle(struct screen_ctx *sc, int flags)
  526. {
  527. struct client_ctx *oldcc, *newcc;
  528. int again = 1;
  529. if (TAILQ_EMPTY(&sc->clientq))
  530. return;
  531. oldcc = client_current();
  532. if (oldcc == NULL)
  533. oldcc = ((flags & CWM_RCYCLE) ?
  534. TAILQ_LAST(&sc->clientq, client_ctx_q) :
  535. TAILQ_FIRST(&sc->clientq));
  536. newcc = oldcc;
  537. while (again) {
  538. again = 0;
  539. newcc = ((flags & CWM_RCYCLE) ? client_prev(newcc) :
  540. client_next(newcc));
  541. /* Only cycle visible and non-ignored windows. */
  542. if ((newcc->flags & (CLIENT_HIDDEN|CLIENT_IGNORE))
  543. || ((flags & CWM_INGROUP) &&
  544. (newcc->group != oldcc->group)))
  545. again = 1;
  546. /* Is oldcc the only non-hidden window? */
  547. if (newcc == oldcc) {
  548. if (again)
  549. return; /* No windows visible. */
  550. break;
  551. }
  552. }
  553. /* reset when cycling mod is released. XXX I hate this hack */
  554. sc->cycling = 1;
  555. client_ptrsave(oldcc);
  556. client_ptrwarp(newcc);
  557. }
  558. void
  559. client_cycle_leave(struct screen_ctx *sc)
  560. {
  561. struct client_ctx *cc;
  562. sc->cycling = 0;
  563. if ((cc = client_current())) {
  564. client_mtf(cc);
  565. group_toggle_membership_leave(cc);
  566. XUngrabKeyboard(X_Dpy, CurrentTime);
  567. }
  568. }
  569. static struct client_ctx *
  570. client_next(struct client_ctx *cc)
  571. {
  572. struct screen_ctx *sc = cc->sc;
  573. struct client_ctx *ccc;
  574. return(((ccc = TAILQ_NEXT(cc, entry)) != NULL) ?
  575. ccc : TAILQ_FIRST(&sc->clientq));
  576. }
  577. static struct client_ctx *
  578. client_prev(struct client_ctx *cc)
  579. {
  580. struct screen_ctx *sc = cc->sc;
  581. struct client_ctx *ccc;
  582. return(((ccc = TAILQ_PREV(cc, client_ctx_q, entry)) != NULL) ?
  583. ccc : TAILQ_LAST(&sc->clientq, client_ctx_q));
  584. }
  585. static void
  586. client_placecalc(struct client_ctx *cc)
  587. {
  588. struct screen_ctx *sc = cc->sc;
  589. int xslack, yslack;
  590. if (cc->hint.flags & (USPosition|PPosition)) {
  591. /*
  592. * Ignore XINERAMA screens, just make sure it's somewhere
  593. * in the virtual desktop. else it stops people putting xterms
  594. * at startup in the screen the mouse doesn't start in *sigh*.
  595. * XRandR bits mean that {x,y}max shouldn't be outside what's
  596. * currently there.
  597. */
  598. xslack = sc->view.w - cc->geom.w - cc->bwidth * 2;
  599. yslack = sc->view.h - cc->geom.h - cc->bwidth * 2;
  600. cc->geom.x = MIN(cc->geom.x, xslack);
  601. cc->geom.y = MIN(cc->geom.y, yslack);
  602. } else {
  603. struct geom area;
  604. int xmouse, ymouse;
  605. xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
  606. area = screen_area(sc, xmouse, ymouse, CWM_GAP);
  607. area.w += area.x;
  608. area.h += area.y;
  609. xmouse = MAX(xmouse, area.x) - cc->geom.w / 2;
  610. ymouse = MAX(ymouse, area.y) - cc->geom.h / 2;
  611. xmouse = MAX(xmouse, area.x);
  612. ymouse = MAX(ymouse, area.y);
  613. xslack = area.w - cc->geom.w - cc->bwidth * 2;
  614. yslack = area.h - cc->geom.h - cc->bwidth * 2;
  615. if (xslack >= area.x) {
  616. cc->geom.x = MAX(MIN(xmouse, xslack), area.x);
  617. } else {
  618. cc->geom.x = area.x;
  619. cc->geom.w = area.w;
  620. }
  621. if (yslack >= area.y) {
  622. cc->geom.y = MAX(MIN(ymouse, yslack), area.y);
  623. } else {
  624. cc->geom.y = area.y;
  625. cc->geom.h = area.h;
  626. }
  627. }
  628. }
  629. static void
  630. client_mtf(struct client_ctx *cc)
  631. {
  632. struct screen_ctx *sc = cc->sc;
  633. TAILQ_REMOVE(&sc->clientq, cc, entry);
  634. TAILQ_INSERT_HEAD(&sc->clientq, cc, entry);
  635. }
  636. void
  637. client_getsizehints(struct client_ctx *cc)
  638. {
  639. long tmp;
  640. XSizeHints size;
  641. if (!XGetWMNormalHints(X_Dpy, cc->win, &size, &tmp))
  642. size.flags = 0;
  643. cc->hint.flags = size.flags;
  644. if (size.flags & PBaseSize) {
  645. cc->hint.basew = size.base_width;
  646. cc->hint.baseh = size.base_height;
  647. } else if (size.flags & PMinSize) {
  648. cc->hint.basew = size.min_width;
  649. cc->hint.baseh = size.min_height;
  650. }
  651. if (size.flags & PMinSize) {
  652. cc->hint.minw = size.min_width;
  653. cc->hint.minh = size.min_height;
  654. } else if (size.flags & PBaseSize) {
  655. cc->hint.minw = size.base_width;
  656. cc->hint.minh = size.base_height;
  657. }
  658. if (size.flags & PMaxSize) {
  659. cc->hint.maxw = size.max_width;
  660. cc->hint.maxh = size.max_height;
  661. }
  662. if (size.flags & PResizeInc) {
  663. cc->hint.incw = size.width_inc;
  664. cc->hint.inch = size.height_inc;
  665. }
  666. cc->hint.incw = MAX(1, cc->hint.incw);
  667. cc->hint.inch = MAX(1, cc->hint.inch);
  668. if (size.flags & PAspect) {
  669. if (size.min_aspect.x > 0)
  670. cc->hint.mina = (float)size.min_aspect.y /
  671. size.min_aspect.x;
  672. if (size.max_aspect.y > 0)
  673. cc->hint.maxa = (float)size.max_aspect.x /
  674. size.max_aspect.y;
  675. }
  676. }
  677. void
  678. client_applysizehints(struct client_ctx *cc)
  679. {
  680. Bool baseismin;
  681. baseismin = (cc->hint.basew == cc->hint.minw) &&
  682. (cc->hint.baseh == cc->hint.minh);
  683. /* temporarily remove base dimensions, ICCCM 4.1.2.3 */
  684. if (!baseismin) {
  685. cc->geom.w -= cc->hint.basew;
  686. cc->geom.h -= cc->hint.baseh;
  687. }
  688. /* adjust for aspect limits */
  689. if (cc->hint.mina && cc->hint.maxa) {
  690. if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h)
  691. cc->geom.w = cc->geom.h * cc->hint.maxa;
  692. else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w)
  693. cc->geom.h = cc->geom.w * cc->hint.mina;
  694. }
  695. /* remove base dimensions for increment */
  696. if (baseismin) {
  697. cc->geom.w -= cc->hint.basew;
  698. cc->geom.h -= cc->hint.baseh;
  699. }
  700. /* adjust for increment value */
  701. cc->geom.w -= cc->geom.w % cc->hint.incw;
  702. cc->geom.h -= cc->geom.h % cc->hint.inch;
  703. /* restore base dimensions */
  704. cc->geom.w += cc->hint.basew;
  705. cc->geom.h += cc->hint.baseh;
  706. /* adjust for min width/height */
  707. cc->geom.w = MAX(cc->geom.w, cc->hint.minw);
  708. cc->geom.h = MAX(cc->geom.h, cc->hint.minh);
  709. /* adjust for max width/height */
  710. if (cc->hint.maxw)
  711. cc->geom.w = MIN(cc->geom.w, cc->hint.maxw);
  712. if (cc->hint.maxh)
  713. cc->geom.h = MIN(cc->geom.h, cc->hint.maxh);
  714. cc->geom.w = MAX(cc->geom.w, 1);
  715. cc->geom.h = MAX(cc->geom.h, 1);
  716. cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
  717. cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
  718. }
  719. static void
  720. client_mwm_hints(struct client_ctx *cc)
  721. {
  722. struct mwm_hints *mwmh;
  723. if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS], cwmh[_MOTIF_WM_HINTS],
  724. MWM_HINTS_ELEMENTS, (unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) {
  725. if (mwmh->flags & MWM_FLAGS_DECORATIONS &&
  726. !(mwmh->decorations & MWM_DECOR_ALL) &&
  727. !(mwmh->decorations & MWM_DECOR_BORDER))
  728. cc->bwidth = 0;
  729. XFree(mwmh);
  730. }
  731. }
  732. void
  733. client_transient(struct client_ctx *cc)
  734. {
  735. struct client_ctx *tc;
  736. Window trans;
  737. if (XGetTransientForHint(X_Dpy, cc->win, &trans)) {
  738. if ((tc = client_find(trans)) && tc->group) {
  739. group_movetogroup(cc, tc->group->num);
  740. if (tc->flags & CLIENT_IGNORE)
  741. cc->flags |= CLIENT_IGNORE;
  742. }
  743. }
  744. }
  745. static int
  746. client_inbound(struct client_ctx *cc, int x, int y)
  747. {
  748. return(x < cc->geom.w && x >= 0 &&
  749. y < cc->geom.h && y >= 0);
  750. }
  751. int
  752. client_snapcalc(int n0, int n1, int e0, int e1, int snapdist)
  753. {
  754. int s0, s1;
  755. s0 = s1 = 0;
  756. if (abs(e0 - n0) <= snapdist)
  757. s0 = e0 - n0;
  758. if (abs(e1 - n1) <= snapdist)
  759. s1 = e1 - n1;
  760. /* possible to snap in both directions */
  761. if (s0 != 0 && s1 != 0)
  762. if (abs(s0) < abs(s1))
  763. return(s0);
  764. else
  765. return(s1);
  766. else if (s0 != 0)
  767. return(s0);
  768. else if (s1 != 0)
  769. return(s1);
  770. else
  771. return(0);
  772. }
  773. void
  774. client_htile(struct client_ctx *cc)
  775. {
  776. struct client_ctx *ci;
  777. struct group_ctx *gc = cc->group;
  778. struct screen_ctx *sc = cc->sc;
  779. struct geom area;
  780. int i, n, mh, x, h, w;
  781. if (!gc)
  782. return;
  783. i = n = 0;
  784. TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
  785. if (ci->flags & CLIENT_HIDDEN ||
  786. ci->flags & CLIENT_IGNORE || (ci == cc))
  787. continue;
  788. n++;
  789. }
  790. if (n == 0)
  791. return;
  792. area = screen_area(sc,
  793. cc->geom.x + cc->geom.w / 2,
  794. cc->geom.y + cc->geom.h / 2, CWM_GAP);
  795. if (cc->flags & CLIENT_VMAXIMIZED ||
  796. cc->geom.h + (cc->bwidth * 2) >= area.h)
  797. return;
  798. cc->flags &= ~CLIENT_HMAXIMIZED;
  799. cc->geom.x = area.x;
  800. cc->geom.y = area.y;
  801. cc->geom.w = area.w - (cc->bwidth * 2);
  802. client_resize(cc, 1);
  803. client_ptrwarp(cc);
  804. mh = cc->geom.h + (cc->bwidth * 2);
  805. x = area.x;
  806. w = area.w / n;
  807. h = area.h - mh;
  808. TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
  809. if (ci->flags & CLIENT_HIDDEN ||
  810. ci->flags & CLIENT_IGNORE || (ci == cc))
  811. continue;
  812. ci->bwidth = Conf.bwidth;
  813. ci->geom.y = area.y + mh;
  814. ci->geom.x = x;
  815. ci->geom.h = h - (ci->bwidth * 2);
  816. ci->geom.w = w - (ci->bwidth * 2);
  817. if (i + 1 == n)
  818. ci->geom.w = area.x + area.w -
  819. ci->geom.x - (ci->bwidth * 2);
  820. x += w;
  821. client_resize(ci, 1);
  822. i++;
  823. }
  824. }
  825. void
  826. client_vtile(struct client_ctx *cc)
  827. {
  828. struct client_ctx *ci;
  829. struct group_ctx *gc = cc->group;
  830. struct screen_ctx *sc = cc->sc;
  831. struct geom area;
  832. int i, n, mw, y, h, w;
  833. if (!gc)
  834. return;
  835. i = n = 0;
  836. TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
  837. if (ci->flags & CLIENT_HIDDEN ||
  838. ci->flags & CLIENT_IGNORE || (ci == cc))
  839. continue;
  840. n++;
  841. }
  842. if (n == 0)
  843. return;
  844. area = screen_area(sc,
  845. cc->geom.x + cc->geom.w / 2,
  846. cc->geom.y + cc->geom.h / 2, CWM_GAP);
  847. if (cc->flags & CLIENT_HMAXIMIZED ||
  848. cc->geom.w + (cc->bwidth * 2) >= area.w)
  849. return;
  850. cc->flags &= ~CLIENT_VMAXIMIZED;
  851. cc->geom.x = area.x;
  852. cc->geom.y = area.y;
  853. cc->geom.h = area.h - (cc->bwidth * 2);
  854. client_resize(cc, 1);
  855. client_ptrwarp(cc);
  856. mw = cc->geom.w + (cc->bwidth * 2);
  857. y = area.y;
  858. h = area.h / n;
  859. w = area.w - mw;
  860. TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
  861. if (ci->flags & CLIENT_HIDDEN ||
  862. ci->flags & CLIENT_IGNORE || (ci == cc))
  863. continue;
  864. ci->bwidth = Conf.bwidth;
  865. ci->geom.y = y;
  866. ci->geom.x = area.x + mw;
  867. ci->geom.h = h - (ci->bwidth * 2);
  868. ci->geom.w = w - (ci->bwidth * 2);
  869. if (i + 1 == n)
  870. ci->geom.h = area.y + area.h -
  871. ci->geom.y - (ci->bwidth * 2);
  872. y += h;
  873. client_resize(ci, 1);
  874. i++;
  875. }
  876. }
  877. long
  878. client_get_wm_state(struct client_ctx *cc)
  879. {
  880. long *p, state = -1;
  881. if (xu_getprop(cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
  882. (unsigned char **)&p) > 0) {
  883. state = *p;
  884. XFree(p);
  885. }
  886. return(state);
  887. }
  888. void
  889. client_set_wm_state(struct client_ctx *cc, long state)
  890. {
  891. long data[] = { state, None };
  892. XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
  893. PropModeReplace, (unsigned char *)data, 2);
  894. }