2 * xwindow.c - X window handling functions
4 * Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
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.
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.
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.
23 #include "common/atoms.h"
24 #include "objects/button.h"
27 #include <xcb/shape.h>
28 #include <cairo-xcb.h>
30 /** Mask shorthands */
31 #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE)
33 /** Set client state (WM_STATE) property.
34 * \param win The window to set state.
35 * \param state The state to set.
38 xwindow_set_state(xcb_window_t win
, uint32_t state
)
40 uint32_t data
[] = { state
, XCB_NONE
};
41 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
, win
,
42 WM_STATE
, WM_STATE
, 32, 2, data
);
45 /** Send request to get a window state (WM_STATE).
46 * \param w A client window.
47 * \return The cookie associated with the request.
49 xcb_get_property_cookie_t
50 xwindow_get_state_unchecked(xcb_window_t w
)
52 return xcb_get_property_unchecked(globalconf
.connection
, false, w
, WM_STATE
,
56 /** Get a window state (WM_STATE).
57 * \param cookie The cookie.
58 * \return The current state of the window, or 0 on error.
61 xwindow_get_state_reply(xcb_get_property_cookie_t cookie
)
63 /* If no property is set, we just assume a sane default. */
64 uint32_t result
= XCB_ICCCM_WM_STATE_NORMAL
;
65 xcb_get_property_reply_t
*prop_r
;
67 if((prop_r
= xcb_get_property_reply(globalconf
.connection
, cookie
, NULL
)))
69 if(xcb_get_property_value_length(prop_r
))
70 result
= *(uint32_t *) xcb_get_property_value(prop_r
);
78 /** Configure a window with its new geometry and border size.
79 * \param win The X window id to configure.
80 * \param geometry The new window geometry.
81 * \param border The new border size.
84 xwindow_configure(xcb_window_t win
, area_t geometry
, int border
)
86 xcb_configure_notify_event_t ce
;
88 ce
.response_type
= XCB_CONFIGURE_NOTIFY
;
91 ce
.x
= geometry
.x
+ border
;
92 ce
.y
= geometry
.y
+ border
;
93 ce
.width
= geometry
.width
;
94 ce
.height
= geometry
.height
;
95 ce
.border_width
= border
;
96 ce
.above_sibling
= XCB_NONE
;
97 ce
.override_redirect
= false;
98 xcb_send_event(globalconf
.connection
, false, win
, XCB_EVENT_MASK_STRUCTURE_NOTIFY
, (char *) &ce
);
101 /** Grab or ungrab buttons on a window.
102 * \param win The window.
103 * \param buttons The buttons to grab.
106 xwindow_buttons_grab(xcb_window_t win
, button_array_t
*buttons
)
111 /* Ungrab everything first */
112 xcb_ungrab_button(globalconf
.connection
, XCB_BUTTON_INDEX_ANY
, win
, XCB_BUTTON_MASK_ANY
);
115 xcb_grab_button(globalconf
.connection
, false, win
, BUTTONMASK
,
116 XCB_GRAB_MODE_SYNC
, XCB_GRAB_MODE_ASYNC
, XCB_NONE
, XCB_NONE
,
117 (*b
)->button
, (*b
)->modifiers
);
120 /** Grab key on a window.
121 * \param win The window.
125 xwindow_grabkey(xcb_window_t win
, keyb_t
*k
)
128 xcb_grab_key(globalconf
.connection
, true, win
,
129 k
->modifiers
, k
->keycode
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
);
132 xcb_keycode_t
*keycodes
= xcb_key_symbols_get_keycode(globalconf
.keysyms
, k
->keysym
);
135 for(xcb_keycode_t
*kc
= keycodes
; *kc
; kc
++)
136 xcb_grab_key(globalconf
.connection
, true, win
,
137 k
->modifiers
, *kc
, XCB_GRAB_MODE_ASYNC
, XCB_GRAB_MODE_ASYNC
);
144 xwindow_grabkeys(xcb_window_t win
, key_array_t
*keys
)
146 /* Ungrab everything first */
147 xcb_ungrab_key(globalconf
.connection
, XCB_GRAB_ANY
, win
, XCB_BUTTON_MASK_ANY
);
150 xwindow_grabkey(win
, *k
);
153 /** Send a request for a window's opacity.
154 * \param win The window
155 * \return A cookie for xwindow_get_opacity_from_reply().
157 xcb_get_property_cookie_t
xwindow_get_opacity_unchecked(xcb_window_t win
)
159 return xcb_get_property_unchecked(globalconf
.connection
, false, win
,
160 _NET_WM_WINDOW_OPACITY
, XCB_ATOM_CARDINAL
, 0L, 1L);
163 /** Get the opacity of a window.
164 * \param win The window.
165 * \return The opacity, between 0 and 1 or -1 or no opacity set.
168 xwindow_get_opacity(xcb_window_t win
)
170 xcb_get_property_cookie_t prop_c
=
171 xwindow_get_opacity_unchecked(win
);
172 return xwindow_get_opacity_from_cookie(prop_c
);
175 /** Get the opacity of a window.
176 * \param cookie A cookie for a reply to a get property request for _NET_WM_WINDOW_OPACITY.
177 * \return The opacity, between 0 and 1.
180 xwindow_get_opacity_from_cookie(xcb_get_property_cookie_t cookie
)
182 xcb_get_property_reply_t
*prop_r
=
183 xcb_get_property_reply(globalconf
.connection
, cookie
, NULL
);
185 if(prop_r
&& prop_r
->value_len
&& prop_r
->format
== 32)
187 uint32_t value
= *(uint32_t *) xcb_get_property_value(prop_r
);
189 return (double) value
/ (double) 0xffffffff;
196 /** Set opacity of a window.
197 * \param win The window.
198 * \param opacity Opacity of the window, between 0 and 1.
201 xwindow_set_opacity(xcb_window_t win
, double opacity
)
205 if(opacity
>= 0 && opacity
<= 1)
207 uint32_t real_opacity
= opacity
* 0xffffffff;
208 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
, win
,
209 _NET_WM_WINDOW_OPACITY
, XCB_ATOM_CARDINAL
, 32, 1L, &real_opacity
);
212 xcb_delete_property(globalconf
.connection
, win
, _NET_WM_WINDOW_OPACITY
);
216 /** Send WM_TAKE_FOCUS client message to window
217 * \param win destination window
220 xwindow_takefocus(xcb_window_t win
)
222 xcb_client_message_event_t ev
;
224 /* Initialize all of event's fields first */
227 ev
.response_type
= XCB_CLIENT_MESSAGE
;
230 ev
.data
.data32
[1] = globalconf
.timestamp
;
231 ev
.type
= WM_PROTOCOLS
;
232 ev
.data
.data32
[0] = WM_TAKE_FOCUS
;
234 xcb_send_event(globalconf
.connection
, false, win
,
235 XCB_EVENT_MASK_NO_EVENT
, (char *) &ev
);
238 /** Set window cursor.
239 * \param w The window.
240 * \param c The cursor.
243 xwindow_set_cursor(xcb_window_t w
, xcb_cursor_t c
)
245 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_CURSOR
,
246 (const uint32_t[]) { c
});
249 /** Set a window border color.
250 * \param w The window.
251 * \param color The color.
254 xwindow_set_border_color(xcb_window_t w
, color_t
*color
)
257 xcb_change_window_attributes(globalconf
.connection
, w
, XCB_CW_BORDER_PIXEL
, &color
->pixel
);
260 /** Get one of a window's shapes as a cairo surface */
262 xwindow_get_shape(xcb_window_t win
, enum xcb_shape_sk_t kind
)
264 if (!globalconf
.have_shape
)
267 xcb_shape_query_extents_cookie_t ecookie
= xcb_shape_query_extents(globalconf
.connection
, win
);
268 xcb_shape_get_rectangles_cookie_t rcookie
= xcb_shape_get_rectangles(globalconf
.connection
, win
, kind
);
269 xcb_shape_query_extents_reply_t
*extents
= xcb_shape_query_extents_reply(globalconf
.connection
, ecookie
, NULL
);
270 xcb_shape_get_rectangles_reply_t
*rects_reply
= xcb_shape_get_rectangles_reply(globalconf
.connection
, rcookie
, NULL
);
272 if (!extents
|| !rects_reply
)
276 /* Create a cairo surface in an error state */
277 return cairo_image_surface_create(CAIRO_FORMAT_INVALID
, -1, -1);
281 uint16_t width
, height
;
283 if (kind
== XCB_SHAPE_SK_BOUNDING
)
285 x
= extents
->bounding_shape_extents_x
;
286 y
= extents
->bounding_shape_extents_y
;
287 width
= extents
->bounding_shape_extents_width
;
288 height
= extents
->bounding_shape_extents_height
;
289 shaped
= extents
->bounding_shaped
;
291 assert(kind
== XCB_SHAPE_SK_CLIP
);
292 x
= extents
->clip_shape_extents_x
;
293 y
= extents
->clip_shape_extents_y
;
294 width
= extents
->clip_shape_extents_width
;
295 height
= extents
->clip_shape_extents_height
;
296 shaped
= extents
->clip_shaped
;
306 cairo_surface_t
*surface
= cairo_image_surface_create(CAIRO_FORMAT_A1
, width
, height
);
307 cairo_t
*cr
= cairo_create(surface
);
308 int num_rects
= xcb_shape_get_rectangles_rectangles_length(rects_reply
);
309 xcb_rectangle_t
*rects
= xcb_shape_get_rectangles_rectangles(rects_reply
);
311 cairo_surface_set_device_offset(surface
, -x
, -y
);
312 cairo_set_fill_rule(cr
, CAIRO_FILL_RULE_WINDING
);
314 for (int i
= 0; i
< num_rects
; i
++)
315 cairo_rectangle(cr
, rects
[i
].x
, rects
[i
].y
, rects
[i
].width
, rects
[i
].height
);
324 /** Turn a cairo surface into a pixmap with depth 1 */
326 xwindow_shape_pixmap(int width
, int height
, cairo_surface_t
*surf
)
328 xcb_pixmap_t pixmap
= xcb_generate_id(globalconf
.connection
);
329 cairo_surface_t
*dest
;
332 if (width
<= 0 || height
<= 0)
335 xcb_create_pixmap(globalconf
.connection
, 1, pixmap
, globalconf
.screen
->root
, width
, height
);
336 dest
= cairo_xcb_surface_create_for_bitmap(globalconf
.connection
, globalconf
.screen
, pixmap
, width
, height
);
338 cr
= cairo_create(dest
);
339 cairo_set_operator(cr
, CAIRO_OPERATOR_SOURCE
);
340 cairo_set_source_surface(cr
, surf
, 0, 0);
344 cairo_surface_flush(dest
);
345 cairo_surface_finish(dest
);
346 cairo_surface_destroy(dest
);
351 /** Set one of a window's shapes */
353 xwindow_set_shape(xcb_window_t win
, int width
, int height
, enum xcb_shape_sk_t kind
, cairo_surface_t
*surf
, int offset
)
355 if (!globalconf
.have_shape
)
358 xcb_pixmap_t pixmap
= XCB_NONE
;
360 pixmap
= xwindow_shape_pixmap(width
, height
, surf
);
362 xcb_shape_mask(globalconf
.connection
, XCB_SHAPE_SO_SET
, kind
, win
, offset
, offset
, pixmap
);
364 if (pixmap
!= XCB_NONE
)
365 xcb_free_pixmap(globalconf
.connection
, pixmap
);
368 /** Calculate the position change that a window needs applied.
369 * \param gravity The window gravity that should be used.
370 * \param change_width_before The window width difference that will be applied.
371 * \param change_height_before The window height difference that will be applied.
372 * \param change_width_after The window width difference that will be applied.
373 * \param change_height_after The window height difference that will be applied.
374 * \param dx On return, this will be set to the amount the pixel has to be moved.
375 * \param dy On return, this will be set to the amount the pixel has to be moved.
377 void xwindow_translate_for_gravity(xcb_gravity_t gravity
, int16_t change_width_before
, int16_t change_height_before
,
378 int16_t change_width_after
, int16_t change_height_after
, int16_t *dx
, int16_t *dy
)
380 int16_t x
= 0, y
= 0;
381 int16_t change_height
= change_height_before
+ change_height_after
;
382 int16_t change_width
= change_width_before
+ change_width_after
;
385 case XCB_GRAVITY_WIN_UNMAP
:
386 case XCB_GRAVITY_NORTH_WEST
:
388 case XCB_GRAVITY_NORTH
:
389 x
= -change_width
/ 2;
391 case XCB_GRAVITY_NORTH_EAST
:
394 case XCB_GRAVITY_WEST
:
395 y
= -change_height
/ 2;
397 case XCB_GRAVITY_CENTER
:
398 x
= -change_width
/ 2;
399 y
= -change_height
/ 2;
401 case XCB_GRAVITY_EAST
:
403 y
= -change_height
/ 2;
405 case XCB_GRAVITY_SOUTH_WEST
:
408 case XCB_GRAVITY_SOUTH
:
409 x
= -change_width
/ 2;
412 case XCB_GRAVITY_SOUTH_EAST
:
416 case XCB_GRAVITY_STATIC
:
417 x
= -change_width_before
;
418 x
= -change_height_before
;
428 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80