aboutsummaryrefslogtreecommitdiff
path: root/libi3
diff options
context:
space:
mode:
authorUli Schlachter <psychon@users.noreply.github.com>2021-05-20 21:37:35 +0200
committerGitHub <noreply@github.com>2021-05-20 21:37:35 +0200
commit60542da0916a62a6daede9293090660b05d4679e (patch)
treea58f2606fd5dbed934a4b0c6cbf2ee69cd9df199 /libi3
parentfcae64f7fd97c02f36200a4c60ab7ec2fd4410df (diff)
downloadi3-60542da0916a62a6daede9293090660b05d4679e.tar.gz
i3-60542da0916a62a6daede9293090660b05d4679e.zip
Do not "set" the wallpaper during startup (#4373)
"Set" the wallpaper during startup only sometimes Since commit 4f5e0e7, i3 would take a screenshot and set that as the background pixmap of the root window during startup. This is the easy part of setting a proper X11 wallpaper. The code in question was added because something either set the background pixmap of the root window to NONE or the X11 server was started with "-background none". This is apparently done by default by e.g. gdm to avoid some flickering while the X11 server starts up. This commit makes this code conditional: Only when no wallpaper is detected is a screenshot taken. Since I could not find any way to query the background of a window, a more direct approach is taken to detect this situation: First, we find some part of the root window that is not currently covered. Then we open a white window there, close it again and grab a screenshot. If a wallpaper is set, the X11 server will draw this wallpaper after the window is closed and something else will be visible in the screenshot. However, the wallpaper could have a white pixel at the tested position. Thus, this procedure is repeated with a black window. Only when this procedure produces two different pixel values is a screenshot taken and set as the wallpaper. Fixes: https://github.com/i3/i3/issues/4371 Fixes: https://github.com/i3/i3/issues/2869 Signed-off-by: Uli Schlachter <psychon@znc.in>
Diffstat (limited to 'libi3')
-rw-r--r--libi3/is_background_set.c117
-rw-r--r--libi3/screenshot_wallpaper.c27
2 files changed, 144 insertions, 0 deletions
diff --git a/libi3/is_background_set.c b/libi3/is_background_set.c
new file mode 100644
index 00000000..b3b6e6a6
--- /dev/null
+++ b/libi3/is_background_set.c
@@ -0,0 +1,117 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include "libi3.h"
+
+#include <xcb/xcb_aux.h>
+
+/**
+ * Find the region in the given window that is not covered by a mapped child
+ * window.
+ */
+static cairo_region_t *unobscured_region(xcb_connection_t *conn, xcb_window_t window,
+ uint16_t window_width, uint16_t window_height) {
+ cairo_rectangle_int_t rectangle;
+ cairo_region_t *region;
+
+ rectangle.x = 0;
+ rectangle.y = 0;
+ rectangle.width = window_width;
+ rectangle.height = window_height;
+ region = cairo_region_create_rectangle(&rectangle);
+
+ xcb_query_tree_reply_t *tree = xcb_query_tree_reply(conn, xcb_query_tree_unchecked(conn, window), NULL);
+ if (!tree) {
+ return region;
+ }
+
+ /* Get information about children */
+ uint16_t n_children = tree->children_len;
+ xcb_window_t *children = xcb_query_tree_children(tree);
+
+ xcb_get_geometry_cookie_t geometries[n_children];
+ xcb_get_window_attributes_cookie_t attributes[n_children];
+
+ for (int i = 0; i < n_children; i++) {
+ geometries[i] = xcb_get_geometry_unchecked(conn, children[i]);
+ attributes[i] = xcb_get_window_attributes_unchecked(conn, children[i]);
+ }
+
+ /* Remove every visible child from the region */
+ for (int i = 0; i < n_children; i++) {
+ xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(conn, geometries[i], NULL);
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(conn, attributes[i], NULL);
+
+ if (geom && attr && attr->map_state == XCB_MAP_STATE_VIEWABLE) {
+ rectangle.x = geom->x;
+ rectangle.y = geom->y;
+ rectangle.width = geom->width;
+ rectangle.height = geom->height;
+ cairo_region_subtract_rectangle(region, &rectangle);
+ }
+
+ free(geom);
+ free(attr);
+ }
+
+ free(tree);
+ return region;
+}
+
+static void 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) {
+ cairo_region_t *region = unobscured_region(conn, window, window_width, window_height);
+ if (cairo_region_num_rectangles(region) > 0) {
+ /* Return the top left pixel of the first rectangle */
+ cairo_rectangle_int_t rect;
+ cairo_region_get_rectangle(region, 0, &rect);
+ *x = rect.x;
+ *y = rect.y;
+ } else {
+ /* No unobscured area found */
+ *x = 0;
+ *y = 0;
+ }
+ cairo_region_destroy(region);
+}
+
+static uint32_t flicker_window_at(xcb_connection_t *conn, xcb_screen_t *screen, int16_t x, int16_t y, xcb_window_t window,
+ uint32_t pixel) {
+ xcb_create_window(conn, XCB_COPY_FROM_PARENT, window, screen->root, x, y, 10, 10,
+ 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
+ XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT, (uint32_t[]){pixel, 1});
+ xcb_map_window(conn, window);
+ xcb_clear_area(conn, 0, window, 0, 0, 0, 0);
+ xcb_aux_sync(conn);
+ xcb_destroy_window(conn, window);
+
+ xcb_get_image_reply_t *img = xcb_get_image_reply(conn,
+ xcb_get_image_unchecked(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, screen->root, x, y, 1, 1, ~0),
+ NULL);
+ uint32_t result = 0;
+ if (img) {
+ uint8_t *data = xcb_get_image_data(img);
+ uint8_t depth = img->depth;
+ for (int i = 0; i < MIN(depth, 4); i++) {
+ result = (result << 8) | data[i];
+ }
+ free(img);
+ }
+ return result;
+}
+
+bool is_background_set(xcb_connection_t *conn, xcb_screen_t *screen) {
+ uint16_t x, y;
+ find_unobscured_pixel(conn, screen->root, screen->width_in_pixels, screen->height_in_pixels, &x, &y);
+
+ xcb_window_t window = xcb_generate_id(conn);
+
+ uint32_t pixel1 = flicker_window_at(conn, screen, x, y, window, screen->black_pixel);
+ uint32_t pixel2 = flicker_window_at(conn, screen, x, y, window, screen->white_pixel);
+ return pixel1 == pixel2;
+}
diff --git a/libi3/screenshot_wallpaper.c b/libi3/screenshot_wallpaper.c
new file mode 100644
index 00000000..2c115a9e
--- /dev/null
+++ b/libi3/screenshot_wallpaper.c
@@ -0,0 +1,27 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include "libi3.h"
+
+void set_screenshot_as_wallpaper(xcb_connection_t *conn, xcb_screen_t *screen) {
+ uint16_t width = screen->width_in_pixels;
+ uint16_t height = screen->height_in_pixels;
+ xcb_pixmap_t pixmap = xcb_generate_id(conn);
+ xcb_gcontext_t gc = xcb_generate_id(conn);
+
+ xcb_create_pixmap(conn, screen->root_depth, pixmap, screen->root, width, height);
+
+ xcb_create_gc(conn, gc, screen->root,
+ XCB_GC_FUNCTION | XCB_GC_PLANE_MASK | XCB_GC_FILL_STYLE | XCB_GC_SUBWINDOW_MODE,
+ (uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
+
+ xcb_copy_area(conn, screen->root, pixmap, gc, 0, 0, 0, 0, width, height);
+ xcb_change_window_attributes(conn, screen->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap});
+ xcb_free_gc(conn, gc);
+ xcb_free_pixmap(conn, pixmap);
+ xcb_flush(conn);
+}