]> git.sur5r.net Git - i3/i3/commitdiff
Migrate i3 rendering to cairo.
authorIngo Bürk <ingo.buerk@tngtech.com>
Mon, 16 Nov 2015 20:26:06 +0000 (21:26 +0100)
committerIngo Bürk <ingo.buerk@tngtech.com>
Mon, 23 Nov 2015 21:18:02 +0000 (22:18 +0100)
This patch migrates all decoration rendering of i3 to cairo. Using the
compile switch CAIRO_SUPPORT, rendering can be switched back to the
previous XCB behavior, just like with the previous migration to cairo
in i3bar.

This patch also fixes a bug in draw_util.c where copying one surface
to another would use incorrect coordinates if the source coordinates
are not 0, 0.

Furthermore, this patch implicitly fixes some minor issues in the
decoration rendering which would be ignored previously due to the fact
that errors would only show up in the event queue, but not cause the
rendering code path to crash. One example is zero-height pixmaps which
are not allowed. Using cairo, these would cause i3 to instantly segfault,
so this patch avoids this.

Lastly, this patch annotates other issues found but not fixed in this patch
using TODO comments, e.g., the zero-height check not working correctly
and the comment that it should probably work the same way for zero-width
pixmaps.

relates to #1278

i3bar/src/xcb.c
include/data.h
include/libi3.h
include/xcb.h
libi3/draw_util.c
src/con.c
src/handlers.c
src/main.c
src/manage.c
src/x.c
src/xcb.c

