new backends: blur interface update
To prepare for different blur methods, the blur interface of backends has been splitted into two parts. Now to use blur, a blur context must be created first; then, the blur method should be called with the blur context created. Updated the existing backends to the new interface. Also implemented handling of the new blur options. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
1eba43f888
commit
67f0ec773a
|
@ -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
|
// We need to blur the bounding shape of the window
|
||||||
// (reg_paint = reg_bound \cap reg_damage)
|
// (reg_paint = reg_bound \cap reg_damage)
|
||||||
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||||
|
ps->backend_blur_context,
|
||||||
®_paint, ®_visible);
|
®_paint, ®_visible);
|
||||||
} else if (frame_transparent && ps->o.blur_background_frame) {
|
} else if (frame_transparent && ps->o.blur_background_frame) {
|
||||||
// Window itself is solid, we only need to blur the 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
|
// make sure reg_blur \in reg_damage
|
||||||
pixman_region32_intersect(®_blur, ®_blur, ®_damage);
|
pixman_region32_intersect(®_blur, ®_blur, ®_damage);
|
||||||
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
||||||
|
ps->backend_blur_context,
|
||||||
®_blur, ®_visible);
|
®_blur, ®_visible);
|
||||||
pixman_region32_fini(®_blur);
|
pixman_region32_fini(®_blur);
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,8 +130,9 @@ struct backend_operations {
|
||||||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
||||||
|
|
||||||
/// Blur a given region of the target.
|
/// Blur a given region of the target.
|
||||||
bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur,
|
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
|
||||||
const region_t *reg_visible) attr_nonnull(1, 3, 4);
|
const region_t *reg_blur, const region_t *reg_visible)
|
||||||
|
attr_nonnull(1, 3, 4, 5);
|
||||||
|
|
||||||
/// Present the back buffer onto the screen.
|
/// Present the back buffer onto the screen.
|
||||||
///
|
///
|
||||||
|
@ -209,6 +210,10 @@ struct backend_operations {
|
||||||
/// returned image should not affect the original image
|
/// returned image should not affect the original image
|
||||||
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
|
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 ============
|
// =========== Hooks ============
|
||||||
/// Let the backend hook into the event handling queue
|
/// Let the backend hook into the event handling queue
|
||||||
void (*set_ready_callback)(backend_t *, backend_ready_callback_t cb);
|
void (*set_ready_callback)(backend_t *, backend_ready_callback_t cb);
|
||||||
|
|
|
@ -17,12 +17,38 @@
|
||||||
#include "string_utils.h"
|
#include "string_utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include "backend/gl/gl_common.h"
|
|
||||||
#include "backend/backend_common.h"
|
#include "backend/backend_common.h"
|
||||||
|
#include "backend/gl/gl_common.h"
|
||||||
|
|
||||||
#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
|
#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
|
||||||
#define QUOTE(...) #__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) {
|
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
|
||||||
log_trace("===\n%s\n===", 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.
|
* 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) {
|
const region_t *reg_visible) {
|
||||||
// Remainder: regions are in Xorg coordinates
|
struct gl_blur_context *bctx = ctx;
|
||||||
struct gl_data *gd = (void *)base;
|
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);
|
const rect_t *extent = pixman_region32_extents((region_t *)reg_blur);
|
||||||
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
|
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
|
||||||
int dst_y = gd->height - extent->y2;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// these should be arguments
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
int nrects;
|
int nrects;
|
||||||
const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects);
|
const rect_t *rects = pixman_region32_rectangles((region_t *)reg_blur, &nrects);
|
||||||
if (!nrects) {
|
if (!nrects) {
|
||||||
|
@ -384,24 +442,24 @@ bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
|
||||||
|
|
||||||
int curr = 0;
|
int curr = 0;
|
||||||
glReadBuffer(GL_BACK);
|
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
|
// Copy the area to be blurred into tmp buffer
|
||||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extent->x1, dst_y, width, height);
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extent->x1, dst_y, width, height);
|
||||||
|
|
||||||
for (int i = 0; i < gd->npasses; ++i) {
|
for (int i = 0; i < bctx->npasses; ++i) {
|
||||||
const gl_blur_shader_t *p = &gd->blur_shader[i];
|
const gl_blur_shader_t *p = &bctx->blur_shader[i];
|
||||||
assert(p->prog);
|
assert(p->prog);
|
||||||
|
|
||||||
assert(gd->blur_texture[curr]);
|
assert(bctx->blur_texture[curr]);
|
||||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[curr]);
|
glBindTexture(GL_TEXTURE_2D, bctx->blur_texture[curr]);
|
||||||
|
|
||||||
glUseProgram(p->prog);
|
glUseProgram(p->prog);
|
||||||
if (i < gd->npasses - 1) {
|
if (i < bctx->npasses - 1) {
|
||||||
// not last pass, draw into framebuffer
|
// 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,
|
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);
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
log_error("Framebuffer attachment failed.");
|
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);
|
glDrawBuffer(GL_BACK);
|
||||||
glUniform1f(p->unifm_opacity, (float)opacity);
|
glUniform1f(p->unifm_opacity, (float)opacity);
|
||||||
}
|
}
|
||||||
if (i == gd->npasses - 1) {
|
if (i == bctx->npasses - 1) {
|
||||||
glUniform2f(p->orig_loc, 0, 0);
|
glUniform2f(p->orig_loc, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
// For other than last pass, we are drawing to a texture, we
|
// For other than last pass, we are drawing to a texture, we
|
||||||
|
@ -460,16 +518,6 @@ end:
|
||||||
return ret;
|
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
|
// clang-format off
|
||||||
const char *vertex_shader = GLSL(330,
|
const char *vertex_shader = GLSL(330,
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
@ -520,32 +568,13 @@ void gl_resize(struct gl_data *gd, int width, int height) {
|
||||||
gd->height = height;
|
gd->height = height;
|
||||||
gd->width = width;
|
gd->width = width;
|
||||||
|
|
||||||
|
// XXX: do we need projection matrix at all?
|
||||||
// Note: OpenGL matrices are column major
|
// Note: OpenGL matrices are column major
|
||||||
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)width, 0, 0, 0},
|
GLfloat projection_matrix[4][4] = {{2.0f / (GLfloat)width, 0, 0, 0},
|
||||||
{0, 2.0f / (GLfloat)height, 0, 0},
|
{0, 2.0f / (GLfloat)height, 0, 0},
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
{-1, -1, 0, 1}};
|
{-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
|
// Update projection matrix in the win shader
|
||||||
glUseProgram(gd->win_shader.prog);
|
glUseProgram(gd->win_shader.prog);
|
||||||
int pml = glGetUniformLocationChecked(gd->win_shader.prog, "projection");
|
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;
|
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.
|
* Initialize GL blur filters.
|
||||||
*/
|
*/
|
||||||
static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels, int nkernels) {
|
void *gl_create_blur_context(backend_t *base, enum blur_method method, void *args) {
|
||||||
if (!nkernels) {
|
bool success = true;
|
||||||
return 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;
|
ctx->method = BLUR_METHOD_KERNEL;
|
||||||
gd->blur_shader = ccalloc(gd->npasses, gl_blur_shader_t);
|
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));
|
char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
|
||||||
// Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
|
// 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;
|
const char *shader_add = FRAG_SHADER_BLUR_ADD;
|
||||||
char *extension = strdup("");
|
char *extension = strdup("");
|
||||||
|
|
||||||
for (int i = 0; i < nkernels; i++) {
|
for (int i = 0; i < ctx->npasses; i++) {
|
||||||
auto kern = kernels[i];
|
auto kern = kernels[i];
|
||||||
// Build shader
|
// Build shader
|
||||||
int width = kern->w, height = kern->h;
|
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) +
|
size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
|
||||||
strlen(shader_body) + 10 /* sum */ +
|
strlen(shader_body) + 10 /* sum */ +
|
||||||
1 /* null terminator */;
|
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);
|
free(shader_str);
|
||||||
if (!pass->prog) {
|
if (!pass->prog) {
|
||||||
log_error("Failed to create GLSL program.");
|
log_error("Failed to create GLSL program.");
|
||||||
goto err;
|
success = false;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
glBindFragDataLocation(pass->prog, 0, "out_color");
|
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->in_texcoord = glGetAttribLocation(pass->prog, "in_texcoord");
|
||||||
pass->coord_loc = glGetAttribLocation(pass->prog, "coord");
|
pass->coord_loc = glGetAttribLocation(pass->prog, "coord");
|
||||||
}
|
}
|
||||||
free(extension);
|
|
||||||
|
|
||||||
// Texture size will be defined by gl_resize
|
// Texture size will be defined by gl_resize
|
||||||
glGenTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture);
|
glGenTextures(ctx->npasses > 1 ? 2 : 1, ctx->blur_texture);
|
||||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
|
glBindTexture(GL_TEXTURE_2D, ctx->blur_texture[0]);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
if (gd->npasses > 1) {
|
if (ctx->npasses > 1) {
|
||||||
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[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_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
// Generate FBO and textures when needed
|
// Generate FBO and textures when needed
|
||||||
glGenFramebuffers(1, &gd->blur_fbo);
|
glGenFramebuffers(1, &ctx->blur_fbo);
|
||||||
if (!gd->blur_fbo) {
|
if (!ctx->blur_fbo) {
|
||||||
log_error("Failed to generate framebuffer object for blur");
|
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
|
// Restore LC_NUMERIC
|
||||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
setlocale(LC_NUMERIC, lc_numeric_old);
|
||||||
free(lc_numeric_old);
|
free(lc_numeric_old);
|
||||||
|
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
|
return ctx;
|
||||||
return true;
|
|
||||||
err:
|
|
||||||
free(extension);
|
|
||||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
|
||||||
free(lc_numeric_old);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// 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);
|
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
|
|
||||||
gl_win_shader_from_string(vertex_shader, win_shader_glsl, &gd->win_shader);
|
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.prog = gl_create_program_from_str(fill_vert, fill_frag);
|
||||||
gd->fill_shader.in_coord_loc =
|
gd->fill_shader.in_coord_loc =
|
||||||
glGetAttribLocation(gd->fill_shader.prog, "in_coord");
|
glGetAttribLocation(gd->fill_shader.prog, "in_coord");
|
||||||
|
@ -857,28 +937,9 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
||||||
return true;
|
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) {
|
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);
|
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) {
|
if (gd->logger) {
|
||||||
log_remove_target_tls(gd->logger);
|
log_remove_target_tls(gd->logger);
|
||||||
gd->logger = NULL;
|
gd->logger = NULL;
|
||||||
|
|
|
@ -66,17 +66,8 @@ struct gl_data {
|
||||||
bool is_nvidia;
|
bool is_nvidia;
|
||||||
// Height and width of the viewport
|
// Height and width of the viewport
|
||||||
int height, width;
|
int height, width;
|
||||||
int npasses;
|
|
||||||
gl_win_shader_t win_shader;
|
gl_win_shader_t win_shader;
|
||||||
gl_fill_shader_t fill_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
|
/// Called when an gl_texture is decoupled from the texture it refers. Returns
|
||||||
/// the decoupled user_data
|
/// the decoupled user_data
|
||||||
|
@ -88,17 +79,6 @@ struct gl_data {
|
||||||
struct log_target *logger;
|
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;
|
typedef struct session session_t;
|
||||||
|
|
||||||
#define GL_PROG_MAIN_INIT \
|
#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);
|
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);
|
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);
|
bool gl_is_image_transparent(backend_t *base, void *image_data);
|
||||||
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
||||||
|
|
|
@ -472,6 +472,8 @@ struct backend_operations glx_ops = {
|
||||||
.buffer_age = glx_buffer_age,
|
.buffer_age = glx_buffer_age,
|
||||||
.render_shadow = default_backend_render_shadow,
|
.render_shadow = default_backend_render_shadow,
|
||||||
.fill = gl_fill,
|
.fill = gl_fill,
|
||||||
|
.create_blur_context = gl_create_blur_context,
|
||||||
|
.destroy_blur_context = gl_destroy_blur_context,
|
||||||
.max_buffer_age = 5, // Why?
|
.max_buffer_age = 5, // Why?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -59,14 +59,17 @@ typedef struct _xrender_data {
|
||||||
/// Width and height of the target pixmap
|
/// Width and height of the target pixmap
|
||||||
int target_width, target_height;
|
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
|
/// Blur kernels converted to X format
|
||||||
struct x_convolution_kernel **x_blur_kernel;
|
struct x_convolution_kernel **x_blur_kernel;
|
||||||
|
|
||||||
/// Number of blur kernels
|
/// Number of blur kernels
|
||||||
int x_blur_kernel_count;
|
int x_blur_kernel_count;
|
||||||
|
};
|
||||||
xcb_special_event_t *present_event;
|
|
||||||
} xrender_data;
|
|
||||||
|
|
||||||
struct _xrender_image_data {
|
struct _xrender_image_data {
|
||||||
// Pixmap that the client window draws to,
|
// 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)}});
|
.height = to_u16_checked(extent->y2 - extent->y1)}});
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur,
|
static bool blur(backend_t *backend_data, double opacity, void *ctx_,
|
||||||
const region_t *reg_visible) {
|
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;
|
struct _xrender_data *xd = (void *)backend_data;
|
||||||
xcb_connection_t *c = xd->base.c;
|
xcb_connection_t *c = xd->base.c;
|
||||||
region_t reg_op;
|
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
|
// For 1 pass, we do
|
||||||
// back -(pass 1)-> tmp0 -(copy)-> target_buffer
|
// back -(pass 1)-> tmp0 -(copy)-> target_buffer
|
||||||
int i;
|
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
|
// Copy from source picture to destination. The filter must
|
||||||
// be applied on source picture, to get the nearby pixels outside the
|
// be applied on source picture, to get the nearby pixels outside the
|
||||||
// window.
|
// window.
|
||||||
// TODO cache converted blur_kerns
|
// TODO cache converted blur_kerns
|
||||||
xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)),
|
xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)),
|
||||||
filter,
|
filter,
|
||||||
to_u32_checked(xd->x_blur_kernel[i]->size),
|
to_u32_checked(bctx->x_blur_kernel[i]->size),
|
||||||
xd->x_blur_kernel[i]->kernel);
|
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
|
// This is not the last pass, or this is the first pass
|
||||||
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||||
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
|
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_render_free_picture(xd->base.c, xd->back[i]);
|
||||||
xcb_free_pixmap(xd->base.c, xd->back_pixmap[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) {
|
if (xd->present_event) {
|
||||||
xcb_unregister_for_special_event(xd->base.c, 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;
|
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) {
|
backend_t *backend_xrender_init(session_t *ps) {
|
||||||
auto xd = ccalloc(1, struct _xrender_data);
|
auto xd = ccalloc(1, struct _xrender_data);
|
||||||
init_backend_base(&xd->base, ps);
|
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(
|
xd->root_pict = x_create_picture_with_visual_and_pixmap(
|
||||||
ps->c, ps->vis, root_pixmap, 0, NULL);
|
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;
|
return &xd->base;
|
||||||
err:
|
err:
|
||||||
deinit(&xd->base);
|
deinit(&xd->base);
|
||||||
|
@ -583,6 +622,8 @@ struct backend_operations xrender_ops = {
|
||||||
|
|
||||||
.image_op = image_op,
|
.image_op = image_op,
|
||||||
.copy = copy,
|
.copy = copy,
|
||||||
|
.create_blur_context = create_blur_context,
|
||||||
|
.destroy_blur_context = destroy_blur_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
// vim: set noet sw=8 ts=8:
|
// vim: set noet sw=8 ts=8:
|
||||||
|
|
|
@ -143,6 +143,9 @@ typedef struct session {
|
||||||
ev_signal int_signal;
|
ev_signal int_signal;
|
||||||
/// backend data
|
/// backend data
|
||||||
backend_t *backend_data;
|
backend_t *backend_data;
|
||||||
|
/// backend blur context
|
||||||
|
void *backend_blur_context;
|
||||||
|
/// graphic drivers used
|
||||||
enum driver drivers;
|
enum driver drivers;
|
||||||
/// libev mainloop
|
/// libev mainloop
|
||||||
struct ev_loop *loop;
|
struct ev_loop *loop;
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
#include <test.h>
|
#include <test.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "config.h"
|
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
#include "compton.h"
|
#include "compton.h"
|
||||||
|
#include "config.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
|
@ -666,11 +666,43 @@ static void destroy_backend(session_t *ps) {
|
||||||
|
|
||||||
if (ps->backend_data) {
|
if (ps->backend_data) {
|
||||||
// deinit backend
|
// 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->ops->deinit(ps->backend_data);
|
||||||
ps->backend_data = NULL;
|
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
|
/// Init the backend and bind all the window pixmap to backend images
|
||||||
static bool initialize_backend(session_t *ps) {
|
static bool initialize_backend(session_t *ps) {
|
||||||
if (ps->o.experimental_backends) {
|
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);
|
ps->backend_data = backend_list[ps->o.backend]->init(ps);
|
||||||
if (!ps->backend_data) {
|
if (!ps->backend_data) {
|
||||||
log_fatal("Failed to initialize backend, aborting...");
|
log_fatal("Failed to initialize backend, aborting...");
|
||||||
ps->quit = true;
|
quit_compton(ps);
|
||||||
ev_break(ps->loop, EVBREAK_ALL);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ps->backend_data->ops = backend_list[ps->o.backend];
|
ps->backend_data->ops = backend_list[ps->o.backend];
|
||||||
|
|
||||||
// window_stack shouldn't include window that's not in the hash table at
|
if (!initialize_blur(ps)) {
|
||||||
// this point. Since there cannot be any fading windows.
|
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) {
|
HASH_ITER2(ps->windows, _w) {
|
||||||
if (!_w->managed) {
|
if (!_w->managed) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -874,8 +912,7 @@ static bool register_cm(session_t *ps) {
|
||||||
{
|
{
|
||||||
auto pid = getpid();
|
auto pid = getpid();
|
||||||
xcb_change_property(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
xcb_change_property(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
||||||
ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL,
|
ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
||||||
32, 1, &pid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set COMPTON_VERSION
|
// Set COMPTON_VERSION
|
||||||
|
|
Loading…
Reference in New Issue