From 1c4100ce5d8f9a7edc46f80f8a20ca50c6d97f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 5 Oct 2015 12:58:05 +0200 Subject: [PATCH 1/4] Use 32-bit visuals for i3bar when possible and allow RGBA colors. This patch creates all necessary windows for i3bar with 32-bit visuals if available. It also introduces the possibility to define RGBA colors (next to RGB colors), which allows the user to set the opacity of any color. This requires running a compositor. With this patch we also start supporting _NET_SYSTEM_TRAY_VISUAL, which is necessary for the tray icons so they create the tray window with the correct depth and visual. --- i3-input/main.c | 4 +- i3-nagbar/main.c | 4 +- i3bar/include/cairo_util.h | 1 + i3bar/src/cairo_util.c | 21 +++++-- i3bar/src/xcb.c | 121 ++++++++++++++++++++++++++++--------- include/libi3.h | 4 +- libi3/font.c | 17 +++--- src/restore_layout.c | 4 +- src/sighandler.c | 2 +- src/x.c | 4 +- 10 files changed, 132 insertions(+), 50 deletions(-) diff --git a/i3-input/main.c b/i3-input/main.c index cf3884e9..4e1be78b 100644 --- a/i3-input/main.c +++ b/i3-input/main.c @@ -141,12 +141,12 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t /* draw the prompt … */ if (prompt != NULL) { - draw_text(prompt, pixmap, pixmap_gc, logical_px(4), logical_px(4), logical_px(492)); + draw_text(prompt, pixmap, pixmap_gc, NULL, logical_px(4), logical_px(4), logical_px(492)); } /* … and the text */ if (input_position > 0) { i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); - draw_text(input, pixmap, pixmap_gc, prompt_offset + logical_px(4), logical_px(4), logical_px(492)); + draw_text(input, pixmap, pixmap_gc, NULL, prompt_offset + logical_px(4), logical_px(4), logical_px(492)); i3string_free(input); } diff --git a/i3-nagbar/main.c b/i3-nagbar/main.c index d86cd69a..d6ace221 100644 --- a/i3-nagbar/main.c +++ b/i3-nagbar/main.c @@ -196,7 +196,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { /* restore font color */ set_font_colors(pixmap_gc, color_text, color_background); - draw_text(prompt, pixmap, pixmap_gc, + draw_text(prompt, pixmap, pixmap_gc, NULL, logical_px(4) + logical_px(4), logical_px(4) + logical_px(4), rect.width - logical_px(4) - logical_px(4)); @@ -264,7 +264,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { values[1] = color_button_background; set_font_colors(pixmap_gc, color_text, color_button_background); /* the x term seems to set left/right padding */ - draw_text(buttons[c].label, pixmap, pixmap_gc, + draw_text(buttons[c].label, pixmap, pixmap_gc, NULL, y - w - line_width + logical_px(6), logical_px(4) + logical_px(3), rect.width - y + w + line_width - logical_px(6)); diff --git a/i3bar/include/cairo_util.h b/i3bar/include/cairo_util.h index 749beec8..390e6a1c 100644 --- a/i3bar/include/cairo_util.h +++ b/i3bar/include/cairo_util.h @@ -15,6 +15,7 @@ typedef struct color_t { double red; double green; double blue; + double alpha; /* For compatibility, we also store the colorpixel for now. */ uint32_t colorpixel; diff --git a/i3bar/src/cairo_util.c b/i3bar/src/cairo_util.c index aff763c3..288969c9 100644 --- a/i3bar/src/cairo_util.c +++ b/i3bar/src/cairo_util.c @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -16,7 +17,7 @@ #include "libi3.h" xcb_connection_t *xcb_connection; -xcb_screen_t *root_screen; +xcb_visualtype_t *visual_type; /* * Initialize the cairo surface to represent the given drawable. @@ -30,7 +31,7 @@ void cairo_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, if (xcb_request_failed(gc_cookie, "Could not create graphical context")) exit(EXIT_FAILURE); - surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, get_visualtype(root_screen), width, height); + surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); surface->cr = cairo_create(surface->surface); } @@ -50,15 +51,25 @@ void cairo_surface_free(surface_t *surface) { * */ color_t cairo_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)}; } @@ -67,5 +78,5 @@ color_t cairo_hex_to_color(const char *color) { * */ void cairo_set_source_color(surface_t *surface, color_t color) { - 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); } diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index f5d065f0..443e30ea 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -64,6 +64,10 @@ static i3Font font; /* Icon size (based on font size) */ int icon_size; +xcb_visualtype_t *visual_type; +uint8_t depth; +xcb_colormap_t colormap; + /* Overall height of the bar (based on font size) */ int bar_height; @@ -170,14 +174,17 @@ static void draw_separator(uint32_t x, struct status_block *block) { uint32_t center_x = x - sep_offset; if (config.separator_symbol == NULL) { /* Draw a classic one pixel, vertical separator. */ + cairo_save(statusline_surface.cr); + cairo_set_operator(statusline_surface.cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_color(&statusline_surface, colors.sep_fg); cairo_rectangle(statusline_surface.cr, center_x, logical_px(sep_voff_px), logical_px(1), bar_height - 2 * logical_px(sep_voff_px)); cairo_fill(statusline_surface.cr); + cairo_restore(statusline_surface.cr); } else { /* Draw a custom separator. */ uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2); set_font_colors(statusline_surface.gc, colors.sep_fg.colorpixel, colors.bar_bg.colorpixel); - draw_text(config.separator_symbol, statusline_surface.id, statusline_surface.gc, + draw_text(config.separator_symbol, statusline_surface.id, statusline_surface.gc, visual_type, separator_x, logical_px(ws_voff_px), x - separator_x); } } @@ -239,9 +246,11 @@ void refresh_statusline(bool use_short_text) { realloc_sl_buffer(); /* Clear the statusline pixmap. */ + cairo_save(statusline_surface.cr); cairo_set_source_color(&statusline_surface, colors.bar_bg); - cairo_rectangle(statusline_surface.cr, 0, 0, MAX(root_screen->width_in_pixels, statusline_width), bar_height); - cairo_fill(statusline_surface.cr); + cairo_set_operator(statusline_surface.cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(statusline_surface.cr); + cairo_restore(statusline_surface.cr); /* Draw the text of each block. */ uint32_t x = 0; @@ -263,7 +272,8 @@ void refresh_statusline(bool use_short_text) { } set_font_colors(statusline_surface.gc, fg_color.colorpixel, colors.bar_bg.colorpixel); - draw_text(block->full_text, statusline_surface.id, statusline_surface.gc, x + block->x_offset, logical_px(ws_voff_px), block->width); + draw_text(block->full_text, statusline_surface.id, statusline_surface.gc, visual_type, + x + block->x_offset, logical_px(ws_voff_px), block->width); x += block->width + block->sep_block_width + block->x_offset + block->x_append; /* If this is not the last block, draw a separator. */ @@ -699,11 +709,15 @@ static void handle_client_message(xcb_client_message_event_t *event) { ELOG("No output found\n"); return; } - xcb_reparent_window(xcb_connection, - client, - output->bar.id, - output->rect.w - icon_size - logical_px(config.tray_padding), - logical_px(config.tray_padding)); + + xcb_void_cookie_t rcookie = xcb_reparent_window(xcb_connection, + client, + output->bar.id, + output->rect.w - icon_size - logical_px(config.tray_padding), + logical_px(config.tray_padding)); + if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?")) + return; + /* We reconfigure the window to use a reasonable size. The systray * specification explicitly says: * Tray icons may be assigned any size by the system tray, and @@ -1106,11 +1120,29 @@ char *init_xcb_early() { root_screen = xcb_aux_get_screen(xcb_connection, screen); xcb_root = root_screen->root; + depth = root_screen->root_depth; + colormap = root_screen->default_colormap; + visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32); + if (visual_type) { + depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id); + colormap = xcb_generate_id(xcb_connection); + xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(xcb_connection, + XCB_COLORMAP_ALLOC_NONE, + colormap, + xcb_root, + visual_type->visual_id); + if (xcb_request_failed(cm_cookie, "Could not allocate colormap")) { + exit(EXIT_FAILURE); + } + } else { + visual_type = get_visualtype(root_screen); + } + /* We draw the statusline to a seperate pixmap, because it looks the same on all bars and * this way, we can choose to crop it */ xcb_pixmap_t statusline_id = xcb_generate_id(xcb_connection); xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, - root_screen->root_depth, + depth, statusline_id, xcb_root, root_screen->width_in_pixels, @@ -1248,17 +1280,17 @@ void init_tray(void) { /* tray support: we need a window to own the selection */ selwin = xcb_generate_id(xcb_connection); - uint32_t selmask = XCB_CW_OVERRIDE_REDIRECT; - uint32_t selval[] = {1}; + uint32_t selmask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP; + uint32_t selval[] = {root_screen->black_pixel, root_screen->black_pixel, 1, colormap}; xcb_create_window(xcb_connection, - root_screen->root_depth, + depth, selwin, xcb_root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, - root_screen->root_visual, + visual_type->visual_id, selmask, selval); @@ -1272,6 +1304,14 @@ void init_tray(void) { 32, 1, &orientation); + xcb_change_property(xcb_connection, + XCB_PROP_MODE_REPLACE, + selwin, + atoms[_NET_SYSTEM_TRAY_VISUAL], + XCB_ATOM_VISUALID, + 32, + 1, + &visual_type->visual_id); init_tray_colors(); @@ -1473,7 +1513,7 @@ void realloc_sl_buffer(void) { xcb_pixmap_t statusline_id = xcb_generate_id(xcb_connection); xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, - root_screen->root_depth, + depth, statusline_id, xcb_root, MAX(root_screen->width_in_pixels, statusline_width), @@ -1551,41 +1591,44 @@ void reconfig_windows(bool redraw_bars) { xcb_window_t bar_id = xcb_generate_id(xcb_connection); xcb_pixmap_t buffer_id = xcb_generate_id(xcb_connection); - mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; - /* Black background */ + mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; + values[0] = colors.bar_bg.colorpixel; + values[1] = root_screen->black_pixel; /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar windows */ - values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1); + values[2] = (config.hide_on_modifier == M_DOCK ? 0 : 1); /* We enable the following EventMask fields: * EXPOSURE, to get expose events (we have to re-draw then) * SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray * child windows use ConfigureWindow * BUTTON_PRESS, to handle clicks on the workspace buttons * */ - values[2] = XCB_EVENT_MASK_EXPOSURE | + values[3] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_BUTTON_PRESS; if (config.hide_on_modifier == M_DOCK) { /* If the bar is normally visible, catch visibility change events to suspend * the status process when the bar is obscured by full-screened windows. */ - values[2] |= XCB_EVENT_MASK_VISIBILITY_CHANGE; + values[3] |= XCB_EVENT_MASK_VISIBILITY_CHANGE; walk->visible = true; } + values[4] = colormap; + xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection, - root_screen->root_depth, + depth, bar_id, xcb_root, walk->rect.x, walk->rect.y + walk->rect.h - bar_height, walk->rect.w, bar_height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, - root_screen->root_visual, + visual_type->visual_id, mask, values); /* The double-buffer we use to render stuff off-screen */ xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, - root_screen->root_depth, + depth, buffer_id, bar_id, walk->rect.w, @@ -1701,7 +1744,7 @@ void reconfig_windows(bool redraw_bars) { DLOG("Recreating buffer for output %s\n", walk->name); xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, - root_screen->root_depth, + depth, walk->buffer.id, walk->bar.id, walk->rect.w, @@ -1766,10 +1809,13 @@ void draw_bars(bool unhide) { /* Oh shit, an active output without an own bar. Create it now! */ reconfig_windows(false); } + /* First things first: clear the backbuffer */ + cairo_save(outputs_walk->buffer.cr); cairo_set_source_color(&(outputs_walk->buffer), colors.bar_bg); - cairo_rectangle(outputs_walk->buffer.cr, 0, 0, outputs_walk->rect.w, bar_height); - cairo_fill(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(outputs_walk->buffer.cr); + cairo_restore(outputs_walk->buffer.cr); if (!config.disable_ws) { i3_ws *ws_walk; @@ -1798,20 +1844,27 @@ void draw_bars(bool unhide) { unhide = true; } + cairo_save(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); + + /* Draw the border of the button. */ cairo_set_source_color(&(outputs_walk->buffer), border_color); cairo_rectangle(outputs_walk->buffer.cr, 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)); cairo_fill(outputs_walk->buffer.cr); + /* Draw the inside of the button. */ cairo_set_source_color(&(outputs_walk->buffer), bg_color); cairo_rectangle(outputs_walk->buffer.cr, workspace_width + logical_px(1), 2 * logical_px(1), ws_walk->name_width + 2 * logical_px(ws_hoff_px), font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); cairo_fill(outputs_walk->buffer.cr); + cairo_restore(outputs_walk->buffer.cr); + set_font_colors(outputs_walk->buffer.gc, fg_color.colorpixel, bg_color.colorpixel); - draw_text(ws_walk->name, outputs_walk->buffer.id, outputs_walk->buffer.gc, + draw_text(ws_walk->name, outputs_walk->buffer.id, outputs_walk->buffer.gc, visual_type, workspace_width + logical_px(ws_hoff_px) + logical_px(1), logical_px(ws_voff_px), ws_walk->name_width); @@ -1828,6 +1881,9 @@ void draw_bars(bool unhide) { color_t fg_color = colors.binding_mode_fg; color_t bg_color = colors.binding_mode_bg; + cairo_save(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_color(&(outputs_walk->buffer), colors.binding_mode_border); cairo_rectangle(outputs_walk->buffer.cr, workspace_width, logical_px(1), binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), @@ -1840,10 +1896,13 @@ void draw_bars(bool unhide) { font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); cairo_fill(outputs_walk->buffer.cr); + cairo_restore(outputs_walk->buffer.cr); + set_font_colors(outputs_walk->buffer.gc, fg_color.colorpixel, bg_color.colorpixel); draw_text(binding.name, outputs_walk->buffer.id, outputs_walk->buffer.gc, + visual_type, workspace_width + logical_px(ws_hoff_px) + logical_px(1), logical_px(ws_voff_px), binding.width); @@ -1876,9 +1935,12 @@ void draw_bars(bool unhide) { 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); + cairo_save(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_surface(outputs_walk->buffer.cr, statusline_surface.surface, x_dest - x_src, 0); cairo_rectangle(outputs_walk->buffer.cr, x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); cairo_fill(outputs_walk->buffer.cr); + cairo_restore(outputs_walk->buffer.cr); } workspace_width = 0; @@ -1906,9 +1968,14 @@ void redraw_bars(void) { if (!outputs_walk->active) { continue; } + + cairo_save(outputs_walk->bar.cr); + cairo_set_operator(outputs_walk->bar.cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_surface(outputs_walk->bar.cr, outputs_walk->buffer.surface, 0, 0); cairo_rectangle(outputs_walk->bar.cr, 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); cairo_fill(outputs_walk->bar.cr); + cairo_restore(outputs_walk->bar.cr); + xcb_flush(xcb_connection); } } diff --git a/include/libi3.h b/include/libi3.h index 9e7ef133..c1e109ef 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -402,8 +402,8 @@ bool font_is_pango(void); * Text must be specified as an i3String. * */ -void draw_text(i3String *text, xcb_drawable_t drawable, - xcb_gcontext_t gc, int x, int y, int max_width); +void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc, + xcb_visualtype_t *visual, int x, int y, int max_width); /** * ASCII version of draw_text to print static strings. diff --git a/libi3/font.c b/libi3/font.c index b8c31b73..8bdf3d60 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -102,12 +102,12 @@ static bool load_pango_font(i3Font *font, const char *desc) { * */ static void draw_text_pango(const char *text, size_t text_len, - xcb_drawable_t drawable, int x, int y, + xcb_drawable_t drawable, xcb_visualtype_t *visual, int x, int y, int max_width, bool is_markup) { /* Create the Pango layout */ /* root_visual_type is cached in load_pango_font */ cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, - root_visual_type, x + max_width, y + savedFont->height); + visual, x + max_width, y + savedFont->height); cairo_t *cr = cairo_create(surface); PangoLayout *layout = create_layout_with_dpi(cr); gint height; @@ -391,9 +391,12 @@ static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawabl * Text must be specified as an i3String. * */ -void draw_text(i3String *text, xcb_drawable_t drawable, - xcb_gcontext_t gc, int x, int y, int max_width) { +void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc, + xcb_visualtype_t *visual, int x, int y, int max_width) { assert(savedFont != NULL); + if (visual == NULL) { + visual = root_visual_type; + } switch (savedFont->type) { case FONT_TYPE_NONE: @@ -407,7 +410,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable, case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), - drawable, x, y, max_width, i3string_is_markup(text)); + drawable, visual, x, y, max_width, i3string_is_markup(text)); return; #endif default: @@ -432,7 +435,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, if (text_len > 255) { /* The text is too long to draw it directly to X */ i3String *str = i3string_from_utf8(text); - draw_text(str, drawable, gc, x, y, max_width); + draw_text(str, drawable, gc, NULL, x, y, max_width); i3string_free(str); } else { /* X11 coordinates for fonts start at the baseline */ @@ -446,7 +449,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(text, strlen(text), - drawable, x, y, max_width, false); + drawable, root_visual_type, x, y, max_width, false); return; #endif default: diff --git a/src/restore_layout.c b/src/restore_layout.c index 439d23cc..70eed523 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -161,7 +161,7 @@ static void update_placeholder_contents(placeholder_state *state) { DLOG("con %p (placeholder 0x%08x) line %d: %s\n", state->con, state->window, n, serialized); i3String *str = i3string_from_utf8(serialized); - draw_text(str, state->pixmap, state->gc, 2, (n * (config.font.height + 2)) + 2, state->rect.width - 2); + draw_text(str, state->pixmap, state->gc, NULL, 2, (n * (config.font.height + 2)) + 2, state->rect.width - 2); i3string_free(str); n++; free(serialized); @@ -172,7 +172,7 @@ static void update_placeholder_contents(placeholder_state *state) { int text_width = predict_text_width(line); int x = (state->rect.width / 2) - (text_width / 2); int y = (state->rect.height / 2) - (config.font.height / 2); - draw_text(line, state->pixmap, state->gc, x, y, text_width); + draw_text(line, state->pixmap, state->gc, NULL, x, y, text_width); i3string_free(line); xcb_flush(conn); xcb_aux_sync(conn); diff --git a/src/sighandler.c b/src/sighandler.c index ceaa4842..555f5e55 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -154,7 +154,7 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei if (i == backtrace_string_index) set_font_colors(pixmap_gc, get_colorpixel(bt_colour), get_colorpixel("#000000")); - draw_text(crash_text_i3strings[i], pixmap, pixmap_gc, + draw_text(crash_text_i3strings[i], pixmap, pixmap_gc, NULL, 8, 5 + i * font_height, width - 16); /* and reset the colour again for other lines */ diff --git a/src/x.c b/src/x.c index 9b9ba6aa..d312666b 100644 --- a/src/x.c +++ b/src/x.c @@ -552,7 +552,7 @@ void x_draw_decoration(Con *con) { FREE(formatted_mark); mark_width = predict_text_width(mark); - draw_text(mark, parent->pixmap, parent->pm_gc, + 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); @@ -561,7 +561,7 @@ 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, + 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)); if (win->title_format != NULL) From a5d4c7c9ab2759676dfe4623eb8848941f38ff7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Wed, 7 Oct 2015 11:03:18 +0200 Subject: [PATCH 2/4] Allow text drawing to use the alpha channel. We pass alpha channel information to the current text drawing code and use it if it is available. The previous behavior of using full opacity for RGB format colors is preserved. --- libi3/font.c | 5 ++++- libi3/get_colorpixel.c | 22 ++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/libi3/font.c b/libi3/font.c index 8bdf3d60..e14bb080 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -29,6 +29,7 @@ static xcb_visualtype_t *root_visual_type; static double pango_font_red; static double pango_font_green; static double pango_font_blue; +static double pango_font_alpha; /* Necessary to track whether the dpi changes and trigger a LOG() message, * which is more easily visible to users. */ @@ -123,7 +124,8 @@ static void draw_text_pango(const char *text, size_t text_len, pango_layout_set_text(layout, text, text_len); /* Do the drawing */ - cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); + cairo_set_source_rgba(cr, pango_font_red, pango_font_green, pango_font_blue, pango_font_alpha); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); pango_cairo_update_layout(cr, layout); pango_layout_get_pixel_size(layout, NULL, &height); /* Center the piece of text vertically if its height is smaller than the @@ -332,6 +334,7 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background pango_font_red = ((foreground >> 16) & 0xff) / 255.0; pango_font_green = ((foreground >> 8) & 0xff) / 255.0; pango_font_blue = (foreground & 0xff) / 255.0; + pango_font_alpha = ((foreground >> 24) & 0xff) / 255.0; break; #endif default: diff --git a/libi3/get_colorpixel.c b/libi3/get_colorpixel.c index 44ad295d..3a62a8e4 100644 --- a/libi3/get_colorpixel.c +++ b/libi3/get_colorpixel.c @@ -7,6 +7,7 @@ */ #include #include +#include #include "libi3.h" @@ -25,14 +26,23 @@ * */ uint32_t get_colorpixel(const char *hex) { - char strgroups[3][3] = {{hex[1], hex[2], '\0'}, - {hex[3], hex[4], '\0'}, - {hex[5], hex[6], '\0'}}; + char alpha[2]; + if (strlen(hex) == strlen("#rrggbbaa")) { + alpha[0] = hex[7]; + alpha[1] = hex[8]; + } else { + alpha[0] = alpha[1] = 'F'; + } + + char strgroups[4][3] = { + {hex[1], hex[2], '\0'}, + {hex[3], hex[4], '\0'}, + {hex[5], hex[6], '\0'}, + {alpha[0], alpha[1], '\0'}}; uint8_t r = strtol(strgroups[0], NULL, 16); uint8_t g = strtol(strgroups[1], NULL, 16); uint8_t b = strtol(strgroups[2], NULL, 16); + uint8_t a = strtol(strgroups[3], NULL, 16); - /* We set the first 8 bits high to have 100% opacity in case of a 32 bit - * color depth visual. */ - return (0xFF << 24) | (r << 16 | g << 8 | b); + return (a << 24) | (r << 16 | g << 8 | b); } From ff0aeddede9ae0c91c5e970ab4c8b2aa18476c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Thu, 8 Oct 2015 12:16:25 +0200 Subject: [PATCH 3/4] When drawing text, mark the surface as dirty. Since libi3 currently creates its own cairo surface for drawing text, we need to mark our own surface as dirty to force cairo to invalidate its cache. Otherwise, this will result in graphical glitches such as the text not showing up at all. This wrapper can be removed in the future when libi3 is adapted to reuse the same cairo surface as we do for all other drawing operations. --- i3bar/include/cairo_util.h | 8 ++++++ i3bar/src/cairo_util.c | 13 ++++++++++ i3bar/src/xcb.c | 53 +++++++++++++++++++++----------------- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/i3bar/include/cairo_util.h b/i3bar/include/cairo_util.h index 390e6a1c..de3fa805 100644 --- a/i3bar/include/cairo_util.h +++ b/i3bar/include/cairo_util.h @@ -64,3 +64,11 @@ color_t cairo_hex_to_color(const char *color); * */ void cairo_set_source_color(surface_t *surface, color_t 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 cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); diff --git a/i3bar/src/cairo_util.c b/i3bar/src/cairo_util.c index 288969c9..75a99d2f 100644 --- a/i3bar/src/cairo_util.c +++ b/i3bar/src/cairo_util.c @@ -80,3 +80,16 @@ color_t cairo_hex_to_color(const char *color) { void cairo_set_source_color(surface_t *surface, color_t color) { cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha); } + +/** + * 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 cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { + set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel); + draw_text(text, surface->id, surface->gc, visual_type, x, y, max_width); + + cairo_surface_mark_dirty(surface->surface); +} diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 443e30ea..8f9c94b0 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -179,13 +179,13 @@ static void draw_separator(uint32_t x, struct status_block *block) { cairo_set_source_color(&statusline_surface, colors.sep_fg); cairo_rectangle(statusline_surface.cr, center_x, logical_px(sep_voff_px), logical_px(1), bar_height - 2 * logical_px(sep_voff_px)); cairo_fill(statusline_surface.cr); + cairo_surface_flush(statusline_surface.surface); cairo_restore(statusline_surface.cr); } else { /* Draw a custom separator. */ uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2); - set_font_colors(statusline_surface.gc, colors.sep_fg.colorpixel, colors.bar_bg.colorpixel); - draw_text(config.separator_symbol, statusline_surface.id, statusline_surface.gc, visual_type, - separator_x, logical_px(ws_voff_px), x - separator_x); + cairo_draw_text(config.separator_symbol, &statusline_surface, colors.sep_fg, colors.bar_bg, + separator_x, logical_px(ws_voff_px), x - separator_x); } } @@ -250,6 +250,7 @@ void refresh_statusline(bool use_short_text) { cairo_set_source_color(&statusline_surface, colors.bar_bg); cairo_set_operator(statusline_surface.cr, CAIRO_OPERATOR_SOURCE); cairo_paint(statusline_surface.cr); + cairo_surface_flush(statusline_surface.surface); cairo_restore(statusline_surface.cr); /* Draw the text of each block. */ @@ -267,13 +268,13 @@ void refresh_statusline(bool use_short_text) { cairo_set_source_color(&statusline_surface, colors.urgent_ws_bg); cairo_rectangle(statusline_surface.cr, x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2)); cairo_fill(statusline_surface.cr); + cairo_surface_flush(statusline_surface.surface); } else { fg_color = (block->color ? cairo_hex_to_color(block->color) : colors.bar_fg); } - set_font_colors(statusline_surface.gc, fg_color.colorpixel, colors.bar_bg.colorpixel); - draw_text(block->full_text, statusline_surface.id, statusline_surface.gc, visual_type, - x + block->x_offset, logical_px(ws_voff_px), block->width); + cairo_draw_text(block->full_text, &statusline_surface, fg_color, colors.bar_bg, + x + block->x_offset, logical_px(ws_voff_px), block->width); x += block->width + block->sep_block_width + block->x_offset + block->x_append; /* If this is not the last block, draw a separator. */ @@ -1815,6 +1816,7 @@ void draw_bars(bool unhide) { cairo_set_source_color(&(outputs_walk->buffer), colors.bar_bg); cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); cairo_paint(outputs_walk->buffer.cr); + cairo_surface_flush(outputs_walk->buffer.surface); cairo_restore(outputs_walk->buffer.cr); if (!config.disable_ws) { @@ -1844,30 +1846,32 @@ void draw_bars(bool unhide) { unhide = true; } + /* Draw the border of the button. */ cairo_save(outputs_walk->buffer.cr); cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - - /* Draw the border of the button. */ cairo_set_source_color(&(outputs_walk->buffer), border_color); cairo_rectangle(outputs_walk->buffer.cr, 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)); cairo_fill(outputs_walk->buffer.cr); + cairo_surface_flush(outputs_walk->buffer.surface); + cairo_restore(outputs_walk->buffer.cr); /* Draw the inside of the button. */ + cairo_save(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_color(&(outputs_walk->buffer), bg_color); cairo_rectangle(outputs_walk->buffer.cr, workspace_width + logical_px(1), 2 * logical_px(1), ws_walk->name_width + 2 * logical_px(ws_hoff_px), font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); cairo_fill(outputs_walk->buffer.cr); - + cairo_surface_flush(outputs_walk->buffer.surface); cairo_restore(outputs_walk->buffer.cr); - set_font_colors(outputs_walk->buffer.gc, fg_color.colorpixel, bg_color.colorpixel); - draw_text(ws_walk->name, outputs_walk->buffer.id, outputs_walk->buffer.gc, visual_type, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - ws_walk->name_width); + cairo_draw_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color, + workspace_width + logical_px(ws_hoff_px) + logical_px(1), + logical_px(ws_voff_px), + 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) @@ -1883,29 +1887,28 @@ void draw_bars(bool unhide) { cairo_save(outputs_walk->buffer.cr); cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&(outputs_walk->buffer), colors.binding_mode_border); cairo_rectangle(outputs_walk->buffer.cr, 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)); cairo_fill(outputs_walk->buffer.cr); + cairo_surface_flush(outputs_walk->buffer.surface); + cairo_restore(outputs_walk->buffer.cr); + cairo_save(outputs_walk->buffer.cr); + cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_color(&(outputs_walk->buffer), bg_color); cairo_rectangle(outputs_walk->buffer.cr, workspace_width + logical_px(1), 2 * logical_px(1), binding.width + 2 * logical_px(ws_hoff_px), font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); cairo_fill(outputs_walk->buffer.cr); - + cairo_surface_flush(outputs_walk->buffer.surface); cairo_restore(outputs_walk->buffer.cr); - set_font_colors(outputs_walk->buffer.gc, fg_color.colorpixel, bg_color.colorpixel); - draw_text(binding.name, - outputs_walk->buffer.id, - outputs_walk->buffer.gc, - visual_type, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - binding.width); + cairo_draw_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, + workspace_width + logical_px(ws_hoff_px) + logical_px(1), + logical_px(ws_voff_px), + binding.width); unhide = true; workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; @@ -1940,6 +1943,7 @@ void draw_bars(bool unhide) { cairo_set_source_surface(outputs_walk->buffer.cr, statusline_surface.surface, x_dest - x_src, 0); cairo_rectangle(outputs_walk->buffer.cr, x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); cairo_fill(outputs_walk->buffer.cr); + cairo_surface_flush(outputs_walk->buffer.surface); cairo_restore(outputs_walk->buffer.cr); } @@ -1974,6 +1978,7 @@ void redraw_bars(void) { cairo_set_source_surface(outputs_walk->bar.cr, outputs_walk->buffer.surface, 0, 0); cairo_rectangle(outputs_walk->bar.cr, 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); cairo_fill(outputs_walk->bar.cr); + cairo_surface_flush(outputs_walk->bar.surface); cairo_restore(outputs_walk->bar.cr); xcb_flush(xcb_connection); From d300a076606be3d5b74d42a51c312ff2f7791f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Thu, 8 Oct 2015 12:31:56 +0200 Subject: [PATCH 4/4] Refactor cairo drawing of rectangles into utility functions. --- i3bar/include/cairo_util.h | 17 +++++++ i3bar/src/cairo_util.c | 50 ++++++++++++++++++++ i3bar/src/xcb.c | 96 ++++++++++++++------------------------ 3 files changed, 101 insertions(+), 62 deletions(-) diff --git a/i3bar/include/cairo_util.h b/i3bar/include/cairo_util.h index de3fa805..ce1d3180 100644 --- a/i3bar/include/cairo_util.h +++ b/i3bar/include/cairo_util.h @@ -72,3 +72,20 @@ void cairo_set_source_color(surface_t *surface, color_t color); * */ void cairo_draw_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. + * 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); + +/** + * 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, + double dest_x, double dest_y, double dest_w, double dest_h); diff --git a/i3bar/src/cairo_util.c b/i3bar/src/cairo_util.c index 75a99d2f..e1728786 100644 --- a/i3bar/src/cairo_util.c +++ b/i3bar/src/cairo_util.c @@ -93,3 +93,53 @@ void cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color cairo_surface_mark_dirty(surface->surface); } + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * 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) { + cairo_save(surface->cr); + + /* 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 + * allows better color control for the user when using opacity. */ + cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_color(surface, color); + + cairo_rectangle(surface->cr, x, y, w, h); + cairo_fill(surface->cr); + + /* Make sure we flush the surface for any text drawing operations that could follow. + * Since we support drawing text via XCB, we need this. */ + cairo_surface_flush(surface->surface); + + cairo_restore(surface->cr); +} + +/** + * 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, + double dest_x, double dest_y, double dest_w, double dest_h) { + cairo_save(dest->cr); + + /* 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 + * 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, src_x, src_y); + + cairo_rectangle(dest->cr, dest_x, dest_y, dest_w, dest_h); + cairo_fill(dest->cr); + + /* Make sure we flush the surface for any text drawing operations that could follow. + * Since we support drawing text via XCB, we need this. */ + cairo_surface_flush(dest->surface); + cairo_restore(dest->cr); +} diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 8f9c94b0..2cd3bd6e 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -174,13 +174,11 @@ static void draw_separator(uint32_t x, struct status_block *block) { uint32_t center_x = x - sep_offset; if (config.separator_symbol == NULL) { /* Draw a classic one pixel, vertical separator. */ - cairo_save(statusline_surface.cr); - cairo_set_operator(statusline_surface.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&statusline_surface, colors.sep_fg); - cairo_rectangle(statusline_surface.cr, center_x, logical_px(sep_voff_px), logical_px(1), bar_height - 2 * logical_px(sep_voff_px)); - cairo_fill(statusline_surface.cr); - cairo_surface_flush(statusline_surface.surface); - cairo_restore(statusline_surface.cr); + cairo_draw_rectangle(&statusline_surface, colors.sep_fg, + center_x, + logical_px(sep_voff_px), + logical_px(1), + bar_height - 2 * logical_px(sep_voff_px)); } else { /* Draw a custom separator. */ uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2); @@ -265,10 +263,11 @@ void refresh_statusline(bool use_short_text) { fg_color = colors.urgent_ws_fg; /* Draw the background */ - cairo_set_source_color(&statusline_surface, colors.urgent_ws_bg); - cairo_rectangle(statusline_surface.cr, x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2)); - cairo_fill(statusline_surface.cr); - cairo_surface_flush(statusline_surface.surface); + cairo_draw_rectangle(&statusline_surface, colors.urgent_ws_bg, + x - logical_px(2), + logical_px(1), + block->width + logical_px(4), + bar_height - logical_px(2)); } else { fg_color = (block->color ? cairo_hex_to_color(block->color) : colors.bar_fg); } @@ -1847,26 +1846,18 @@ void draw_bars(bool unhide) { } /* Draw the border of the button. */ - cairo_save(outputs_walk->buffer.cr); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&(outputs_walk->buffer), border_color); - cairo_rectangle(outputs_walk->buffer.cr, 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)); - cairo_fill(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + cairo_draw_rectangle(&(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. */ - cairo_save(outputs_walk->buffer.cr); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&(outputs_walk->buffer), bg_color); - cairo_rectangle(outputs_walk->buffer.cr, workspace_width + logical_px(1), 2 * logical_px(1), - ws_walk->name_width + 2 * logical_px(ws_hoff_px), - font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); - cairo_fill(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, + workspace_width + logical_px(1), + 2 * logical_px(1), + ws_walk->name_width + 2 * logical_px(ws_hoff_px), + 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, workspace_width + logical_px(ws_hoff_px) + logical_px(1), @@ -1885,25 +1876,17 @@ void draw_bars(bool unhide) { color_t fg_color = colors.binding_mode_fg; color_t bg_color = colors.binding_mode_bg; - cairo_save(outputs_walk->buffer.cr); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&(outputs_walk->buffer), colors.binding_mode_border); - cairo_rectangle(outputs_walk->buffer.cr, 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)); - cairo_fill(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + cairo_draw_rectangle(&(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)); - cairo_save(outputs_walk->buffer.cr); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(&(outputs_walk->buffer), bg_color); - cairo_rectangle(outputs_walk->buffer.cr, workspace_width + logical_px(1), 2 * logical_px(1), - binding.width + 2 * logical_px(ws_hoff_px), - font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); - cairo_fill(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, + workspace_width + logical_px(1), + 2 * logical_px(1), + binding.width + 2 * logical_px(ws_hoff_px), + font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); cairo_draw_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, workspace_width + logical_px(ws_hoff_px) + logical_px(1), @@ -1938,13 +1921,8 @@ void draw_bars(bool unhide) { 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); - cairo_save(outputs_walk->buffer.cr); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(outputs_walk->buffer.cr, statusline_surface.surface, x_dest - x_src, 0); - cairo_rectangle(outputs_walk->buffer.cr, x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); - cairo_fill(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + cairo_copy_surface(&statusline_surface, &(outputs_walk->buffer), x_dest - x_src, 0, + x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); } workspace_width = 0; @@ -1973,14 +1951,8 @@ void redraw_bars(void) { continue; } - cairo_save(outputs_walk->bar.cr); - cairo_set_operator(outputs_walk->bar.cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(outputs_walk->bar.cr, outputs_walk->buffer.surface, 0, 0); - cairo_rectangle(outputs_walk->bar.cr, 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); - cairo_fill(outputs_walk->bar.cr); - cairo_surface_flush(outputs_walk->bar.surface); - cairo_restore(outputs_walk->bar.cr); - + cairo_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, + 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); xcb_flush(xcb_connection); } }