index 92b0d1abddc470af476d5139c84940a3bfa1a0be..c6c9846b5d3f194979a2f05b53341a26149be5bb 100644 (file)
@@ -174,7 +174,7 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
     uint32_t center_x = x - sep_offset;
     if (config.separator_symbol == NULL) {
         /* Draw a classic one pixel, vertical separator. */
-        draw_util_rectangle(&output->statusline_buffer, sep_fg,
+        draw_util_rectangle(xcb_connection, &output->statusline_buffer, sep_fg,
                             center_x,
                             logical_px(sep_voff_px),
                             logical_px(1),
@@ -243,7 +243,7 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color
     struct status_block *block;
 
     color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
-    draw_util_clear_surface(&output->statusline_buffer, bar_color);
+    draw_util_clear_surface(xcb_connection, &output->statusline_buffer, bar_color);
 
     /* Use unsigned integer wraparound to clip off the left side.
      * For example, if clip_left is 75, then x will start at the very large
@@ -294,13 +294,13 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color
             }
 
             /* Draw the border. */
-            draw_util_rectangle(&output->statusline_buffer, border_color,
+            draw_util_rectangle(xcb_connection, &output->statusline_buffer, border_color,
                                 x, logical_px(1),
                                 full_render_width,
                                 bar_height - logical_px(2));
 
             /* Draw the background. */
-            draw_util_rectangle(&output->statusline_buffer, bg_color,
+            draw_util_rectangle(xcb_connection, &output->statusline_buffer, bg_color,
                                 x + border_width,
                                 logical_px(1) + border_width,
                                 full_render_width - 2 * border_width,
@@ -1709,9 +1709,9 @@ void reconfig_windows(bool redraw_bars) {
                                                                 1,
                                                                 (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
 
-            draw_util_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height);
-            draw_util_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height);
-            draw_util_surface_init(&walk->statusline_buffer, statusline_buffer_id, walk->rect.w, bar_height);
+            draw_util_surface_init(xcb_connection, &walk->bar, bar_id, NULL, walk->rect.w, bar_height);
+            draw_util_surface_init(xcb_connection, &walk->buffer, buffer_id, NULL, walk->rect.w, bar_height);
+            draw_util_surface_init(xcb_connection, &walk->statusline_buffer, statusline_buffer_id, NULL, walk->rect.w, bar_height);
 
             xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
 
@@ -1820,12 +1820,12 @@ void reconfig_windows(bool redraw_bars) {
                                                                       walk->rect.w,
                                                                       bar_height);
 
-            draw_util_surface_free(&(walk->bar));
-            draw_util_surface_free(&(walk->buffer));
-            draw_util_surface_free(&(walk->statusline_buffer));
-            draw_util_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height);
-            draw_util_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height);
-            draw_util_surface_init(&(walk->statusline_buffer), walk->statusline_buffer.id, walk->rect.w, bar_height);
+            draw_util_surface_free(xcb_connection, &(walk->bar));
+            draw_util_surface_free(xcb_connection, &(walk->buffer));
+            draw_util_surface_free(xcb_connection, &(walk->statusline_buffer));
+            draw_util_surface_init(xcb_connection, &(walk->bar), walk->bar.id, NULL, walk->rect.w, bar_height);
+            draw_util_surface_init(xcb_connection, &(walk->buffer), walk->buffer.id, NULL, walk->rect.w, bar_height);
+            draw_util_surface_init(xcb_connection, &(walk->statusline_buffer), walk->statusline_buffer.id, NULL, walk->rect.w, bar_height);
 
             xcb_void_cookie_t map_cookie, umap_cookie;
             if (redraw_bars) {
@@ -1886,7 +1886,7 @@ void draw_bars(bool unhide) {
         bool use_focus_colors = output_has_focus(outputs_walk);
 
         /* First things first: clear the backbuffer */
-        draw_util_clear_surface(&(outputs_walk->buffer),
+        draw_util_clear_surface(xcb_connection, &(outputs_walk->buffer),
                                 (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg));
 
         if (!config.disable_ws) {
@@ -1917,14 +1917,14 @@ void draw_bars(bool unhide) {
                 }
 
                 /* Draw the border of the button. */
-                draw_util_rectangle(&(outputs_walk->buffer), border_color,
+                draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), border_color,
                                     workspace_width,
                                     logical_px(1),
                                     ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
                                     font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
 
                 /* Draw the inside of the button. */
-                draw_util_rectangle(&(outputs_walk->buffer), bg_color,
+                draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), bg_color,
                                     workspace_width + logical_px(1),
                                     2 * logical_px(1),
                                     ws_walk->name_width + 2 * logical_px(ws_hoff_px),
@@ -1947,13 +1947,13 @@ void draw_bars(bool unhide) {
             color_t fg_color = colors.binding_mode_fg;
             color_t bg_color = colors.binding_mode_bg;
 
-            draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border,
+            draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), colors.binding_mode_border,
                                 workspace_width,
                                 logical_px(1),
                                 binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
                                 font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
 
-            draw_util_rectangle(&(outputs_walk->buffer), bg_color,
+            draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), bg_color,
                                 workspace_width + logical_px(1),
                                 2 * logical_px(1),
                                 binding.width + 2 * logical_px(ws_hoff_px),
@@ -1989,7 +1989,7 @@ void draw_bars(bool unhide) {
             int x_dest = outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width;
 
             draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
-            draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
+            draw_util_copy_surface(xcb_connection, &outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
                                    x_dest, 0, visible_statusline_width, (int16_t)bar_height);
 
             outputs_walk->statusline_width = statusline_width;
@@ -2020,7 +2020,7 @@ void redraw_bars(void) {
             continue;
         }
 
-        draw_util_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0,
+        draw_util_copy_surface(xcb_connection, &(outputs_walk->buffer), &(outputs_walk->bar), 0, 0,
                                0, 0, outputs_walk->rect.w, outputs_walk->rect.h);
         xcb_flush(xcb_connection);
     }
index e5a764628b0e13e57c02784e38b1993c882f9b58..78a42ff4b9a040d78fb899bae399ada744de166d 100644 (file)
@@ -551,11 +551,10 @@ struct Con {
      * change. */
     uint8_t ignore_unmap;
 
-    /* ids/pixmap/graphics context for the frame window */
+    /* The surface used for the frame window. */
+    surface_t frame;
+    surface_t frame_buffer;
     bool pixmap_recreated;
-    xcb_window_t frame;
-    xcb_pixmap_t pixmap;
-    xcb_gcontext_t pm_gc;
 
     enum {
         CT_ROOT = 0,
index 15dffdb736766cea989f1759a332c28bf1d76f30..0cb2532fcd491818bda8434f5cb5d1b96137ec9b 100644 (file)
@@ -513,6 +513,8 @@ typedef struct surface_t {
     /* A classic XCB graphics context. */
     xcb_gcontext_t gc;
 
+    xcb_visualtype_t *visual_type;
+
     int width;
     int height;
 
@@ -530,13 +532,20 @@ typedef struct surface_t {
  * Initialize the surface to represent the given drawable.
  *
  */
-void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height);
+void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
+                            xcb_visualtype_t *visual, int width, int height);
+
+/**
+ * Resize the surface to the given size.
+ *
+ */
+void draw_util_surface_set_size(surface_t *surface, int width, int height);
 
 /**
  * Destroys the surface.
  *
  */
-void draw_util_surface_free(surface_t *surface);
+void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface);
 
 /**
  * Parses the given color in hex format to an internal color representation.
@@ -545,6 +554,8 @@ void draw_util_surface_free(surface_t *surface);
  */
 color_t draw_util_hex_to_color(const char *color);
 
+color_t draw_util_colorpixel_to_color(uint32_t colorpixel);
+
 /**
  * Draw the given text using libi3.
  * This function also marks the surface dirty which is needed if other means of
@@ -559,17 +570,17 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_
  * surface as well as restoring the cairo state.
  *
  */
-void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h);
+void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h);
 
 /**
  * Clears a surface with the given color.
  *
  */
-void draw_util_clear_surface(surface_t *surface, color_t color);
+void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color);
 
 /**
  * Copies a surface onto another surface.
  *
  */
-void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
+void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y,
                             double dest_x, double dest_y, double width, double height);
index c1f989bd8c455b58fccfd9a877785cde302aba70..7fae41f5dc0525c1f72c7c6f782b6370aa21559f 100644 (file)
@@ -139,6 +139,7 @@ void xcb_set_root_cursor(int cursor);
  *
  */
 uint16_t get_visual_depth(xcb_visualid_t visual_id);
+xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id);
 
 /**
  * Get visualid with specified depth
index 90b1f26fd66a55e8678b47315f85d0f6771e177c..e7e7c09c94f1841aaa9bcab88317be175ce77dd0 100644 (file)
 
 #include "libi3.h"
 
-xcb_connection_t *xcb_connection;
+/* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */
 xcb_visualtype_t *visual_type;
 
 /* Forward declarations */
-static void draw_util_set_source_color(surface_t *surface, color_t color);
+static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color);
 
 /*
  * Initialize the surface to represent the given drawable.
  *
  */
-void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) {
+void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
+                            xcb_visualtype_t *visual, int width, int height) {
     surface->id = drawable;
+    surface->visual_type = (visual == NULL) ? visual_type : visual;
     surface->width = width;
     surface->height = height;
 
-    surface->gc = xcb_generate_id(xcb_connection);
-    xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection, surface->gc, surface->id, 0, NULL);
+    surface->gc = xcb_generate_id(conn);
+    xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(conn, surface->gc, surface->id, 0, NULL);
 
-    xcb_generic_error_t *error = xcb_request_check(xcb_connection, gc_cookie);
+    xcb_generic_error_t *error = xcb_request_check(conn, gc_cookie);
     if (error != NULL) {
         ELOG("Could not create graphical context. Error code: %d\n", error->error_code);
         exit(EXIT_FAILURE);
     }
 
 #ifdef CAIRO_SUPPORT
-    surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height);
+    surface->surface = cairo_xcb_surface_create(conn, surface->id, surface->visual_type, width, height);
     surface->cr = cairo_create(surface->surface);
 #endif
 }
