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
This commit is contained in:
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user