diff --git a/src/backend/backend.c b/src/backend/backend.c index 27ca8d9..97a228d 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -174,6 +174,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // We need to blur the bounding shape of the window // (reg_paint = reg_bound \cap reg_damage) ps->backend_data->ops->blur(ps->backend_data, w->opacity, + ps->backend_blur_context, ®_paint, ®_visible); } else if (frame_transparent && ps->o.blur_background_frame) { // Window itself is solid, we only need to blur the frame @@ -183,6 +184,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // make sure reg_blur \in reg_damage pixman_region32_intersect(®_blur, ®_blur, ®_damage); ps->backend_data->ops->blur(ps->backend_data, w->opacity, + ps->backend_blur_context, ®_blur, ®_visible); pixman_region32_fini(®_blur); } diff --git a/src/backend/backend.h b/src/backend/backend.h index 1a8dac9..f69ae85 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -130,8 +130,9 @@ struct backend_operations { void (*fill)(backend_t *backend_data, struct color, const region_t *clip); /// Blur a given region of the target. - bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur, - const region_t *reg_visible) attr_nonnull(1, 3, 4); + bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx, + const region_t *reg_blur, const region_t *reg_visible) + attr_nonnull(1, 3, 4, 5); /// Present the back buffer onto the screen. /// @@ -209,6 +210,10 @@ struct backend_operations { /// returned image should not affect the original image void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible); + /// Create a blur context that can be used to call `blur` + void *(*create_blur_context)(backend_t *base, enum blur_method, void *args); + void (*destroy_blur_context)(backend_t *base, void *ctx); + // =========== Hooks ============ /// Let the backend hook into the event handling queue void (*set_ready_callback)(backend_t *, backend_ready_callback_t cb); diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 02913e1..8a7a1fb 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -17,12 +17,38 @@ #include "string_utils.h" #include "utils.h" -#include "backend/gl/gl_common.h" #include "backend/backend_common.h" +#include "backend/gl/gl_common.h" #define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__ #define QUOTE(...) #__VA_ARGS__ +struct gl_blur_context { + enum blur_method method; + gl_blur_shader_t *blur_shader; + + // Temporary textures used for blurring. They are always the same size as the + // target, so they are always big enough without resizing. + // Turns out calling glTexImage to resize is expensive, so we avoid that. + GLuint blur_texture[2]; + // Temporary fbo used for blurring + GLuint blur_fbo; + + int texture_width, texture_height; + + int npasses; +}; + +static GLint glGetUniformLocationChecked(GLuint p, const char *name) { + auto ret = glGetUniformLocation(p, name); + if (ret < 0) { + log_error("Failed to get location of uniform '%s'. compton might not " + "work correctly.", + name); + } + return ret; +} + GLuint gl_create_shader(GLenum shader_type, const char *shader_str) { log_trace("===\n%s\n===", shader_str); @@ -345,10 +371,44 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y, /** * Blur contents in a particular region. */ -bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, +bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blur, const region_t *reg_visible) { - // Remainder: regions are in Xorg coordinates + struct gl_blur_context *bctx = ctx; struct gl_data *gd = (void *)base; + + if (gd->width != bctx->texture_width || gd->height != bctx->texture_height) { + // Resize the temporary textures used for blur in case the root + // size changed + glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + if (bctx->npasses > 1) { + glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + + bctx->texture_width = gd->width; + bctx->texture_height = gd->height; + + // XXX: do we need projection matrix for blur at all? + // Note: OpenGL matrices are column major + GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)gd->width, 0, 0, 0}, + {0, 2.0f / (GLfloat)gd->height, 0, 0}, + {0, 0, 0, 0}, + {-1, -1, 0, 1}}; + + // Update projection matrices in the blur shaders + for (int i = 0; i < bctx->npasses; i++) { + assert(bctx->blur_shader[i].prog); + glUseProgram(bctx->blur_shader[i].prog); + int pml = glGetUniformLocationChecked(bctx->blur_shader[i].prog, + "projection"); + glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); + } + } + + // Remainder: regions are in Xorg coordinates const rect_t *extent = pixman_region32_extents((region_t *)reg_blur); int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1; int dst_y = gd->height - extent->y2; @@ -356,9 +416,7 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, return true; } - // these should be arguments bool ret = false; - int nrects; const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects); if (!nrects) { @@ -384,24 +442,24 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, int curr = 0; glReadBuffer(GL_BACK); - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]); + glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[0]); // Copy the area to be blurred into tmp buffer glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extent->x1, dst_y, width, height); - for (int i = 0; i < gd->npasses; ++i) { - const gl_blur_shader_t *p = &gd->blur_shader[i]; + for (int i = 0; i < bctx->npasses; ++i) { + const gl_blur_shader_t *p = &bctx->blur_shader[i]; assert(p->prog); - assert(gd->blur_texture[curr]); - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[curr]); + assert(bctx->blur_texture[curr]); + glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[curr]); glUseProgram(p->prog); - if (i < gd->npasses - 1) { + if (i < bctx->npasses - 1) { // not last pass, draw into framebuffer - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->blur_fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbo); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gd->blur_texture[!curr], 0); + GL_TEXTURE_2D, bctx->blur_texture[!curr], 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { log_error("Framebuffer attachment failed."); @@ -414,7 +472,7 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, glDrawBuffer(GL_BACK); glUniform1f(p->unifm_opacity, (float)opacity); } - if (i == gd->npasses - 1) { + if (i == bctx->npasses - 1) { glUniform2f(p->orig_loc, 0, 0); } else { // For other than last pass, we are drawing to a texture, we @@ -460,16 +518,6 @@ end: return ret; } -static GLint glGetUniformLocationChecked(GLuint p, const char *name) { - auto ret = glGetUniformLocation(p, name); - if (ret < 0) { - log_error("Failed to get location of uniform '%s'. compton might not " - "work correctly.", - name); - } - return ret; -} - // clang-format off const char *vertex_shader = GLSL(330, uniform mat4 projection; @@ -520,32 +568,13 @@ void gl_resize(struct gl_data *gd, int width, int height) { gd->height = height; gd->width = width; + // XXX: do we need projection matrix at all? // Note: OpenGL matrices are column major GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)width, 0, 0, 0}, {0, 2.0f / (GLfloat)height, 0, 0}, {0, 0, 0, 0}, {-1, -1, 0, 1}}; - if (gd->npasses > 0) { - // Resize the temporary textures used for blur - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0, - GL_BGRA, GL_UNSIGNED_BYTE, NULL); - if (gd->npasses > 1) { - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0, - GL_BGRA, GL_UNSIGNED_BYTE, NULL); - } - - // Update projection matrices in the blur shaders - for (int i = 0; i < gd->npasses; i++) { - assert(gd->blur_shader[i].prog); - glUseProgram(gd->blur_shader[i].prog); - int pml = glGetUniformLocationChecked(gd->blur_shader[i].prog, - "projection"); - glUniformMatrix4fv(pml, 1, false, projection_matrix[0]); - } - } // Update projection matrix in the win shader glUseProgram(gd->win_shader.prog); int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection"); @@ -660,16 +689,60 @@ void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visib return new_img; } +static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { + if (shader->prog) { + glDeleteProgram(shader->prog); + } + + shader->prog = 0; +} + +void gl_destroy_blur_context(backend_t *base, void *ctx) { + struct gl_blur_context *bctx = ctx; + // Free GLSL shaders/programs + for (int i = 0; i < bctx->npasses; ++i) { + gl_free_blur_shader(&bctx->blur_shader[i]); + } + free(bctx->blur_shader); + + glDeleteTextures(bctx->npasses > 1 ? 2 : 1, bctx->blur_texture); + if (bctx->npasses > 1) { + glDeleteFramebuffers(1, &bctx->blur_fbo); + } + free(bctx); + + gl_check_err(); +} + /** * Initialize GL blur filters. */ -static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nkernels) { - if (!nkernels) { - return true; +void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) { + bool success = true; + auto gd = (struct gl_data *)base; + + struct conv **kernels; + auto ctx = cmalloc(struct gl_blur_context); + + if (!method || method >= BLUR_METHOD_INVALID) { + ctx->method = BLUR_METHOD_NONE; + return ctx; } - gd->npasses = nkernels; - gd->blur_shader = ccalloc(gd->npasses, gl_blur_shader_t); + ctx->method = BLUR_METHOD_KERNEL; + if (method == BLUR_METHOD_KERNEL) { + ctx->npasses = ((struct kernel_blur_args *)args)->kernel_count; + kernels = ((struct kernel_blur_args *)args)->kernels; + } else { + kernels = generate_blur_kernel(method, args, &ctx->npasses); + } + + if (!ctx->npasses) { + ctx->method = BLUR_METHOD_NONE; + return ctx; + } + + ctx->blur_shader = ccalloc(ctx->npasses, gl_blur_shader_t); char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL)); // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane @@ -701,7 +774,7 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke const char *shader_add = FRAG_SHADER_BLUR_ADD; char *extension = strdup(""); - for (int i = 0; i < nkernels; i++) { + for (int i = 0; i < ctx->npasses; i++) { auto kern = kernels[i]; // Build shader int width = kern->w, height = kern->h; @@ -726,7 +799,7 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke } } - auto pass = gd->blur_shader + i; + auto pass = ctx->blur_shader + i; size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) + strlen(shader_body) + 10 /* sum */ + 1 /* null terminator */; @@ -742,7 +815,8 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke free(shader_str); if (!pass->prog) { log_error("Failed to create GLSL program."); - goto err; + success = false; + goto out; } glBindFragDataLocation(pass->prog, 0, "out_color"); @@ -756,37 +830,46 @@ static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nke pass->in_texcoord = glGetAttribLocation(pass->prog, "in_texcoord"); pass->coord_loc = glGetAttribLocation(pass->prog, "coord"); } - free(extension); // Texture size will be defined by gl_resize - glGenTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture); - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]); + glGenTextures(ctx->npasses > 1 ? 2 : 1, ctx->blur_texture); + glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if (gd->npasses > 1) { - glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]); + if (ctx->npasses > 1) { + glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Generate FBO and textures when needed - glGenFramebuffers(1, &gd->blur_fbo); - if (!gd->blur_fbo) { + glGenFramebuffers(1, &ctx->blur_fbo); + if (!ctx->blur_fbo) { log_error("Failed to generate framebuffer object for blur"); - return false; + success = false; + goto out; } } +out: + if (method != BLUR_METHOD_KERNEL) { + // We generated the blur kernels, so we need to free them + for (int i = 0; i < ctx->npasses; i++) { + free(kernels[i]); + } + free(kernels); + } + + if (!success) { + gl_destroy_blur_context(&gd->base, ctx); + ctx = NULL; + } + + free(extension); // Restore LC_NUMERIC setlocale(LC_NUMERIC, lc_numeric_old); free(lc_numeric_old); gl_check_err(); - - return true; -err: - free(extension); - setlocale(LC_NUMERIC, lc_numeric_old); - free(lc_numeric_old); - return false; + return ctx; } // clang-format off @@ -828,9 +911,6 @@ bool gl_init(struct gl_data *gd, session_t *ps) { glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader); - if (!gl_init_blur(gd, ps->o.blur_kerns, ps->o.blur_kernel_count)) { - return false; - } gd->fill_shader.prog = gl_create_program_from_str(fill_vert, fill_frag); gd->fill_shader.in_coord_loc = glGetAttribLocation(gd->fill_shader.prog, "in_coord"); @@ -857,28 +937,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) { return true; } -static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { - if (shader->prog) { - glDeleteProgram(shader->prog); - } - - shader->prog = 0; -} - void gl_deinit(struct gl_data *gd) { - // Free GLSL shaders/programs - for (int i = 0; i < gd->npasses; ++i) { - gl_free_blur_shader(&gd->blur_shader[i]); - } - free(gd->blur_shader); - gl_free_prog_main(&gd->win_shader); - glDeleteTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture); - if (gd->npasses > 1) { - glDeleteFramebuffers(1, &gd->blur_fbo); - } - if (gd->logger) { log_remove_target_tls(gd->logger); gd->logger = NULL; diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index edf5c27..f964140 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -66,17 +66,8 @@ struct gl_data { bool is_nvidia; // Height and width of the viewport int height, width; - int npasses; gl_win_shader_t win_shader; gl_fill_shader_t fill_shader; - gl_blur_shader_t *blur_shader; - - // Temporary textures used for blurring. They are always the same size as the - // target, so they are always big enough without resizing. - // Turns out calling glTexImage to resize is expensive, so we avoid that. - GLuint blur_texture[2]; - // Temporary fbo used for blurring - GLuint blur_fbo; /// Called when an gl_texture is decoupled from the texture it refers. Returns /// the decoupled user_data @@ -88,17 +79,6 @@ struct gl_data { struct log_target *logger; }; -typedef struct { - /// Framebuffer used for blurring. - GLuint fbo; - /// Textures used for blurring. - GLuint textures[2]; - /// Width of the textures. - int width; - /// Height of the textures. - int height; -} gl_blur_cache_t; - typedef struct session session_t; #define GL_PROG_MAIN_INIT \ @@ -128,8 +108,10 @@ void gl_release_image(backend_t *base, void *image_data); void *gl_copy(backend_t *base, const void *image_data, const region_t *reg_visible); -bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur, +bool gl_blur(backend_t *base, double opacity, void *, const region_t *reg_blur, const region_t *reg_visible); +void *gl_create_blur_context(backend_t *base, enum blur_method, void *args); +void gl_destroy_blur_context(backend_t *base, void *ctx); bool gl_is_image_transparent(backend_t *base, void *image_data); void gl_fill(backend_t *base, struct color, const region_t *clip); diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index 67e3fcb..6b76b5b 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -472,6 +472,8 @@ struct backend_operations glx_ops = { .buffer_age = glx_buffer_age, .render_shadow = default_backend_render_shadow, .fill = gl_fill, + .create_blur_context = gl_create_blur_context, + .destroy_blur_context = gl_destroy_blur_context, .max_buffer_age = 5, // Why? }; diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 57fcd9a..815523e 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -59,14 +59,17 @@ typedef struct _xrender_data { /// Width and height of the target pixmap int target_width, target_height; + xcb_special_event_t *present_event; +} xrender_data; + +struct _xrender_blur_context { + enum blur_method method; /// Blur kernels converted to X format struct x_convolution_kernel **x_blur_kernel; /// Number of blur kernels int x_blur_kernel_count; - - xcb_special_event_t *present_event; -} xrender_data; +}; struct _xrender_image_data { // Pixmap that the client window draws to, @@ -123,8 +126,13 @@ static void fill(backend_t *base, struct color c, const region_t *clip) { .height = to_u16_checked(extent->y2 - extent->y1)}}); } -static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur, - const region_t *reg_visible) { +static bool blur(backend_t *backend_data, double opacity, void *ctx_, + const region_t *reg_blur, const region_t *reg_visible) { + struct _xrender_blur_context *bctx = ctx_; + if (bctx->method == BLUR_METHOD_NONE) { + return true; + } + struct _xrender_data *xd = (void *)backend_data; xcb_connection_t *c = xd->base.c; region_t reg_op; @@ -178,17 +186,17 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_bl // For 1 pass, we do // back -(pass 1)-> tmp0 -(copy)-> target_buffer int i; - for (i = 0; i < xd->x_blur_kernel_count; i++) { + for (i = 0; i < bctx->x_blur_kernel_count; i++) { // Copy from source picture to destination. The filter must // be applied on source picture, to get the nearby pixels outside the // window. // TODO cache converted blur_kerns xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)), filter, - to_u32_checked(xd->x_blur_kernel[i]->size), - xd->x_blur_kernel[i]->kernel); + to_u32_checked(bctx->x_blur_kernel[i]->size), + bctx->x_blur_kernel[i]->kernel); - if (i < xd->x_blur_kernel_count - 1 || i == 0) { + if (i < bctx->x_blur_kernel_count - 1 || i == 0) { // This is not the last pass, or this is the first pass xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0, @@ -273,10 +281,6 @@ static void deinit(backend_t *backend_data) { xcb_render_free_picture(xd->base.c, xd->back[i]); xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]); } - for (int i = 0; i < xd->x_blur_kernel_count; i++) { - free(xd->x_blur_kernel[i]); - } - free(xd->x_blur_kernel); if (xd->present_event) { xcb_unregister_for_special_event(xd->base.c, xd->present_event); } @@ -468,6 +472,50 @@ static void *copy(backend_t *base, const void *image, const region_t *reg) { return new_img; } +void *create_blur_context(backend_t *base, enum blur_method method, void *args) { + auto ret = cmalloc(struct _xrender_blur_context); + if (!method || method >= BLUR_METHOD_INVALID) { + ret->method = BLUR_METHOD_NONE; + return ret; + } + + ret->method = BLUR_METHOD_KERNEL; + struct conv **kernels; + int kernel_count; + if (method == BLUR_METHOD_KERNEL) { + kernels = ((struct kernel_blur_args *)args)->kernels; + kernel_count = ((struct kernel_blur_args *)args)->kernel_count; + } else { + kernels = generate_blur_kernel(method, args, &kernel_count); + } + + ret->x_blur_kernel = ccalloc(kernel_count, struct x_convolution_kernel *); + for (int i = 0; i < kernel_count; i++) { + int center = kernels[i]->h * kernels[i]->w / 2; + x_create_convolution_kernel(kernels[i], kernels[i]->data[center], + &ret->x_blur_kernel[i]); + } + ret->x_blur_kernel_count = kernel_count; + + if (method != BLUR_METHOD_KERNEL) { + // Kernels generated by generate_blur_kernel, so we need to free them. + for (int i = 0; i < kernel_count; i++) { + free(kernels[i]); + } + free(kernels); + } + return ret; +} + +void destroy_blur_context(backend_t *base, void *ctx_) { + struct _xrender_blur_context *ctx = ctx_; + for (int i = 0; i < ctx->x_blur_kernel_count; i++) { + free(ctx->x_blur_kernel[i]); + } + free(ctx->x_blur_kernel); + free(ctx); +} + backend_t *backend_xrender_init(session_t *ps) { auto xd = ccalloc(1, struct _xrender_data); init_backend_base(&xd->base, ps); @@ -550,15 +598,6 @@ backend_t *backend_xrender_init(session_t *ps) { xd->root_pict = x_create_picture_with_visual_and_pixmap( ps->c, ps->vis, root_pixmap, 0, NULL); } - - xd->x_blur_kernel = ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *); - for (int i = 0; i < ps->o.blur_kernel_count; i++) { - int center = ps->o.blur_kerns[i]->h * ps->o.blur_kerns[i]->w / 2; - x_create_convolution_kernel(ps->o.blur_kerns[i], - ps->o.blur_kerns[i]->data[center], - &xd->x_blur_kernel[i]); - } - xd->x_blur_kernel_count = ps->o.blur_kernel_count; return &xd->base; err: deinit(&xd->base); @@ -583,6 +622,8 @@ struct backend_operations xrender_ops = { .image_op = image_op, .copy = copy, + .create_blur_context = create_blur_context, + .destroy_blur_context = destroy_blur_context, }; // vim: set noet sw=8 ts=8: diff --git a/src/common.h b/src/common.h index 5e7af63..2fb1dd2 100644 --- a/src/common.h +++ b/src/common.h @@ -143,6 +143,9 @@ typedef struct session { ev_signal int_signal; /// backend data backend_t *backend_data; + /// backend blur context + void *backend_blur_context; + /// graphic drivers used enum driver drivers; /// libev mainloop struct ev_loop *loop; diff --git a/src/compton.c b/src/compton.c index 89b7d4e..cdd53d7 100644 --- a/src/compton.c +++ b/src/compton.c @@ -29,9 +29,9 @@ #include #include "common.h" -#include "config.h" #include "compiler.h" #include "compton.h" +#include "config.h" #include "err.h" #include "kernel.h" #ifdef CONFIG_OPENGL @@ -666,11 +666,43 @@ static void destroy_backend(session_t *ps) { if (ps->backend_data) { // deinit backend + ps->backend_data->ops->destroy_blur_context(ps->backend_data, + ps->backend_blur_context); + ps->backend_blur_context = NULL; ps->backend_data->ops->deinit(ps->backend_data); ps->backend_data = NULL; } } +static bool initialize_blur(session_t *ps) { + struct kernel_blur_args kargs; + struct gaussian_blur_args gargs; + struct box_blur_args bargs; + + void *args = NULL; + switch (ps->o.blur_method) { + case BLUR_METHOD_BOX: + bargs.size = ps->o.blur_radius; + args = (void *)&bargs; + break; + case BLUR_METHOD_KERNEL: + kargs.kernel_count = ps->o.blur_kernel_count; + kargs.kernels = ps->o.blur_kerns; + args = (void *)&kargs; + break; + case BLUR_METHOD_GAUSSIAN: + gargs.size = ps->o.blur_radius; + gargs.deviation = ps->o.blur_deviation; + args = (void *)&gargs; + break; + default: return true; + } + + ps->backend_blur_context = ps->backend_data->ops->create_blur_context( + ps->backend_data, ps->o.blur_method, args); + return ps->backend_blur_context != NULL; +} + /// Init the backend and bind all the window pixmap to backend images static bool initialize_backend(session_t *ps) { if (ps->o.experimental_backends) { @@ -679,14 +711,20 @@ static bool initialize_backend(session_t *ps) { ps->backend_data = backend_list[ps->o.backend]->init(ps); if (!ps->backend_data) { log_fatal("Failed to initialize backend, aborting..."); - ps->quit = true; - ev_break(ps->loop, EVBREAK_ALL); + quit_compton(ps); return false; } ps->backend_data->ops = backend_list[ps->o.backend]; - // window_stack shouldn't include window that's not in the hash table at - // this point. Since there cannot be any fading windows. + if (!initialize_blur(ps)) { + log_fatal("Failed to prepare for background blur, aborting..."); + quit_compton(ps); + return false; + } + + // window_stack shouldn't include window that's + // not in the hash table at this point. Since + // there cannot be any fading windows. HASH_ITER2(ps->windows, _w) { if (!_w->managed) { continue; @@ -874,8 +912,7 @@ static bool register_cm(session_t *ps) { { auto pid = getpid(); xcb_change_property(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, - ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, - 32, 1, &pid); + ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid); } // Set COMPTON_VERSION