@@ -51,14 +53,26 @@ void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int wid
  * Destroys the surface.
  *
  */
-void draw_util_surface_free(surface_t *surface) {
-    xcb_free_gc(xcb_connection, surface->gc);
+void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) {
+    xcb_free_gc(conn, surface->gc);
 #ifdef CAIRO_SUPPORT
     cairo_surface_destroy(surface->surface);
     cairo_destroy(surface->cr);
 #endif
 }
 
+/*
+ * Resize the surface to the given size.
+ *
+ */
+void draw_util_surface_set_size(surface_t *surface, int width, int height) {
+    surface->width = width;
+    surface->height = height;
+#ifdef CAIRO_SUPPORT
+    cairo_xcb_surface_set_size(surface->surface, width, height);
+#endif
+}
+
 /*
  * Parses the given color in hex format to an internal color representation.
  * Note that the input must begin with a hash sign, e.g., "#3fbc59".
@@ -77,16 +91,24 @@ color_t draw_util_hex_to_color(const char *color) {
         .colorpixel = get_colorpixel(color)};
 }
 
+color_t draw_util_colorpixel_to_color(uint32_t colorpixel) {
+    return (color_t){
+        .red = ((colorpixel >> 16) & 0xFF) / 255.0,
+        .green = ((colorpixel >> 8) & 0xFF) / 255.0,
+        .blue = (colorpixel & 0xFF) / 255.0,
+        .colorpixel = colorpixel};
+}
+
 /*
  * Set the given color as the source color on the surface.
  *
  */
-static void draw_util_set_source_color(surface_t *surface, color_t color) {
+static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color) {
 #ifdef CAIRO_SUPPORT
     cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue);
 #else
     uint32_t colorpixel = color.colorpixel;
-    xcb_change_gc(xcb_connection, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND,
+    xcb_change_gc(conn, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND,
                   (uint32_t[]){colorpixel, colorpixel});
 #endif
 }
@@ -104,7 +126,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_
 #endif
 
     set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel);
-    draw_text(text, surface->id, surface->gc, visual_type, x, y, max_width);
+    draw_text(text, surface->id, surface->gc, surface->visual_type, x, y, max_width);
 
 #ifdef CAIRO_SUPPORT
     /* Notify cairo that we (possibly) used another way to draw on the surface. */
@@ -118,7 +140,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_
  * surface as well as restoring the cairo state.
  *
  */
-void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) {
+void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h) {
 #ifdef CAIRO_SUPPORT
     cairo_save(surface->cr);
 
@@ -126,7 +148,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y,
      * onto the surface rather than blending it. This is a bit more efficient and
      * allows better color control for the user when using opacity. */
     cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
-    draw_util_set_source_color(surface, color);
+    draw_util_set_source_color(conn, surface, color);
 
     cairo_rectangle(surface->cr, x, y, w, h);
     cairo_fill(surface->cr);
@@ -137,10 +159,10 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y,
 
     cairo_restore(surface->cr);
 #else
-    draw_util_set_source_color(surface, color);
+    draw_util_set_source_color(conn, surface, color);
 
     xcb_rectangle_t rect = {x, y, w, h};
-    xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect);
+    xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect);
 #endif
 }
 
@@ -148,7 +170,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y,
  * Clears a surface with the given color.
  *
  */
-void draw_util_clear_surface(surface_t *surface, color_t color) {
+void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color) {
 #ifdef CAIRO_SUPPORT
     cairo_save(surface->cr);
 
@@ -156,7 +178,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) {
      * onto the surface rather than blending it. This is a bit more efficient and
      * allows better color control for the user when using opacity. */
     cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
-    draw_util_set_source_color(surface, color);
+    draw_util_set_source_color(conn, surface, color);
 
     cairo_paint(surface->cr);
 
@@ -166,10 +188,10 @@ void draw_util_clear_surface(surface_t *surface, color_t color) {
 
     cairo_restore(surface->cr);
 #else
-    draw_util_set_source_color(surface, color);
+    draw_util_set_source_color(conn, surface, color);
 
     xcb_rectangle_t rect = {0, 0, surface->width, surface->height};
-    xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect);
+    xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect);
 #endif
 }
 
