From b66504988366b123ebaa5e45160d584495c1f9a1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ingo=20B=C3=BCrk?= Date: Mon, 16 Nov 2015 21:26:06 +0100 Subject: [PATCH] Migrate i3 rendering to cairo. This patch migrates all decoration rendering of i3 to cairo. Using the compile switch CAIRO_SUPPORT, rendering can be switched back to the previous XCB behavior, just like with the previous migration to cairo in i3bar. This patch also fixes a bug in draw_util.c where copying one surface to another would use incorrect coordinates if the source coordinates are not 0, 0. Furthermore, this patch implicitly fixes some minor issues in the decoration rendering which would be ignored previously due to the fact that errors would only show up in the event queue, but not cause the rendering code path to crash. One example is zero-height pixmaps which are not allowed. Using cairo, these would cause i3 to instantly segfault, so this patch avoids this. Lastly, this patch annotates other issues found but not fixed in this patch using TODO comments, e.g., the zero-height check not working correctly and the comment that it should probably work the same way for zero-width pixmaps. relates to #1278 --- i3bar/src/xcb.c | 40 +++--- include/data.h | 7 +- include/libi3.h | 21 +++- include/xcb.h | 1 + libi3/draw_util.c | 66 ++++++---- src/con.c | 2 +- src/handlers.c | 11 +- src/main.c | 2 + src/manage.c | 2 +- src/x.c | 315 ++++++++++++++++++++++++---------------------- src/xcb.c | 16 +++ 11 files changed, 276 insertions(+), 207 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 92b0d1ab..c6c9846b 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -174,7 +174,7 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b uint32_t center_x = x - sep_offset; if (config.separator_symbol == NULL) { /* Draw a classic one pixel, vertical separator. */ - draw_util_rectangle(&output->statusline_buffer, sep_fg, + draw_util_rectangle(xcb_connection, &output->statusline_buffer, sep_fg, center_x, logical_px(sep_voff_px), logical_px(1), @@ -243,7 +243,7 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color struct status_block *block; color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg); - draw_util_clear_surface(&output->statusline_buffer, bar_color); + draw_util_clear_surface(xcb_connection, &output->statusline_buffer, bar_color); /* Use unsigned integer wraparound to clip off the left side. * For example, if clip_left is 75, then x will start at the very large @@ -294,13 +294,13 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color } /* Draw the border. */ - draw_util_rectangle(&output->statusline_buffer, border_color, + draw_util_rectangle(xcb_connection, &output->statusline_buffer, border_color, x, logical_px(1), full_render_width, bar_height - logical_px(2)); /* Draw the background. */ - draw_util_rectangle(&output->statusline_buffer, bg_color, + draw_util_rectangle(xcb_connection, &output->statusline_buffer, bg_color, x + border_width, logical_px(1) + border_width, full_render_width - 2 * border_width, @@ -1709,9 +1709,9 @@ void reconfig_windows(bool redraw_bars) { 1, (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]); - draw_util_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height); - draw_util_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height); - draw_util_surface_init(&walk->statusline_buffer, statusline_buffer_id, walk->rect.w, bar_height); + draw_util_surface_init(xcb_connection, &walk->bar, bar_id, NULL, walk->rect.w, bar_height); + draw_util_surface_init(xcb_connection, &walk->buffer, buffer_id, NULL, walk->rect.w, bar_height); + draw_util_surface_init(xcb_connection, &walk->statusline_buffer, statusline_buffer_id, NULL, walk->rect.w, bar_height); xcb_void_cookie_t strut_cookie = config_strut_partial(walk); @@ -1820,12 +1820,12 @@ void reconfig_windows(bool redraw_bars) { walk->rect.w, bar_height); - draw_util_surface_free(&(walk->bar)); - draw_util_surface_free(&(walk->buffer)); - draw_util_surface_free(&(walk->statusline_buffer)); - draw_util_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height); - draw_util_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height); - draw_util_surface_init(&(walk->statusline_buffer), walk->statusline_buffer.id, walk->rect.w, bar_height); + draw_util_surface_free(xcb_connection, &(walk->bar)); + draw_util_surface_free(xcb_connection, &(walk->buffer)); + draw_util_surface_free(xcb_connection, &(walk->statusline_buffer)); + draw_util_surface_init(xcb_connection, &(walk->bar), walk->bar.id, NULL, walk->rect.w, bar_height); + draw_util_surface_init(xcb_connection, &(walk->buffer), walk->buffer.id, NULL, walk->rect.w, bar_height); + draw_util_surface_init(xcb_connection, &(walk->statusline_buffer), walk->statusline_buffer.id, NULL, walk->rect.w, bar_height); xcb_void_cookie_t map_cookie, umap_cookie; if (redraw_bars) { @@ -1886,7 +1886,7 @@ void draw_bars(bool unhide) { bool use_focus_colors = output_has_focus(outputs_walk); /* First things first: clear the backbuffer */ - draw_util_clear_surface(&(outputs_walk->buffer), + draw_util_clear_surface(xcb_connection, &(outputs_walk->buffer), (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg)); if (!config.disable_ws) { @@ -1917,14 +1917,14 @@ void draw_bars(bool unhide) { } /* Draw the border of the button. */ - draw_util_rectangle(&(outputs_walk->buffer), border_color, + draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), border_color, workspace_width, logical_px(1), ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); /* Draw the inside of the button. */ - draw_util_rectangle(&(outputs_walk->buffer), bg_color, + draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), bg_color, workspace_width + logical_px(1), 2 * logical_px(1), ws_walk->name_width + 2 * logical_px(ws_hoff_px), @@ -1947,13 +1947,13 @@ void draw_bars(bool unhide) { color_t fg_color = colors.binding_mode_fg; color_t bg_color = colors.binding_mode_bg; - draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border, + draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), colors.binding_mode_border, workspace_width, logical_px(1), binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); - draw_util_rectangle(&(outputs_walk->buffer), bg_color, + draw_util_rectangle(xcb_connection, &(outputs_walk->buffer), bg_color, workspace_width + logical_px(1), 2 * logical_px(1), binding.width + 2 * logical_px(ws_hoff_px), @@ -1989,7 +1989,7 @@ void draw_bars(bool unhide) { int x_dest = outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width; draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text); - draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0, + draw_util_copy_surface(xcb_connection, &outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0, x_dest, 0, visible_statusline_width, (int16_t)bar_height); outputs_walk->statusline_width = statusline_width; @@ -2020,7 +2020,7 @@ void redraw_bars(void) { continue; } - draw_util_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, + draw_util_copy_surface(xcb_connection, &(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); xcb_flush(xcb_connection); } diff --git a/include/data.h b/include/data.h index e5a76462..78a42ff4 100644 --- a/include/data.h +++ b/include/data.h @@ -551,11 +551,10 @@ struct Con { * change. */ uint8_t ignore_unmap; - /* ids/pixmap/graphics context for the frame window */ + /* The surface used for the frame window. */ + surface_t frame; + surface_t frame_buffer; bool pixmap_recreated; - xcb_window_t frame; - xcb_pixmap_t pixmap; - xcb_gcontext_t pm_gc; enum { CT_ROOT = 0, diff --git a/include/libi3.h b/include/libi3.h index 15dffdb7..0cb2532f 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -513,6 +513,8 @@ typedef struct surface_t { /* A classic XCB graphics context. */ xcb_gcontext_t gc; + xcb_visualtype_t *visual_type; + int width; int height; @@ -530,13 +532,20 @@ typedef struct surface_t { * Initialize the surface to represent the given drawable. * */ -void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height); +void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable, + xcb_visualtype_t *visual, int width, int height); + +/** + * Resize the surface to the given size. + * + */ +void draw_util_surface_set_size(surface_t *surface, int width, int height); /** * Destroys the surface. * */ -void draw_util_surface_free(surface_t *surface); +void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface); /** * Parses the given color in hex format to an internal color representation. @@ -545,6 +554,8 @@ void draw_util_surface_free(surface_t *surface); */ color_t draw_util_hex_to_color(const char *color); +color_t draw_util_colorpixel_to_color(uint32_t colorpixel); + /** * Draw the given text using libi3. * This function also marks the surface dirty which is needed if other means of @@ -559,17 +570,17 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_ * surface as well as restoring the cairo state. * */ -void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h); +void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h); /** * Clears a surface with the given color. * */ -void draw_util_clear_surface(surface_t *surface, color_t color); +void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color); /** * Copies a surface onto another surface. * */ -void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, +void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y, double dest_x, double dest_y, double width, double height); diff --git a/include/xcb.h b/include/xcb.h index c1f989bd..7fae41f5 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -139,6 +139,7 @@ void xcb_set_root_cursor(int cursor); * */ uint16_t get_visual_depth(xcb_visualid_t visual_id); +xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id); /** * Get visualid with specified depth diff --git a/libi3/draw_util.c b/libi3/draw_util.c index 90b1f26f..e7e7c09c 100644 --- a/libi3/draw_util.c +++ b/libi3/draw_util.c @@ -17,32 +17,34 @@ #include "libi3.h" -xcb_connection_t *xcb_connection; +/* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */ xcb_visualtype_t *visual_type; /* Forward declarations */ -static void draw_util_set_source_color(surface_t *surface, color_t color); +static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color); /* * Initialize the surface to represent the given drawable. * */ -void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) { +void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable, + xcb_visualtype_t *visual, int width, int height) { surface->id = drawable; + surface->visual_type = (visual == NULL) ? visual_type : visual; surface->width = width; surface->height = height; - surface->gc = xcb_generate_id(xcb_connection); - xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection, surface->gc, surface->id, 0, NULL); + surface->gc = xcb_generate_id(conn); + xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(conn, surface->gc, surface->id, 0, NULL); - xcb_generic_error_t *error = xcb_request_check(xcb_connection, gc_cookie); + xcb_generic_error_t *error = xcb_request_check(conn, gc_cookie); if (error != NULL) { ELOG("Could not create graphical context. Error code: %d\n", error->error_code); exit(EXIT_FAILURE); } #ifdef CAIRO_SUPPORT - surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); + surface->surface = cairo_xcb_surface_create(conn, surface->id, surface->visual_type, width, height); surface->cr = cairo_create(surface->surface); #endif } @@ -51,14 +53,26 @@ void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int wid * Destroys the surface. * */ -void draw_util_surface_free(surface_t *surface) { - xcb_free_gc(xcb_connection, surface->gc); +void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) { + xcb_free_gc(conn, surface->gc); #ifdef CAIRO_SUPPORT cairo_surface_destroy(surface->surface); cairo_destroy(surface->cr); #endif } +/* + * Resize the surface to the given size. + * + */ +void draw_util_surface_set_size(surface_t *surface, int width, int height) { + surface->width = width; + surface->height = height; +#ifdef CAIRO_SUPPORT + cairo_xcb_surface_set_size(surface->surface, width, height); +#endif +} + /* * Parses the given color in hex format to an internal color representation. * Note that the input must begin with a hash sign, e.g., "#3fbc59". @@ -77,16 +91,24 @@ color_t draw_util_hex_to_color(const char *color) { .colorpixel = get_colorpixel(color)}; } +color_t draw_util_colorpixel_to_color(uint32_t colorpixel) { + return (color_t){ + .red = ((colorpixel >> 16) & 0xFF) / 255.0, + .green = ((colorpixel >> 8) & 0xFF) / 255.0, + .blue = (colorpixel & 0xFF) / 255.0, + .colorpixel = colorpixel}; +} + /* * Set the given color as the source color on the surface. * */ -static void draw_util_set_source_color(surface_t *surface, color_t color) { +static void draw_util_set_source_color(xcb_connection_t *conn, surface_t *surface, color_t color) { #ifdef CAIRO_SUPPORT cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); #else uint32_t colorpixel = color.colorpixel; - xcb_change_gc(xcb_connection, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, + xcb_change_gc(conn, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, (uint32_t[]){colorpixel, colorpixel}); #endif } @@ -104,7 +126,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_ #endif set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel); - draw_text(text, surface->id, surface->gc, visual_type, x, y, max_width); + draw_text(text, surface->id, surface->gc, surface->visual_type, x, y, max_width); #ifdef CAIRO_SUPPORT /* Notify cairo that we (possibly) used another way to draw on the surface. */ @@ -118,7 +140,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_ * surface as well as restoring the cairo state. * */ -void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) { +void draw_util_rectangle(xcb_connection_t *conn, surface_t *surface, color_t color, double x, double y, double w, double h) { #ifdef CAIRO_SUPPORT cairo_save(surface->cr); @@ -126,7 +148,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, * onto the surface rather than blending it. This is a bit more efficient and * allows better color control for the user when using opacity. */ cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); - draw_util_set_source_color(surface, color); + draw_util_set_source_color(conn, surface, color); cairo_rectangle(surface->cr, x, y, w, h); cairo_fill(surface->cr); @@ -137,10 +159,10 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, cairo_restore(surface->cr); #else - draw_util_set_source_color(surface, color); + draw_util_set_source_color(conn, surface, color); xcb_rectangle_t rect = {x, y, w, h}; - xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); + xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect); #endif } @@ -148,7 +170,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, * Clears a surface with the given color. * */ -void draw_util_clear_surface(surface_t *surface, color_t color) { +void draw_util_clear_surface(xcb_connection_t *conn, surface_t *surface, color_t color) { #ifdef CAIRO_SUPPORT cairo_save(surface->cr); @@ -156,7 +178,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) { * onto the surface rather than blending it. This is a bit more efficient and * allows better color control for the user when using opacity. */ cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); - draw_util_set_source_color(surface, color); + draw_util_set_source_color(conn, surface, color); cairo_paint(surface->cr); @@ -166,10 +188,10 @@ void draw_util_clear_surface(surface_t *surface, color_t color) { cairo_restore(surface->cr); #else - draw_util_set_source_color(surface, color); + draw_util_set_source_color(conn, surface, color); xcb_rectangle_t rect = {0, 0, surface->width, surface->height}; - xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); + xcb_poly_fill_rectangle(conn, surface->id, surface->gc, 1, &rect); #endif } @@ -177,7 +199,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) { * Copies a surface onto another surface. * */ -void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, +void draw_util_copy_surface(xcb_connection_t *conn, surface_t *src, surface_t *dest, double src_x, double src_y, double dest_x, double dest_y, double width, double height) { #ifdef CAIRO_SUPPORT cairo_save(dest->cr); @@ -198,7 +220,7 @@ void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, doubl cairo_restore(dest->cr); #else - xcb_copy_area(xcb_connection, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, + xcb_copy_area(conn, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height); #endif } diff --git a/src/con.c b/src/con.c index bd002d35..03fd0ee6 100644 --- a/src/con.c +++ b/src/con.c @@ -514,7 +514,7 @@ Con *con_by_window_id(xcb_window_t window) { Con *con_by_frame_id(xcb_window_t frame) { Con *con; TAILQ_FOREACH(con, &all_cons, all_cons) - if (con->frame == frame) + if (con->frame.id == frame) return con; return NULL; } diff --git a/src/handlers.c b/src/handlers.c index e3ddf701..6f08d25c 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -656,15 +656,14 @@ static void handle_expose_event(xcb_expose_event_t *event) { return; } - /* Since we render to our pixmap on every change anyways, expose events + /* Since we render to our surface on every change anyways, expose events * only tell us that the X server lost (parts of) the window contents. We - * can handle that by copying the appropriate part from our pixmap to the + * can handle that by copying the appropriate part from our surface to the * window. */ - xcb_copy_area(conn, parent->pixmap, parent->frame, parent->pm_gc, - event->x, event->y, event->x, event->y, - event->width, event->height); + draw_util_copy_surface(conn, &(parent->frame_buffer), &(parent->frame), + event->x, event->y, event->x, event->y, + event->width, event->height); xcb_flush(conn); - return; } diff --git a/src/main.c b/src/main.c index 59c00146..1e0ec4a5 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,7 @@ xcb_window_t root; * otherwise the root window’s default (usually 24 bit TrueColor). */ uint8_t root_depth; xcb_visualid_t visual_id; +xcb_visualtype_t *visual_type; xcb_colormap_t colormap; struct ev_loop *main_loop; @@ -487,6 +488,7 @@ int main(int argc, char *argv[]) { * transparency) and use it if so. */ root_depth = root_screen->root_depth; visual_id = root_screen->root_visual; + visual_type = get_visualtype(root_screen); colormap = root_screen->default_colormap; DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_id); diff --git a/src/manage.c b/src/manage.c index 87d19ff8..05ac15f0 100644 --- a/src/manage.c +++ b/src/manage.c @@ -473,7 +473,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki values[0] = XCB_NONE; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); - xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0); + xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame.id, 0, 0); if (xcb_request_check(conn, rcookie) != NULL) { LOG("Could not reparent the window, aborting\n"); goto geom_out; diff --git a/src/x.c b/src/x.c index 094a33f0..86a0c797 100644 --- a/src/x.c +++ b/src/x.c @@ -144,10 +144,11 @@ void x_con_init(Con *con, uint16_t depth) { } Rect dims = {-15, -15, 10, 10}; - con->frame = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); + xcb_window_t frame_id = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); + draw_util_surface_init(conn, &(con->frame), frame_id, get_visualtype_by_id(visual), dims.width, dims.height); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, - con->frame, + con->frame.id, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, @@ -158,7 +159,7 @@ void x_con_init(Con *con, uint16_t depth) { xcb_free_colormap(conn, win_colormap); struct con_state *state = scalloc(1, sizeof(struct con_state)); - state->id = con->frame; + state->id = con->frame.id; state->mapped = false; state->initial = true; DLOG("Adding window 0x%08x to lists\n", state->id); @@ -177,7 +178,7 @@ void x_con_init(Con *con, uint16_t depth) { void x_reinit(Con *con) { struct con_state *state; - if ((state = state_for_frame(con->frame)) == NULL) { + if ((state = state_for_frame(con->frame.id)) == NULL) { ELOG("window state not found\n"); return; } @@ -196,13 +197,13 @@ void x_reinit(Con *con) { */ void x_reparent_child(Con *con, Con *old) { struct con_state *state; - if ((state = state_for_frame(con->frame)) == NULL) { + if ((state = state_for_frame(con->frame.id)) == NULL) { ELOG("window state for con not found\n"); return; } state->need_reparent = true; - state->old_frame = old->frame; + state->old_frame = old->frame.id; } /* @@ -212,12 +213,12 @@ void x_reparent_child(Con *con, Con *old) { void x_move_win(Con *src, Con *dest) { struct con_state *state_src, *state_dest; - if ((state_src = state_for_frame(src->frame)) == NULL) { + if ((state_src = state_for_frame(src->frame.id)) == NULL) { ELOG("window state for src not found\n"); return; } - if ((state_dest = state_for_frame(dest->frame)) == NULL) { + if ((state_dest = state_for_frame(dest->frame.id)) == NULL) { ELOG("window state for dest not found\n"); return; } @@ -239,10 +240,11 @@ void x_move_win(Con *src, Con *dest) { void x_con_kill(Con *con) { con_state *state; - xcb_destroy_window(conn, con->frame); - xcb_free_pixmap(conn, con->pixmap); - xcb_free_gc(conn, con->pm_gc); - state = state_for_frame(con->frame); + draw_util_surface_free(conn, &(con->frame)); + draw_util_surface_free(conn, &(con->frame_buffer)); + xcb_destroy_window(conn, con->frame.id); + xcb_free_pixmap(conn, con->frame_buffer.id); + state = state_for_frame(con->frame.id); CIRCLEQ_REMOVE(&state_head, state, state); CIRCLEQ_REMOVE(&old_state_head, state, old_state); TAILQ_REMOVE(&initial_mapping_head, state, initial_mapping_order); @@ -312,6 +314,54 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) { free(event); } +static void x_draw_decoration_border(Con *con, struct deco_render_params *p) { + assert(con->parent != NULL); + + Rect *dr = &(con->deco_rect); + adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; + int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width; + int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width; + if (con->parent->layout == L_TABBED || + (con->parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) { + deco_diff_l = 0; + deco_diff_r = 0; + } + + draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border), + dr->x, dr->y, dr->width, 1); + + draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border), + dr->x + deco_diff_l, dr->y + dr->height - 1, dr->width - (deco_diff_l + deco_diff_r), 1); +} + +static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p) { + assert(con->parent != NULL); + + Rect *dr = &(con->deco_rect); + Rect br = con_border_style_rect(con); + + /* Redraw the right border to cut off any text that went past it. + * This is necessary when the text was drawn using XCB since cutting text off + * automatically does not work there. For pango rendering, this isn't necessary. */ + draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + dr->x + dr->width + br.width, dr->y, -br.width, dr->height); + + /* Draw a 1px separator line before and after every tab, so that tabs can + * be easily distinguished. */ + if (con->parent->layout == L_TABBED) { + /* Left side */ + draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border), + dr->x, dr->y, 1, dr->height); + + /* Right side */ + draw_util_rectangle(conn, &(con->parent->frame_buffer), draw_util_colorpixel_to_color(p->color->border), + dr->x + dr->width - 1, dr->y, 1, dr->height); + } + + /* Redraw the border. */ + x_draw_decoration_border(con, p); +} + /* * Draws the decoration of the given container onto its parent. * @@ -343,7 +393,7 @@ void x_draw_decoration(Con *con) { /* Skip containers whose pixmap has not yet been created (can happen when * decoration rendering happens recursively for a window for which * x_push_node() was not yet called) */ - if (leaf && con->pixmap == XCB_NONE) + if (leaf && con->frame_buffer.id == XCB_NONE) return; /* 1: build deco_params and compare with cache */ @@ -395,29 +445,20 @@ void x_draw_decoration(Con *con) { con->pixmap_recreated = false; con->mark_changed = false; - /* 2: draw the client.background, but only for the parts around the client_rect */ + /* 2: draw the client.background, but only for the parts around the window_rect */ if (con->window != NULL) { - xcb_rectangle_t background[] = { - /* top area */ - {0, 0, r->width, w->y}, - /* bottom area */ - {0, (w->y + w->height), r->width, r->height - (w->y + w->height)}, - /* left area */ - {0, 0, w->x, r->height}, - /* right area */ - {w->x + w->width, 0, r->width - (w->x + w->width), r->height}}; -#if 0 - for (int i = 0; i < 4; i++) - DLOG("rect is (%d, %d) with %d x %d\n", - background[i].x, - background[i].y, - background[i].width, - background[i].height - ); -#endif - - xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){config.client.background}); - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background); + /* top area */ + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background), + 0, 0, r->width, w->y); + /* bottom area */ + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background), + 0, w->y + w->height, r->width, r->height - (w->y + w->height)); + /* left area */ + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background), + 0, 0, w->x, r->height); + /* right area */ + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(config.client.background), + w->x + w->width, 0, r->width - (w->x + w->width), r->height); } /* 3: draw a rectangle in border color around the client */ @@ -433,27 +474,26 @@ void x_draw_decoration(Con *con) { DLOG("window_rect spans (%d, %d) with %d x %d\n", con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height); #endif - /* These rectangles represents the border around the child window + /* These rectangles represent the border around the child window * (left, bottom and right part). We don’t just fill the whole * rectangle because some childs are not freely resizable and we want * their background color to "shine through". */ - xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background}); if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) { - xcb_rectangle_t leftline = {0, 0, br.x, r->height}; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline); + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + 0, 0, br.x, r->height); } if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) { - xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height}; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline); + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + r->width + (br.width + br.x), 0, -(br.width + br.x), r->height); } if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) { - xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)); } - /* 1pixel border needs an additional line at the top */ + /* pixel border needs an additional line at the top */ if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { - xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y}; - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline); + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + br.x, 0, r->width + br.width, br.y); } /* Highlight the side of the border at which the next window will be @@ -463,13 +503,13 @@ void x_draw_decoration(Con *con) { if (TAILQ_NEXT(con, nodes) == NULL && TAILQ_PREV(con, nodes_head, nodes) == NULL && con->parent->type != CT_FLOATING_CON) { - xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->indicator}); - if (p->parent_layout == L_SPLITH) - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){ - {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}}); - else if (p->parent_layout == L_SPLITV) - xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){ - {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}}); + if (p->parent_layout == L_SPLITH) { + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->indicator), + r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height); + } else if (p->parent_layout == L_SPLITV) { + draw_util_rectangle(conn, &(con->frame_buffer), draw_util_colorpixel_to_color(p->color->indicator), + br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)); + } } } @@ -478,31 +518,19 @@ void x_draw_decoration(Con *con) { if (p->border_style != BS_NORMAL) goto copy_pixmaps; + /* If the parent hasn't been set up yet, skip the decoratin rendering + * for now. */ + if (parent->frame_buffer.id == XCB_NONE) + goto copy_pixmaps; + /* 4: paint the bar */ - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background}); - xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height}; - xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect); + draw_util_rectangle(conn, &(parent->frame_buffer), draw_util_colorpixel_to_color(p->color->background), + con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height); /* 5: draw two unconnected horizontal lines in border color */ - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border}); - Rect *dr = &(con->deco_rect); - adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; - int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width; - int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width; - if (parent->layout == L_TABBED || - (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) { - deco_diff_l = 0; - deco_diff_r = 0; - } - xcb_segment_t segments[] = { - {dr->x, dr->y, - dr->x + dr->width - 1, dr->y}, - {dr->x + deco_diff_l, dr->y + dr->height - 1, - dr->x - deco_diff_r + dr->width - 1, dr->y + dr->height - 1}}; - xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments); + x_draw_decoration_border(con, p); /* 6: draw the title */ - set_font_colors(parent->pm_gc, p->color->text, p->color->background); int text_offset_y = (con->deco_rect.height - config.font.height) / 2; struct Window *win = con->window; @@ -510,16 +538,18 @@ void x_draw_decoration(Con *con) { /* we have a split container which gets a representation * of its children as title */ - char *title; + char *_title; char *tree = con_get_tree_representation(con); - sasprintf(&title, "i3: %s", tree); + sasprintf(&_title, "i3: %s", tree); free(tree); - draw_text_ascii(title, - parent->pixmap, parent->pm_gc, - con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, - con->deco_rect.width - 2); - free(title); + i3String *title = i3string_from_utf8(_title); + draw_util_text(title, &(parent->frame_buffer), + draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background), + con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, + con->deco_rect.width - 2); + FREE(_title); + I3STRING_FREE(title); goto after_title; } @@ -565,9 +595,10 @@ void x_draw_decoration(Con *con) { i3String *mark = i3string_from_utf8(formatted_mark); mark_width = predict_text_width(mark); - draw_text(mark, parent->pixmap, parent->pm_gc, NULL, - con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2), - con->deco_rect.y + text_offset_y, mark_width); + draw_util_text(mark, &(parent->frame_buffer), + draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background), + con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2), + con->deco_rect.y + text_offset_y, mark_width); I3STRING_FREE(mark); } @@ -576,41 +607,17 @@ void x_draw_decoration(Con *con) { } i3String *title = win->title_format == NULL ? win->name : window_parse_title_format(win); - draw_text(title, - parent->pixmap, parent->pm_gc, NULL, - con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y, - con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2)); + draw_util_text(title, &(parent->frame_buffer), + draw_util_colorpixel_to_color(p->color->text), draw_util_colorpixel_to_color(p->color->background), + con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y, + con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2)); if (win->title_format != NULL) I3STRING_FREE(title); after_title: - /* Since we don’t clip the text at all, it might in some cases be painted - * on the border pixels on the right side of a window. Therefore, we draw - * the right border again after rendering the text (and the unconnected - * lines in border color). */ - - /* Draw a 1px separator line before and after every tab, so that tabs can - * be easily distinguished. */ - if (parent->layout == L_TABBED) { - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border}); - } else { - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background}); - } - xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6, - (xcb_point_t[]){ - {dr->x + dr->width, dr->y}, - {dr->x + dr->width, dr->y + dr->height}, - {dr->x + dr->width - 1, dr->y}, - {dr->x + dr->width - 1, dr->y + dr->height}, - {dr->x, dr->y + dr->height}, - {dr->x, dr->y}, - }); - - xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border}); - xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments); - + x_draw_decoration_after_title(con, p); copy_pixmaps: - xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height); + draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); } /* @@ -623,7 +630,7 @@ void x_deco_recurse(Con *con) { Con *current; bool leaf = TAILQ_EMPTY(&(con->nodes_head)) && TAILQ_EMPTY(&(con->floating_head)); - con_state *state = state_for_frame(con->frame); + con_state *state = state_for_frame(con->frame.id); if (!leaf) { TAILQ_FOREACH(current, &(con->nodes_head), nodes) @@ -632,8 +639,9 @@ void x_deco_recurse(Con *con) { TAILQ_FOREACH(current, &(con->floating_head), floating_windows) x_deco_recurse(current); - if (state->mapped) - xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height); + if (state->mapped) { + draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); + } } if ((con->type != CT_ROOT && con->type != CT_OUTPUT) && @@ -650,7 +658,7 @@ static void set_hidden_state(Con *con) { return; } - con_state *state = state_for_frame(con->frame); + con_state *state = state_for_frame(con->frame.id); bool should_be_hidden = con_is_hidden(con); if (should_be_hidden == state->is_hidden) return; @@ -678,12 +686,12 @@ void x_push_node(Con *con) { Rect rect = con->rect; //DLOG("Pushing changes for node %p / %s\n", con, con->name); - state = state_for_frame(con->frame); + state = state_for_frame(con->frame.id); if (state->name != NULL) { DLOG("pushing name %s for con %p\n", state->name, con); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame, + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame.id, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(state->name), state->name); FREE(state->name); } @@ -716,7 +724,7 @@ void x_push_node(Con *con) { xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values); xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values); - xcb_reparent_window(conn, con->window->id, con->frame, 0, 0); + xcb_reparent_window(conn, con->window->id, con->frame.id, 0, 0); values[0] = FRAME_EVENT_MASK; xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values); @@ -741,8 +749,8 @@ void x_push_node(Con *con) { bool fake_notify = false; /* Set new position if rect changed (and if height > 0) or if the pixmap * needs to be recreated */ - if ((is_pixmap_needed && con->pixmap == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 && - rect.height > 0)) { + if ((is_pixmap_needed && con->frame_buffer.id == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 && + rect.height > 0)) { /* We first create the new pixmap, then render to it, set it as the * background and only afterwards change the window size. This reduces * flickering. */ @@ -755,38 +763,47 @@ void x_push_node(Con *con) { /* Check if the container has an unneeded pixmap left over from * previously having a border or titlebar. */ - if (!is_pixmap_needed && con->pixmap != XCB_NONE) { - xcb_free_pixmap(conn, con->pixmap); - con->pixmap = XCB_NONE; + if (!is_pixmap_needed && con->frame_buffer.id != XCB_NONE) { + draw_util_surface_free(conn, &(con->frame_buffer)); + xcb_free_pixmap(conn, con->frame_buffer.id); + con->frame_buffer.id = XCB_NONE; } - if (is_pixmap_needed && (has_rect_changed || con->pixmap == XCB_NONE)) { - if (con->pixmap == 0) { - con->pixmap = xcb_generate_id(conn); - con->pm_gc = xcb_generate_id(conn); + if (is_pixmap_needed && (has_rect_changed || con->frame_buffer.id == XCB_NONE)) { + if (con->frame_buffer.id == XCB_NONE) { + con->frame_buffer.id = xcb_generate_id(conn); } else { - xcb_free_pixmap(conn, con->pixmap); - xcb_free_gc(conn, con->pm_gc); + draw_util_surface_free(conn, &(con->frame_buffer)); + xcb_free_pixmap(conn, con->frame_buffer.id); } uint16_t win_depth = root_depth; if (con->window) win_depth = con->window->depth; - xcb_create_pixmap(conn, win_depth, con->pixmap, con->frame, rect.width, rect.height); + /* Ensure we have valid dimensions for our surface. */ + // TODO This is probably a bug in the condition above as we should never enter this path + // for height == 0. Also, we should probably handle width == 0 the same way. + int width = MAX(rect.width, 1); + int height = MAX(rect.height, 1); + + xcb_create_pixmap_checked(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height); + draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id, + get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height); /* For the graphics context, we disable GraphicsExposure events. * Those will be sent when a CopyArea request cannot be fulfilled * properly due to parts of the source being unmapped or otherwise * unavailable. Since we always copy from pixmaps to windows, this * is not a concern for us. */ - uint32_t values[] = {0}; - xcb_create_gc(conn, con->pm_gc, con->pixmap, XCB_GC_GRAPHICS_EXPOSURES, values); + xcb_change_gc(conn, con->frame_buffer.gc, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0}); + draw_util_surface_set_size(&(con->frame), width, height); con->pixmap_recreated = true; /* Don’t render the decoration for windows inside a stack which are * not visible right now */ + // TODO Should this work the same way for L_TABBED? if (!con->parent || con->parent->layout != L_STACKED || TAILQ_FIRST(&(con->parent->focus_head)) == con) @@ -802,9 +819,10 @@ void x_push_node(Con *con) { * window get lost when resizing it, therefore we want to provide it as * fast as possible) */ xcb_flush(conn); - xcb_set_window_rect(conn, con->frame, rect); - if (con->pixmap != XCB_NONE) - xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height); + xcb_set_window_rect(conn, con->frame.id, rect); + if (con->frame_buffer.id != XCB_NONE) { + draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); + } xcb_flush(conn); memcpy(&(state->rect), &rect, sizeof(Rect)); @@ -848,17 +866,18 @@ void x_push_node(Con *con) { state->child_mapped = true; } - cookie = xcb_map_window(conn, con->frame); + cookie = xcb_map_window(conn, con->frame.id); values[0] = FRAME_EVENT_MASK; - xcb_change_window_attributes(conn, con->frame, XCB_CW_EVENT_MASK, values); + xcb_change_window_attributes(conn, con->frame.id, XCB_CW_EVENT_MASK, values); /* copy the pixmap contents to the frame window immediately after mapping */ - if (con->pixmap != XCB_NONE) - xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height); + if (con->frame_buffer.id != XCB_NONE) { + draw_util_copy_surface(conn, &(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height); + } xcb_flush(conn); - DLOG("mapping container %08x (serial %d)\n", con->frame, cookie.sequence); + DLOG("mapping container %08x (serial %d)\n", con->frame.id, cookie.sequence); state->mapped = con->mapped; } @@ -892,7 +911,7 @@ static void x_push_node_unmaps(Con *con) { con_state *state; //DLOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name); - state = state_for_frame(con->frame); + state = state_for_frame(con->frame.id); /* map/unmap if map state changed, also ensure that the child window * is changed if we are mapped *and* in initial state (meaning the @@ -906,14 +925,14 @@ static void x_push_node_unmaps(Con *con) { A_WM_STATE, A_WM_STATE, 32, 2, data); } - cookie = xcb_unmap_window(conn, con->frame); + cookie = xcb_unmap_window(conn, con->frame.id); DLOG("unmapping container %p / %s (serial %d)\n", con, con->name, cookie.sequence); /* we need to increase ignore_unmap for this container (if it * contains a window) and for every window "under" this one which * contains a window */ if (con->window != NULL) { con->ignore_unmap++; - DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame, con->ignore_unmap); + DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame.id, con->ignore_unmap); } state->mapped = con->mapped; } @@ -1067,7 +1086,7 @@ void x_push_changes(Con *con) { x_deco_recurse(con); - xcb_window_t to_focus = focused->frame; + xcb_window_t to_focus = focused->frame.id; if (focused->window != NULL) to_focus = focused->window->id; @@ -1161,7 +1180,7 @@ void x_push_changes(Con *con) { */ void x_raise_con(Con *con) { con_state *state; - state = state_for_frame(con->frame); + state = state_for_frame(con->frame.id); //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id); CIRCLEQ_REMOVE(&state_head, state, state); @@ -1177,7 +1196,7 @@ void x_raise_con(Con *con) { void x_set_name(Con *con, const char *name) { struct con_state *state; - if ((state = state_for_frame(con->frame)) == NULL) { + if ((state = state_for_frame(con->frame.id)) == NULL) { ELOG("window state not found\n"); return; } diff --git a/src/xcb.c b/src/xcb.c index 90d591c7..60fd4212 100644 --- a/src/xcb.c +++ b/src/xcb.c @@ -252,6 +252,22 @@ uint16_t get_visual_depth(xcb_visualid_t visual_id) { } return 0; } +xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id) { + xcb_depth_iterator_t depth_iter; + + depth_iter = xcb_screen_allowed_depths_iterator(root_screen); + for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { + xcb_visualtype_iterator_t visual_iter; + + visual_iter = xcb_depth_visuals_iterator(depth_iter.data); + for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { + if (visual_id == visual_iter.data->visual_id) { + return visual_iter.data; + } + } + } + return 0; +} /* * Get visualid with specified depth -- 2.39.2