From: Michael Stapelberg Date: Fri, 9 Oct 2015 16:32:52 +0000 (-0700) Subject: Merge pull request #1981 from Airblader/feature-i3bar-rgba X-Git-Tag: 4.12~144 X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=commitdiff_plain;h=47723715ce9278be2ec33d3a3fb0a4404e0074f5;hp=ddd5e9a824e27eb9eeec0dcf657aa984c387ad05 Merge pull request #1981 from Airblader/feature-i3bar-rgba Allow 32-bit depth visuals and RGBA in i3bar --- 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..ce1d3180 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; @@ -63,3 +64,28 @@ 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); + +/** + * 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 aff763c3..e1728786 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,68 @@ 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); +} + +/** + * 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); +} + +/** + * 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 f5d065f0..2cd3bd6e 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,15 +174,16 @@ 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_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_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); - 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, - 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); } } @@ -239,9 +244,12 @@ 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_surface_flush(statusline_surface.surface); + cairo_restore(statusline_surface.cr); /* Draw the text of each block. */ uint32_t x = 0; @@ -255,15 +263,17 @@ 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_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); } - 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); + 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. */ @@ -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,14 @@ 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_surface_flush(outputs_walk->buffer.surface); + cairo_restore(outputs_walk->buffer.cr); if (!config.disable_ws) { i3_ws *ws_walk; @@ -1798,23 +1845,24 @@ void draw_bars(bool unhide) { unhide = true; } - 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_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); - - 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, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - ws_walk->name_width); + /* Draw the border of the button. */ + 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_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), + 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) @@ -1828,25 +1876,22 @@ void draw_bars(bool unhide) { color_t fg_color = colors.binding_mode_fg; color_t bg_color = colors.binding_mode_bg; - 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_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); - - 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, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - binding.width); + 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_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), + logical_px(ws_voff_px), + binding.width); unhide = true; workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; @@ -1876,9 +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_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_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; @@ -1906,9 +1950,9 @@ void redraw_bars(void) { if (!outputs_walk->active) { continue; } - 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_copy_surface(&(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/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..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. */ @@ -102,12 +103,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; @@ -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: @@ -391,9 +394,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 +413,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 +438,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 +452,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/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); } 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)