]> git.sur5r.net Git - i3/i3/commitdiff
Re-implement rendering to pixmaps (double-buffering) and caching decorations
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 20 Mar 2011 13:07:16 +0000 (14:07 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 20 Mar 2011 13:25:09 +0000 (14:25 +0100)
include/data.h
include/xcb.h
src/tree.c
src/window.c
src/x.c
src/xcb.c

index 088561c416c54246ee0706682bdc2493d51762ed..58856c3826fcb6707a4a1dfc257303553b64304c 100644 (file)
@@ -86,6 +86,33 @@ struct reservedpx {
     uint32_t bottom;
 };
 
+/**
+ * Stores a width/height pair, used as part of deco_render_params to check
+ * whether the rects width/height have changed.
+ *
+ */
+struct width_height {
+    uint32_t w;
+    uint32_t h;
+};
+
+/**
+ * Stores the parameters for rendering a window decoration. This structure is
+ * cached in every Con and no re-rendering will be done if the parameters have
+ * not changed (only the pixmaps will be copied).
+ *
+ */
+struct deco_render_params {
+    struct Colortriple *color;
+    int border_style;
+    struct width_height con_rect;
+    struct width_height con_window_rect;
+    struct width_height con_deco_rect;
+    uint32_t background;
+    bool con_is_leaf;
+    xcb_font_t font;
+};
+
 /**
  * Used for the cache of colorpixels.
  *
@@ -96,22 +123,6 @@ struct Colorpixel {
     SLIST_ENTRY(Colorpixel) colorpixels;
 };
 
-struct Cached_Pixmap {
-    xcb_pixmap_t id;
-
-    /* We’re going to paint on it, so a graphics context will be needed */
-    xcb_gcontext_t gc;
-
-    /* The rect with which the pixmap was created */
-    Rect rect;
-
-    /* The rect of the object to which this pixmap belongs. Necessary to
-     * find out when we need to re-create the pixmap. */
-    Rect *referred_rect;
-
-    xcb_drawable_t referred_drawable;
-};
-
 struct Ignore_Event {
     int sequence;
     time_t added;
@@ -236,6 +247,9 @@ struct Window {
      * application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */
     char *name_x;
 
+    /** Flag to force re-rendering the decoration upon changes */
+    bool name_x_changed;
+
     /** The name of the window as used in JSON (in UTF-8 if the application
      * supports _NET_WM_NAME, in COMPOUND_TEXT otherwise) */
     char *name_json;
@@ -353,9 +367,14 @@ struct Con {
      * inside this container (if any) sets the urgency hint, for example. */
     bool urgent;
 
-    /* ids/gc for the frame window */
+    /* ids/pixmap/graphics context for the frame window */
     xcb_window_t frame;
-    xcb_gcontext_t gc;
+    xcb_pixmap_t pixmap;
+    xcb_gcontext_t pm_gc;
+    bool pixmap_recreated;
+
+    /** Cache for the decoration rendering */
+    struct deco_render_params *deco_render_params;
 
     /* Only workspace-containers can have floating clients */
     TAILQ_HEAD(floating_head, Con) floating_head;
index 4a09766fe478aeb4bd49e842489de15f8de3fcc4..673b5b392a88872450e6bee5cdee00241821cef9 100644 (file)
@@ -135,15 +135,6 @@ void xcb_get_numlock_mask(xcb_connection_t *conn);
  */
 void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
 
-/**
- *
- * Prepares the given Cached_Pixmap for usage (checks whether the size of the
- * object this pixmap is related to (e.g. a window) has changed and re-creates
- * the pixmap if so).
- *
- */
-void cached_pixmap_prepare(xcb_connection_t *conn, struct Cached_Pixmap *pixmap);
-
 /**
  * Calculate the width of the given text (16-bit characters, UCS) with given
  * real length (amount of glyphs) using the given font.
index a66e0f4946d2edde9c8f3ee28e09de71f894d3ca..b68af36b6d62837ead529e02251fdd203c110495 100644 (file)
@@ -167,6 +167,7 @@ void tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
     }
 
     free(con->name);
+    FREE(con->deco_render_params);
     TAILQ_REMOVE(&all_cons, con, all_cons);
     free(con);
 
index ffc73cd0160eeb1c97e5236234d0c7328eb76787..cd96890cb8baf972b62ab30424be15f66efb8671 100644 (file)
@@ -66,6 +66,7 @@ void window_update_name(i3Window *win, xcb_get_property_reply_t *prop) {
     win->name_json = new_name;
     win->name_x = ucs2_name;
     win->name_len = len;
+    win->name_x_changed = true;
     LOG("_NET_WM_NAME changed to \"%s\"\n", win->name_json);
 
     win->uses_net_wm_name = true;
@@ -104,6 +105,7 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop) {
     win->name_x = new_name;
     win->name_json = sstrdup(new_name);
     win->name_len = strlen(new_name);
+    win->name_x_changed = true;
 }
 
 /*
diff --git a/src/x.c b/src/x.c
index 444b39ac1eec6cf569e043fb2d038dd61f15e236..0fcb74ee5245d03c1755272da3d0636db6693527 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -84,8 +84,6 @@ void x_con_init(Con *con) {
 
     Rect dims = { -15, -15, 10, 10 };
     con->frame = create_window(conn, dims, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
-    con->gc = xcb_generate_id(conn);
-    xcb_create_gc(conn, con->gc, con->frame, 0, 0);
 
     struct con_state *state = scalloc(sizeof(struct con_state));
     state->id = con->frame;
@@ -230,6 +228,7 @@ void x_window_kill(xcb_window_t window) {
  *
  */
 void x_draw_decoration(Con *con) {
+    const Con *parent = con->parent;
     /* This code needs to run for:
      *  • leaf containers
      *  • non-leaf containers which are in a stacked/tabbed container
@@ -238,30 +237,61 @@ void x_draw_decoration(Con *con) {
      *  • floating containers (they don’t have a decoration)
      */
     if ((!con_is_leaf(con) &&
-         con->parent->layout != L_STACKED &&
-         con->parent->layout != L_TABBED) ||
+         parent->layout != L_STACKED &&
+         parent->layout != L_TABBED) ||
         con->type == CT_FLOATING_CON)
         return;
     DLOG("decoration should be rendered for con %p\n", con);
 
-    /* 1: find out which colors to use */
-    struct Colortriple *color;
+    /* Skip containers whose height is 0 (for example empty dockareas) */
+    if (con->rect.height == 0) {
+        DLOG("height == 0, not rendering\n");
+        return;
+    }
+
+    /* 1: build deco_params and compare with cache */
+    struct deco_render_params *p = scalloc(sizeof(struct deco_render_params));
+
+    /* find out which colors to use */
     if (con->urgent)
-        color = &config.client.urgent;
+        p->color = &config.client.urgent;
     else if (con == focused)
-        color = &config.client.focused;
-    else if (con == TAILQ_FIRST(&(con->parent->focus_head)))
-        color = &config.client.focused_inactive;
+        p->color = &config.client.focused;
+    else if (con == TAILQ_FIRST(&(parent->focus_head)))
+        p->color = &config.client.focused_inactive;
     else
-        color = &config.client.unfocused;
+        p->color = &config.client.unfocused;
 
-    Con *parent = con->parent;
-    int border_style = con_border_style(con);
+    p->border_style = con_border_style(con);
 
-    /* 2: draw the client.background, but only for the parts around the client_rect */
     Rect *r = &(con->rect);
     Rect *w = &(con->window_rect);
+    p->con_rect = (struct width_height){ r->width, r->height };
+    p->con_window_rect = (struct width_height){ w->width, w->height };
+    p->con_deco_rect = (struct width_height){ con->deco_rect.width, con->deco_rect.height };
+    p->background = config.client.background;
+    p->con_is_leaf = con_is_leaf(con);
+    p->font = config.font.id;
+
+    if (con->deco_render_params != NULL &&
+        (con->window == NULL || !con->window->name_x_changed) &&
+        !con->parent->pixmap_recreated &&
+        memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) {
+        DLOG("CACHE HIT, copying existing pixmaps\n");
+        free(p);
+        goto copy_pixmaps;
+    }
+
+    DLOG("CACHE MISS\n");
+    FREE(con->deco_render_params);
+    con->deco_render_params = p;
+
+    if (con->window != NULL && con->window->name_x_changed)
+        con->window->name_x_changed = false;
+
+    con->parent->pixmap_recreated = false;
 
+    /* 2: draw the client.background, but only for the parts around the client_rect */
     xcb_rectangle_t background[] = {
         /* top area */
         { 0, con->deco_rect.height, r->width, w->y },
@@ -280,11 +310,11 @@ void x_draw_decoration(Con *con) {
             );
 #endif
 
-    xcb_change_gc_single(conn, con->gc, XCB_GC_FOREGROUND, config.client.background);
-    xcb_poly_fill_rectangle(conn, con->frame, con->gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
+    xcb_change_gc_single(conn, con->pm_gc, XCB_GC_FOREGROUND, config.client.background);
+    xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
 
     /* 3: draw a rectangle in border color around the client */
-    if (border_style != BS_NONE && con_is_leaf(con)) {
+    if (p->border_style != BS_NONE && p->con_is_leaf) {
         Rect br = con_border_style_rect(con);
 #if 0
         DLOG("con->rect spans %d x %d\n", con->rect.width, con->rect.height);
@@ -296,48 +326,53 @@ void x_draw_decoration(Con *con) {
          * (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_single(conn, con->gc, XCB_GC_FOREGROUND, color->background);
+        xcb_change_gc_single(conn, con->pm_gc, XCB_GC_FOREGROUND, p->color->background);
         xcb_rectangle_t borders[] = {
             { 0, 0, br.x, r->height },
             { 0, r->height + br.height + br.y, r->width, r->height },
             { r->width + br.width + br.x, 0, r->width, r->height }
         };
-        xcb_poly_fill_rectangle(conn, con->frame, con->gc, 3, borders);
+        xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 3, borders);
         /* 1pixel border needs an additional line at the top */
-        if (border_style == BS_1PIXEL) {
+        if (p->border_style == BS_1PIXEL) {
             xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y };
-            xcb_poly_fill_rectangle(conn, con->frame, con->gc, 1, &topline);
+            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
         }
     }
 
     /* if this is a borderless/1pixel window, we don’t * need to render the
      * decoration. */
-    if (border_style != BS_NORMAL) {
+    if (p->border_style != BS_NORMAL) {
         DLOG("border style not BS_NORMAL, aborting rendering of decoration\n");
-        return;
+        goto copy_pixmaps;
     }
 
     /* 4: paint the bar */
-    xcb_change_gc_single(conn, parent->gc, XCB_GC_FOREGROUND, color->background);
+    xcb_change_gc_single(conn, parent->pm_gc, XCB_GC_FOREGROUND, p->color->background);
+            free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
     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->frame, parent->gc, 1, &drect);
+            free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
+    xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect);
+            free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
 
     /* 5: draw the two lines in border color */
-    xcb_draw_line(conn, parent->frame, parent->gc, color->border,
+    xcb_draw_line(conn, parent->pixmap, parent->pm_gc, p->color->border,
             con->deco_rect.x, /* x */
             con->deco_rect.y, /* y */
             con->deco_rect.x + con->deco_rect.width, /* to_x */
             con->deco_rect.y); /* to_y */
-    xcb_draw_line(conn, parent->frame, parent->gc, color->border,
+            free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
+    xcb_draw_line(conn, parent->pixmap, parent->pm_gc, p->color->border,
             con->deco_rect.x, /* x */
             con->deco_rect.y + con->deco_rect.height - 1, /* y */
             con->deco_rect.x + con->deco_rect.width, /* to_x */
             con->deco_rect.y + con->deco_rect.height - 1); /* to_y */
+            free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
 
     /* 6: draw the title */
     uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
-    uint32_t values[] = { color->text, color->background, config.font.id };
-    xcb_change_gc(conn, parent->gc, mask, values);
+    uint32_t values[] = { p->color->text, p->color->background, config.font.id };
+    xcb_change_gc(conn, parent->pm_gc, mask, values);
     int text_offset_y = config.font.height + (con->deco_rect.height - config.font.height) / 2 - 1;
 
     struct Window *win = con->window;
@@ -347,13 +382,14 @@ void x_draw_decoration(Con *con) {
         xcb_image_text_8(
             conn,
             strlen("another container"),
-            parent->frame,
-            parent->gc,
+            parent->pixmap,
+            parent->pm_gc,
             con->deco_rect.x + 2,
             con->deco_rect.y + text_offset_y,
             "another container"
         );
-        return;
+
+        goto copy_pixmaps;
     }
 
     int indent_level = 0,
@@ -377,8 +413,8 @@ void x_draw_decoration(Con *con) {
         xcb_image_text_16(
             conn,
             win->name_len,
-            parent->frame,
-            parent->gc,
+            parent->pixmap,
+            parent->pm_gc,
             con->deco_rect.x + 2 + indent_px,
             con->deco_rect.y + text_offset_y,
             (xcb_char2b_t*)win->name_x
@@ -387,12 +423,16 @@ void x_draw_decoration(Con *con) {
         xcb_image_text_8(
             conn,
             win->name_len,
-            parent->frame,
-            parent->gc,
+            parent->pixmap,
+            parent->pm_gc,
             con->deco_rect.x + 2 + indent_px,
             con->deco_rect.y + text_offset_y,
             win->name_x
         );
+
+copy_pixmaps:
+    xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
+    xcb_copy_area(conn, parent->pixmap, parent->frame, parent->pm_gc, 0, 0, 0, 0, parent->rect.width, parent->rect.height);
 }
 
 /*
@@ -467,6 +507,25 @@ static void x_push_node(Con *con) {
     if (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0) {
         DLOG("setting rect (%d, %d, %d, %d)\n", rect.x, rect.y, rect.width, rect.height);
         xcb_set_window_rect(conn, con->frame, rect);
+
+        /* As the pixmap only depends on the size and not on the position, it
+         * is enough to check if width/height have changed */
+        if (state->rect.width != rect.width ||
+            state->rect.height != rect.height) {
+            DLOG("CACHE: creating new pixmap\n");
+            if (con->pixmap == 0) {
+                con->pixmap = xcb_generate_id(conn);
+                con->pm_gc = xcb_generate_id(conn);
+            } else {
+                xcb_free_pixmap(conn, con->pixmap);
+                xcb_free_gc(conn, con->pm_gc);
+            }
+            xcb_create_pixmap(conn, root_depth, con->pixmap, con->frame, rect.width, rect.height);
+                free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
+            xcb_create_gc(conn, con->pm_gc, con->pixmap, 0, 0);
+                free(xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL));
+            con->pixmap_recreated = true;
+        }
         memcpy(&(state->rect), &rect, sizeof(Rect));
         fake_notify = true;
     }
index 7382cc615efe91f718890c812e2b9e38e91a86f3..a7758ad3552f5232fa6adc30beacdf4694d6b8e0 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -289,38 +289,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window) {
     xcb_configure_window(conn, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
 }
 
-/*
- *
- * Prepares the given Cached_Pixmap for usage (checks whether the size of the
- * object this pixmap is related to (e.g. a window) has changed and re-creates
- * the pixmap if so).
- *
- */
-void cached_pixmap_prepare(xcb_connection_t *conn, struct Cached_Pixmap *pixmap) {
-    DLOG("preparing pixmap\n");
-
-    /* If the Rect did not change, the pixmap does not need to be recreated */
-    if (memcmp(&(pixmap->rect), pixmap->referred_rect, sizeof(Rect)) == 0)
-        return;
-
-    memcpy(&(pixmap->rect), pixmap->referred_rect, sizeof(Rect));
-
-    if (pixmap->id == 0 || pixmap->gc == 0) {
-        DLOG("Creating new pixmap...\n");
-        pixmap->id = xcb_generate_id(conn);
-        pixmap->gc = xcb_generate_id(conn);
-    } else {
-        DLOG("Re-creating this pixmap...\n");
-        xcb_free_gc(conn, pixmap->gc);
-        xcb_free_pixmap(conn, pixmap->id);
-    }
-
-    xcb_create_pixmap(conn, root_depth, pixmap->id,
-                      pixmap->referred_drawable, pixmap->rect.width, pixmap->rect.height);
-
-    xcb_create_gc(conn, pixmap->gc, pixmap->id, 0, 0);
-}
-
 /*
  * Query the width of the given text (16-bit characters, UCS) with given real
  * length (amount of glyphs) using the given font.