@@ -177,7 +199,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) {
  * Copies a surface onto another surface.
  *
  */
-void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
+void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y,
                             double dest_x, double dest_y, double width, double height) {
 #ifdef CAIRO_SUPPORT
     cairo_save(dest->cr);
@@ -198,7 +220,7 @@ void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, doubl
 
     cairo_restore(dest->cr);
 #else
-    xcb_copy_area(xcb_connection, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y,
+    xcb_copy_area(conn, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y,
                   (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height);
 #endif
 }
index bd002d35ca245517372ef7f5f38ec1c7973a60ef..03fd0ee6c163da2c8be3b50c9dd8bf53b4bdd6e1 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -514,7 +514,7 @@ Con *con_by_window_id(xcb_window_t window) {
 Con *con_by_frame_id(xcb_window_t frame) {
     Con *con;
     TAILQ_FOREACH(con, &all_cons, all_cons)
-    if (con->frame == frame)
+    if (con->frame.id == frame)
         return con;
     return NULL;
 }
index e3ddf701161886c1ea6bbff68e5d3615d37c1342..6f08d25c5500f83a9079baef37ec7730e4344c9b 100644 (file)
@@ -656,15 +656,14 @@ static void handle_expose_event(xcb_expose_event_t *event) {
         return;
     }
 
-    /* Since we render to our pixmap on every change anyways, expose events
+    /* Since we render to our surface on every change anyways, expose events
      * only tell us that the X server lost (parts of) the window contents. We
-     * can handle that by copying the appropriate part from our pixmap to the
+     * can handle that by copying the appropriate part from our surface to the
      * window. */
-    xcb_copy_area(conn, parent->pixmap, parent->frame, parent->pm_gc,
-                  event->x, event->y, event->x, event->y,
-                  event->width, event->height);
+    draw_util_copy_surface(conn, &(parent->frame_buffer), &(parent->frame),
+                           event->x, event->y, event->x, event->y,
+                           event->width, event->height);
     xcb_flush(conn);
-
     return;
 }
 
index 59c0014639bacf0ca19450e772219ffe197b157f..1e0ec4a5501c0ca7f4b56a0a114b736089ee9322 100644 (file)
@@ -60,6 +60,7 @@ xcb_window_t root;
  * otherwise the root window’s default (usually 24 bit TrueColor). */
 uint8_t root_depth;
 xcb_visualid_t visual_id;
+xcb_visualtype_t *visual_type;
 xcb_colormap_t colormap;
 
 struct ev_loop *main_loop;
@@ -487,6 +488,7 @@ int main(int argc, char *argv[]) {
      * transparency) and use it if so. */
     root_depth = root_screen->root_depth;
     visual_id = root_screen->root_visual;
+    visual_type = get_visualtype(root_screen);
     colormap = root_screen->default_colormap;
 
     DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id);
index 87d19ff841608954c1797cb6e07cd05ccce3269d..05ac15f052960ed5ed83b3023eb3fce4c007a366 100644 (file)
@@ -473,7 +473,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     values[0] = XCB_NONE;
     xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
 
-    xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0);
+    xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame.id, 0, 0);
     if (xcb_request_check(conn, rcookie) != NULL) {
         LOG("Could not reparent the window, aborting\n");
         goto geom_out;
diff --git a/src/x.c b/src/x.c
index 094a33f0feb07955ee1b8299b5221dc50432f8f1..86a0c79738323f581c9d1957ffa243e6c4771ca9 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -144,10 +144,11 @@ void x_con_init(Con *con, uint16_t depth) {
     }
 
     Rect dims = {-15, -15, 10, 10};
-    con->frame = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
+    xcb_window_t frame_id = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
+    draw_util_surface_init(conn, &(con->frame), frame_id, get_visualtype_by_id(visual), dims.width, dims.height);
     xcb_change_property(conn,
                         XCB_PROP_MODE_REPLACE,
-                        con->frame,
+                        con->frame.id,
                         XCB_ATOM_WM_CLASS,
                         XCB_ATOM_STRING,
                         8,
@@ -158,7 +159,7 @@ void x_con_init(Con *con, uint16_t depth) {
         xcb_free_colormap(conn, win_colormap);
 
     struct con_state *state = scalloc(1, sizeof(struct con_state));
-    state->id = con->frame;
+    state->id = con->frame.id;
     state->mapped = false;
     state->initial = true;
     DLOG("Adding window 0x%08x to lists\n", state->id);
@@ -177,7 +178,7 @@ void x_con_init(Con *con, uint16_t depth) {
 void x_reinit(Con *con) {
     struct con_state *state;
 
-    if ((state = state_for_frame(con->frame)) == NULL) {
+    if ((state = state_for_frame(con->frame.id)) == NULL) {
         ELOG("window state not found\n");
         return;
     }
@@ -196,13 +197,13 @@ void x_reinit(Con *con) {
  */
 void x_reparent_child(Con *con, Con *old) {
     struct con_state *state;
-    if ((state = state_for_frame(con->frame)) == NULL) {
+    if ((state = state_for_frame(con->frame.id)) == NULL) {
         ELOG("window state for con not found\n");
         return;
     }
 
     state->need_reparent = true;
-    state->old_frame = old->frame;
+    state->old_frame = old->frame.id;
 }
 
 /*
@@ -212,12 +213,12 @@ void x_reparent_child(Con *con, Con *old) {
 void x_move_win(Con *src, Con *dest) {
     struct con_state *state_src, *state_dest;
 
-    if ((state_src = state_for_frame(src->frame)) == NULL) {
+    if ((state_src = state_for_frame(src->frame.id)) == NULL) {
         ELOG("window state for src not found\n");
         return;
     }
 
-    if ((state_dest = state_for_frame(dest->frame)) == NULL) {
+    if ((state_dest = state_for_frame(dest->frame.id)) == NULL) {
         ELOG("window state for dest not found\n");
         return;
     }
@@ -239,10 +240,11 @@ void x_move_win(Con *src, Con *dest) {
 void x_con_kill(Con *con) {
     con_state *state;
 
-    xcb_destroy_window(conn, con->frame);
-    xcb_free_pixmap(conn, con->pixmap);
-    xcb_free_gc(conn, con->pm_gc);
-    state = state_for_frame(con->frame);
+    draw_util_surface_free(conn, &(con->frame));
+    draw_util_surface_free(conn, &(con->frame_buffer));
+    xcb_destroy_window(conn, con->frame.id);
+    xcb_free_pixmap(conn, con->frame_buffer.id);
+    state = state_for_frame(con->frame.id);
     CIRCLEQ_REMOVE(&state_head, state, state);
     CIRCLEQ_REMOVE(&old_state_head, state, old_state);
     TAILQ_REMOVE(&initial_mapping_head, state, initial_mapping_order);
@@ -312,6 +314,54 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
     free(event);
 }
 
+static void x_draw_decoration_border(Con *con, struct deco_render_params *p) {
+    assert(con->parent != NULL);
+
+    Rect *dr = &(con->deco_rect);
+    adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
+    int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
+    int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width;
+    if (con->parent->layout == L_TABBED ||
+        (con->parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
+        deco_diff_l = 0;
+        deco_diff_r = 0;
+    }
+
+    draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border),
+                        dr->x, dr->y, dr->width, 1);
+
+    draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border),
+                        dr->x + deco_diff_l, dr->y + dr->height - 1, dr->width - (deco_diff_l + deco_diff_r), 1);
+}
+
+static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p) {
+    assert(con->parent != NULL);
+
+    Rect *dr = &(con->deco_rect);
+    Rect br = con_border_style_rect(con);
+
+    /* Redraw the right border to cut off any text that went past it.
+     * This is necessary when the text was drawn using XCB since cutting text off
+     * automatically does not work there. For pango rendering, this isn't necessary. */
+    draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                        dr->x + dr->width + br.width, dr->y, -br.width, dr->height);
+
+    /* Draw a 1px separator line before and after every tab, so that tabs can
+     * be easily distinguished. */
+    if (con->parent->layout == L_TABBED) {
+        /* Left side */
+        draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border),
+                            dr->x, dr->y, 1, dr->height);
+
+        /* Right side */
+        draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border),
+                            dr->x + dr->width - 1, dr->y, 1, dr->height);
+    }
+
+    /* Redraw the border. */
+    x_draw_decoration_border(con, p);
+}
+
 /*
  * Draws the decoration of the given container onto its parent.
  *
@@ -343,7 +393,7 @@ void x_draw_decoration(Con *con) {
     /* Skip containers whose pixmap has not yet been created (can happen when
      * decoration rendering happens recursively for a window for which
      * x_push_node() was not yet called) */
-    if (leaf && con->pixmap == XCB_NONE)
+    if (leaf && con->frame_buffer.id == XCB_NONE)
         return;
 
     /* 1: build deco_params and compare with cache */
@@ -395,29 +445,20 @@ void x_draw_decoration(Con *con) {
     con->pixmap_recreated = false;
     con->mark_changed = false;
 
-    /* 2: draw the client.background, but only for the parts around the client_rect */
+    /* 2: draw the client.background, but only for the parts around the window_rect */
     if (con->window != NULL) {
-        xcb_rectangle_t background[] = {
-            /* top area */
-            {0, 0, r->width, w->y},
-            /* bottom area */
-            {0, (w->y + w->height), r->width, r->height - (w->y + w->height)},
-            /* left area */
-            {0, 0, w->x, r->height},
-            /* right area */
-            {w->x + w->width, 0, r->width - (w->x + w->width), r->height}};
-#if 0
-        for (int i = 0; i < 4; i++)
-            DLOG("rect is (%d, %d) with %d x %d\n",
-                    background[i].x,
-                    background[i].y,
-                    background[i].width,
-                    background[i].height
-                );
-#endif
-
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){config.client.background});
-        xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
+        /* top area */
+        draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background),
+                            0, 0, r->width, w->y);
+        /* bottom area */
+        draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background),
+                            0, w->y + w->height, r->width, r->height - (w->y + w->height));
+        /* left area */
+        draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background),
+                            0, 0, w->x, r->height);
+        /* right area */
+        draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background),
+                            w->x + w->width, 0, r->width - (w->x + w->width), r->height);
     }
 
     /* 3: draw a rectangle in border color around the client */
