1 /*
2 * vim:ts=4:sw=4:expandtab
3 *
4 * i3 - an improved dynamic tiling window manager
5 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
6 *
7 */
8 #include "libi3.h"
9
10 #include <xcb/xcb_aux.h>
11
12 /**
13 * Find the region in the given window that is not covered by a mapped child
14 * window.
15 */
unobscured_region(xcb_connection_t * conn,xcb_window_t window,uint16_t window_width,uint16_t window_height)16 static cairo_region_t *unobscured_region(xcb_connection_t *conn, xcb_window_t window,
17 uint16_t window_width, uint16_t window_height) {
18 cairo_rectangle_int_t rectangle;
19 cairo_region_t *region;
20
21 rectangle.x = 0;
22 rectangle.y = 0;
23 rectangle.width = window_width;
24 rectangle.height = window_height;
25 region = cairo_region_create_rectangle(&rectangle);
26
27 xcb_query_tree_reply_t *tree = xcb_query_tree_reply(conn, xcb_query_tree_unchecked(conn, window), NULL);
28 if (!tree) {
29 return region;
30 }
31
32 /* Get information about children */
33 uint16_t n_children = tree->children_len;
34 xcb_window_t *children = xcb_query_tree_children(tree);
35
36 xcb_get_geometry_cookie_t geometries[n_children];
37 xcb_get_window_attributes_cookie_t attributes[n_children];
38
39 for (int i = 0; i < n_children; i++) {
40 geometries[i] = xcb_get_geometry_unchecked(conn, children[i]);
41 attributes[i] = xcb_get_window_attributes_unchecked(conn, children[i]);
42 }
43
44 /* Remove every visible child from the region */
45 for (int i = 0; i < n_children; i++) {
46 xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(conn, geometries[i], NULL);
47 xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(conn, attributes[i], NULL);
48
49 if (geom && attr && attr->map_state == XCB_MAP_STATE_VIEWABLE) {
50 rectangle.x = geom->x;
51 rectangle.y = geom->y;
52 rectangle.width = geom->width;
53 rectangle.height = geom->height;
54 cairo_region_subtract_rectangle(region, &rectangle);
55 }
56
57 free(geom);
58 free(attr);
59 }
60
61 free(tree);
62 return region;
63 }
64
find_unobscured_pixel(xcb_connection_t * conn,xcb_window_t window,uint16_t window_width,uint16_t window_height,uint16_t * x,uint16_t * y)65 static void find_unobscured_pixel(xcb_connection_t *conn, xcb_window_t window,
66 uint16_t window_width, uint16_t window_height,
67 uint16_t *x, uint16_t *y) {
68 cairo_region_t *region = unobscured_region(conn, window, window_width, window_height);
69 if (cairo_region_num_rectangles(region) > 0) {
70 /* Return the top left pixel of the first rectangle */
71 cairo_rectangle_int_t rect;
72 cairo_region_get_rectangle(region, 0, &rect);
73 *x = rect.x;
74 *y = rect.y;
75 } else {
76 /* No unobscured area found */
77 *x = 0;
78 *y = 0;
79 }
80 cairo_region_destroy(region);
81 }
82
flicker_window_at(xcb_connection_t * conn,xcb_screen_t * screen,int16_t x,int16_t y,xcb_window_t window,uint32_t pixel)83 static uint32_t flicker_window_at(xcb_connection_t *conn, xcb_screen_t *screen, int16_t x, int16_t y, xcb_window_t window,
84 uint32_t pixel) {
85 xcb_create_window(conn, XCB_COPY_FROM_PARENT, window, screen->root, x, y, 10, 10,
86 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
87 XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT, (uint32_t[]){pixel, 1});
88 xcb_map_window(conn, window);
89 xcb_clear_area(conn, 0, window, 0, 0, 0, 0);
90 xcb_aux_sync(conn);
91 xcb_destroy_window(conn, window);
92
93 xcb_get_image_reply_t *img = xcb_get_image_reply(conn,
94 xcb_get_image_unchecked(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, screen->root, x, y, 1, 1, ~0),
95 NULL);
96 uint32_t result = 0;
97 if (img) {
98 uint8_t *data = xcb_get_image_data(img);
99 uint8_t depth = img->depth;
100 for (int i = 0; i < MIN(depth, 4); i++) {
101 result = (result << 8) | data[i];
102 }
103 free(img);
104 }
105 return result;
106 }
107
is_background_set(xcb_connection_t * conn,xcb_screen_t * screen)108 bool is_background_set(xcb_connection_t *conn, xcb_screen_t *screen) {
109 uint16_t x, y;
110 find_unobscured_pixel(conn, screen->root, screen->width_in_pixels, screen->height_in_pixels, &x, &y);
111
112 xcb_window_t window = xcb_generate_id(conn);
113
114 uint32_t pixel1 = flicker_window_at(conn, screen, x, y, window, screen->black_pixel);
115 uint32_t pixel2 = flicker_window_at(conn, screen, x, y, window, screen->white_pixel);
116 return pixel1 == pixel2;
117 }
118