@@ -182,7 +182,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
}
|
||||
|
||||
if (ps->root_image) {
|
||||
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
|
||||
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
|
||||
(coord_t){0}, NULL, (coord_t){0},
|
||||
®_paint, ®_visible);
|
||||
} else {
|
||||
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
|
||||
@@ -203,6 +204,24 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
// The bounding shape of the window, in global/target coordinates
|
||||
// reminder: bounding shape contains the WM frame
|
||||
auto reg_bound = win_get_bounding_shape_global_by_val(w);
|
||||
auto reg_bound_no_corner =
|
||||
win_get_bounding_shape_global_without_corners_by_val(w);
|
||||
region_t reg_bound_local;
|
||||
pixman_region32_init(®_bound_local);
|
||||
pixman_region32_copy(®_bound_local, ®_bound);
|
||||
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
||||
|
||||
if (!w->mask_image) {
|
||||
// TODO(yshui) only allocate a mask if the window is shaped or has
|
||||
// rounded corners.
|
||||
w->mask_image = ps->backend_data->ops->make_mask(
|
||||
ps->backend_data,
|
||||
(geometry_t){.width = w->g.width, .height = w->g.height},
|
||||
®_bound_local);
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->mask_image,
|
||||
(double[]){w->corner_radius});
|
||||
}
|
||||
|
||||
// The clip region for the current window, in global/target coordinates
|
||||
// reg_paint_in_bound \in reg_paint
|
||||
@@ -296,6 +315,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
}
|
||||
}
|
||||
|
||||
coord_t window_coord = {.x = w->g.x, .y = w->g.y};
|
||||
// Draw shadow on target
|
||||
if (w->shadow) {
|
||||
assert(!(w->flags & WIN_FLAGS_SHADOW_NONE));
|
||||
@@ -303,9 +323,6 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
// reg_shadow \in reg_paint
|
||||
auto reg_shadow = win_extents_by_val(w);
|
||||
pixman_region32_intersect(®_shadow, ®_shadow, ®_paint);
|
||||
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
||||
pixman_region32_subtract(®_shadow, ®_shadow, ®_bound);
|
||||
}
|
||||
|
||||
// Mask out the region we don't want shadow on
|
||||
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
|
||||
@@ -341,9 +358,28 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image,
|
||||
&w->opacity);
|
||||
coord_t shadow_coord = {.x = w->g.x + w->shadow_dx,
|
||||
.y = w->g.y + w->shadow_dy};
|
||||
|
||||
auto inverted_mask = NULL;
|
||||
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
||||
pixman_region32_subtract(®_shadow, ®_shadow,
|
||||
®_bound_no_corner);
|
||||
if (w->mask_image) {
|
||||
inverted_mask = w->mask_image;
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
||||
inverted_mask, (bool[]){true});
|
||||
}
|
||||
}
|
||||
ps->backend_data->ops->compose(
|
||||
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
|
||||
w->g.y + w->shadow_dy, ®_shadow, ®_visible);
|
||||
ps->backend_data, w->shadow_image, shadow_coord,
|
||||
inverted_mask, window_coord, ®_shadow, ®_visible);
|
||||
if (inverted_mask) {
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
||||
inverted_mask, (bool[]){false});
|
||||
}
|
||||
pixman_region32_fini(®_shadow);
|
||||
}
|
||||
|
||||
@@ -406,7 +442,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
// Draw window on target
|
||||
if (w->frame_opacity == 1) {
|
||||
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
||||
w->g.x, w->g.y,
|
||||
window_coord, NULL, window_coord,
|
||||
®_paint_in_bound, ®_visible);
|
||||
} else {
|
||||
// For window image processing, we don't have to limit the process
|
||||
@@ -420,10 +456,6 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
region_t reg_visible_local;
|
||||
{
|
||||
// The bounding shape, in window local coordinates
|
||||
region_t reg_bound_local;
|
||||
pixman_region32_init(®_bound_local);
|
||||
pixman_region32_copy(®_bound_local, ®_bound);
|
||||
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
||||
|
||||
pixman_region32_init(®_visible_local);
|
||||
pixman_region32_intersect(®_visible_local,
|
||||
@@ -436,7 +468,6 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
// region, not the clip region.
|
||||
pixman_region32_intersect(
|
||||
®_visible_local, ®_visible_local, ®_bound_local);
|
||||
pixman_region32_fini(®_bound_local);
|
||||
}
|
||||
|
||||
auto new_img = ps->backend_data->ops->clone_image(
|
||||
@@ -446,14 +477,16 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, ®_frame,
|
||||
®_visible_local, (double[]){w->frame_opacity});
|
||||
pixman_region32_fini(®_frame);
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
|
||||
w->g.y, ®_paint_in_bound,
|
||||
®_visible);
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img,
|
||||
window_coord, NULL, window_coord,
|
||||
®_paint_in_bound, ®_visible);
|
||||
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
||||
pixman_region32_fini(®_visible_local);
|
||||
}
|
||||
skip:
|
||||
pixman_region32_fini(®_bound);
|
||||
pixman_region32_fini(®_bound_local);
|
||||
pixman_region32_fini(®_bound_no_corner);
|
||||
pixman_region32_fini(®_paint_in_bound);
|
||||
}
|
||||
pixman_region32_fini(®_paint);
|
||||
|
||||
@@ -30,6 +30,15 @@ typedef struct backend_base {
|
||||
// ...
|
||||
} backend_t;
|
||||
|
||||
typedef struct geometry {
|
||||
int width;
|
||||
int height;
|
||||
} geometry_t;
|
||||
|
||||
typedef struct coord {
|
||||
int x, y;
|
||||
} coord_t;
|
||||
|
||||
typedef void (*backend_ready_callback_t)(void *);
|
||||
|
||||
// This mimics OpenGL's ARB_robustness extension, which enables detection of GPU context
|
||||
@@ -44,8 +53,8 @@ enum device_status {
|
||||
// When image properties are actually applied to the image, they are applied in a
|
||||
// particular order:
|
||||
//
|
||||
// Color inversion -> Dimming -> Opacity multiply -> Limit maximum brightness
|
||||
// (Corner radius could be applied in any order)
|
||||
// Corner radius -> Color inversion -> Dimming -> Opacity multiply -> Limit maximum
|
||||
// brightness
|
||||
enum image_properties {
|
||||
// Whether the color of the image is inverted
|
||||
// 1 boolean, default: false
|
||||
@@ -158,16 +167,19 @@ struct backend_operations {
|
||||
void (*prepare)(backend_t *backend_data, const region_t *reg_damage);
|
||||
|
||||
/**
|
||||
* Paint the content of an image onto the rendering buffer
|
||||
* Paint the content of an image onto the rendering buffer.
|
||||
*
|
||||
* @param backend_data the backend data
|
||||
* @param image_data the image to paint
|
||||
* @param dst_x, dst_y the top left corner of the image in the target
|
||||
* @param mask the mask image, the top left of the mask is aligned with
|
||||
* the top left of the image
|
||||
* @param reg_paint the clip region, in target coordinates
|
||||
* @param reg_visible the visible region, in target coordinates
|
||||
*/
|
||||
void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y,
|
||||
const region_t *reg_paint, const region_t *reg_visible);
|
||||
void (*compose)(backend_t *backend_data, void *image_data, coord_t image_dst,
|
||||
void *mask, coord_t mask_dst, const region_t *reg_paint,
|
||||
const region_t *reg_visible);
|
||||
|
||||
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
|
||||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
||||
@@ -203,6 +215,18 @@ struct backend_operations {
|
||||
void *(*render_shadow)(backend_t *backend_data, int width, int height,
|
||||
const conv *kernel, double r, double g, double b, double a);
|
||||
|
||||
/// Create a mask image from region `reg`. This region can be used to create
|
||||
/// shadow, or used as a mask for composing. When used as a mask, it should mask
|
||||
/// out everything that is not inside the region used to create it.
|
||||
///
|
||||
/// Image properties might be set on masks too, at least the INVERTED and
|
||||
/// CORNER_RADIUS properties must be supported. Inversion should invert the inside
|
||||
/// and outside of the mask. Corner radius should exclude the corners from the
|
||||
/// mask. Corner radius should be applied before the inversion.
|
||||
///
|
||||
/// Required.
|
||||
void *(*make_mask)(backend_t *backend_data, geometry_t size, const region_t *reg);
|
||||
|
||||
// ============ Resource management ===========
|
||||
|
||||
/// Free resources associated with an image data structure
|
||||
|
||||
@@ -23,6 +23,8 @@ struct dummy_image {
|
||||
struct dummy_data {
|
||||
struct backend_base base;
|
||||
struct dummy_image *images;
|
||||
|
||||
struct backend_image mask;
|
||||
};
|
||||
|
||||
struct backend_base *dummy_init(struct session *ps attr_unused) {
|
||||
@@ -47,6 +49,9 @@ void dummy_deinit(struct backend_base *data) {
|
||||
|
||||
static void dummy_check_image(struct backend_base *base, const struct dummy_image *img) {
|
||||
auto dummy = (struct dummy_data *)base;
|
||||
if (img == (struct dummy_image *)&dummy->mask) {
|
||||
return;
|
||||
}
|
||||
struct dummy_image *tmp = NULL;
|
||||
HASH_FIND_INT(dummy->images, &img->pixmap, tmp);
|
||||
if (!tmp) {
|
||||
@@ -56,10 +61,13 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag
|
||||
assert(*tmp->refcount > 0);
|
||||
}
|
||||
|
||||
void dummy_compose(struct backend_base *base, void *image, int dst_x attr_unused,
|
||||
int dst_y attr_unused, const region_t *reg_paint attr_unused,
|
||||
void dummy_compose(struct backend_base *base, void *image, coord_t dst attr_unused,
|
||||
void *mask attr_unused, coord_t mask_dst attr_unused,
|
||||
const region_t *reg_paint attr_unused,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
auto dummy attr_unused = (struct dummy_data *)base;
|
||||
dummy_check_image(base, image);
|
||||
assert(mask == NULL || mask == &dummy->mask);
|
||||
}
|
||||
|
||||
void dummy_fill(struct backend_base *backend_data attr_unused, struct color c attr_unused,
|
||||
@@ -94,6 +102,9 @@ void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
||||
|
||||
void dummy_release_image(backend_t *base, void *image) {
|
||||
auto dummy = (struct dummy_data *)base;
|
||||
if (image == &dummy->mask) {
|
||||
return;
|
||||
}
|
||||
auto img = (struct dummy_image *)image;
|
||||
assert(*img->refcount > 0);
|
||||
(*img->refcount)--;
|
||||
@@ -121,6 +132,11 @@ bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unu
|
||||
return true;
|
||||
}
|
||||
|
||||
void *dummy_make_mask(struct backend_base *base, geometry_t size attr_unused,
|
||||
const region_t *reg attr_unused) {
|
||||
return &(((struct dummy_data *)base)->mask);
|
||||
}
|
||||
|
||||
bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
|
||||
void *image, void *arg attr_unused) {
|
||||
dummy_check_image(base, image);
|
||||
@@ -159,6 +175,7 @@ struct backend_operations dummy_ops = {
|
||||
.blur = dummy_blur,
|
||||
.bind_pixmap = dummy_bind_pixmap,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = dummy_make_mask,
|
||||
.release_image = dummy_release_image,
|
||||
.is_image_transparent = dummy_is_image_transparent,
|
||||
.buffer_age = dummy_buffer_age,
|
||||
|
||||
@@ -385,9 +385,12 @@ static GLuint gl_average_texture_color(backend_t *base, struct backend_image *im
|
||||
* @param reg_visible ignored
|
||||
*/
|
||||
static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target,
|
||||
GLint *coord, GLuint *indices, int nrects) {
|
||||
struct backend_image *mask, coord_t mask_offset, GLint *coord,
|
||||
GLuint *indices, int nrects) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
auto mask_texture =
|
||||
mask ? ((struct gl_texture *)mask->inner)->texture : gd->default_mask_texture;
|
||||
if (!img || !inner->texture) {
|
||||
log_error("Missing texture.");
|
||||
return;
|
||||
@@ -438,9 +441,24 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
glUniform1f(win_shader->uniform_time,
|
||||
(float)ts.tv_sec * 1000.0f + (float)ts.tv_nsec / 1.0e6f);
|
||||
(float)ts.tv_sec * 1000.0F + (float)ts.tv_nsec / 1.0e6F);
|
||||
}
|
||||
|
||||
glUniform1i(win_shader->uniform_mask_tex, 2);
|
||||
glUniform2f(win_shader->uniform_mask_offset, (float)mask_offset.x,
|
||||
(float)mask_offset.y);
|
||||
if (mask != NULL) {
|
||||
glUniform1i(win_shader->uniform_mask_inverted, mask->color_inverted);
|
||||
glUniform1f(win_shader->uniform_mask_corner_radius,
|
||||
(GLfloat)mask->corner_radius);
|
||||
} else {
|
||||
glUniform1i(win_shader->uniform_mask_inverted, 0);
|
||||
glUniform1f(win_shader->uniform_mask_corner_radius, 0);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, mask_texture);
|
||||
|
||||
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n",
|
||||
// x, y, width, height, dx, dy, ptex->width, ptex->height, z);
|
||||
|
||||
@@ -500,11 +518,11 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
||||
/// @param[in] y_inverted whether the texture is y inverted
|
||||
/// @param[out] coord, indices output
|
||||
static void
|
||||
x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int texture_height,
|
||||
x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst, int texture_height,
|
||||
int root_height, bool y_inverted, GLint *coord, GLuint *indices) {
|
||||
dst_y = root_height - dst_y;
|
||||
image_dst.y = root_height - image_dst.y;
|
||||
if (y_inverted) {
|
||||
dst_y -= texture_height;
|
||||
image_dst.y -= texture_height;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nrects; i++) {
|
||||
@@ -515,7 +533,8 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text
|
||||
|
||||
// Calculate texture coordinates
|
||||
// (texture_x1, texture_y1), texture coord for the _bottom left_ corner
|
||||
GLint texture_x1 = crect.x1 - dst_x, texture_y1 = crect.y2 - dst_y,
|
||||
GLint texture_x1 = crect.x1 - image_dst.x,
|
||||
texture_y1 = crect.y2 - image_dst.y,
|
||||
texture_x2 = texture_x1 + (crect.x2 - crect.x1),
|
||||
texture_y2 = texture_y1 + (crect.y1 - crect.y2);
|
||||
|
||||
@@ -555,8 +574,9 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text
|
||||
}
|
||||
|
||||
// TODO(yshui) make use of reg_visible
|
||||
void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
||||
const region_t *reg_tgt, const region_t *reg_visible attr_unused) {
|
||||
void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask,
|
||||
coord_t mask_dst, const region_t *reg_tgt,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
struct backend_image *img = image_data;
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
@@ -579,9 +599,10 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
|
||||
|
||||
auto coord = ccalloc(nrects * 16, GLint);
|
||||
auto indices = ccalloc(nrects * 6, GLuint);
|
||||
x_rect_to_coords(nrects, rects, dst_x, dst_y, inner->height, gd->height,
|
||||
coord_t mask_offset = {.x = mask_dst.x - image_dst.x, .y = mask_dst.y - image_dst.y};
|
||||
x_rect_to_coords(nrects, rects, image_dst, inner->height, gd->height,
|
||||
inner->y_inverted, coord, indices);
|
||||
_gl_compose(base, img, gd->back_fbo, coord, indices, nrects);
|
||||
_gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects);
|
||||
|
||||
free(indices);
|
||||
free(coord);
|
||||
@@ -837,14 +858,16 @@ bool gl_blur(backend_t *base, double opacity, void *ctx, const region_t *reg_blu
|
||||
|
||||
auto coord = ccalloc(nrects * 16, GLint);
|
||||
auto indices = ccalloc(nrects * 6, GLuint);
|
||||
x_rect_to_coords(nrects, rects, extent_resized->x1, extent_resized->y2,
|
||||
x_rect_to_coords(nrects, rects,
|
||||
(coord_t){.x = extent_resized->x1, .y = extent_resized->y2},
|
||||
bctx->fb_height, gd->height, false, coord, indices);
|
||||
|
||||
auto coord_resized = ccalloc(nrects_resized * 16, GLint);
|
||||
auto indices_resized = ccalloc(nrects_resized * 6, GLuint);
|
||||
x_rect_to_coords(nrects_resized, rects_resized, extent_resized->x1,
|
||||
extent_resized->y2, bctx->fb_height, bctx->fb_height, false,
|
||||
coord_resized, indices_resized);
|
||||
x_rect_to_coords(nrects_resized, rects_resized,
|
||||
(coord_t){.x = extent_resized->x1, .y = extent_resized->y2},
|
||||
bctx->fb_height, bctx->fb_height, false, coord_resized,
|
||||
indices_resized);
|
||||
pixman_region32_fini(®_blur_resized);
|
||||
|
||||
GLuint vao[2];
|
||||
@@ -943,6 +966,11 @@ static bool gl_win_shader_from_stringv(const char **vshader_strv,
|
||||
bind_uniform(ret, border_width);
|
||||
bind_uniform(ret, time);
|
||||
|
||||
bind_uniform(ret, mask_tex);
|
||||
bind_uniform(ret, mask_offset);
|
||||
bind_uniform(ret, mask_inverted);
|
||||
bind_uniform(ret, mask_corner_radius);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return true;
|
||||
@@ -1082,9 +1110,45 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) {
|
||||
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
|
||||
}
|
||||
|
||||
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||
auto tex = ccalloc(1, struct gl_texture);
|
||||
auto img = default_new_backend_image(size.width, size.height);
|
||||
tex->width = size.width;
|
||||
tex->height = size.height;
|
||||
tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
tex->has_alpha = false;
|
||||
tex->y_inverted = true;
|
||||
img->inner = (struct backend_image_inner_base *)tex;
|
||||
img->inner->refcount = 1;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, size.width, size.height, 0, GL_RED,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
GLuint fbo;
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
tex->texture, 0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_gl_fill(base, (struct color){1, 1, 1, 1}, reg, fbo, size.height, false);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
return img;
|
||||
}
|
||||
|
||||
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
gd->release_user_data(base, inner);
|
||||
if (inner->user_data) {
|
||||
gd->release_user_data(base, inner);
|
||||
}
|
||||
assert(inner->user_data == NULL);
|
||||
|
||||
glDeleteTextures(1, &inner->texture);
|
||||
@@ -1579,6 +1643,11 @@ const char *win_shader_glsl = GLSL(330,
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D brightness;
|
||||
uniform float max_brightness;
|
||||
|
||||
uniform sampler2D mask_tex;
|
||||
uniform vec2 mask_offset;
|
||||
uniform float mask_corner_radius;
|
||||
uniform bool mask_inverted;
|
||||
// Signed distance field for rectangle center at (0, 0), with size of
|
||||
// half_size * 2
|
||||
float rectangle_sdf(vec2 point, vec2 half_size) {
|
||||
@@ -1627,7 +1696,21 @@ const char *win_shader_glsl = GLSL(330,
|
||||
vec4 window_shader();
|
||||
|
||||
void main() {
|
||||
gl_FragColor = window_shader();
|
||||
vec2 mask_size = textureSize(mask_tex, 0);
|
||||
vec2 maskcoord = texcoord - mask_offset;
|
||||
vec4 mask = texture2D(mask_tex, maskcoord / mask_size);
|
||||
if (mask_corner_radius != 0) {
|
||||
vec2 inner_size = mask_size - vec2(mask_corner_radius) * 2.0f;
|
||||
float dist = rectangle_sdf(maskcoord - mask_size / 2.0f,
|
||||
inner_size / 2.0f) - mask_corner_radius;
|
||||
if (dist > 0.0f) {
|
||||
mask.r = (1.0f - clamp(dist, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
if (mask_inverted) {
|
||||
mask.rgb = 1.0 - mask.rgb;
|
||||
}
|
||||
gl_FragColor = window_shader() * mask.r;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1732,6 +1815,17 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
gd->default_mask_texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
if (!gd->default_mask_texture) {
|
||||
log_error("Failed to generate a default mask texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gd->default_mask_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
|
||||
(GLbyte[]){'\xff'});
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Initialize shaders
|
||||
gd->default_shader = gl_create_window_shader(NULL, win_shader_default);
|
||||
if (!gd->default_shader) {
|
||||
|
||||
@@ -40,6 +40,11 @@ typedef struct {
|
||||
GLint uniform_corner_radius;
|
||||
GLint uniform_border_width;
|
||||
GLint uniform_time;
|
||||
|
||||
GLint uniform_mask_tex;
|
||||
GLint uniform_mask_offset;
|
||||
GLint uniform_mask_corner_radius;
|
||||
GLint uniform_mask_inverted;
|
||||
} gl_win_shader_t;
|
||||
|
||||
// Program and uniforms for brightness shader
|
||||
@@ -90,6 +95,8 @@ struct gl_data {
|
||||
GLuint back_texture, back_fbo;
|
||||
GLuint present_prog;
|
||||
|
||||
GLuint default_mask_texture;
|
||||
|
||||
/// Called when an gl_texture is decoupled from the texture it refers. Returns
|
||||
/// the decoupled user_data
|
||||
void *(*decouple_texture_user_data)(backend_t *base, void *user_data);
|
||||
@@ -117,8 +124,8 @@ bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
||||
/**
|
||||
* @brief Render a region with texture data.
|
||||
*/
|
||||
void gl_compose(backend_t *, void *image_data, int dst_x, int dst_y,
|
||||
const region_t *reg_tgt, const region_t *reg_visible);
|
||||
void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask,
|
||||
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible);
|
||||
|
||||
void gl_resize(struct gl_data *, int width, int height);
|
||||
|
||||
@@ -131,6 +138,7 @@ bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
||||
const region_t *reg_op, const region_t *reg_visible, void *arg);
|
||||
|
||||
void gl_release_image(backend_t *base, void *image_data);
|
||||
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
|
||||
|
||||
void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
|
||||
|
||||
|
||||
@@ -393,11 +393,10 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
}
|
||||
|
||||
log_trace("Binding pixmap %#010x", pixmap);
|
||||
auto wd = ccalloc(1, struct backend_image);
|
||||
wd->max_brightness = 1;
|
||||
auto wd = default_new_backend_image(r->width, r->height);
|
||||
auto inner = ccalloc(1, struct gl_texture);
|
||||
inner->width = wd->ewidth = r->width;
|
||||
inner->height = wd->eheight = r->height;
|
||||
inner->width = r->width;
|
||||
inner->height = r->height;
|
||||
wd->inner = (struct backend_image_inner_base *)inner;
|
||||
free(r);
|
||||
|
||||
@@ -445,9 +444,6 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
inner->user_data = glxpixmap;
|
||||
inner->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||
inner->has_alpha = fmt.alpha_size != 0;
|
||||
wd->opacity = 1;
|
||||
wd->color_inverted = false;
|
||||
wd->dim = 0;
|
||||
wd->inner->refcount = 1;
|
||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||
glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
@@ -541,6 +537,7 @@ struct backend_operations glx_ops = {
|
||||
.present = glx_present,
|
||||
.buffer_age = glx_buffer_age,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = gl_make_mask,
|
||||
.fill = gl_fill,
|
||||
.create_blur_context = gl_create_blur_context,
|
||||
.destroy_blur_context = gl_destroy_blur_context,
|
||||
|
||||
@@ -170,11 +170,60 @@ make_rounded_corner_cache(xcb_connection_t *c, xcb_render_picture_t src,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, int dst_x,
|
||||
int dst_y, const region_t *reg_paint,
|
||||
const region_t *reg_visible, xcb_render_picture_t result) {
|
||||
static xcb_render_picture_t process_mask(struct _xrender_data *xd, struct xrender_image *mask,
|
||||
xcb_render_picture_t alpha_pict, bool *allocated) {
|
||||
auto inner = (struct _xrender_image_data_inner *)mask->base.inner;
|
||||
if (!mask->base.color_inverted && mask->base.corner_radius == 0) {
|
||||
*allocated = false;
|
||||
return inner->pict;
|
||||
}
|
||||
const auto tmpw = to_u16_checked(inner->width);
|
||||
const auto tmph = to_u16_checked(inner->height);
|
||||
*allocated = true;
|
||||
x_clear_picture_clip_region(xd->base.c, inner->pict);
|
||||
auto ret = x_create_picture_with_visual(
|
||||
xd->base.c, xd->base.root, inner->width, inner->height, inner->visual,
|
||||
XCB_RENDER_CP_REPEAT,
|
||||
(xcb_render_create_picture_value_list_t[]){XCB_RENDER_REPEAT_PAD});
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
// Remember: the mask has a 1-pixel border
|
||||
if (mask->base.corner_radius != 0) {
|
||||
if (mask->rounded_rectangle == NULL) {
|
||||
mask->rounded_rectangle = make_rounded_corner_cache(
|
||||
xd->base.c, xd->white_pixel, xd->base.root, inner->width - 2,
|
||||
inner->height - 2, (int)mask->base.corner_radius);
|
||||
}
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
mask->rounded_rectangle->p, XCB_NONE, ret, 0, 0, 0,
|
||||
0, 1, 1, (uint16_t)(tmpw - 2), (uint16_t)(tmph - 2));
|
||||
}
|
||||
|
||||
if (mask->base.color_inverted) {
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_XOR, xd->white_pixel,
|
||||
XCB_NONE, ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
|
||||
if (alpha_pict != XCB_NONE) {
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, ret, alpha_pict,
|
||||
ret, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
struct xrender_image *mask, coord_t mask_dst, const region_t *reg_paint,
|
||||
const region_t *reg_visible, xcb_render_picture_t result) {
|
||||
const struct backend_image *img = &xrimg->base;
|
||||
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
|
||||
bool mask_allocated = false;
|
||||
auto mask_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
|
||||
if (mask != NULL) {
|
||||
mask_pict = process_mask(
|
||||
xd, mask, img->opacity < 1.0 ? mask_pict : XCB_NONE, &mask_allocated);
|
||||
}
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
region_t reg;
|
||||
|
||||
@@ -183,6 +232,9 @@ static void compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg,
|
||||
const auto tmph = to_u16_checked(inner->height);
|
||||
const auto tmpew = to_u16_checked(img->ewidth);
|
||||
const auto tmpeh = to_u16_checked(img->eheight);
|
||||
// Remember: the mask has a 1-pixel border
|
||||
const auto mask_dst_x = to_i16_checked(dst.x - mask_dst.x + 1);
|
||||
const auto mask_dst_y = to_i16_checked(dst.y - mask_dst.y + 1);
|
||||
const xcb_render_color_t dim_color = {
|
||||
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)};
|
||||
|
||||
@@ -207,11 +259,19 @@ static void compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg,
|
||||
inner->height, inner->visual, 0, NULL);
|
||||
|
||||
// Set clip region translated to source coordinate
|
||||
x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst_x),
|
||||
to_i16_checked(-dst_y), ®);
|
||||
x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst.x),
|
||||
to_i16_checked(-dst.y), ®);
|
||||
// Copy source -> tmp
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
|
||||
if (img->corner_radius != 0 && xrimg->rounded_rectangle != NULL) {
|
||||
// Clip tmp_pict with a rounded rectangle
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
xrimg->rounded_rectangle->p, XCB_NONE,
|
||||
tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
|
||||
if (img->color_inverted) {
|
||||
if (inner->has_alpha) {
|
||||
auto tmp_pict2 = x_create_picture_with_visual(
|
||||
@@ -247,38 +307,32 @@ static void compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg,
|
||||
tmp_pict, dim_color, 1, &rect);
|
||||
}
|
||||
|
||||
if (img->corner_radius != 0 && xrimg->rounded_rectangle != NULL) {
|
||||
// Clip tmp_pict with a rounded rectangle
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
xrimg->rounded_rectangle->p, XCB_NONE,
|
||||
tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
|
||||
alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
mask_pict, result, 0, 0, mask_dst_x, mask_dst_y,
|
||||
to_i16_checked(dst.x), to_i16_checked(dst.y), tmpew,
|
||||
tmpeh);
|
||||
xcb_render_free_picture(xd->base.c, tmp_pict);
|
||||
} else {
|
||||
uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
||||
|
||||
xcb_render_composite(xd->base.c, op, inner->pict, alpha_pict, result, 0,
|
||||
0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
xcb_render_composite(xd->base.c, op, inner->pict, mask_pict, result, 0, 0,
|
||||
mask_dst_x, mask_dst_y, to_i16_checked(dst.x),
|
||||
to_i16_checked(dst.y), tmpew, tmpeh);
|
||||
if (img->dim != 0 || img->color_inverted) {
|
||||
// Apply properties, if we reach here, then has_alpha == false
|
||||
assert(!has_alpha);
|
||||
if (img->color_inverted) {
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, result, 0,
|
||||
0, 0, 0, to_i16_checked(dst_x),
|
||||
to_i16_checked(dst_y), tmpew, tmpeh);
|
||||
0, 0, 0, to_i16_checked(dst.x),
|
||||
to_i16_checked(dst.y), tmpew, tmpeh);
|
||||
}
|
||||
|
||||
if (img->dim != 0) {
|
||||
// Dim the actually content of window
|
||||
xcb_rectangle_t rect = {
|
||||
.x = to_i16_checked(dst_x),
|
||||
.y = to_i16_checked(dst_y),
|
||||
.x = to_i16_checked(dst.x),
|
||||
.y = to_i16_checked(dst.y),
|
||||
.width = tmpew,
|
||||
.height = tmpeh,
|
||||
};
|
||||
@@ -288,13 +342,17 @@ static void compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mask_allocated) {
|
||||
xcb_render_free_picture(xd->base.c, mask_pict);
|
||||
}
|
||||
pixman_region32_fini(®);
|
||||
}
|
||||
|
||||
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
|
||||
static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst,
|
||||
const region_t *reg_paint, const region_t *reg_visible) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
return compose_impl(xd, img_data, dst_x, dst_y, reg_paint, reg_visible, xd->back[2]);
|
||||
return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible,
|
||||
xd->back[2]);
|
||||
}
|
||||
|
||||
static void fill(backend_t *base, struct color c, const region_t *clip) {
|
||||
@@ -620,6 +678,52 @@ new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
|
||||
return new_inner;
|
||||
}
|
||||
|
||||
static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
// Give the mask a 1 pixel wide border to emulate the clamp to border behavior of
|
||||
// OpenGL textures.
|
||||
auto w16 = to_u16_checked(size.width + 2);
|
||||
auto h16 = to_u16_checked(size.height + 2);
|
||||
auto inner =
|
||||
new_inner(base, size.width + 2, size.height + 2,
|
||||
x_get_visual_for_standard(base->c, XCB_PICT_STANDARD_ARGB_32), 32);
|
||||
xcb_render_change_picture(base->c, inner->pict, XCB_RENDER_CP_REPEAT,
|
||||
(uint32_t[]){XCB_RENDER_REPEAT_PAD});
|
||||
const rect_t *extent = pixman_region32_extents((region_t *)reg);
|
||||
x_set_picture_clip_region(base->c, xd->back[2], 1, 1, reg);
|
||||
xcb_render_fill_rectangles(
|
||||
base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
(xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0xffff}, 1,
|
||||
(xcb_rectangle_t[]){{.x = to_i16_checked(extent->x1 + 1),
|
||||
.y = to_i16_checked(extent->y1 + 1),
|
||||
.width = to_u16_checked(extent->x2 - extent->x1),
|
||||
.height = to_u16_checked(extent->y2 - extent->y1)}});
|
||||
x_clear_picture_clip_region(xd->base.c, inner->pict);
|
||||
|
||||
// Paint the border transparent
|
||||
xcb_render_fill_rectangles(
|
||||
base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
(xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0}, 4,
|
||||
(xcb_rectangle_t[]){{.x = 0, .y = 0, .width = w16, .height = 1},
|
||||
{.x = 0, .y = 0, .width = 1, .height = h16},
|
||||
{.x = 0, .y = (short)(h16 - 1), .width = w16, .height = 1},
|
||||
{.x = (short)(w16 - 1), .y = 0, .width = 1, .height = h16}});
|
||||
inner->refcount = 1;
|
||||
|
||||
auto img = ccalloc(1, struct xrender_image);
|
||||
img->base.eheight = size.height + 2;
|
||||
img->base.ewidth = size.width + 2;
|
||||
img->base.border_width = 0;
|
||||
img->base.color_inverted = false;
|
||||
img->base.corner_radius = 0;
|
||||
img->base.max_brightness = 1;
|
||||
img->base.opacity = 1;
|
||||
img->base.dim = 0;
|
||||
img->base.inner = (struct backend_image_inner_base *)inner;
|
||||
img->rounded_rectangle = NULL;
|
||||
return img;
|
||||
}
|
||||
|
||||
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
|
||||
if (img->inner->refcount == 1) {
|
||||
return true;
|
||||
@@ -857,6 +961,7 @@ struct backend_operations xrender_ops = {
|
||||
.bind_pixmap = bind_pixmap,
|
||||
.release_image = release_image,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = make_mask,
|
||||
//.prepare_win = prepare_win,
|
||||
//.release_win = release_win,
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
|
||||
30
src/utils.h
30
src/utils.h
@@ -82,39 +82,39 @@ safe_isnan(double a) {
|
||||
|
||||
#define to_int_checked(val) \
|
||||
({ \
|
||||
int64_t tmp = (val); \
|
||||
ASSERT_IN_RANGE(tmp, INT_MIN, INT_MAX); \
|
||||
(int)tmp; \
|
||||
int64_t __to_tmp = (val); \
|
||||
ASSERT_IN_RANGE(__to_tmp, INT_MIN, INT_MAX); \
|
||||
(int)__to_tmp; \
|
||||
})
|
||||
|
||||
#define to_char_checked(val) \
|
||||
({ \
|
||||
int64_t tmp = (val); \
|
||||
ASSERT_IN_RANGE(tmp, CHAR_MIN, CHAR_MAX); \
|
||||
(char)tmp; \
|
||||
int64_t __to_tmp = (val); \
|
||||
ASSERT_IN_RANGE(__to_tmp, CHAR_MIN, CHAR_MAX); \
|
||||
(char)__to_tmp; \
|
||||
})
|
||||
|
||||
#define to_u16_checked(val) \
|
||||
({ \
|
||||
auto tmp = (val); \
|
||||
ASSERT_IN_RANGE(tmp, 0, UINT16_MAX); \
|
||||
(uint16_t) tmp; \
|
||||
auto __to_tmp = (val); \
|
||||
ASSERT_IN_RANGE(__to_tmp, 0, UINT16_MAX); \
|
||||
(uint16_t) __to_tmp; \
|
||||
})
|
||||
|
||||
#define to_i16_checked(val) \
|
||||
({ \
|
||||
int64_t tmp = (val); \
|
||||
ASSERT_IN_RANGE(tmp, INT16_MIN, INT16_MAX); \
|
||||
(int16_t) tmp; \
|
||||
int64_t __to_tmp = (val); \
|
||||
ASSERT_IN_RANGE(__to_tmp, INT16_MIN, INT16_MAX); \
|
||||
(int16_t) __to_tmp; \
|
||||
})
|
||||
|
||||
#define to_u32_checked(val) \
|
||||
({ \
|
||||
auto tmp = (val); \
|
||||
auto __to_tmp = (val); \
|
||||
int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
|
||||
comparison warning*/ \
|
||||
ASSERT_IN_RANGE(tmp, 0, max); \
|
||||
(uint32_t) tmp; \
|
||||
ASSERT_IN_RANGE(__to_tmp, 0, max); \
|
||||
(uint32_t) __to_tmp; \
|
||||
})
|
||||
/**
|
||||
* Normalize an int value to a specific range.
|
||||
|
||||
12
src/win.c
12
src/win.c
@@ -314,6 +314,13 @@ static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void win_release_mask(backend_t *base, struct managed_win *w) {
|
||||
if (w->mask_image) {
|
||||
base->ops->release_image(base, w->mask_image);
|
||||
w->mask_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w) {
|
||||
assert(!w->win_image);
|
||||
auto pixmap = x_new_id(b->c);
|
||||
@@ -373,6 +380,8 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
|
||||
assert(!win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE));
|
||||
win_release_shadow(backend, w);
|
||||
}
|
||||
|
||||
win_release_mask(backend, w);
|
||||
}
|
||||
|
||||
/// Returns true if the `prop` property is stale, as well as clears the stale flag.
|
||||
@@ -1205,6 +1214,7 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// Invalidate the shadow we built
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
win_release_mask(ps->backend_data, w);
|
||||
ps->pending_updates = true;
|
||||
free_paint(ps, &w->shadow_paint);
|
||||
}
|
||||
@@ -1496,6 +1506,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
// is mapped
|
||||
.win_image = NULL,
|
||||
.shadow_image = NULL,
|
||||
.mask_image = NULL,
|
||||
.prev_trans = NULL,
|
||||
.shadow = false,
|
||||
.clip_shadow_above = false,
|
||||
@@ -1927,6 +1938,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
||||
// Window shape changed, we should free old wpaint and shadow pict
|
||||
// log_trace("free out dated pict");
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
win_release_mask(ps->backend_data, w);
|
||||
ps->pending_updates = true;
|
||||
|
||||
free_paint(ps, &w->paint);
|
||||
|
||||
Reference in New Issue
Block a user