@@ -433,27 +474,26 @@ void x_draw_decoration(Con *con) {
         DLOG("window_rect spans (%d, %d) with %d x %d\n", con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height);
 #endif
 
-        /* These rectangles represents the border around the child window
+        /* These rectangles represent the border around the child window
          * (left, bottom and right part). We don’t just fill the whole
          * rectangle because some childs are not freely resizable and we want
          * their background color to "shine through". */
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
         if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
-            xcb_rectangle_t leftline = {0, 0, br.x, r->height};
-            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
+            draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                                0, 0, br.x, r->height);
         }
         if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
-            xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height};
-            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
+            draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                                r->width + (br.width + br.x), 0, -(br.width + br.x), r->height);
         }
         if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
-            xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)};
-            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
+            draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                                br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y));
         }
-        /* 1pixel border needs an additional line at the top */
+        /* pixel border needs an additional line at the top */
         if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
-            xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y};
-            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
+            draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                                br.x, 0, r->width + br.width, br.y);
         }
 
         /* Highlight the side of the border at which the next window will be
@@ -463,13 +503,13 @@ void x_draw_decoration(Con *con) {
         if (TAILQ_NEXT(con, nodes) == NULL &&
             TAILQ_PREV(con, nodes_head, nodes) == NULL &&
             con->parent->type != CT_FLOATING_CON) {
-            xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->indicator});
-            if (p->parent_layout == L_SPLITH)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
-                                                                              {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
-            else if (p->parent_layout == L_SPLITV)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
-                                                                              {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
+            if (p->parent_layout == L_SPLITH) {
+                draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->indicator),
+                                    r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height);
+            } else if (p->parent_layout == L_SPLITV) {
+                draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->indicator),
+                                    br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y));
+            }
         }
     }
 
@@ -478,31 +518,19 @@ void x_draw_decoration(Con *con) {
     if (p->border_style != BS_NORMAL)
         goto copy_pixmaps;
 
+    /* If the parent hasn't been set up yet, skip the decoratin rendering
+     * for now. */
+    if (parent->frame_buffer.id == XCB_NONE)
+        goto copy_pixmaps;
+
     /* 4: paint the bar */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
