Mirror of Awesome WM window manager
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.

xwindow.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /*
  2. * xwindow.c - X window handling functions
  3. *
  4. * Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. */
  21. #include "xwindow.h"
  22. #include "common/atoms.h"
  23. #include "objects/button.h"
  24. #include <xcb/xcb.h>
  25. #include <xcb/shape.h>
  26. #include <cairo-xcb.h>
  27. /** Mask shorthands */
  28. #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE)
  29. /** Set client state (WM_STATE) property.
  30. * \param win The window to set state.
  31. * \param state The state to set.
  32. */
  33. void
  34. xwindow_set_state(xcb_window_t win, uint32_t state)
  35. {
  36. uint32_t data[] = { state, XCB_NONE };
  37. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, win,
  38. WM_STATE, WM_STATE, 32, 2, data);
  39. }
  40. /** Send request to get a window state (WM_STATE).
  41. * \param w A client window.
  42. * \return The cookie associated with the request.
  43. */
  44. xcb_get_property_cookie_t
  45. xwindow_get_state_unchecked(xcb_window_t w)
  46. {
  47. return xcb_get_property_unchecked(globalconf.connection, false, w, WM_STATE,
  48. WM_STATE, 0L, 2L);
  49. }
  50. /** Get a window state (WM_STATE).
  51. * \param cookie The cookie.
  52. * \return The current state of the window, or 0 on error.
  53. */
  54. uint32_t
  55. xwindow_get_state_reply(xcb_get_property_cookie_t cookie)
  56. {
  57. /* If no property is set, we just assume a sane default. */
  58. uint32_t result = XCB_ICCCM_WM_STATE_NORMAL;
  59. xcb_get_property_reply_t *prop_r;
  60. if((prop_r = xcb_get_property_reply(globalconf.connection, cookie, NULL)))
  61. {
  62. if(xcb_get_property_value_length(prop_r))
  63. result = *(uint32_t *) xcb_get_property_value(prop_r);
  64. p_delete(&prop_r);
  65. }
  66. return result;
  67. }
  68. /** Configure a window with its new geometry and border size.
  69. * \param win The X window id to configure.
  70. * \param geometry The new window geometry.
  71. * \param border The new border size.
  72. */
  73. void
  74. xwindow_configure(xcb_window_t win, area_t geometry, int border)
  75. {
  76. xcb_configure_notify_event_t ce;
  77. ce.response_type = XCB_CONFIGURE_NOTIFY;
  78. ce.event = win;
  79. ce.window = win;
  80. ce.x = geometry.x + border;
  81. ce.y = geometry.y + border;
  82. ce.width = geometry.width;
  83. ce.height = geometry.height;
  84. ce.border_width = border;
  85. ce.above_sibling = XCB_NONE;
  86. ce.override_redirect = false;
  87. xcb_send_event(globalconf.connection, false, win,
  88. XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *) &ce);
  89. }
  90. /** Grab or ungrab buttons on a window.
  91. * \param win The window.
  92. * \param buttons The buttons to grab.
  93. */
  94. void
  95. xwindow_buttons_grab(xcb_window_t win, button_array_t *buttons)
  96. {
  97. if(win == XCB_NONE)
  98. return;
  99. /* Ungrab everything first */
  100. xcb_ungrab_button(globalconf.connection, XCB_BUTTON_INDEX_ANY, win, XCB_BUTTON_MASK_ANY);
  101. foreach(b, *buttons)
  102. xcb_grab_button(globalconf.connection, false, win, BUTTONMASK,
  103. XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE,
  104. (*b)->button, (*b)->modifiers);
  105. }
  106. /** Grab key on a window.
  107. * \param win The window.
  108. * \param k The key.
  109. */
  110. static void
  111. xwindow_grabkey(xcb_window_t win, keyb_t *k)
  112. {
  113. if(k->keycode)
  114. xcb_grab_key(globalconf.connection, true, win,
  115. k->modifiers, k->keycode, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
  116. else if(k->keysym)
  117. {
  118. xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(globalconf.keysyms, k->keysym);
  119. if(keycodes)
  120. {
  121. for(xcb_keycode_t *kc = keycodes; *kc; kc++)
  122. xcb_grab_key(globalconf.connection, true, win,
  123. k->modifiers, *kc, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
  124. p_delete(&keycodes);
  125. }
  126. }
  127. }
  128. void
  129. xwindow_grabkeys(xcb_window_t win, key_array_t *keys)
  130. {
  131. /* Ungrab everything first */
  132. xcb_ungrab_key(globalconf.connection, XCB_GRAB_ANY, win, XCB_BUTTON_MASK_ANY);
  133. foreach(k, *keys)
  134. xwindow_grabkey(win, *k);
  135. }
  136. /** Send a request for a window's opacity.
  137. * \param win The window
  138. * \return A cookie for xwindow_get_opacity_from_reply().
  139. */
  140. xcb_get_property_cookie_t
  141. xwindow_get_opacity_unchecked(xcb_window_t win)
  142. {
  143. return xcb_get_property_unchecked(globalconf.connection, false, win,
  144. _NET_WM_WINDOW_OPACITY, XCB_ATOM_CARDINAL, 0L, 1L);
  145. }
  146. /** Get the opacity of a window.
  147. * \param win The window.
  148. * \return The opacity, between 0 and 1 or -1 or no opacity set.
  149. */
  150. double
  151. xwindow_get_opacity(xcb_window_t win)
  152. {
  153. xcb_get_property_cookie_t prop_c =
  154. xwindow_get_opacity_unchecked(win);
  155. return xwindow_get_opacity_from_cookie(prop_c);
  156. }
  157. /** Get the opacity of a window.
  158. * \param cookie A cookie for a reply to a get property request for _NET_WM_WINDOW_OPACITY.
  159. * \return The opacity, between 0 and 1.
  160. */
  161. double
  162. xwindow_get_opacity_from_cookie(xcb_get_property_cookie_t cookie)
  163. {
  164. xcb_get_property_reply_t *prop_r =
  165. xcb_get_property_reply(globalconf.connection, cookie, NULL);
  166. if(prop_r && prop_r->value_len && prop_r->format == 32)
  167. {
  168. uint32_t value = *(uint32_t *) xcb_get_property_value(prop_r);
  169. p_delete(&prop_r);
  170. return (double) value / (double) 0xffffffff;
  171. }
  172. p_delete(&prop_r);
  173. return -1;
  174. }
  175. /** Set opacity of a window.
  176. * \param win The window.
  177. * \param opacity Opacity of the window, between 0 and 1.
  178. */
  179. void
  180. xwindow_set_opacity(xcb_window_t win, double opacity)
  181. {
  182. if(win)
  183. {
  184. if(opacity >= 0 && opacity <= 1)
  185. {
  186. uint32_t real_opacity = opacity * 0xffffffff;
  187. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, win,
  188. _NET_WM_WINDOW_OPACITY, XCB_ATOM_CARDINAL, 32, 1L, &real_opacity);
  189. }
  190. else
  191. xcb_delete_property(globalconf.connection, win, _NET_WM_WINDOW_OPACITY);
  192. }
  193. }
  194. /** Send WM_TAKE_FOCUS client message to window
  195. * \param win destination window
  196. */
  197. void
  198. xwindow_takefocus(xcb_window_t win)
  199. {
  200. xcb_client_message_event_t ev;
  201. /* Initialize all of event's fields first */
  202. p_clear(&ev, 1);
  203. ev.response_type = XCB_CLIENT_MESSAGE;
  204. ev.window = win;
  205. ev.format = 32;
  206. ev.data.data32[1] = globalconf.timestamp;
  207. ev.type = WM_PROTOCOLS;
  208. ev.data.data32[0] = WM_TAKE_FOCUS;
  209. xcb_send_event(globalconf.connection, false, win,
  210. XCB_EVENT_MASK_NO_EVENT, (char *) &ev);
  211. }
  212. /** Set window cursor.
  213. * \param w The window.
  214. * \param c The cursor.
  215. */
  216. void
  217. xwindow_set_cursor(xcb_window_t w, xcb_cursor_t c)
  218. {
  219. xcb_change_window_attributes(globalconf.connection, w, XCB_CW_CURSOR,
  220. (const uint32_t[]) { c });
  221. }
  222. /** Set a window border color.
  223. * \param w The window.
  224. * \param color The color.
  225. */
  226. void
  227. xwindow_set_border_color(xcb_window_t w, color_t *color)
  228. {
  229. if(w)
  230. xcb_change_window_attributes(globalconf.connection, w, XCB_CW_BORDER_PIXEL, &color->pixel);
  231. }
  232. /** Get one of a window's shapes as a cairo surface */
  233. cairo_surface_t *
  234. xwindow_get_shape(xcb_window_t win, enum xcb_shape_sk_t kind)
  235. {
  236. if (!globalconf.have_shape)
  237. return NULL;
  238. if (kind == XCB_SHAPE_SK_INPUT && !globalconf.have_input_shape)
  239. return NULL;
  240. int16_t x, y;
  241. uint16_t width, height;
  242. xcb_shape_get_rectangles_cookie_t rcookie = xcb_shape_get_rectangles(globalconf.connection, win, kind);
  243. if (kind == XCB_SHAPE_SK_INPUT)
  244. {
  245. /* We cannot query the size/existence of an input shape... */
  246. xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(globalconf.connection,
  247. xcb_get_geometry(globalconf.connection, win), NULL);
  248. if (!geom)
  249. {
  250. xcb_discard_reply(globalconf.connection, rcookie.sequence);
  251. /* Create a cairo surface in an error state */
  252. return cairo_image_surface_create(CAIRO_FORMAT_INVALID, -1, -1);
  253. }
  254. x = 0;
  255. y = 0;
  256. width = geom->width;
  257. height = geom->height;
  258. }
  259. else
  260. {
  261. xcb_shape_query_extents_cookie_t ecookie = xcb_shape_query_extents(globalconf.connection, win);
  262. xcb_shape_query_extents_reply_t *extents = xcb_shape_query_extents_reply(globalconf.connection, ecookie, NULL);
  263. bool shaped;
  264. if (!extents)
  265. {
  266. xcb_discard_reply(globalconf.connection, rcookie.sequence);
  267. /* Create a cairo surface in an error state */
  268. return cairo_image_surface_create(CAIRO_FORMAT_INVALID, -1, -1);
  269. }
  270. if (kind == XCB_SHAPE_SK_BOUNDING)
  271. {
  272. x = extents->bounding_shape_extents_x;
  273. y = extents->bounding_shape_extents_y;
  274. width = extents->bounding_shape_extents_width;
  275. height = extents->bounding_shape_extents_height;
  276. shaped = extents->bounding_shaped;
  277. } else {
  278. check(kind == XCB_SHAPE_SK_CLIP);
  279. x = extents->clip_shape_extents_x;
  280. y = extents->clip_shape_extents_y;
  281. width = extents->clip_shape_extents_width;
  282. height = extents->clip_shape_extents_height;
  283. shaped = extents->clip_shaped;
  284. }
  285. p_delete(&extents);
  286. if (!shaped)
  287. {
  288. xcb_discard_reply(globalconf.connection, rcookie.sequence);
  289. return NULL;
  290. }
  291. }
  292. xcb_shape_get_rectangles_reply_t *rects_reply = xcb_shape_get_rectangles_reply(globalconf.connection, rcookie, NULL);
  293. if (!rects_reply)
  294. {
  295. /* Create a cairo surface in an error state */
  296. return cairo_image_surface_create(CAIRO_FORMAT_INVALID, -1, -1);
  297. }
  298. cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
  299. cairo_t *cr = cairo_create(surface);
  300. int num_rects = xcb_shape_get_rectangles_rectangles_length(rects_reply);
  301. xcb_rectangle_t *rects = xcb_shape_get_rectangles_rectangles(rects_reply);
  302. cairo_surface_set_device_offset(surface, -x, -y);
  303. cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
  304. for (int i = 0; i < num_rects; i++)
  305. cairo_rectangle(cr, rects[i].x, rects[i].y, rects[i].width, rects[i].height);
  306. cairo_fill(cr);
  307. cairo_destroy(cr);
  308. free(rects_reply);
  309. return surface;
  310. }
  311. /** Turn a cairo surface into a pixmap with depth 1 */
  312. static xcb_pixmap_t
  313. xwindow_shape_pixmap(int width, int height, cairo_surface_t *surf)
  314. {
  315. xcb_pixmap_t pixmap = xcb_generate_id(globalconf.connection);
  316. cairo_surface_t *dest;
  317. cairo_t *cr;
  318. if (width <= 0 || height <= 0)
  319. return XCB_NONE;
  320. xcb_create_pixmap(globalconf.connection, 1, pixmap, globalconf.screen->root, width, height);
  321. dest = cairo_xcb_surface_create_for_bitmap(globalconf.connection, globalconf.screen, pixmap, width, height);
  322. cr = cairo_create(dest);
  323. cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  324. cairo_set_source_surface(cr, surf, 0, 0);
  325. cairo_paint(cr);
  326. cairo_destroy(cr);
  327. cairo_surface_flush(dest);
  328. cairo_surface_finish(dest);
  329. cairo_surface_destroy(dest);
  330. return pixmap;
  331. }
  332. /** Set one of a window's shapes */
  333. void
  334. xwindow_set_shape(xcb_window_t win, int width, int height, enum xcb_shape_sk_t kind, cairo_surface_t *surf, int offset)
  335. {
  336. if (!globalconf.have_shape)
  337. return;
  338. if (kind == XCB_SHAPE_SK_INPUT && !globalconf.have_input_shape)
  339. return;
  340. xcb_pixmap_t pixmap = XCB_NONE;
  341. if (surf)
  342. pixmap = xwindow_shape_pixmap(width, height, surf);
  343. xcb_shape_mask(globalconf.connection, XCB_SHAPE_SO_SET, kind, win, offset, offset, pixmap);
  344. if (pixmap != XCB_NONE)
  345. xcb_free_pixmap(globalconf.connection, pixmap);
  346. }
  347. /** Calculate the position change that a window needs applied.
  348. * \param gravity The window gravity that should be used.
  349. * \param change_width_before The window width difference that will be applied.
  350. * \param change_height_before The window height difference that will be applied.
  351. * \param change_width_after The window width difference that will be applied.
  352. * \param change_height_after The window height difference that will be applied.
  353. * \param dx On return, this will be changed by the amount the pixel has to be moved.
  354. * \param dy On return, this will be changed by the amount the pixel has to be moved.
  355. */
  356. void xwindow_translate_for_gravity(xcb_gravity_t gravity, int16_t change_width_before, int16_t change_height_before,
  357. int16_t change_width_after, int16_t change_height_after, int16_t *dx, int16_t *dy)
  358. {
  359. int16_t x = 0, y = 0;
  360. int16_t change_height = change_height_before + change_height_after;
  361. int16_t change_width = change_width_before + change_width_after;
  362. switch (gravity) {
  363. case XCB_GRAVITY_WIN_UNMAP:
  364. case XCB_GRAVITY_NORTH_WEST:
  365. break;
  366. case XCB_GRAVITY_NORTH:
  367. x = -change_width / 2;
  368. break;
  369. case XCB_GRAVITY_NORTH_EAST:
  370. x = -change_width;
  371. break;
  372. case XCB_GRAVITY_WEST:
  373. y = -change_height / 2;
  374. break;
  375. case XCB_GRAVITY_CENTER:
  376. x = -change_width / 2;
  377. y = -change_height / 2;
  378. break;
  379. case XCB_GRAVITY_EAST:
  380. x = -change_width;
  381. y = -change_height / 2;
  382. break;
  383. case XCB_GRAVITY_SOUTH_WEST:
  384. y = -change_height;
  385. break;
  386. case XCB_GRAVITY_SOUTH:
  387. x = -change_width / 2;
  388. y = -change_height;
  389. break;
  390. case XCB_GRAVITY_SOUTH_EAST:
  391. x = -change_width;
  392. y = -change_height;
  393. break;
  394. case XCB_GRAVITY_STATIC:
  395. x = -change_width_before;
  396. y = -change_height_before;
  397. break;
  398. }
  399. if (dx)
  400. *dx += x;
  401. if (dy)
  402. *dy += y;
  403. }
  404. // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80