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