-    xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height};
-    xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect);
+    draw_util_rectangle(conn, &(parent->frame_buffer), draw_util_colorpixel_to_color(p->color->background),
+                        con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height);
 
     /* 5: draw two unconnected horizontal lines in border color */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
-    Rect *dr = &(con->deco_rect);
-    adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
-    int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
-    int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width;
-    if (parent->layout == L_TABBED ||
-        (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
-        deco_diff_l = 0;
-        deco_diff_r = 0;
-    }
-    xcb_segment_t segments[] = {
-        {dr->x, dr->y,
-         dr->x + dr->width - 1, dr->y},
-        {dr->x + deco_diff_l, dr->y + dr->height - 1,
-         dr->x - deco_diff_r + dr->width - 1, dr->y + dr->height - 1}};
-    xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
+    x_draw_decoration_border(con, p);
 
     /* 6: draw the title */
-    set_font_colors(parent->pm_gc, p->color->text, p->color->background);
     int text_offset_y = (con->deco_rect.height - config.font.height) / 2;
 
     struct Window *win = con->window;
@@ -510,16 +538,18 @@ void x_draw_decoration(Con *con) {
         /* we have a split container which gets a representation
          * of its children as title
          */
-        char *title;
+        char *_title;
         char *tree = con_get_tree_representation(con);
-        sasprintf(&title, "i3: %s", tree);
+        sasprintf(&_title, "i3: %s", tree);
         free(tree);
 
-        draw_text_ascii(title,
-                        parent->pixmap, parent->pm_gc,
-                        con->deco_rect.x + 2, con->deco_rect.y + text_offset_y,
-                        con->deco_rect.width - 2);
-        free(title);
+        i3String *title = i3string_from_utf8(_title);
+        draw_util_text(title, &(parent->frame_buffer),
+                       draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background),
+                       con->deco_rect.x + 2, con->deco_rect.y + text_offset_y,
+                       con->deco_rect.width - 2);
+        FREE(_title);
+        I3STRING_FREE(title);
 
         goto after_title;
     }
@@ -565,9 +595,10 @@ void x_draw_decoration(Con *con) {
             i3String *mark = i3string_from_utf8(formatted_mark);
             mark_width = predict_text_width(mark);
 
-            draw_text(mark, parent->pixmap, parent->pm_gc, NULL,
-                      con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
-                      con->deco_rect.y + text_offset_y, mark_width);
+            draw_util_text(mark, &(parent->frame_buffer),
+                           draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background),
+                           con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
+                           con->deco_rect.y + text_offset_y, mark_width);
 
             I3STRING_FREE(mark);
         }
@@ -576,41 +607,17 @@ void x_draw_decoration(Con *con) {
     }
 
     i3String *title = win->title_format == NULL ? win->name : window_parse_title_format(win);
-    draw_text(title,
-              parent->pixmap, parent->pm_gc, NULL,
-              con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y,
-              con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2));
+    draw_util_text(title, &(parent->frame_buffer),
+                   draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background),
+                   con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y,
+                   con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2));
     if (win->title_format != NULL)
         I3STRING_FREE(title);
 
 after_title:
