From 90d94298fa388e58a0f54607f0ab7a46108e84e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Wed, 11 Nov 2015 23:39:15 +0100 Subject: [PATCH 1/8] Move draw_util.c to libi3. In order to prepare for using cairo for rendering i3 decorations, we need to make the draw_util.c from i3bar available via libi3 such that both i3bar and i3 can use it. relates to #1278 --- i3bar/include/common.h | 1 - i3bar/include/draw_util.h | 103 ------------------------------- i3bar/include/outputs.h | 1 - include/libi3.h | 93 ++++++++++++++++++++++++++++ {i3bar/src => libi3}/draw_util.c | 7 ++- 5 files changed, 98 insertions(+), 107 deletions(-) delete mode 100644 i3bar/include/draw_util.h rename {i3bar/src => libi3}/draw_util.c (97%) diff --git a/i3bar/include/common.h b/i3bar/include/common.h index aa706bbe..0d46ab6a 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -88,4 +88,3 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "config.h" #include "libi3.h" #include "parse_json_header.h" -#include "draw_util.h" diff --git a/i3bar/include/draw_util.h b/i3bar/include/draw_util.h deleted file mode 100644 index f2e12ec5..00000000 --- a/i3bar/include/draw_util.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * © 2015 Ingo Bürk and contributors (see also: LICENSE) - * - * draw.h: Utility for drawing. - * - */ -#pragma once - -#ifdef CAIRO_SUPPORT -#include -#endif - -#ifdef CAIRO_SUPPORT -/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 - * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ -#define CAIRO_SURFACE_FLUSH(surface) \ - do { \ - cairo_surface_flush(surface); \ - cairo_surface_flush(surface); \ - } while (0) -#endif - -/* Represents a color split by color channel. */ -typedef struct color_t { - double red; - double green; - double blue; - - /* For compatibility, we also store the colorpixel for now. */ - uint32_t colorpixel; -} color_t; - -/* A wrapper grouping an XCB drawable and both a graphics context - * and the corresponding cairo objects representing it. */ -typedef struct surface_t { - /* The drawable which is being represented. */ - xcb_drawable_t id; - - /* A classic XCB graphics context. */ - xcb_gcontext_t gc; - - int width; - int height; - -#ifdef CAIRO_SUPPORT - /* A cairo surface representing the drawable. */ - cairo_surface_t *surface; - - /* The cairo object representing the drawale. In general, - * this is what one should use for any drawing operation. */ - cairo_t *cr; -#endif -} 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); - -/** - * Destroys the surface. - * - */ -void draw_util_surface_free(surface_t *surface); - -/** - * 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". - * - */ -color_t draw_util_hex_to_color(const char *color); - -/** - * Draw the given text using libi3. - * This function also marks the surface dirty which is needed if other means of - * drawing are used. This will be the case when using XCB to draw text. - * - */ -void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); - -/** - * Draws a filled rectangle. - * This function is a convenience wrapper and takes care of flushing the - * 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); - -/** - * Clears a surface with the given color. - * - */ -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, - double dest_x, double dest_y, double width, double height); diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 08adefd9..63cfca7f 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -13,7 +13,6 @@ #include #include "common.h" -#include "draw_util.h" typedef struct i3_output i3_output; diff --git a/include/libi3.h b/include/libi3.h index 75d3639b..15dffdb7 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -20,6 +20,9 @@ #if PANGO_SUPPORT #include #endif +#ifdef CAIRO_SUPPORT +#include +#endif #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) @@ -480,3 +483,93 @@ char *get_config_path(const char *override_configpath, bool use_system_paths); */ int mkdirp(const char *path, mode_t mode); #endif + +#ifdef CAIRO_SUPPORT +/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 + * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ +#define CAIRO_SURFACE_FLUSH(surface) \ + do { \ + cairo_surface_flush(surface); \ + cairo_surface_flush(surface); \ + } while (0) +#endif + +/* Represents a color split by color channel. */ +typedef struct color_t { + double red; + double green; + double blue; + + /* The colorpixel we use for direct XCB calls. */ + uint32_t colorpixel; +} color_t; + +/* A wrapper grouping an XCB drawable and both a graphics context + * and the corresponding cairo objects representing it. */ +typedef struct surface_t { + /* The drawable which is being represented. */ + xcb_drawable_t id; + + /* A classic XCB graphics context. */ + xcb_gcontext_t gc; + + int width; + int height; + +#ifdef CAIRO_SUPPORT + /* A cairo surface representing the drawable. */ + cairo_surface_t *surface; + + /* The cairo object representing the drawable. In general, + * this is what one should use for any drawing operation. */ + cairo_t *cr; +#endif +} 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); + +/** + * Destroys the surface. + * + */ +void draw_util_surface_free(surface_t *surface); + +/** + * 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". + * + */ +color_t draw_util_hex_to_color(const char *color); + +/** + * Draw the given text using libi3. + * This function also marks the surface dirty which is needed if other means of + * drawing are used. This will be the case when using XCB to draw text. + * + */ +void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * 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); + +/** + * Clears a surface with the given color. + * + */ +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, + double dest_x, double dest_y, double width, double height); diff --git a/i3bar/src/draw_util.c b/libi3/draw_util.c similarity index 97% rename from i3bar/src/draw_util.c rename to libi3/draw_util.c index 86435351..04dc0ce5 100644 --- a/i3bar/src/draw_util.c +++ b/libi3/draw_util.c @@ -15,7 +15,6 @@ #include #endif -#include "common.h" #include "libi3.h" xcb_connection_t *xcb_connection; @@ -35,8 +34,12 @@ void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int wid 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); - if (xcb_request_failed(gc_cookie, "Could not create graphical context")) + + xcb_generic_error_t *error = xcb_request_check(xcb_connection, 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); From d9bbfb7b351f13a3a86e43f69fc7d2e4a18582f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Sun, 15 Nov 2015 17:25:12 +0100 Subject: [PATCH 2/8] Fix draw_util_copy_surface. This patch fixes a bug when copying one surface to another. Since it only exposes itself when used with non-trivial source coordinates, it didn't surface before when only used for i3bar. relates to #1278 --- libi3/draw_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi3/draw_util.c b/libi3/draw_util.c index 04dc0ce5..90b1f26f 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -186,7 +186,7 @@ void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, doubl * 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(dest->cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, src_y); + cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, dest_y - src_y); cairo_rectangle(dest->cr, dest_x, dest_y, width, height); cairo_fill(dest->cr); From b66504988366b123ebaa5e45160d584495c1f9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 16 Nov 2015 21:26:06 +0100 Subject: [PATCH 3/8] Migrate i3 rendering to cairo. 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 | 40 +++--- include/data.h | 7 +- include/libi3.h | 21 +++- include/xcb.h | 1 + libi3/draw_util.c | 66 ++++++---- src/con.c | 2 +- src/handlers.c | 11 +- src/main.c | 2 + src/manage.c | 2 +- src/x.c | 315 ++++++++++++++++++++++++---------------------- src/xcb.c | 16 +++ 11 files changed, 276 insertions(+), 207 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 92b0d1ab..c6c9846b 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -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); } diff --git a/include/data.h b/include/data.h index e5a76462..78a42ff4 100644 --- a/include/data.h +++ b/include/data.h @@ -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, diff --git a/include/libi3.h b/include/libi3.h index 15dffdb7..0cb2532f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -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); diff --git a/include/xcb.h b/include/xcb.h index c1f989bd..7fae41f5 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -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 diff --git a/libi3/draw_util.c b/libi3/draw_util.c index 90b1f26f..e7e7c09c 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -17,32 +17,34 @@ #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 } diff --git a/src/con.c b/src/con.c index bd002d35..03fd0ee6 100644 --- 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; } diff --git a/src/handlers.c b/src/handlers.c index e3ddf701..6f08d25c 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -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; } diff --git a/src/main.c b/src/main.c index 59c00146..1e0ec4a5 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/manage.c b/src/manage.c index 87d19ff8..05ac15f0 100644 --- a/src/manage.c +++ b/src/manage.c @@ -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 094a33f0..86a0c797 100644 --- 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; } diff --git a/src/xcb.c b/src/xcb.c index 90d591c7..60fd4212 100644 --- 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 From fdeb4e0c368c8354fd539c9fae98a2ddfc077e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 16 Nov 2015 21:28:33 +0100 Subject: [PATCH 4/8] Skip drawing for uninitialized surfaces. We return early from drawing functions if the surface to draw to is not initialized properly. There is no immediate need to do so, at least no crashes have been observed, but it mirrors the previous behavior a bit more closely. Furthermore, i3 should not crash due to not being able to make some rendering call, so this provides some stability. relates to #1278 --- libi3/draw_util.c | 19 +++++++++++++++++++ src/x.c | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libi3/draw_util.c b/libi3/draw_util.c index e7e7c09c..816a81df 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -23,6 +23,14 @@ xcb_visualtype_t *visual_type; /* Forward declarations */ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color); +#define RETURN_UNLESS_SURFACE_INITIALIZED(surface) \ + do { \ + if ((surface)->id == XCB_NONE) { \ + ELOG("Surface %p is not initialized, skipping drawing.\n", surface); \ + return; \ + } \ + } while (0) + /* * Initialize the surface to represent the given drawable. * @@ -104,6 +112,8 @@ color_t draw_util_colorpixel_to_color(uint32_t colorpixel) { * */ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color) { + RETURN_UNLESS_SURFACE_INITIALIZED(surface); + #ifdef CAIRO_SUPPORT cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); #else @@ -120,6 +130,8 @@ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surfac * */ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { + RETURN_UNLESS_SURFACE_INITIALIZED(surface); + #ifdef CAIRO_SUPPORT /* Flush any changes before we draw the text as this might use XCB directly. */ CAIRO_SURFACE_FLUSH(surface->surface); @@ -141,6 +153,8 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_ * */ void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h) { + RETURN_UNLESS_SURFACE_INITIALIZED(surface); + #ifdef CAIRO_SUPPORT cairo_save(surface->cr); @@ -171,6 +185,8 @@ void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t col * */ void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color) { + RETURN_UNLESS_SURFACE_INITIALIZED(surface); + #ifdef CAIRO_SUPPORT cairo_save(surface->cr); @@ -201,6 +217,9 @@ void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t */ 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) { + RETURN_UNLESS_SURFACE_INITIALIZED(src); + RETURN_UNLESS_SURFACE_INITIALIZED(dest); + #ifdef CAIRO_SUPPORT cairo_save(dest->cr); diff --git a/src/x.c b/src/x.c index 86a0c797..49325af4 100644 --- a/src/x.c +++ b/src/x.c @@ -518,7 +518,7 @@ 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 + /* If the parent hasn't been set up yet, skip the decoration rendering * for now. */ if (parent->frame_buffer.id == XCB_NONE) goto copy_pixmaps; From a4afd1b642af8d0b6382024e823ddc305a26883d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 16 Nov 2015 23:03:39 +0100 Subject: [PATCH 5/8] Parse colors as color_t instead of colorpixel. With this patch we remove the temporary draw_util_colorpixel_to_color function we introduced previously by parsing the colors as color_t to begin with. relates to #1278 --- include/config.h | 10 +++++----- include/data.h | 2 +- include/libi3.h | 2 -- libi3/draw_util.c | 8 -------- src/config.c | 10 +++++----- src/config_directives.c | 22 +++++++++++----------- src/resize.c | 2 +- src/restore_layout.c | 6 +++--- src/x.c | 38 +++++++++++++++++++------------------- 9 files changed, 45 insertions(+), 55 deletions(-) diff --git a/include/config.h b/include/config.h index 5b98ce6e..f9badf10 100644 --- a/include/config.h +++ b/include/config.h @@ -50,10 +50,10 @@ struct context { * */ struct Colortriple { - uint32_t border; - uint32_t background; - uint32_t text; - uint32_t indicator; + color_t border; + color_t background; + color_t text; + color_t indicator; }; /** @@ -202,7 +202,7 @@ struct Config { /* Color codes are stored here */ struct config_client { - uint32_t background; + color_t background; struct Colortriple focused; struct Colortriple focused_inactive; struct Colortriple unfocused; diff --git a/include/data.h b/include/data.h index 78a42ff4..636092d2 100644 --- a/include/data.h +++ b/include/data.h @@ -179,7 +179,7 @@ struct deco_render_params { struct width_height con_rect; struct width_height con_window_rect; Rect con_deco_rect; - uint32_t background; + color_t background; layout_t parent_layout; bool con_is_leaf; }; diff --git a/include/libi3.h b/include/libi3.h index 0cb2532f..02988837 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -554,8 +554,6 @@ void draw_util_surface_free(xcb_connection_t *conn, 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 diff --git a/libi3/draw_util.c b/libi3/draw_util.c index 816a81df..4015ba92 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -99,14 +99,6 @@ 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. * diff --git a/src/config.c b/src/config.c index fac4e265..833ea6b6 100644 --- a/src/config.c +++ b/src/config.c @@ -191,13 +191,13 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, /* Initialize default colors */ #define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \ do { \ - x.border = get_colorpixel(cborder); \ - x.background = get_colorpixel(cbackground); \ - x.text = get_colorpixel(ctext); \ - x.indicator = get_colorpixel(cindicator); \ + x.border = draw_util_hex_to_color(cborder); \ + x.background = draw_util_hex_to_color(cbackground); \ + x.text = draw_util_hex_to_color(ctext); \ + x.indicator = draw_util_hex_to_color(cindicator); \ } while (0) - config.client.background = get_colorpixel("#000000"); + config.client.background = draw_util_hex_to_color("#000000"); INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4"); INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50"); INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e"); diff --git a/src/config_directives.c b/src/config_directives.c index ba267427..0d32f008 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -332,20 +332,20 @@ CFGFUN(popup_during_fullscreen, const char *value) { CFGFUN(color_single, const char *colorclass, const char *color) { /* used for client.background only currently */ - config.client.background = get_colorpixel(color); + config.client.background = draw_util_hex_to_color(color); } CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator) { -#define APPLY_COLORS(classname) \ - do { \ - if (strcmp(colorclass, "client." #classname) == 0) { \ - config.client.classname.border = get_colorpixel(border); \ - config.client.classname.background = get_colorpixel(background); \ - config.client.classname.text = get_colorpixel(text); \ - if (indicator != NULL) { \ - config.client.classname.indicator = get_colorpixel(indicator); \ - } \ - } \ +#define APPLY_COLORS(classname) \ + do { \ + if (strcmp(colorclass, "client." #classname) == 0) { \ + config.client.classname.border = draw_util_hex_to_color(border); \ + config.client.classname.background = draw_util_hex_to_color(background); \ + config.client.classname.text = draw_util_hex_to_color(text); \ + if (indicator != NULL) { \ + config.client.classname.indicator = draw_util_hex_to_color(indicator); \ + } \ + } \ } while (0) APPLY_COLORS(focused_inactive); diff --git a/src/resize.c b/src/resize.c index 05fe5055..31a78354 100644 --- a/src/resize.c +++ b/src/resize.c @@ -146,7 +146,7 @@ int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, } mask = XCB_CW_BACK_PIXEL; - values[0] = config.client.focused.border; + values[0] = config.client.focused.border.colorpixel; mask |= XCB_CW_OVERRIDE_REDIRECT; values[1] = 1; diff --git a/src/restore_layout.c b/src/restore_layout.c index 70eed523..5ca4cff5 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -125,7 +125,7 @@ void restore_connect(void) { static void update_placeholder_contents(placeholder_state *state) { xcb_change_gc(restore_conn, state->gc, XCB_GC_FOREGROUND, - (uint32_t[]){config.client.placeholder.background}); + (uint32_t[]){config.client.placeholder.background.colorpixel}); xcb_poly_fill_rectangle(restore_conn, state->pixmap, state->gc, 1, (xcb_rectangle_t[]){{0, 0, state->rect.width, state->rect.height}}); @@ -133,7 +133,7 @@ static void update_placeholder_contents(placeholder_state *state) { xcb_flush(restore_conn); xcb_aux_sync(restore_conn); - set_font_colors(state->gc, config.client.placeholder.text, config.client.placeholder.background); + set_font_colors(state->gc, config.client.placeholder.text.colorpixel, config.client.placeholder.background.colorpixel); Match *swallows; int n = 0; @@ -193,7 +193,7 @@ static void open_placeholder_window(Con *con) { true, XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (uint32_t[]){ - config.client.placeholder.background, + config.client.placeholder.background.colorpixel, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, }); /* Make i3 not focus this window. */ diff --git a/src/x.c b/src/x.c index 49325af4..63cc4b48 100644 --- a/src/x.c +++ b/src/x.c @@ -327,10 +327,10 @@ static void x_draw_decoration_border(Con *con, struct deco_render_params *p) { deco_diff_r = 0; } - draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border), + draw_util_rectangle(conn, &(con->parent->frame_buffer), 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), + draw_util_rectangle(conn, &(con->parent->frame_buffer), p->color->border, dr->x + deco_diff_l, dr->y + dr->height - 1, dr->width - (deco_diff_l + deco_diff_r), 1); } @@ -343,18 +343,18 @@ static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p /* 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), + draw_util_rectangle(conn, &(con->parent->frame_buffer), 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), + draw_util_rectangle(conn, &(con->parent->frame_buffer), 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), + draw_util_rectangle(conn, &(con->parent->frame_buffer), p->color->border, dr->x + dr->width - 1, dr->y, 1, dr->height); } @@ -448,16 +448,16 @@ void x_draw_decoration(Con *con) { /* 2: draw the client.background, but only for the parts around the window_rect */ if (con->window != NULL) { /* top area */ - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background), + draw_util_rectangle(conn, &(con->frame_buffer), 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), + draw_util_rectangle(conn, &(con->frame_buffer), 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), + draw_util_rectangle(conn, &(con->frame_buffer), 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), + draw_util_rectangle(conn, &(con->frame_buffer), config.client.background, w->x + w->width, 0, r->width - (w->x + w->width), r->height); } @@ -479,20 +479,20 @@ void x_draw_decoration(Con *con) { * rectangle because some childs are not freely resizable and we want * their background color to "shine through". */ if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) { - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + draw_util_rectangle(conn, &(con->frame_buffer), p->color->background, 0, 0, br.x, r->height); } if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) { - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + draw_util_rectangle(conn, &(con->frame_buffer), p->color->background, r->width + (br.width + br.x), 0, -(br.width + br.x), r->height); } if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) { - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + draw_util_rectangle(conn, &(con->frame_buffer), p->color->background, br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)); } /* pixel border needs an additional line at the top */ if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + draw_util_rectangle(conn, &(con->frame_buffer), p->color->background, br.x, 0, r->width + br.width, br.y); } @@ -504,10 +504,10 @@ void x_draw_decoration(Con *con) { TAILQ_PREV(con, nodes_head, nodes) == NULL && con->parent->type != CT_FLOATING_CON) { if (p->parent_layout == L_SPLITH) { - draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->indicator), + draw_util_rectangle(conn, &(con->frame_buffer), 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), + draw_util_rectangle(conn, &(con->frame_buffer), p->color->indicator, br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)); } } @@ -524,7 +524,7 @@ void x_draw_decoration(Con *con) { goto copy_pixmaps; /* 4: paint the bar */ - draw_util_rectangle(conn, &(parent->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + draw_util_rectangle(conn, &(parent->frame_buffer), 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 */ @@ -545,7 +545,7 @@ void x_draw_decoration(Con *con) { 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), + p->color->text, p->color->background, con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, con->deco_rect.width - 2); FREE(_title); @@ -596,7 +596,7 @@ void x_draw_decoration(Con *con) { mark_width = predict_text_width(mark); draw_util_text(mark, &(parent->frame_buffer), - draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background), + p->color->text, 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); @@ -608,7 +608,7 @@ void x_draw_decoration(Con *con) { i3String *title = win->title_format == NULL ? win->name : window_parse_title_format(win); draw_util_text(title, &(parent->frame_buffer), - draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background), + p->color->text, 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) From d2126027cef86a3a6cf3e22ec551156636e4d9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 16 Nov 2015 23:40:41 +0100 Subject: [PATCH 6/8] Don't create a pixmap for CT_ROOT and CT_OUTPUT containers. Such containers never require a pixmap anyway. In particular for the __i3 output (used for the scratchpad workspace), this would cause an allocation error anyway because it can be very big -- so big, in fact, that X cannot allocate the pixmap for it. Until now, this error was silently ignored due to the fact that we did not create the pixmap checked (and asserted its success), but with cairo this would cause a crash because we'd try to create a surface for a pixmap which doesn't exist. relates to #1278 --- src/x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/x.c b/src/x.c index 63cc4b48..17970fb8 100644 --- a/src/x.c +++ b/src/x.c @@ -746,6 +746,12 @@ void x_push_node(Con *con) { con->parent->layout == L_STACKED || con->parent->layout == L_TABBED); + /* The root con and output cons will never require a pixmap. In particular for the + * __i3 output, this will likely not work anyway because it might be ridiculously + * large, causing an XCB_ALLOC error. */ + if (con->type == CT_ROOT || con->type == CT_OUTPUT) + is_pixmap_needed = false; + bool fake_notify = false; /* Set new position if rect changed (and if height > 0) or if the pixmap * needs to be recreated */ From 780cb8d15d07febbb9be47cb1bc1201072b31838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Tue, 17 Nov 2015 12:50:06 +0100 Subject: [PATCH 7/8] Use 32-bit visual by default if available. With this patch, we use 32-bit visuals per default whenever it is available. Otherwise, we fall back to the actual root window's depth, which will typically be 24-bit. Before this patch, we already used 32-bit depth for containers with a window that uses 32-bit. However, this means that we didn't use 32-bit for split parent containers on which decoration is drawn. For 32-bit windows using transparency, this caused a graphical glitch because the decoration pixmap behind it would show through. This behavior is fixed with this change. relates to #1278 --- include/libi3.h | 3 +++ include/xcb.h | 5 ++++ libi3/draw_util.c | 18 ++++++++++--- src/con.c | 2 +- src/main.c | 28 ++++++++++++++------ src/x.c | 65 +++++++++++++++++++++-------------------------- src/xcb.c | 5 ++++ 7 files changed, 77 insertions(+), 49 deletions(-) diff --git a/include/libi3.h b/include/libi3.h index 02988837..08b01402 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -499,11 +499,14 @@ typedef struct color_t { double red; double green; double blue; + double alpha; /* The colorpixel we use for direct XCB calls. */ uint32_t colorpixel; } color_t; +#define COLOR_TRANSPARENT ((color_t){.red = 0.0, .green = 0.0, .blue = 0.0, .colorpixel = 0}) + /* A wrapper grouping an XCB drawable and both a graphics context * and the corresponding cairo objects representing it. */ typedef struct surface_t { diff --git a/include/xcb.h b/include/xcb.h index 7fae41f5..86019c5d 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -139,6 +139,11 @@ void xcb_set_root_cursor(int cursor); * */ uint16_t get_visual_depth(xcb_visualid_t visual_id); + +/** + * Get visual type specified by visualid + * + */ xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id); /** diff --git a/libi3/draw_util.c b/libi3/draw_util.c index 4015ba92..f6c53865 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -38,7 +38,7 @@ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surfac 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->visual_type = ((visual == NULL) ? visual_type : visual); surface->width = width; surface->height = height; @@ -87,15 +87,25 @@ void draw_util_surface_set_size(surface_t *surface, int width, int height) { * */ color_t draw_util_hex_to_color(const char *color) { - char groups[3][3] = { + char alpha[2]; + if (strlen(color) == strlen("#rrggbbaa")) { + alpha[0] = color[7]; + alpha[1] = color[8]; + } else { + alpha[0] = alpha[1] = 'F'; + } + + char groups[4][3] = { {color[1], color[2], '\0'}, {color[3], color[4], '\0'}, - {color[5], color[6], '\0'}}; + {color[5], color[6], '\0'}, + {alpha[0], alpha[1], '\0'}}; return (color_t){ .red = strtol(groups[0], NULL, 16) / 255.0, .green = strtol(groups[1], NULL, 16) / 255.0, .blue = strtol(groups[2], NULL, 16) / 255.0, + .alpha = strtol(groups[3], NULL, 16) / 255.0, .colorpixel = get_colorpixel(color)}; } @@ -107,7 +117,7 @@ static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surfac RETURN_UNLESS_SURFACE_INITIALIZED(surface); #ifdef CAIRO_SUPPORT - cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); + cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha); #else uint32_t colorpixel = color.colorpixel; xcb_change_gc(conn, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, diff --git a/src/con.c b/src/con.c index 03fd0ee6..64dbec69 100644 --- a/src/con.c +++ b/src/con.c @@ -47,7 +47,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) { new->depth = window->depth; new->window->aspect_ratio = 0.0; } else { - new->depth = XCB_COPY_FROM_PARENT; + new->depth = root_depth; } DLOG("opening window\n"); diff --git a/src/main.c b/src/main.c index 1e0ec4a5..9c67b42a 100644 --- a/src/main.c +++ b/src/main.c @@ -59,7 +59,6 @@ xcb_window_t root; * pixmaps. Will use 32 bit depth and an appropriate visual, if available, * 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; @@ -482,16 +481,29 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - /* By default, we use the same depth and visual as the root window, which - * usually is TrueColor (24 bit depth) and the corresponding visual. - * However, we also check if a 32 bit depth and visual are available (for - * 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; + visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32); + if (visual_type != NULL) { + root_depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id); + colormap = xcb_generate_id(conn); - DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id); + xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(conn, + XCB_COLORMAP_ALLOC_NONE, + colormap, + root, + visual_type->visual_id); + + xcb_generic_error_t *error = xcb_request_check(conn, cm_cookie); + if (error != NULL) { + ELOG("Could not create colormap. Error code: %d\n", error->error_code); + exit(EXIT_FAILURE); + } + } else { + visual_type = get_visualtype(root_screen); + } + + DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_type->visual_id); DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d, dpi = %d\n", root_screen->height_in_pixels, root_screen->height_in_millimeters, (int)((double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters)); diff --git a/src/x.c b/src/x.c index 17970fb8..f1f971e9 100644 --- a/src/x.c +++ b/src/x.c @@ -101,47 +101,32 @@ void x_con_init(Con *con, uint16_t depth) { uint32_t mask = 0; uint32_t values[5]; - xcb_visualid_t visual = XCB_COPY_FROM_PARENT; - xcb_colormap_t win_colormap = XCB_NONE; - if (depth != root_depth && depth != XCB_COPY_FROM_PARENT) { - /* For custom visuals, we need to create a colormap before creating - * this window. It will be freed directly after creating the window. */ - visual = get_visualid_by_depth(depth); - win_colormap = xcb_generate_id(conn); - xcb_create_colormap_checked(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual); + /* For custom visuals, we need to create a colormap before creating + * this window. It will be freed directly after creating the window. */ + xcb_visualid_t visual = get_visualid_by_depth(depth); + xcb_colormap_t win_colormap = xcb_generate_id(conn); + xcb_create_colormap_checked(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual); - /* We explicitly set a background color and border color (even though we - * don’t even have a border) because the X11 server requires us to when - * using 32 bit color depths, see - * http://stackoverflow.com/questions/3645632 */ - mask |= XCB_CW_BACK_PIXEL; - values[0] = root_screen->black_pixel; + /* We explicitly set a background color and border color (even though we + * don’t even have a border) because the X11 server requires us to when + * using 32 bit color depths, see + * http://stackoverflow.com/questions/3645632 */ + mask |= XCB_CW_BACK_PIXEL; + values[0] = root_screen->black_pixel; - mask |= XCB_CW_BORDER_PIXEL; - values[1] = root_screen->black_pixel; + mask |= XCB_CW_BORDER_PIXEL; + values[1] = root_screen->black_pixel; - /* our own frames should not be managed */ - mask |= XCB_CW_OVERRIDE_REDIRECT; - values[2] = 1; + /* our own frames should not be managed */ + mask |= XCB_CW_OVERRIDE_REDIRECT; + values[2] = 1; - /* see include/xcb.h for the FRAME_EVENT_MASK */ - mask |= XCB_CW_EVENT_MASK; - values[3] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; + /* see include/xcb.h for the FRAME_EVENT_MASK */ + mask |= XCB_CW_EVENT_MASK; + values[3] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; - mask |= XCB_CW_COLORMAP; - values[4] = win_colormap; - } else { - /* our own frames should not be managed */ - mask = XCB_CW_OVERRIDE_REDIRECT; - values[0] = 1; - - /* see include/xcb.h for the FRAME_EVENT_MASK */ - mask |= XCB_CW_EVENT_MASK; - values[1] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; - - mask |= XCB_CW_COLORMAP; - values[2] = colormap; - } + mask |= XCB_CW_COLORMAP; + values[4] = win_colormap; Rect dims = {-15, -15, 10, 10}; xcb_window_t frame_id = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); @@ -523,6 +508,14 @@ void x_draw_decoration(Con *con) { if (parent->frame_buffer.id == XCB_NONE) goto copy_pixmaps; + /* For the first child, we clear the parent pixmap to ensure there's no + * garbage left on there. This is important to avoid tearing when using + * transparency. */ + if (con == TAILQ_FIRST(&(con->parent->nodes_head))) { + draw_util_clear_surface(conn, &(con->parent->frame_buffer), COLOR_TRANSPARENT); + FREE(con->parent->deco_render_params); + } + /* 4: paint the bar */ draw_util_rectangle(conn, &(parent->frame_buffer), p->color->background, con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height); diff --git a/src/xcb.c b/src/xcb.c index 60fd4212..07f9281a 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -252,6 +252,11 @@ uint16_t get_visual_depth(xcb_visualid_t visual_id) { } return 0; } + +/* + * Get visual type specified by visualid + * + */ xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id) { xcb_depth_iterator_t depth_iter; From bf442ff5de277537dca40ee194bec984eeaa33c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Wed, 18 Nov 2015 15:32:45 +0100 Subject: [PATCH 8/8] Make freeing surfaces idempotent If a window with border is set to "border none" and then closed, we would call cairo_destroy / cairo_surface_destroy twice, causing an assertion failure in cairo as the objects already had zero references the second time. We fix this by explicitly setting these objects to NULL. relates to #1278 --- libi3/draw_util.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libi3/draw_util.c b/libi3/draw_util.c index f6c53865..e33a9ecb 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -66,6 +66,12 @@ void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) { #ifdef CAIRO_SUPPORT cairo_surface_destroy(surface->surface); cairo_destroy(surface->cr); + + /* We need to explicitly set these to NULL to avoid assertion errors in + * cairo when calling this multiple times. This can happen, for example, + * when setting the border of a window to none and then closing it. */ + surface->surface = NULL; + surface->cr = NULL; #endif }