Introduce switch for the drawing backend

This commit restores the old XCB drawing code paths while keeping the
cairo drawing available via a compile-time switch (I3BAR_CAIRO). This
is necessary as cairo currently has a bug that breaks i3bar for users
without the RENDER extension, which might be the case, e.g., for VNC
users.

For more context, see  and the discussions about its fix. Once the
cairo fix is available in a stable release, i3 can depend on that version
and remove the XCB drawing code paths.

fixes 
This commit is contained in:
Ingo Bürk
2015-10-14 20:49:52 +02:00
parent 46bcc55f6f
commit 02468296c4
6 changed files with 142 additions and 112 deletions

@ -80,4 +80,4 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
#include "config.h" #include "config.h"
#include "libi3.h" #include "libi3.h"
#include "parse_json_header.h" #include "parse_json_header.h"
#include "cairo_util.h" #include "draw_util.h"

@ -3,13 +3,16 @@
* *
* © 2015 Ingo Bürk and contributors (see also: LICENSE) * © 2015 Ingo Bürk and contributors (see also: LICENSE)
* *
* cairo_util.h: Utility for operations using cairo. * draw.h: Utility for drawing.
* *
*/ */
#pragma once #pragma once
#ifdef I3BAR_CAIRO
#include <cairo/cairo-xcb.h> #include <cairo/cairo-xcb.h>
#endif
#ifdef I3BAR_CAIRO
/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 /* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989
* and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */
#define CAIRO_SURFACE_FLUSH(surface) \ #define CAIRO_SURFACE_FLUSH(surface) \
@ -17,6 +20,7 @@
cairo_surface_flush(surface); \ cairo_surface_flush(surface); \
cairo_surface_flush(surface); \ cairo_surface_flush(surface); \
} while (0) } while (0)
#endif
/* Represents a color split by color channel. */ /* Represents a color split by color channel. */
typedef struct color_t { typedef struct color_t {
@ -34,43 +38,40 @@ typedef struct surface_t {
/* The drawable which is being represented. */ /* The drawable which is being represented. */
xcb_drawable_t id; xcb_drawable_t id;
// TODO remove this once i3 uses solely cairo for drawing operations /* A classic XCB graphics context. */
/* A classic XCB graphics context. This should not be used for
* drawing operations. */
xcb_gcontext_t gc; xcb_gcontext_t gc;
int width;
int height;
#ifdef I3BAR_CAIRO
/* A cairo surface representing the drawable. */ /* A cairo surface representing the drawable. */
cairo_surface_t *surface; cairo_surface_t *surface;
/* The cairo object representing the drawale. In general, /* The cairo object representing the drawale. In general,
* this is what one should use for any drawing operation. */ * this is what one should use for any drawing operation. */
cairo_t *cr; cairo_t *cr;
#endif
} surface_t; } surface_t;
/** /**
* Initialize the cairo surface to represent the given drawable. * Initialize the surface to represent the given drawable.
* *
*/ */
void cairo_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height); void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height);
/** /**
* Destroys the surface. * Destroys the surface.
* *
*/ */
void cairo_surface_free(surface_t *surface); void draw_util_surface_free(surface_t *surface);
/** /**
* Parses the given color in hex format to an internal color representation. * 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". * Note that the input must begin with a hash sign, e.g., "#3fbc59".
* *
*/ */
color_t cairo_hex_to_color(const char *color); color_t draw_util_hex_to_color(const char *color);
/**
* Set the given color as the source color on the surface.
*
*/
void cairo_set_source_color(surface_t *surface, color_t color);
/** /**
* Draw the given text using libi3. * Draw the given text using libi3.
@ -78,28 +79,25 @@ void cairo_set_source_color(surface_t *surface, color_t color);
* drawing are used. This will be the case when using XCB to draw text. * drawing are used. This will be the case when using XCB to draw text.
* *
*/ */
void cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); 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. * Draws a filled rectangle.
* This function is a convenience wrapper and takes care of flushing the * This function is a convenience wrapper and takes care of flushing the
* surface as well as restoring the cairo state. * surface as well as restoring the cairo state.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_draw_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h); 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. * Clears a surface with the given color.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_clear_surface(surface_t *surface, color_t color); void draw_util_clear_surface(surface_t *surface, color_t color);
/** /**
* Copies a surface onto another surface. * Copies a surface onto another surface.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
double dest_x, double dest_y, double dest_w, double dest_h); double dest_x, double dest_y, double width, double height);

@ -13,7 +13,7 @@
#include <cairo/cairo-xcb.h> #include <cairo/cairo-xcb.h>
#include "common.h" #include "common.h"
#include "cairo_util.h" #include "draw_util.h"
typedef struct i3_output i3_output; typedef struct i3_output i3_output;

@ -3,7 +3,7 @@
* *
* © 2015 Ingo Bürk and contributors (see also: LICENSE) * © 2015 Ingo Bürk and contributors (see also: LICENSE)
* *
* cairo_util.c: Utility for operations using cairo. * draw.c: Utility for drawing.
* *
*/ */
#include <stdlib.h> #include <stdlib.h>
@ -11,7 +11,9 @@
#include <string.h> #include <string.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#ifdef I3BAR_CAIRO
#include <cairo/cairo-xcb.h> #include <cairo/cairo-xcb.h>
#endif
#include "common.h" #include "common.h"
#include "libi3.h" #include "libi3.h"
@ -19,30 +21,39 @@
xcb_connection_t *xcb_connection; xcb_connection_t *xcb_connection;
xcb_visualtype_t *visual_type; xcb_visualtype_t *visual_type;
/* Forward declarations */
static void draw_util_set_source_color(surface_t *surface, color_t color);
/* /*
* Initialize the cairo surface to represent the given drawable. * Initialize the surface to represent the given drawable.
* *
*/ */
void cairo_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) { void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) {
surface->id = drawable; surface->id = drawable;
surface->width = width;
surface->height = height;
surface->gc = xcb_generate_id(xcb_connection); 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); 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")) if (xcb_request_failed(gc_cookie, "Could not create graphical context"))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
#ifdef I3BAR_CAIRO
surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height);
surface->cr = cairo_create(surface->surface); surface->cr = cairo_create(surface->surface);
#endif
} }
/* /*
* Destroys the surface. * Destroys the surface.
* *
*/ */
void cairo_surface_free(surface_t *surface) { void draw_util_surface_free(surface_t *surface) {
xcb_free_gc(xcb_connection, surface->gc); xcb_free_gc(xcb_connection, surface->gc);
#ifdef I3BAR_CAIRO
cairo_surface_destroy(surface->surface); cairo_surface_destroy(surface->surface);
cairo_destroy(surface->cr); cairo_destroy(surface->cr);
#endif
} }
/* /*
@ -50,7 +61,7 @@ void cairo_surface_free(surface_t *surface) {
* Note that the input must begin with a hash sign, e.g., "#3fbc59". * Note that the input must begin with a hash sign, e.g., "#3fbc59".
* *
*/ */
color_t cairo_hex_to_color(const char *color) { color_t draw_util_hex_to_color(const char *color) {
char groups[3][3] = { char groups[3][3] = {
{color[1], color[2], '\0'}, {color[1], color[2], '\0'},
{color[3], color[4], '\0'}, {color[3], color[4], '\0'},
@ -67,8 +78,14 @@ color_t cairo_hex_to_color(const char *color) {
* Set the given color as the source color on the surface. * Set the given color as the source color on the surface.
* *
*/ */
void cairo_set_source_color(surface_t *surface, color_t color) { static void draw_util_set_source_color(surface_t *surface, color_t color) {
#ifdef I3BAR_CAIRO
cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); 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,
(uint32_t[]){colorpixel, colorpixel});
#endif
} }
/** /**
@ -77,32 +94,36 @@ void cairo_set_source_color(surface_t *surface, color_t color) {
* drawing are used. This will be the case when using XCB to draw text. * drawing are used. This will be the case when using XCB to draw text.
* *
*/ */
void cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) {
#ifdef I3BAR_CAIRO
/* Flush any changes before we draw the text as this might use XCB directly. */ /* Flush any changes before we draw the text as this might use XCB directly. */
CAIRO_SURFACE_FLUSH(surface->surface); CAIRO_SURFACE_FLUSH(surface->surface);
#endif
set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel); 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, visual_type, x, y, max_width);
#ifdef I3BAR_CAIRO
/* Notify cairo that we (possibly) used another way to draw on the surface. */ /* Notify cairo that we (possibly) used another way to draw on the surface. */
cairo_surface_mark_dirty(surface->surface); cairo_surface_mark_dirty(surface->surface);
#endif
} }
/** /**
* Draws a filled rectangle. * Draws a filled rectangle.
* This function is a convenience wrapper and takes care of flushing the * This function is a convenience wrapper and takes care of flushing the
* surface as well as restoring the cairo state. * surface as well as restoring the cairo state.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_draw_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) { void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) {
#ifdef I3BAR_CAIRO
cairo_save(surface->cr); cairo_save(surface->cr);
/* Using the SOURCE operator will copy both color and alpha information directly /* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and * onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */ * allows better color control for the user when using opacity. */
cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_color(surface, color); draw_util_set_source_color(surface, color);
cairo_rectangle(surface->cr, x, y, w, h); cairo_rectangle(surface->cr, x, y, w, h);
cairo_fill(surface->cr); cairo_fill(surface->cr);
@ -112,21 +133,27 @@ void cairo_draw_rectangle(surface_t *surface, color_t color, double x, double y,
CAIRO_SURFACE_FLUSH(surface->surface); CAIRO_SURFACE_FLUSH(surface->surface);
cairo_restore(surface->cr); cairo_restore(surface->cr);
#else
draw_util_set_source_color(surface, color);
xcb_rectangle_t rect = {x, y, w, h};
xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect);
#endif
} }
/** /**
* Clears a surface with the given color. * Clears a surface with the given color.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_clear_surface(surface_t *surface, color_t color) { void draw_util_clear_surface(surface_t *surface, color_t color) {
#ifdef I3BAR_CAIRO
cairo_save(surface->cr); cairo_save(surface->cr);
/* Using the SOURCE operator will copy both color and alpha information directly /* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and * onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */ * allows better color control for the user when using opacity. */
cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_color(surface, color); draw_util_set_source_color(surface, color);
cairo_paint(surface->cr); cairo_paint(surface->cr);
@ -135,29 +162,40 @@ void cairo_clear_surface(surface_t *surface, color_t color) {
CAIRO_SURFACE_FLUSH(surface->surface); CAIRO_SURFACE_FLUSH(surface->surface);
cairo_restore(surface->cr); cairo_restore(surface->cr);
#else
draw_util_set_source_color(surface, color);
xcb_rectangle_t rect = {0, 0, surface->width, surface->height};
xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect);
#endif
} }
/** /**
* Copies a surface onto another surface. * Copies a surface onto another surface.
* Note that the drawing is done using CAIRO_OPERATOR_SOURCE.
* *
*/ */
void cairo_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
double dest_x, double dest_y, double dest_w, double dest_h) { double dest_x, double dest_y, double width, double height) {
#ifdef I3BAR_CAIRO
cairo_save(dest->cr); cairo_save(dest->cr);
/* Using the SOURCE operator will copy both color and alpha information directly /* Using the SOURCE operator will copy both color and alpha information directly
* onto the surface rather than blending it. This is a bit more efficient and * onto the surface rather than blending it. This is a bit more efficient and
* allows better color control for the user when using opacity. */ * allows better color control for the user when using opacity. */
cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE); cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface(dest->cr, src->surface, src_x, src_y); cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, src_y);
cairo_rectangle(dest->cr, dest_x, dest_y, dest_w, dest_h); cairo_rectangle(dest->cr, dest_x, dest_y, width, height);
cairo_fill(dest->cr); cairo_fill(dest->cr);
/* Make sure we flush the surface for any text drawing operations that could follow. /* Make sure we flush the surface for any text drawing operations that could follow.
* Since we support drawing text via XCB, we need this. */ * Since we support drawing text via XCB, we need this. */
CAIRO_SURFACE_FLUSH(src->surface); CAIRO_SURFACE_FLUSH(src->surface);
CAIRO_SURFACE_FLUSH(dest->surface); CAIRO_SURFACE_FLUSH(dest->surface);
cairo_restore(dest->cr); cairo_restore(dest->cr);
#else
xcb_copy_area(xcb_connection, 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
} }

