xutil.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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: xutil.c,v 1.99 2015/03/28 23:12:47 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 unsigned int ign_mods[] = { 0, LockMask, Mod2Mask, Mod2Mask | LockMask };
  31. void
  32. xu_btn_grab(Window win, int mask, unsigned int btn)
  33. {
  34. unsigned int i;
  35. for (i = 0; i < nitems(ign_mods); i++)
  36. XGrabButton(X_Dpy, btn, (mask | ign_mods[i]), win,
  37. False, BUTTONMASK, GrabModeAsync,
  38. GrabModeSync, None, None);
  39. }
  40. void
  41. xu_btn_ungrab(Window win)
  42. {
  43. XUngrabButton(X_Dpy, AnyButton, AnyModifier, win);
  44. }
  45. void
  46. xu_key_grab(Window win, unsigned int mask, KeySym keysym)
  47. {
  48. KeyCode code;
  49. unsigned int i;
  50. code = XKeysymToKeycode(X_Dpy, keysym);
  51. if ((XkbKeycodeToKeysym(X_Dpy, code, 0, 0) != keysym) &&
  52. (XkbKeycodeToKeysym(X_Dpy, code, 0, 1) == keysym))
  53. mask |= ShiftMask;
  54. for (i = 0; i < nitems(ign_mods); i++)
  55. XGrabKey(X_Dpy, code, (mask | ign_mods[i]), win,
  56. True, GrabModeAsync, GrabModeAsync);
  57. }
  58. void
  59. xu_key_ungrab(Window win)
  60. {
  61. XUngrabKey(X_Dpy, AnyKey, AnyModifier, win);
  62. }
  63. int
  64. xu_ptr_grab(Window win, unsigned int mask, Cursor curs)
  65. {
  66. return(XGrabPointer(X_Dpy, win, False, mask,
  67. GrabModeAsync, GrabModeAsync,
  68. None, curs, CurrentTime) == GrabSuccess ? 0 : -1);
  69. }
  70. int
  71. xu_ptr_regrab(unsigned int mask, Cursor curs)
  72. {
  73. return(XChangeActivePointerGrab(X_Dpy, mask,
  74. curs, CurrentTime) == GrabSuccess ? 0 : -1);
  75. }
  76. void
  77. xu_ptr_ungrab(void)
  78. {
  79. XUngrabPointer(X_Dpy, CurrentTime);
  80. }
  81. void
  82. xu_ptr_getpos(Window win, int *x, int *y)
  83. {
  84. Window w0, w1;
  85. int tmp0, tmp1;
  86. unsigned int tmp2;
  87. XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
  88. }
  89. void
  90. xu_ptr_setpos(Window win, int x, int y)
  91. {
  92. XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
  93. }
  94. int
  95. xu_getprop(Window win, Atom atm, Atom type, long len, unsigned char **p)
  96. {
  97. Atom realtype;
  98. unsigned long n, extra;
  99. int format;
  100. if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
  101. &realtype, &format, &n, &extra, p) != Success || *p == NULL)
  102. return(-1);
  103. if (n == 0)
  104. XFree(*p);
  105. return(n);
  106. }
  107. int
  108. xu_getstrprop(Window win, Atom atm, char **text) {
  109. XTextProperty prop;
  110. char **list;
  111. int nitems = 0;
  112. *text = NULL;
  113. XGetTextProperty(X_Dpy, win, &prop, atm);
  114. if (!prop.nitems)
  115. return(0);
  116. if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
  117. &nitems) == Success && nitems > 0 && *list) {
  118. if (nitems > 1) {
  119. XTextProperty prop2;
  120. if (Xutf8TextListToTextProperty(X_Dpy, list, nitems,
  121. XUTF8StringStyle, &prop2) == Success) {
  122. *text = xstrdup((const char *)prop2.value);
  123. XFree(prop2.value);
  124. }
  125. } else {
  126. *text = xstrdup(*list);
  127. }
  128. XFreeStringList(list);
  129. }
  130. XFree(prop.value);
  131. return(nitems);
  132. }
  133. /* Root Window Properties */
  134. void
  135. xu_ewmh_net_supported(struct screen_ctx *sc)
  136. {
  137. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
  138. XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
  139. }
  140. void
  141. xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
  142. {
  143. Window w;
  144. w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
  145. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK],
  146. XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
  147. XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
  148. XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
  149. XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
  150. cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)WMNAME,
  151. strlen(WMNAME));
  152. }
  153. void
  154. xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
  155. {
  156. long geom[2] = { sc->view.w, sc->view.h };
  157. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY],
  158. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
  159. }
  160. void
  161. xu_ewmh_net_workarea(struct screen_ctx *sc)
  162. {
  163. long workareas[CALMWM_NGROUPS][4];
  164. int i;
  165. for (i = 0; i < CALMWM_NGROUPS; i++) {
  166. workareas[i][0] = sc->work.x;
  167. workareas[i][1] = sc->work.y;
  168. workareas[i][2] = sc->work.w;
  169. workareas[i][3] = sc->work.h;
  170. }
  171. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
  172. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workareas,
  173. CALMWM_NGROUPS * 4);
  174. }
  175. void
  176. xu_ewmh_net_client_list(struct screen_ctx *sc)
  177. {
  178. struct client_ctx *cc;
  179. Window *winlist;
  180. int i = 0, j = 0;
  181. TAILQ_FOREACH(cc, &sc->clientq, entry)
  182. i++;
  183. if (i == 0)
  184. return;
  185. winlist = xreallocarray(NULL, i, sizeof(*winlist));
  186. TAILQ_FOREACH(cc, &sc->clientq, entry)
  187. winlist[j++] = cc->win;
  188. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST],
  189. XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
  190. free(winlist);
  191. }
  192. void
  193. xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
  194. {
  195. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
  196. XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
  197. }
  198. void
  199. xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc)
  200. {
  201. long viewports[2] = {0, 0};
  202. /* We don't support large desktops, so this is (0, 0). */
  203. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
  204. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
  205. }
  206. void
  207. xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
  208. {
  209. long ndesks = CALMWM_NGROUPS;
  210. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
  211. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
  212. }
  213. void
  214. xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
  215. {
  216. long zero = 0;
  217. /* We don't support `showing desktop' mode, so this is zero.
  218. * Note that when we hide all groups, or when all groups are
  219. * hidden we could technically set this later on.
  220. */
  221. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
  222. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
  223. }
  224. void
  225. xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
  226. {
  227. /* We don't support virtual roots, so delete if set by previous wm. */
  228. XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
  229. }
  230. void
  231. xu_ewmh_net_current_desktop(struct screen_ctx *sc)
  232. {
  233. long num = sc->group_active->num;
  234. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP],
  235. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
  236. }
  237. void
  238. xu_ewmh_net_desktop_names(struct screen_ctx *sc)
  239. {
  240. struct group_ctx *gc;
  241. char *p, *q;
  242. unsigned char *prop_ret;
  243. int i = 0, j = 0, nstrings = 0, n = 0;
  244. size_t len = 0, tlen, slen;
  245. /* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
  246. if ((j = xu_getprop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
  247. cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
  248. prop_ret[j - 1] = '\0'; /* paranoia */
  249. while (i < j) {
  250. if (prop_ret[i++] == '\0')
  251. nstrings++;
  252. }
  253. }
  254. p = (char *)prop_ret;
  255. while (n < nstrings) {
  256. TAILQ_FOREACH(gc, &sc->groupq, entry) {
  257. if (gc->num == n) {
  258. free(gc->name);
  259. gc->name = xstrdup(p);
  260. p += strlen(p) + 1;
  261. break;
  262. }
  263. }
  264. n++;
  265. }
  266. if (prop_ret != NULL)
  267. XFree(prop_ret);
  268. TAILQ_FOREACH(gc, &sc->groupq, entry)
  269. len += strlen(gc->name) + 1;
  270. q = p = xreallocarray(NULL, len, sizeof(*p));
  271. tlen = len;
  272. TAILQ_FOREACH(gc, &sc->groupq, entry) {
  273. slen = strlen(gc->name) + 1;
  274. (void)strlcpy(q, gc->name, tlen);
  275. tlen -= slen;
  276. q += slen;
  277. }
  278. XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
  279. cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
  280. free(p);
  281. }
  282. /* Application Window Properties */
  283. void
  284. xu_ewmh_net_wm_desktop(struct client_ctx *cc)
  285. {
  286. long num = 0xffffffff;
  287. if (cc->group)
  288. num = cc->group->num;
  289. XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP],
  290. XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
  291. }
  292. Atom *
  293. xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
  294. {
  295. Atom *state, *p = NULL;
  296. if ((*n = xu_getprop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
  297. (unsigned char **)&p)) <= 0)
  298. return(NULL);
  299. state = xreallocarray(NULL, *n, sizeof(Atom));
  300. (void)memcpy(state, p, *n * sizeof(Atom));
  301. XFree((char *)p);
  302. return(state);
  303. }
  304. void
  305. xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
  306. Atom first, Atom second)
  307. {
  308. unsigned int i;
  309. static struct handlers {
  310. int atom;
  311. int property;
  312. void (*toggle)(struct client_ctx *);
  313. } handlers[] = {
  314. { _NET_WM_STATE_STICKY,
  315. CLIENT_STICKY,
  316. client_toggle_sticky },
  317. { _NET_WM_STATE_MAXIMIZED_VERT,
  318. CLIENT_VMAXIMIZED,
  319. client_toggle_vmaximize },
  320. { _NET_WM_STATE_MAXIMIZED_HORZ,
  321. CLIENT_HMAXIMIZED,
  322. client_toggle_hmaximize },
  323. { _NET_WM_STATE_HIDDEN,
  324. CLIENT_HIDDEN,
  325. client_toggle_hidden },
  326. { _NET_WM_STATE_FULLSCREEN,
  327. CLIENT_FULLSCREEN,
  328. client_toggle_fullscreen },
  329. { _NET_WM_STATE_DEMANDS_ATTENTION,
  330. CLIENT_URGENCY,
  331. client_urgency },
  332. };
  333. for (i = 0; i < nitems(handlers); i++) {
  334. if (first != ewmh[handlers[i].atom] &&
  335. second != ewmh[handlers[i].atom])
  336. continue;
  337. switch (action) {
  338. case _NET_WM_STATE_ADD:
  339. if (!(cc->flags & handlers[i].property))
  340. handlers[i].toggle(cc);
  341. break;
  342. case _NET_WM_STATE_REMOVE:
  343. if (cc->flags & handlers[i].property)
  344. handlers[i].toggle(cc);
  345. break;
  346. case _NET_WM_STATE_TOGGLE:
  347. handlers[i].toggle(cc);
  348. }
  349. }
  350. }
  351. void
  352. xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
  353. {
  354. Atom *atoms;
  355. int i, n;
  356. atoms = xu_ewmh_get_net_wm_state(cc, &n);
  357. for (i = 0; i < n; i++) {
  358. if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
  359. client_toggle_sticky(cc);
  360. if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
  361. client_toggle_hmaximize(cc);
  362. if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
  363. client_toggle_vmaximize(cc);
  364. if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN])
  365. client_toggle_hidden(cc);
  366. if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN])
  367. client_toggle_fullscreen(cc);
  368. if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
  369. client_urgency(cc);
  370. }
  371. free(atoms);
  372. }
  373. void
  374. xu_ewmh_set_net_wm_state(struct client_ctx *cc)
  375. {
  376. Atom *atoms, *oatoms;
  377. int n, i, j;
  378. oatoms = xu_ewmh_get_net_wm_state(cc, &n);
  379. atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom));
  380. for (i = j = 0; i < n; i++) {
  381. if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] &&
  382. oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
  383. oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] &&
  384. oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
  385. oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
  386. oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
  387. atoms[j++] = oatoms[i];
  388. }
  389. free(oatoms);
  390. if (cc->flags & CLIENT_STICKY)
  391. atoms[j++] = ewmh[_NET_WM_STATE_STICKY];
  392. if (cc->flags & CLIENT_HIDDEN)
  393. atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN];
  394. if (cc->flags & CLIENT_FULLSCREEN)
  395. atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN];
  396. else {
  397. if (cc->flags & CLIENT_HMAXIMIZED)
  398. atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
  399. if (cc->flags & CLIENT_VMAXIMIZED)
  400. atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
  401. }
  402. if (cc->flags & CLIENT_URGENCY)
  403. atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
  404. if (j > 0)
  405. XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE],
  406. XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
  407. else
  408. XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
  409. free(atoms);
  410. }
  411. void
  412. xu_xorcolor(XftColor a, XftColor b, XftColor *r)
  413. {
  414. r->pixel = a.pixel ^ b.pixel;
  415. r->color.red = a.color.red ^ b.color.red;
  416. r->color.green = a.color.green ^ b.color.green;
  417. r->color.blue = a.color.blue ^ b.color.blue;
  418. r->color.alpha = 0xffff;
  419. }
  420. int
  421. xu_xft_width(XftFont *xftfont, const char *text, int len)
  422. {
  423. XGlyphInfo extents;
  424. XftTextExtentsUtf8(X_Dpy, xftfont, (const FcChar8*)text,
  425. len, &extents);
  426. return(extents.xOff);
  427. }
  428. void
  429. xu_xft_draw(struct screen_ctx *sc, const char *text, int color, int x, int y)
  430. {
  431. XftDrawStringUtf8(sc->xftdraw, &sc->xftcolor[color], sc->xftfont,
  432. x, y, (const FcChar8*)text, strlen(text));
  433. }