From: Ingo Bürk Date: Wed, 11 Nov 2015 22:39:15 +0000 (+0100) Subject: Move draw_util.c to libi3. X-Git-Tag: 4.12~97^2~7 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=90d94298fa388e58a0f54607f0ab7a46108e84e7;p=i3%2Fi3 Move draw_util.c to libi3. In order to prepare for using cairo for rendering i3 decorations, we need to make the draw_util.c from i3bar available via libi3 such that both i3bar and i3 can use it. relates to #1278 --- diff --git a/i3bar/include/common.h b/i3bar/include/common.h index aa706bbe..0d46ab6a 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -88,4 +88,3 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "config.h" #include "libi3.h" #include "parse_json_header.h" -#include "draw_util.h" diff --git a/i3bar/include/draw_util.h b/i3bar/include/draw_util.h deleted file mode 100644 index f2e12ec5..00000000 --- a/i3bar/include/draw_util.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * © 2015 Ingo Bürk and contributors (see also: LICENSE) - * - * draw.h: Utility for drawing. - * - */ -#pragma once - -#ifdef CAIRO_SUPPORT -#include -#endif - -#ifdef CAIRO_SUPPORT -/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 - * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ -#define CAIRO_SURFACE_FLUSH(surface) \ - do { \ - cairo_surface_flush(surface); \ - cairo_surface_flush(surface); \ - } while (0) -#endif - -/* Represents a color split by color channel. */ -typedef struct color_t { - double red; - double green; - double blue; - - /* For compatibility, we also store the colorpixel for now. */ - uint32_t colorpixel; -} color_t; - -/* A wrapper grouping an XCB drawable and both a graphics context - * and the corresponding cairo objects representing it. */ -typedef struct surface_t { - /* The drawable which is being represented. */ - xcb_drawable_t id; - - /* A classic XCB graphics context. */ - xcb_gcontext_t gc; - - int width; - int height; - -#ifdef CAIRO_SUPPORT - /* A cairo surface representing the drawable. */ - cairo_surface_t *surface; - - /* The cairo object representing the drawale. In general, - * this is what one should use for any drawing operation. */ - cairo_t *cr; -#endif -} 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); - -/** - * Destroys the surface. - * - */ -void draw_util_surface_free(surface_t *surface); - -/** - * 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". - * - */ -color_t draw_util_hex_to_color(const char *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 draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); - -/** - * Draws a filled rectangle. - * This function is a convenience wrapper and takes care of flushing the - * 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); - -/** - * Clears a surface with the given color. - * - */ -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, - double dest_x, double dest_y, double width, double height); diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 08adefd9..63cfca7f 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -13,7 +13,6 @@ #include #include "common.h" -#include "draw_util.h" typedef struct i3_output i3_output; diff --git a/i3bar/src/draw_util.c b/i3bar/src/draw_util.c deleted file mode 100644 index 86435351..00000000 --- a/i3bar/src/draw_util.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * © 2015 Ingo Bürk and contributors (see also: LICENSE) - * - * draw.c: Utility for drawing. - * - */ -#include -#include -#include -#include -#include -#ifdef CAIRO_SUPPORT -#include -#endif - -#include "common.h" -#include "libi3.h" - -xcb_connection_t *xcb_connection; -xcb_visualtype_t *visual_type; - -/* Forward declarations */ -static void draw_util_set_source_color(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) { - surface->id = drawable; - 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); - if (xcb_request_failed(gc_cookie, "Could not create graphical context")) - exit(EXIT_FAILURE); - -#ifdef CAIRO_SUPPORT - surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); - surface->cr = cairo_create(surface->surface); -#endif -} - -/* - * Destroys the surface. - * - */ -void draw_util_surface_free(surface_t *surface) { - xcb_free_gc(xcb_connection, surface->gc); -#ifdef CAIRO_SUPPORT - cairo_surface_destroy(surface->surface); - cairo_destroy(surface->cr); -#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". - * - */ -color_t draw_util_hex_to_color(const char *color) { - char groups[3][3] = { - {color[1], color[2], '\0'}, - {color[3], color[4], '\0'}, - {color[5], color[6], '\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, - .colorpixel = get_colorpixel(color)}; -} - -/* - * Set the given color as the source color on the surface. - * - */ -static void draw_util_set_source_color(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, - (uint32_t[]){colorpixel, colorpixel}); -#endif -} - -/** - * 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 draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { -#ifdef CAIRO_SUPPORT - /* Flush any changes before we draw the text as this might use XCB directly. */ - CAIRO_SURFACE_FLUSH(surface->surface); -#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); - -#ifdef CAIRO_SUPPORT - /* Notify cairo that we (possibly) used another way to draw on the surface. */ - cairo_surface_mark_dirty(surface->surface); -#endif -} - -/** - * Draws a filled rectangle. - * This function is a convenience wrapper and takes care of flushing the - * 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) { -#ifdef CAIRO_SUPPORT - 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); - draw_util_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); -#else - draw_util_set_source_color(surface, color); - - xcb_rectangle_t rect = {x, y, w, h}; - xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); -#endif -} - -/** - * Clears a surface with the given color. - * - */ -void draw_util_clear_surface(surface_t *surface, color_t color) { -#ifdef CAIRO_SUPPORT - 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); - draw_util_set_source_color(surface, color); - - cairo_paint(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); -#else - draw_util_set_source_color(surface, color); - - xcb_rectangle_t rect = {0, 0, surface->width, surface->height}; - xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); -#endif -} - -/** - * Copies a surface onto another surface. - * - */ -void draw_util_copy_surface(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); - - /* 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, dest_x - src_x, src_y); - - cairo_rectangle(dest->cr, dest_x, dest_y, width, height); - 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(src->surface); - CAIRO_SURFACE_FLUSH(dest->surface); - - cairo_restore(dest->cr); -#else - xcb_copy_area(xcb_connection, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, - (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height); -#endif -} diff --git a/include/libi3.h b/include/libi3.h index 75d3639b..15dffdb7 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -20,6 +20,9 @@ #if PANGO_SUPPORT #include #endif +#ifdef CAIRO_SUPPORT +#include +#endif #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) @@ -480,3 +483,93 @@ char *get_config_path(const char *override_configpath, bool use_system_paths); */ int mkdirp(const char *path, mode_t mode); #endif + +#ifdef CAIRO_SUPPORT +/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 + * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ +#define CAIRO_SURFACE_FLUSH(surface) \ + do { \ + cairo_surface_flush(surface); \ + cairo_surface_flush(surface); \ + } while (0) +#endif + +/* Represents a color split by color channel. */ +typedef struct color_t { + double red; + double green; + double blue; + + /* The colorpixel we use for direct XCB calls. */ + uint32_t colorpixel; +} color_t; + +/* A wrapper grouping an XCB drawable and both a graphics context + * and the corresponding cairo objects representing it. */ +typedef struct surface_t { + /* The drawable which is being represented. */ + xcb_drawable_t id; + + /* A classic XCB graphics context. */ + xcb_gcontext_t gc; + + int width; + int height; + +#ifdef CAIRO_SUPPORT + /* A cairo surface representing the drawable. */ + cairo_surface_t *surface; + + /* The cairo object representing the drawable. In general, + * this is what one should use for any drawing operation. */ + cairo_t *cr; +#endif +} 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); + +/** + * Destroys the surface. + * + */ +void draw_util_surface_free(surface_t *surface); + +/** + * 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". + * + */ +color_t draw_util_hex_to_color(const char *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 draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * 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); + +/** + * Clears a surface with the given color. + * + */ +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, + double dest_x, double dest_y, double width, double height); diff --git a/libi3/draw_util.c b/libi3/draw_util.c new file mode 100644 index 00000000..04dc0ce5 --- /dev/null +++ b/libi3/draw_util.c @@ -0,0 +1,204 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * © 2015 Ingo Bürk and contributors (see also: LICENSE) + * + * draw.c: Utility for drawing. + * + */ +#include +#include +#include +#include +#include +#ifdef CAIRO_SUPPORT +#include +#endif + +#include "libi3.h" + +xcb_connection_t *xcb_connection; +xcb_visualtype_t *visual_type; + +/* Forward declarations */ +static void draw_util_set_source_color(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) { + surface->id = drawable; + 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); + + xcb_generic_error_t *error = xcb_request_check(xcb_connection, 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->cr = cairo_create(surface->surface); +#endif +} + +/* + * Destroys the surface. + * + */ +void draw_util_surface_free(surface_t *surface) { + xcb_free_gc(xcb_connection, surface->gc); +#ifdef CAIRO_SUPPORT + cairo_surface_destroy(surface->surface); + cairo_destroy(surface->cr); +#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". + * + */ +color_t draw_util_hex_to_color(const char *color) { + char groups[3][3] = { + {color[1], color[2], '\0'}, + {color[3], color[4], '\0'}, + {color[5], color[6], '\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, + .colorpixel = get_colorpixel(color)}; +} + +/* + * Set the given color as the source color on the surface. + * + */ +static void draw_util_set_source_color(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, + (uint32_t[]){colorpixel, colorpixel}); +#endif +} + +/** + * 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 draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { +#ifdef CAIRO_SUPPORT + /* Flush any changes before we draw the text as this might use XCB directly. */ + CAIRO_SURFACE_FLUSH(surface->surface); +#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); + +#ifdef CAIRO_SUPPORT + /* Notify cairo that we (possibly) used another way to draw on the surface. */ + cairo_surface_mark_dirty(surface->surface); +#endif +} + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * 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) { +#ifdef CAIRO_SUPPORT + 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); + draw_util_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); +#else + draw_util_set_source_color(surface, color); + + xcb_rectangle_t rect = {x, y, w, h}; + xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); +#endif +} + +/** + * Clears a surface with the given color. + * + */ +void draw_util_clear_surface(surface_t *surface, color_t color) { +#ifdef CAIRO_SUPPORT + 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); + draw_util_set_source_color(surface, color); + + cairo_paint(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); +#else + draw_util_set_source_color(surface, color); + + xcb_rectangle_t rect = {0, 0, surface->width, surface->height}; + xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); +#endif +} + +/** + * Copies a surface onto another surface. + * + */ +void draw_util_copy_surface(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); + + /* 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, dest_x - src_x, src_y); + + cairo_rectangle(dest->cr, dest_x, dest_y, width, height); + 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(src->surface); + CAIRO_SURFACE_FLUSH(dest->surface); + + cairo_restore(dest->cr); +#else + xcb_copy_area(xcb_connection, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, + (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height); +#endif +}