@ -30,7 +30,6 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/XKBlib.h> #include <X11/XKBlib.h>
#include <X11/extensions/XKB.h> #include <X11/extensions/XKB.h>
#include <cairo/cairo-xcb.h>
#include "common.h" #include "common.h"
#include "libi3.h" #include "libi3.h"
@ -174,16 +173,16 @@ static void draw_separator(uint32_t x, struct status_block *block) {
uint32_t center_x = x - sep_offset; uint32_t center_x = x - sep_offset;
if (config.separator_symbol == NULL) { if (config.separator_symbol == NULL) {
/* Draw a classic one pixel, vertical separator. */ /* Draw a classic one pixel, vertical separator. */
cairo_draw_rectangle(&statusline_surface, colors.sep_fg, draw_util_rectangle(&statusline_surface, colors.sep_fg,
center_x, center_x,
logical_px(sep_voff_px), logical_px(sep_voff_px),
logical_px(1), logical_px(1),
bar_height - 2 * logical_px(sep_voff_px)); bar_height - 2 * logical_px(sep_voff_px));
} else { } else {
/* Draw a custom separator. */ /* Draw a custom separator. */
uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2); uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
cairo_draw_text(config.separator_symbol, &statusline_surface, colors.sep_fg, colors.bar_bg, draw_util_text(config.separator_symbol, &statusline_surface, colors.sep_fg, colors.bar_bg,
separator_x, logical_px(ws_voff_px), x - separator_x); separator_x, logical_px(ws_voff_px), x - separator_x);
} }
} }
@ -244,7 +243,7 @@ void refresh_statusline(bool use_short_text) {
realloc_sl_buffer(); realloc_sl_buffer();
/* Clear the statusline pixmap. */ /* Clear the statusline pixmap. */
cairo_clear_surface(&statusline_surface, colors.bar_bg); draw_util_clear_surface(&statusline_surface, colors.bar_bg);
/* Draw the text of each block. */ /* Draw the text of each block. */
uint32_t x = 0; uint32_t x = 0;
@ -258,17 +257,17 @@ void refresh_statusline(bool use_short_text) {
fg_color = colors.urgent_ws_fg; fg_color = colors.urgent_ws_fg;
/* Draw the background */ /* Draw the background */
cairo_draw_rectangle(&statusline_surface, colors.urgent_ws_bg, draw_util_rectangle(&statusline_surface, colors.urgent_ws_bg,
x - logical_px(2), x - logical_px(2),
logical_px(1), logical_px(1),
block->width + logical_px(4), block->width + logical_px(4),
bar_height - logical_px(2)); bar_height - logical_px(2));
} else { } else {
fg_color = (block->color ? cairo_hex_to_color(block->color) : colors.bar_fg); fg_color = (block->color ? draw_util_hex_to_color(block->color) : colors.bar_fg);
} }
cairo_draw_text(block->full_text, &statusline_surface, fg_color, colors.bar_bg, draw_util_text(block->full_text, &statusline_surface, fg_color, colors.bar_bg,
x + block->x_offset, logical_px(ws_voff_px), block->width); x + block->x_offset, logical_px(ws_voff_px), block->width);
x += block->width + block->sep_block_width + block->x_offset + block->x_append; x += block->width + block->sep_block_width + block->x_offset + block->x_append;
/* If this is not the last block, draw a separator. */ /* If this is not the last block, draw a separator. */
@ -346,9 +345,9 @@ void unhide_bars(void) {
* *
*/ */
void init_colors(const struct xcb_color_strings_t *new_colors) { void init_colors(const struct xcb_color_strings_t *new_colors) {
#define PARSE_COLOR(name, def) \ #define PARSE_COLOR(name, def) \
do { \ do { \
colors.name = cairo_hex_to_color(new_colors->name ? new_colors->name : def); \ colors.name = draw_util_hex_to_color(new_colors->name ? new_colors->name : def); \
} while (0) } while (0)
PARSE_COLOR(bar_fg, "#FFFFFF"); PARSE_COLOR(bar_fg, "#FFFFFF");
PARSE_COLOR(bar_bg, "#000000"); PARSE_COLOR(bar_bg, "#000000");
@ -367,9 +366,9 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
PARSE_COLOR(focus_ws_border, "#4c7899"); PARSE_COLOR(focus_ws_border, "#4c7899");
#undef PARSE_COLOR #undef PARSE_COLOR
#define PARSE_COLOR_FALLBACK(name, fallback) \ #define PARSE_COLOR_FALLBACK(name, fallback) \
do { \ do { \
colors.name = new_colors->name ? cairo_hex_to_color(new_colors->name) : colors.fallback; \ colors.name = new_colors->name ? draw_util_hex_to_color(new_colors->name) : colors.fallback; \
} while (0) } while (0)
/* For the binding mode indicator colors, we don't hardcode a default. /* For the binding mode indicator colors, we don't hardcode a default.
@ -1128,7 +1127,7 @@ char *init_xcb_early() {
xcb_root, xcb_root,
root_screen->width_in_pixels, root_screen->width_in_pixels,
root_screen->height_in_pixels); root_screen->height_in_pixels);
cairo_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); draw_util_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels);
/* The various watchers to communicate with xcb */ /* The various watchers to communicate with xcb */
xcb_io = smalloc(sizeof(ev_io)); xcb_io = smalloc(sizeof(ev_io));
@ -1490,7 +1489,7 @@ void realloc_sl_buffer(void) {
DLOG("Re-allocating statusline buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n", DLOG("Re-allocating statusline buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
statusline_width, root_screen->width_in_pixels); statusline_width, root_screen->width_in_pixels);
xcb_free_pixmap(xcb_connection, statusline_surface.id); xcb_free_pixmap(xcb_connection, statusline_surface.id);
cairo_surface_free(&statusline_surface); draw_util_surface_free(&statusline_surface);
xcb_pixmap_t statusline_id = xcb_generate_id(xcb_connection); xcb_pixmap_t statusline_id = xcb_generate_id(xcb_connection);
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection,
@ -1499,7 +1498,7 @@ void realloc_sl_buffer(void) {
xcb_root, xcb_root,
MAX(root_screen->width_in_pixels, statusline_width), MAX(root_screen->width_in_pixels, statusline_width),
bar_height); bar_height);
cairo_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); draw_util_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels);
if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer")) if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer"))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -1650,8 +1649,8 @@ void reconfig_windows(bool redraw_bars) {
1, 1,
(unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]); (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
cairo_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height); draw_util_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height);
cairo_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height); draw_util_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height);
xcb_void_cookie_t strut_cookie = config_strut_partial(walk); xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
@ -1731,10 +1730,10 @@ void reconfig_windows(bool redraw_bars) {
walk->rect.w, walk->rect.w,
bar_height); bar_height);
cairo_surface_free(&(walk->bar)); draw_util_surface_free(&(walk->bar));
cairo_surface_free(&(walk->buffer)); draw_util_surface_free(&(walk->buffer));
cairo_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height); draw_util_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height);
cairo_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height); draw_util_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height);
xcb_void_cookie_t map_cookie, umap_cookie; xcb_void_cookie_t map_cookie, umap_cookie;
if (redraw_bars) { if (redraw_bars) {
@ -1792,7 +1791,7 @@ void draw_bars(bool unhide) {
} }
/* First things first: clear the backbuffer */ /* First things first: clear the backbuffer */
cairo_clear_surface(&(outputs_walk->buffer), colors.bar_bg); draw_util_clear_surface(&(outputs_walk->buffer), colors.bar_bg);
if (!config.disable_ws) { if (!config.disable_ws) {
i3_ws *ws_walk; i3_ws *ws_walk;
@ -1822,23 +1821,23 @@ void draw_bars(bool unhide) {
} }
/* Draw the border of the button. */ /* Draw the border of the button. */
cairo_draw_rectangle(&(outputs_walk->buffer), border_color, draw_util_rectangle(&(outputs_walk->buffer), border_color,
workspace_width, workspace_width,
logical_px(1), logical_px(1),
ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * 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)); font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
/* Draw the inside of the button. */ /* Draw the inside of the button. */
cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, draw_util_rectangle(&(outputs_walk->buffer), bg_color,
workspace_width + logical_px(1), workspace_width + logical_px(1),
2 * logical_px(1), 2 * logical_px(1),
ws_walk->name_width + 2 * logical_px(ws_hoff_px), ws_walk->name_width + 2 * logical_px(ws_hoff_px),
font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1));
cairo_draw_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color, draw_util_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color,
workspace_width + logical_px(ws_hoff_px) + logical_px(1), workspace_width + logical_px(ws_hoff_px) + logical_px(1),
logical_px(ws_voff_px), logical_px(ws_voff_px),
ws_walk->name_width); ws_walk->name_width);
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width; workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
if (TAILQ_NEXT(ws_walk, tailq) != NULL) if (TAILQ_NEXT(ws_walk, tailq) != NULL)
@ -1852,22 +1851,22 @@ void draw_bars(bool unhide) {
color_t fg_color = colors.binding_mode_fg; color_t fg_color = colors.binding_mode_fg;
color_t bg_color = colors.binding_mode_bg; color_t bg_color = colors.binding_mode_bg;
cairo_draw_rectangle(&(outputs_walk->buffer), colors.binding_mode_border, draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border,
workspace_width, workspace_width,
logical_px(1), logical_px(1),
binding.width + 2 * logical_px(ws_hoff_px) + 2 * 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)); font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, draw_util_rectangle(&(outputs_walk->buffer), bg_color,
workspace_width + logical_px(1), workspace_width + logical_px(1),
2 * logical_px(1), 2 * logical_px(1),
binding.width + 2 * logical_px(ws_hoff_px), binding.width + 2 * logical_px(ws_hoff_px),
font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1));
cairo_draw_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, draw_util_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color,
workspace_width + logical_px(ws_hoff_px) + logical_px(1), workspace_width + logical_px(ws_hoff_px) + logical_px(1),
logical_px(ws_voff_px), logical_px(ws_voff_px),
binding.width); binding.width);
unhide = true; unhide = true;
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width;
@ -1897,8 +1896,8 @@ void draw_bars(bool unhide) {
int x_src = (int16_t)(statusline_width - visible_statusline_width); int x_src = (int16_t)(statusline_width - visible_statusline_width);
int x_dest = (int16_t)(outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width); int x_dest = (int16_t)(outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width);
cairo_copy_surface(&statusline_surface, &(outputs_walk->buffer), x_dest - x_src, 0, draw_util_copy_surface(&statusline_surface, &(outputs_walk->buffer), x_src, 0,
x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height);
} }
workspace_width = 0; workspace_width = 0;
@ -1927,8 +1926,8 @@ void redraw_bars(void) {
continue; continue;
} }
cairo_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, draw_util_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0,
0, 0, outputs_walk->rect.w, outputs_walk->rect.h); 0, 0, outputs_walk->rect.w, outputs_walk->rect.h);
xcb_flush(xcb_connection); xcb_flush(xcb_connection);
} }
} }

@ -133,11 +133,6 @@ static void draw_text_pango(const char *text, size_t text_len,
cairo_move_to(cr, x, y - yoffset); cairo_move_to(cr, x, y - yoffset);
pango_cairo_show_layout(cr, layout); pango_cairo_show_layout(cr, layout);
/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989
* and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */
cairo_surface_flush(surface);
cairo_surface_flush(surface);
/* Free resources */ /* Free resources */
g_object_unref(layout); g_object_unref(layout);
cairo_destroy(cr); cairo_destroy(cr);