-    /* Since we don’t clip the text at all, it might in some cases be painted
-     * on the border pixels on the right side of a window. Therefore, we draw
-     * the right border again after rendering the text (and the unconnected
-     * lines in border color). */
-
-    /* Draw a 1px separator line before and after every tab, so that tabs can
-     * be easily distinguished. */
-    if (parent->layout == L_TABBED) {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
-    } else {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
-    }
-    xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6,
-                  (xcb_point_t[]){
-                      {dr->x + dr->width, dr->y},
-                      {dr->x + dr->width, dr->y + dr->height},
-                      {dr->x + dr->width - 1, dr->y},
-                      {dr->x + dr->width - 1, dr->y + dr->height},
-                      {dr->x, dr->y + dr->height},
-                      {dr->x, dr->y},
-                  });
-
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
-    xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
-
+    x_draw_decoration_after_title(con, p);
 copy_pixmaps:
-    xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+    draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
 }
 
 /*
@@ -623,7 +630,7 @@ void x_deco_recurse(Con *con) {
     Con *current;
     bool leaf = TAILQ_EMPTY(&(con->nodes_head)) &&
                 TAILQ_EMPTY(&(con->floating_head));
-    con_state *state = state_for_frame(con->frame);
+    con_state *state = state_for_frame(con->frame.id);
 
     if (!leaf) {
         TAILQ_FOREACH(current, &(con->nodes_head), nodes)
@@ -632,8 +639,9 @@ void x_deco_recurse(Con *con) {
         TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
         x_deco_recurse(current);
 
-        if (state->mapped)
-            xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+        if (state->mapped) {
+            draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
+        }
     }
 
     if ((con->type != CT_ROOT && con->type != CT_OUTPUT) &&
@@ -650,7 +658,7 @@ static void set_hidden_state(Con *con) {
         return;
     }
 
-    con_state *state = state_for_frame(con->frame);
+    con_state *state = state_for_frame(con->frame.id);
     bool should_be_hidden = con_is_hidden(con);
     if (should_be_hidden == state->is_hidden)
         return;
@@ -678,12 +686,12 @@ void x_push_node(Con *con) {
     Rect rect = con->rect;
 
     //DLOG("Pushing changes for node %p / %s\n", con, con->name);
-    state = state_for_frame(con->frame);
+    state = state_for_frame(con->frame.id);
 
     if (state->name != NULL) {
         DLOG("pushing name %s for con %p\n", state->name, con);
 
-        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame,
+        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame.id,
                             XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(state->name), state->name);
         FREE(state->name);
     }
@@ -716,7 +724,7 @@ void x_push_node(Con *con) {
         xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values);
         xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
 
-        xcb_reparent_window(conn, con->window->id, con->frame, 0, 0);
+        xcb_reparent_window(conn, con->window->id, con->frame.id, 0, 0);
 
         values[0] = FRAME_EVENT_MASK;
         xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values);
@@ -741,8 +749,8 @@ void x_push_node(Con *con) {
     bool fake_notify = false;
     /* Set new position if rect changed (and if height > 0) or if the pixmap
      * needs to be recreated */
