From 2ce9c4024f701d1e8b7d748bc99d06c904f5046c Mon Sep 17 00:00:00 2001 From: Axel Wagner Date: Fri, 17 Sep 2010 01:18:50 +0200 Subject: [PATCH] Draw the statusline to a seperate buffer-pixmap --- i3bar/src/xcb.c | 177 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 25 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 42a49ced..88f4be35 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -42,16 +42,67 @@ xcb_screen_t *xcb_screens; xcb_window_t xcb_root; xcb_font_t xcb_font; +/* We need to cache some data to speed up text-width-prediction */ +xcb_query_font_reply_t *font_info; +xcb_charinfo_t *font_table; + +/* These are only relevant for XKB, which we only need for grabbing modifiers */ Display *xkb_dpy; int xkb_event_base; int mod_pressed; +/* Because the statusline is the same on all outputs, we have + * global buffer to render it on */ +xcb_gcontext_t statusline_ctx; +xcb_pixmap_t statusline_pm; +uint32_t statusline_width; + /* Event-Watchers, to interact with the user */ ev_prepare *xcb_prep; ev_check *xcb_chk; ev_io *xcb_io; ev_io *xkb_io; +/* + * Predicts the length of text based on cached data + * + */ +uint32_t predict_text_extents(xcb_char2b_t *text, uint32_t length) { + /* If we don't have per-character data, return the maximum width */ + if (font_table == NULL) { + return (font_info->max_bounds.character_width * length); + } + + uint32_t width = 0; + uint32_t i; + + for (i = 0; i < length; i++) { + xcb_charinfo_t *info; + int row = text[i].byte1; + int col = text[i].byte2; + + if (row < font_info->min_byte1 || row > font_info->max_byte1 || + col < font_info->min_char_or_byte2 || col > font_info->max_char_or_byte2) { + continue; + } + + /* Don't you ask me, how this one works… */ + info = &font_table[((row - font_info->min_byte1) * + (font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) + + (col - font_info->min_char_or_byte2)]; + + if (info->character_width != 0 || + (info->right_side_bearing | + info->left_side_bearing | + info->ascent | + info->descent) != 0) { + width += info->character_width; + } + } + + return width; +} + /* * Converts a colorstring to a colorpixel as expected from xcb_change_gc. * s is assumed to be in the format "rrggbb" @@ -67,6 +118,52 @@ uint32_t get_colorpixel(const char *s) { return (r << 16 | g << 8 | b); } +/* + * Redraws the statusline to the buffer + * + */ +void refresh_statusline() { + int glyph_count; + uint32_t root_width = xcb_screens->width_in_pixels; + if (statusline == NULL) { + return; + } + + xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count); + statusline_width = predict_text_extents(text, glyph_count); + int crop_x = MIN(0, ((int32_t)root_width) - ((int32_t)statusline_width)); + printf("Cropping statusline with %d glyphs at x=%d\n", glyph_count, crop_x); + statusline_width = MIN((int32_t)statusline_width, (int32_t)root_width); + + xcb_free_pixmap(xcb_connection, statusline_pm); + statusline_pm = xcb_generate_id(xcb_connection); + xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, + xcb_screens->root_depth, + statusline_pm, + xcb_root, + statusline_width, + font_height); + + xcb_void_cookie_t text_cookie = xcb_image_text_16(xcb_connection, + glyph_count, + statusline_pm, + statusline_ctx, + 0, + font_height, + text); + + xcb_generic_error_t *err; + if ((err = xcb_request_check(xcb_connection, sl_pm_cookie)) != NULL) { + printf("ERROR: Could not allocate statusline-buffer! XCB-error: %d\n", err->error_code); + exit(EXIT_FAILURE); + } + + if ((err = xcb_request_check(xcb_connection, text_cookie)) != NULL) { + printf("ERROR: Could not draw text to buffer! XCB-error: %d\n", err->error_code); + exit(EXIT_FAILURE); + } +} + /* * Hides all bars (unmaps them) * @@ -353,6 +450,10 @@ void init_xcb(char *fontname) { strlen(fontname), fontname); + xcb_query_font_cookie_t query_font_cookie; + query_font_cookie = xcb_query_font(xcb_connection, + xcb_font); + if (config.hide_on_modifier) { int xkb_major, xkb_minor, xkb_errbase, xkb_err; xkb_major = XkbMajorVersion; @@ -392,6 +493,22 @@ void init_xcb(char *fontname) { XFlush(xkb_dpy); } + /* 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 */ + statusline_ctx = xcb_generate_id(xcb_connection); + uint32_t mask = XCB_GC_FOREGROUND | + XCB_GC_BACKGROUND | + XCB_GC_FONT; + uint32_t vals[3] = { xcb_screens->white_pixel, xcb_screens->black_pixel, xcb_font }; + + xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection, + statusline_ctx, + xcb_root, + mask, + vals); + + statusline_pm = xcb_generate_id(xcb_connection); + /* The varios Watchers to communicate with xcb */ xcb_io = malloc(sizeof(ev_io)); xcb_prep = malloc(sizeof(ev_prepare)); @@ -416,7 +533,27 @@ void init_xcb(char *fontname) { font_height = reply->font_ascent + reply->font_descent; FREE(reply); + font_info = xcb_query_font_reply(xcb_connection, + query_font_cookie, + &err); + + if (err != NULL) { + printf("ERROR: Could not query font! XCB-error: %d\n", err->error_code); + exit(EXIT_FAILURE); + } + + if (xcb_query_font_char_infos_length(font_info) == 0) { + font_table = NULL; + } else { + font_table = xcb_query_font_char_infos(font_info); + } + printf("Calculated Font-height: %d\n", font_height); + + if((err = xcb_request_check(xcb_connection, sl_ctx_cookie)) != NULL) { + printf("ERROR: Could not create context for statusline! XCB-error: %d\n", err->error_code); + exit(EXIT_FAILURE); + } } /* @@ -605,6 +742,9 @@ void reconfig_windows() { void draw_bars() { printf("Drawing Bars...\n"); int i = 0; + + refresh_statusline(); + i3_output *outputs_walk; SLIST_FOREACH(outputs_walk, outputs, slist) { if (!outputs_walk->active) { @@ -627,34 +767,21 @@ void draw_bars() { &rect); if (statusline != NULL) { printf("Printing statusline!\n"); - xcb_change_gc(xcb_connection, + + xcb_void_cookie_t ca_cookie = xcb_copy_area(xcb_connection, + statusline_pm, + outputs_walk->buffer, outputs_walk->bargc, - XCB_GC_BACKGROUND, - &color); - color = get_colorpixel("FFFFFF"); - xcb_change_gc(xcb_connection, - outputs_walk->bargc, - XCB_GC_FOREGROUND, - &color); - - int glyph_count; - xcb_char2b_t *text = (xcb_char2b_t*) convert_utf8_to_ucs2(statusline, &glyph_count); - - xcb_void_cookie_t cookie; - cookie = xcb_image_text_16(xcb_connection, - glyph_count, - outputs_walk->buffer, - outputs_walk->bargc, - outputs_walk->rect.w - get_string_width(text, glyph_count) - 4, - font_height + 1, - (xcb_char2b_t*) text); - - xcb_generic_error_t *err = xcb_request_check(xcb_connection, cookie); - - if (err != NULL) { - printf("XCB-Error: %d\n", err->error_code); + 0, 0, + MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width)), 1, + (uint16_t)outputs_walk->rect.w, font_height); + xcb_generic_error_t *err; + if ((err = xcb_request_check(xcb_connection, ca_cookie)) != NULL) { + printf("ERROR: Can not copy statusline-buffer! XCB-error: %d\n", err->error_code); + free(err); } } + i3_ws *ws_walk; TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { printf("Drawing Button for WS %s at x = %d\n", ws_walk->name, i);