-    if ((is_pixmap_needed && con->pixmap == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 &&
-                                                          rect.height > 0)) {
+    if ((is_pixmap_needed && con->frame_buffer.id == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 &&
+                                                                   rect.height > 0)) {
         /* We first create the new pixmap, then render to it, set it as the
          * background and only afterwards change the window size. This reduces
          * flickering. */
@@ -755,38 +763,47 @@ void x_push_node(Con *con) {
 
         /* Check if the container has an unneeded pixmap left over from
          * previously having a border or titlebar. */
-        if (!is_pixmap_needed && con->pixmap != XCB_NONE) {
-            xcb_free_pixmap(conn, con->pixmap);
-            con->pixmap = XCB_NONE;
+        if (!is_pixmap_needed && con->frame_buffer.id != XCB_NONE) {
+            draw_util_surface_free(conn, &(con->frame_buffer));
+            xcb_free_pixmap(conn, con->frame_buffer.id);
+            con->frame_buffer.id = XCB_NONE;
         }
 
-        if (is_pixmap_needed && (has_rect_changed || con->pixmap == XCB_NONE)) {
-            if (con->pixmap == 0) {
-                con->pixmap = xcb_generate_id(conn);
-                con->pm_gc = xcb_generate_id(conn);
+        if (is_pixmap_needed && (has_rect_changed || con->frame_buffer.id == XCB_NONE)) {
+            if (con->frame_buffer.id == XCB_NONE) {
+                con->frame_buffer.id = xcb_generate_id(conn);
             } else {
-                xcb_free_pixmap(conn, con->pixmap);
-                xcb_free_gc(conn, con->pm_gc);
+                draw_util_surface_free(conn, &(con->frame_buffer));
+                xcb_free_pixmap(conn, con->frame_buffer.id);
             }
 
             uint16_t win_depth = root_depth;
             if (con->window)
                 win_depth = con->window->depth;
 
-            xcb_create_pixmap(conn, win_depth, con->pixmap, con->frame, rect.width, rect.height);
+            /* Ensure we have valid dimensions for our surface. */
+            // TODO This is probably a bug in the condition above as we should never enter this path
+            //      for height == 0. Also, we should probably handle width == 0 the same way.
+            int width = MAX(rect.width, 1);
+            int height = MAX(rect.height, 1);
+
+            xcb_create_pixmap_checked(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height);
+            draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id,
+                                   get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height);
 
             /* For the graphics context, we disable GraphicsExposure events.
              * Those will be sent when a CopyArea request cannot be fulfilled
              * properly due to parts of the source being unmapped or otherwise
              * unavailable. Since we always copy from pixmaps to windows, this
              * is not a concern for us. */
-            uint32_t values[] = {0};
-            xcb_create_gc(conn, con->pm_gc, con->pixmap, XCB_GC_GRAPHICS_EXPOSURES, values);
+            xcb_change_gc(conn, con->frame_buffer.gc, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
 
+            draw_util_surface_set_size(&(con->frame), width, height);
             con->pixmap_recreated = true;
 
             /* Don’t render the decoration for windows inside a stack which are
              * not visible right now */
+            // TODO Should this work the same way for L_TABBED?
             if (!con->parent ||
                 con->parent->layout != L_STACKED ||
                 TAILQ_FIRST(&(con->parent->focus_head)) == con)
@@ -802,9 +819,10 @@ void x_push_node(Con *con) {
          * window get lost when resizing it, therefore we want to provide it as
          * fast as possible) */
         xcb_flush(conn);
-        xcb_set_window_rect(conn, con->frame, rect);
-        if (con->pixmap != XCB_NONE)
-            xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+        xcb_set_window_rect(conn, con->frame.id, rect);
+        if (con->frame_buffer.id != XCB_NONE) {
+            draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
+        }
         xcb_flush(conn);
 
         memcpy(&(state->rect), &rect, sizeof(Rect));
@@ -848,17 +866,18 @@ void x_push_node(Con *con) {
             state->child_mapped = true;
         }
 
-        cookie = xcb_map_window(conn, con->frame);
+        cookie = xcb_map_window(conn, con->frame.id);
 
         values[0] = FRAME_EVENT_MASK;
-        xcb_change_window_attributes(conn, con->frame, XCB_CW_EVENT_MASK, values);
+        xcb_change_window_attributes(conn, con->frame.id, XCB_CW_EVENT_MASK, values);
 
         /* copy the pixmap contents to the frame window immediately after mapping */
-        if (con->pixmap != XCB_NONE)
-            xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+        if (con->frame_buffer.id != XCB_NONE) {
+            draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
+        }
         xcb_flush(conn);
 
-        DLOG("mapping container %08x (serial %d)\n", con->frame, cookie.sequence);
+        DLOG("mapping container %08x (serial %d)\n", con->frame.id, cookie.sequence);
         state->mapped = con->mapped;
     }
 
@@ -892,7 +911,7 @@ static void x_push_node_unmaps(Con *con) {
     con_state *state;
 
     //DLOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name);
-    state = state_for_frame(con->frame);
+    state = state_for_frame(con->frame.id);
 
     /* map/unmap if map state changed, also ensure that the child window
      * is changed if we are mapped *and* in initial state (meaning the
@@ -906,14 +925,14 @@ static void x_push_node_unmaps(Con *con) {
                                 A_WM_STATE, A_WM_STATE, 32, 2, data);
         }
 
-        cookie = xcb_unmap_window(conn, con->frame);
+        cookie = xcb_unmap_window(conn, con->frame.id);
         DLOG("unmapping container %p / %s (serial %d)\n", con, con->name, cookie.sequence);
         /* we need to increase ignore_unmap for this container (if it
          * contains a window) and for every window "under" this one which
          * contains a window */
         if (con->window != NULL) {
             con->ignore_unmap++;
-            DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, con->ignore_unmap);
+            DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame.id, con->ignore_unmap);
         }
         state->mapped = con->mapped;
     }
@@ -1067,7 +1086,7 @@ void x_push_changes(Con *con) {
 
     x_deco_recurse(con);
 
-    xcb_window_t to_focus = focused->frame;
+    xcb_window_t to_focus = focused->frame.id;
     if (focused->window != NULL)
         to_focus = focused->window->id;
 
@@ -1161,7 +1180,7 @@ void x_push_changes(Con *con) {
  */
 void x_raise_con(Con *con) {
     con_state *state;
-    state = state_for_frame(con->frame);
+    state = state_for_frame(con->frame.id);
     //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id);
 
     CIRCLEQ_REMOVE(&state_head, state, state);
@@ -1177,7 +1196,7 @@ void x_raise_con(Con *con) {
 void x_set_name(Con *con, const char *name) {
     struct con_state *state;
 
-    if ((state = state_for_frame(con->frame)) == NULL) {
+    if ((state = state_for_frame(con->frame.id)) == NULL) {
         ELOG("window state not found\n");
         return;
     }
index 90d591c746303efa43d3be56cec2c51867b6449d..60fd421239480b1939a0c18dc83544007ad706e2 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -252,6 +252,22 @@ uint16_t get_visual_depth(xcb_visualid_t visual_id) {
     }
     return 0;
 }
+xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id) {
+    xcb_depth_iterator_t depth_iter;
+
+    depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
+    for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
+        xcb_visualtype_iterator_t visual_iter;
+
+        visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
+        for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
+            if (visual_id == visual_iter.data->visual_id) {
+                return visual_iter.data;
+            }
+        }
+    }
+    return 0;
+}
 
 /*
  * Get visualid with specified depth