Added New Backend Dual Kawase Blur + Rounded Corners - merged with Ibhagwan

This commit is contained in:
jon
2020-07-14 18:04:45 +02:00
parent d6bc68146b
commit 0375dad5d1
54 changed files with 4696 additions and 1620 deletions

View File

@@ -55,7 +55,7 @@ region_t get_damage(session_t *ps, bool all_damage) {
/// paint all windows
void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
if (ps->o.xrender_sync_fence) {
if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) {
if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be "
"disabled from now on.");
@@ -151,11 +151,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, t, ps->root_image, 0, 0,
&reg_paint, &reg_visible);
} else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
&reg_paint);
}
// Windows are sorted from bottom to top
@@ -171,7 +168,7 @@ 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 = win_get_bounding_shape_global_by_val(w, true);
// The clip region for the current window, in global/target coordinates
// reg_paint_in_bound \in reg_paint
@@ -190,6 +187,17 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_paint_in_bound, &reg_visible);
}
// Store the window background for rounded corners
// If rounded corners backup the region first
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
ps->backend_data->ops->store_back_texture(ps->backend_data, w,
ps->backend_round_context, &reg_bound, x, y, wid, hei);
}
// Blur window background
// TODO since the background might change the content of the window (e.g.
// with shaders), we should consult the background whether the window
@@ -204,36 +212,20 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// itself is not opaque, only the frame is.
double blur_opacity = 1;
if (w->opacity < (1.0 / MAX_ALPHA)) {
// Hide blur for fully transparent windows.
blur_opacity = 0;
} else if (w->state == WSTATE_MAPPING) {
if (w->state == WSTATE_MAPPING) {
// Gradually increase the blur intensity during
// fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target;
} else if (w->state == WSTATE_UNMAPPING ||
w->state == WSTATE_DESTROYING) {
// Gradually decrease the blur intensity during
// fading out.
assert(w->opacity <= w->opacity_target_old);
blur_opacity = w->opacity / w->opacity_target_old;
} else if (w->state == WSTATE_FADING) {
if (w->opacity < w->opacity_target &&
w->opacity_target_old < (1.0 / MAX_ALPHA)) {
// Gradually increase the blur intensity during
// fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target;
} else if (w->opacity > w->opacity_target &&
w->opacity_target < (1.0 / MAX_ALPHA)) {
// Gradually decrease the blur intensity during
// fading out.
assert(w->opacity <= w->opacity_target_old);
blur_opacity = w->opacity / w->opacity_target_old;
}
blur_opacity =
w->opacity / win_calc_opacity_target(ps, w, true);
} else if (!ps->o.blur_background_fixed) {
// Apply blur intensity depending on the window opacity.
blur_opacity = w->opacity;
}
assert(blur_opacity >= 0 && blur_opacity <= 1);
if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
// We need to blur the bounding shape of the window
@@ -249,7 +241,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
assert(ps->o.blur_background_frame);
assert(real_win_mode == WMODE_FRAME_TRANS);
auto reg_blur = win_get_region_frame_local_by_val(w);
auto reg_blur = win_get_region_frame_local_by_val(w, true);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
// make sure reg_blur \in reg_paint
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint);
@@ -305,7 +297,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
assert(w->shadow_image);
if (w->opacity == 1) {
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
ps->backend_data, w, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
} else {
auto new_img = ps->backend_data->ops->copy(
@@ -314,7 +306,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible, (double[]){w->opacity});
ps->backend_data->ops->compose(
ps->backend_data, new_img, w->g.x + w->shadow_dx,
ps->backend_data, w, new_img, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
}
@@ -330,7 +322,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
ps->backend_data->ops->compose(ps->backend_data, w, w->win_image,
w->g.x, w->g.y,
&reg_paint_in_bound, &reg_visible);
} else if (w->opacity * MAX_ALPHA >= 1) {
@@ -379,7 +371,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_visible_local, (double[]){dim_opacity});
}
if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_frame_local_by_val(w);
auto reg_frame = win_get_region_frame_local_by_val(w, true);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity});
@@ -390,13 +382,21 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible_local, (double[]){w->opacity});
}
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
ps->backend_data->ops->compose(ps->backend_data, w, new_img, w->g.x,
w->g.y, &reg_paint_in_bound,
&reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
// Round the corners as last step after blur/shadow/dim/etc
if (w->corner_radius > 0.0) {
ps->backend_data->ops->round(ps->backend_data, w,
ps->backend_round_context, w->win_image,
&reg_bound, &reg_visible);
}
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint_in_bound);
}

View File

@@ -28,6 +28,9 @@ typedef struct backend_base {
/// Whether the backend can accept new render request at the moment
bool busy;
// ...
// Session data
session_t *ps;
} backend_t;
typedef void (*backend_ready_callback_t)(void *);
@@ -56,6 +59,11 @@ struct gaussian_blur_args {
double deviation;
};
struct dual_kawase_blur_args {
int size;
blur_strength_t strength;
};
struct box_blur_args {
int size;
};
@@ -65,6 +73,11 @@ struct kernel_blur_args {
int kernel_count;
};
struct round_corners_args {
int corner_radius;
bool round_borders;
};
struct backend_operations {
// =========== Initialization ===========
@@ -126,7 +139,7 @@ struct backend_operations {
* @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,
void (*compose)(backend_t *backend_data, struct managed_win *const w, void *image_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible);
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
@@ -137,6 +150,11 @@ struct backend_operations {
const region_t *reg_blur, const region_t *reg_visible)
attr_nonnull(1, 3, 4, 5);
/// Round a given region of the rendering buffer.
bool (*round)(backend_t *backend_data, struct managed_win *w, void *round_ctx,
void *image_data, const region_t *reg_round, const region_t *reg_visible)
attr_nonnull(1, 2, 3, 5, 6);
/// Update part of the back buffer with the rendering buffer, then present the
/// back buffer onto the target window (if not back buffered, update part of the
/// target window directly).
@@ -218,6 +236,15 @@ struct backend_operations {
/// Get how many pixels outside of the blur area is needed for blur
void (*get_blur_size)(void *blur_context, int *width, int *height);
/// Backup our current window background so we can use it for "erasing" corners
bool (*store_back_texture)(backend_t *base, struct managed_win *w, void *ctx_,
const region_t *reg_tgt, int x, int y, int width, int height);
/// Create a rounded corners context
void *(*create_round_context)(backend_t *base, void *args);
/// Destroy a rounded corners context
void (*destroy_round_context)(backend_t *base, void *ctx);
// =========== Hooks ============
/// Let the backend hook into the event handling queue
/// Not implemented yet

View File

@@ -362,10 +362,106 @@ struct conv **generate_blur_kernel(enum blur_method method, void *args, int *ker
return NULL;
}
/// Generate kernel parameters for dual-kawase blur method. Falls back on approximating
/// standard gauss radius if strength is not supplied
struct dual_kawase_params *generate_dual_kawase_params(void *args) {
struct dual_kawase_blur_args *blur_args = args;
static const struct {
int iterations;
float offset;
} strength_levels[20] = {
{.iterations = 1, .offset = 1.25f}, // LVL 1 => radius 4
{.iterations = 1, .offset = 2.25f}, // LVL 2 => radius 7
{.iterations = 2, .offset = 2.00f}, // LVL 3 => radius 14
{.iterations = 2, .offset = 3.00f}, // LVL 4 => radius 20
{.iterations = 2, .offset = 4.25f}, // LVL 5 => radius 28
{.iterations = 3, .offset = 2.50f}, // LVL 6 => radius 35
{.iterations = 3, .offset = 3.25f}, // LVL 7 => radius 45
{.iterations = 3, .offset = 4.25f}, // LVL 8 => radius 57
{.iterations = 3, .offset = 5.50f}, // LVL 9 => radius 74
{.iterations = 4, .offset = 3.25f}, // LVL 10 => radius 91
{.iterations = 4, .offset = 4.00f}, // LVL 11 => radius 110
{.iterations = 4, .offset = 5.00f}, // LVL 12 => radius 135
{.iterations = 4, .offset = 6.00f}, // LVL 13 => radius 161
{.iterations = 4, .offset = 7.25f}, // LVL 14 => radius 195
{.iterations = 4, .offset = 8.25f}, // LVL 15 => radius 221
{.iterations = 5, .offset = 4.50f}, // LVL 16 => radius 250
{.iterations = 5, .offset = 5.25f}, // LVL 17 => radius 287
{.iterations = 5, .offset = 6.25f}, // LVL 18 => radius 330
{.iterations = 5, .offset = 7.25f}, // LVL 19 => radius 383
{.iterations = 5, .offset = 8.50f}, // LVL 20 => radius >450
};
auto params = ccalloc(1, struct dual_kawase_params);
params->iterations = 0;
params->offset = 1.0f;
if (blur_args->strength.strength <= 0 && blur_args->size) {
// approximate blur_strength with gaussian blur_radius
if (blur_args->size < 6) {
blur_args->strength.strength = 1;
} else if (blur_args->size < 11) {
blur_args->strength.strength = 2;
} else if (blur_args->size < 17) {
blur_args->strength.strength = 3;
} else if (blur_args->size < 24) {
blur_args->strength.strength = 4;
} else if (blur_args->size < 32) {
blur_args->strength.strength = 5;
} else if (blur_args->size < 40) {
blur_args->strength.strength = 6;
} else if (blur_args->size < 51) {
blur_args->strength.strength = 7;
} else if (blur_args->size < 67) {
blur_args->strength.strength = 8;
} else if (blur_args->size < 83) {
blur_args->strength.strength = 9;
} else if (blur_args->size < 101) {
blur_args->strength.strength = 10;
} else if (blur_args->size < 123) {
blur_args->strength.strength = 11;
} else if (blur_args->size < 148) {
blur_args->strength.strength = 12;
} else if (blur_args->size < 177) {
blur_args->strength.strength = 13;
} else if (blur_args->size < 208) {
blur_args->strength.strength = 14;
} else if (blur_args->size < 236) {
blur_args->strength.strength = 15;
} else if (blur_args->size < 269) {
blur_args->strength.strength = 16;
} else if (blur_args->size < 309) {
blur_args->strength.strength = 17;
} else if (blur_args->size < 357) {
blur_args->strength.strength = 18;
} else if (blur_args->size < 417) {
blur_args->strength.strength = 19;
} else {
blur_args->strength.strength = 20;
}
}
if (blur_args->strength.strength > 0) {
assert(blur_args->strength.strength <= 20);
params->iterations = strength_levels[blur_args->strength.strength - 1].iterations;
params->offset = strength_levels[blur_args->strength.strength - 1].offset;
}
params->expand = 2 * (int)exp2f((float)params->iterations) *
(256 - (int)(256.0f - params->offset)) +
1;
log_info("blur-strength: %d [.iter = %d, .offset = %f]",
blur_args->strength.strength, params->iterations, params->offset);
return params;
}
void init_backend_base(struct backend_base *base, session_t *ps) {
base->c = ps->c;
base->loop = ps->loop;
base->root = ps->root;
base->busy = false;
base->ops = NULL;
base->ps = ps;
}

View File

@@ -16,6 +16,15 @@ typedef struct conv conv;
typedef struct backend_base backend_t;
struct backend_operations;
typedef struct dual_kawase_params {
/// Number of downsample passes
int iterations;
/// Pixel offset for down- and upsample
float offset;
/// Save area around blur target (@ref resize_width, @ref resize_height)
int expand;
} dual_kawase_params_t;
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
@@ -41,3 +50,5 @@ default_backend_render_shadow(backend_t *backend_data, int width, int height,
void init_backend_base(struct backend_base *base, session_t *ps);
struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
struct dual_kawase_params *generate_dual_kawase_params(void *args);

View File

@@ -12,14 +12,6 @@
#include "compiler.h"
#include "log.h"
/// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver driver) {
if (driver & DRIVER_NVIDIA) {
setenv("__GL_YIELD", "usleep", true);
ps->o.xrender_sync_fence = true;
}
}
enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {
enum driver ret = 0;
// First we try doing backend agnostic detection using RANDR

View File

@@ -32,9 +32,6 @@ enum driver {
/// Note, this is a best-effort test, so no guarantee all drivers will be detected.
enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_t);
/// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver);
// Print driver names to stdout, for diagnostics
static inline void print_drivers(enum driver drivers) {
if (drivers & DRIVER_AMDGPU) {

View File

@@ -56,7 +56,7 @@ 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,
void dummy_compose(struct backend_base *base, struct managed_win *w attr_unused, void *image, int dst_x attr_unused,
int dst_y attr_unused, const region_t *reg_paint attr_unused,
const region_t *reg_visible attr_unused) {
dummy_check_image(base, image);
@@ -72,6 +72,12 @@ bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity at
return true;
}
bool dummy_round(struct backend_base *backend_data attr_unused, struct managed_win *w attr_unused,
void *ctx_ attr_unused, void *image_data attr_unused, const region_t *reg_round attr_unused,
const region_t *reg_visible attr_unused) {
return true;
}
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned attr_unused) {
auto dummy = (struct dummy_data *)base;
@@ -138,6 +144,14 @@ void *dummy_create_blur_context(struct backend_base *base attr_unused,
void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
void *dummy_create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void dummy_destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
// These numbers are arbitrary, to make sure the reisze_region code path is
// covered.
@@ -145,12 +159,18 @@ void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
*height = 5;
}
bool dummy_store_back_texture(backend_t *backend_data attr_unused, struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused, int y attr_unused, int width attr_unused, int height attr_unused) {
return true;
}
struct backend_operations dummy_ops = {
.init = dummy_init,
.deinit = dummy_deinit,
.compose = dummy_compose,
.fill = dummy_fill,
.blur = dummy_blur,
.round = dummy_round,
.bind_pixmap = dummy_bind_pixmap,
.render_shadow = default_backend_render_shadow,
.release_image = dummy_release_image,
@@ -162,6 +182,9 @@ struct backend_operations dummy_ops = {
.copy = dummy_image_copy,
.create_blur_context = dummy_create_blur_context,
.destroy_blur_context = dummy_destroy_blur_context,
.create_round_context = dummy_create_round_context,
.destroy_round_context = dummy_destroy_round_context,
.get_blur_size = dummy_get_blur_size,
.store_back_texture = dummy_store_back_texture
};

File diff suppressed because it is too large Load Diff

View File

@@ -33,10 +33,25 @@ typedef struct {
typedef struct {
GLuint prog;
GLint unifm_opacity;
GLint unifm_texture_size;
GLint unifm_halfpixel;
GLint orig_loc;
GLint texorig_loc;
GLint projection_loc;
} gl_blur_shader_t;
typedef struct {
GLuint prog;
GLint projection_loc;
GLint unifm_radius;
GLint unifm_texcoord;
GLint unifm_texsize;
GLint unifm_borderw;
GLint unifm_resolution;
GLint unifm_tex_bg;
GLint unifm_tex_wnd;
} gl_round_shader_t;
typedef struct {
GLuint prog;
GLint color_loc;
@@ -68,7 +83,7 @@ struct gl_data {
backend_t base;
// If we are using proprietary NVIDIA driver
bool is_nvidia;
// Height and width of the root window
// Height and width of the viewport
int height, width;
gl_win_shader_t win_shader;
gl_brightness_shader_t brightness_shader;
@@ -98,7 +113,7 @@ GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_
/**
* @brief Render a region with texture data.
*/
void gl_compose(backend_t *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt,
void gl_compose(backend_t *, struct managed_win *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt,
const region_t *reg_visible);
void gl_resize(struct gl_data *, int width, int height);
@@ -121,6 +136,14 @@ void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx);
void gl_get_blur_size(void *blur_context, int *width, int *height);
bool gl_round(backend_t *backend_data, struct managed_win *w, void *ctx_,
void *image_data, const region_t *reg_round, const region_t *reg_visible);
void *gl_create_round_context(backend_t *base, void *args);
void gl_destroy_round_context(backend_t *base, void *ctx);
bool gl_store_back_texture(backend_t *backend_data, struct managed_win *w,
void *ctx_, const region_t *reg_tgt, int x, int y, int width, int height);
bool gl_is_image_transparent(backend_t *base, void *image_data);
void gl_fill(backend_t *base, struct color, const region_t *clip);

View File

@@ -45,6 +45,8 @@ struct _glx_data {
Display *display;
int screen;
xcb_window_t target_win;
int glx_event;
int glx_error;
GLXContext ctx;
};
@@ -244,7 +246,7 @@ static backend_t *glx_init(session_t *ps) {
XVisualInfo *pvis = NULL;
// Check for GLX extension
if (!ps->glx_exists) {
if (!glXQueryExtension(ps->dpy, &gd->glx_event, &gd->glx_error)) {
log_error("No GLX extension.");
goto end;
}
@@ -466,7 +468,10 @@ static void glx_present(backend_t *base, const region_t *region attr_unused) {
struct _glx_data *gd = (void *)base;
gl_present(base, region);
glXSwapBuffers(gd->display, gd->target_win);
glFinish();
// XXX there should be no need to block, the core should wait for render to finish
if (!gd->gl.is_nvidia) {
glFinish();
}
}
static int glx_buffer_age(backend_t *base) {
@@ -489,6 +494,7 @@ struct backend_operations glx_ops = {
.image_op = gl_image_op,
.copy = gl_copy,
.blur = gl_blur,
.round = gl_round,
.is_image_transparent = gl_is_image_transparent,
.present = glx_present,
.buffer_age = glx_buffer_age,
@@ -496,7 +502,10 @@ struct backend_operations glx_ops = {
.fill = gl_fill,
.create_blur_context = gl_create_blur_context,
.destroy_blur_context = gl_destroy_blur_context,
.create_round_context = gl_create_round_context,
.destroy_round_context = gl_destroy_round_context,
.get_blur_size = gl_get_blur_size,
.store_back_texture = gl_store_back_texture,
.max_buffer_age = 5, // Why?
};

View File

@@ -90,7 +90,9 @@ struct _xrender_image_data {
bool owned;
};
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps, int cr, int wid, int hei);
static void compose(backend_t *base, struct managed_win *w, void *img_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data;
@@ -104,10 +106,52 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
// sure we get everything into the buffer
x_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0,
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
// Are we rounding corners?
session_t *ps = base->ps;
int cr = (w ? w->corner_radius : 0);
if (cr == 0) {
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0,
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
} else {
// Rounded corners
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w-> heightb : 0;
//const int fullwid = img->width;
//const int fullhei = img->height;
//log_warn("f(%d, %d) imge(%d %d) imgf(%d %d) sdw(%d %d) dst(%d %d) s:%d b:%d", fullwid, fullhei, img->ewidth, img->eheight, img->width, img->height, w->shadow_width, w->shadow_height, dst_x, dst_y, w->shadow, w->g.border_width);
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
ps->c, ps->root, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {
.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0,
.y = 0,
.width = to_u16_checked(fullwid),
.height = to_u16_checked(fullhei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
p_tmp, trans, 1, &rect);
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, fullwid, fullhei);
xcb_render_trapezoids(
ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
0, 0, n, traps);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OVER, img->pict, p_tmp, xd->back[2],
0, 0, 0, 0,
//0, 0, to_i16_checked(x), to_i16_checked(y),
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
xcb_render_free_picture(ps->c, p_tmp);
}
pixman_region32_fini(&reg);
}
@@ -245,6 +289,15 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
return true;
}
static bool x_round(struct backend_base *backend_data attr_unused, struct managed_win *w attr_unused,
void *ctx_ attr_unused, void *image_data attr_unused, const region_t *reg_blur attr_unused,
const region_t *reg_visible attr_unused) {
// dummy implementation, we already perform the rounding in compose
// TODO: should move the compose code here and call it from here
return true;
}
static void *
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
xcb_generic_error_t *e;
@@ -506,6 +559,13 @@ void *create_blur_context(backend_t *base attr_unused, enum blur_method method,
return ret;
}
if (method == BLUR_METHOD_DUAL_KAWASE || method == BLUR_METHOD_ALT_KAWASE) {
log_warn("Blur method 'kawase' is not compatible with the 'xrender' "
"backend.");
ret->method = BLUR_METHOD_NONE;
return ret;
}
ret->method = BLUR_METHOD_KERNEL;
struct conv **kernels;
int kernel_count;
@@ -551,6 +611,19 @@ void get_blur_size(void *blur_context, int *width, int *height) {
*height = ctx->resize_height;
}
bool store_back_texture(backend_t *backend_data attr_unused, struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused, int y attr_unused, int width attr_unused, int height attr_unused) {
return true;
}
void *create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data);
init_backend_base(&xd->base, ps);
@@ -638,6 +711,7 @@ struct backend_operations xrender_ops = {
.init = backend_xrender_init,
.deinit = deinit,
.blur = blur,
.round = x_round,
.present = present,
.compose = compose,
.fill = fill,
@@ -654,7 +728,11 @@ struct backend_operations xrender_ops = {
.copy = copy,
.create_blur_context = create_blur_context,
.destroy_blur_context = destroy_blur_context,
.create_round_context = create_round_context,
.destroy_round_context = destroy_round_context,
.get_blur_size = get_blur_size,
.store_back_texture = store_back_texture
};
// vim: set noet sw=8 ts=8:

View File

@@ -75,6 +75,9 @@
/// @brief Maximum OpenGL buffer age.
#define CGLX_MAX_BUFFER_AGE 5
/// @brief Maximum passes for blur.
#define MAX_BLUR_PASS 6
// Window flags
// === Types ===
@@ -157,6 +160,8 @@ typedef struct session {
backend_t *backend_data;
/// backend blur context
void *backend_blur_context;
/// round corners context
void *backend_round_context;
/// graphic drivers used
enum driver drivers;
/// file watch handle
@@ -214,7 +219,6 @@ typedef struct session {
/// Custom GLX program used for painting window.
// XXX should be in struct glx_session
glx_prog_main_t glx_prog_win;
struct glx_fbconfig_info *argb_fbconfig;
#endif
/// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence;
@@ -340,12 +344,14 @@ typedef struct session {
int randr_error;
/// Whether X Present extension exists.
bool present_exists;
#ifdef CONFIG_OPENGL
/// Whether X GLX extension exists.
bool glx_exists;
/// Event base number for X GLX extension.
int glx_event;
/// Error base number for X GLX extension.
int glx_error;
#endif
/// Whether X Xinerama extension exists.
bool xinerama_exists;
/// Xinerama screen regions.
@@ -528,7 +534,8 @@ static inline void wintype_arr_enable(bool arr[]) {
}
}
/**
* Get current system clock in 40 microseconds.
* Get current system clock in milliseconds.
*/
int64_t get_time_ms(void);

View File

@@ -88,6 +88,11 @@ enum blur_method parse_blur_method(const char *src) {
return BLUR_METHOD_BOX;
} else if (strcmp(src, "gaussian") == 0) {
return BLUR_METHOD_GAUSSIAN;
} else if (strcmp(src, "kawase") == 0 || strcmp(src, "dual_kawase") == 0) {
return BLUR_METHOD_DUAL_KAWASE;
} else if (strcmp(src, "kawase_alt") == 0 || strcmp(src, "alt_kawase") == 0) {
// new code from tryone144's `improved_dbo` branch
return BLUR_METHOD_ALT_KAWASE;
} else if (strcmp(src, "none") == 0) {
return BLUR_METHOD_NONE;
}
@@ -489,6 +494,12 @@ void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_en
// opacity logic is complicated, and needs an "unset" state
opt->wintype_option[i].opacity = NAN;
}
if (!mask[i].corner_radius) {
opt->wintype_option[i].corner_radius = -1;
}
if (!mask[i].round_borders) {
opt->wintype_option[i].round_borders = -1;
}
}
}
@@ -497,15 +508,6 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
*opt = (struct options){
.backend = BKEND_XRENDER,
.glx_no_stencil = false,
.transition_length = 300,
.transition_pow_x = 0.1,
.transition_pow_y = 0.1,
.transition_pow_w = 0.1,
.transition_pow_h = 0.1,
.size_transition = true,
.no_scale_down = false,
.spawn_center_screen = false,
.spawn_center = true,
.mark_wmwin_focused = false,
.mark_ovredir_focused = false,
.detect_rounded_corners = false,
@@ -519,6 +521,15 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.benchmark = 0,
.benchmark_wid = XCB_NONE,
.logpath = NULL,
.transition_length = 300,
.transition_pow_x = 0.1,
.transition_pow_y = 0.1,
.transition_pow_w = 0.1,
.transition_pow_h = 0.1,
.size_transition = true,
.no_scale_down = false,
.spawn_center_screen = false,
.spawn_center = true,
.refresh_rate = 0,
.sw_opti = false,
@@ -551,6 +562,7 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.blur_method = BLUR_METHOD_NONE,
.blur_radius = 3,
.blur_deviation = 0.84089642,
.blur_strength = {.strength = -1, .iterations = 3, .offset = 2.75, .expand = 50},
.blur_background_frame = false,
.blur_background_fixed = false,
.blur_background_blacklist = NULL,
@@ -569,7 +581,9 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.no_ewmh_fullscreen = false,
.track_leader = false,
};
.rounded_corners_blacklist = NULL,
.round_borders_blacklist = NULL};
char *ret = NULL;
#ifdef CONFIG_LIBCONFIG

View File

@@ -44,6 +44,8 @@ typedef struct win_option_mask {
bool full_shadow : 1;
bool redir_ignore : 1;
bool opacity : 1;
bool corner_radius : 1;
bool round_borders : 1;
} win_option_mask_t;
typedef struct win_option {
@@ -53,6 +55,8 @@ typedef struct win_option {
bool full_shadow;
bool redir_ignore;
double opacity;
int corner_radius;
int round_borders;
} win_option_t;
enum blur_method {
@@ -60,9 +64,18 @@ enum blur_method {
BLUR_METHOD_KERNEL,
BLUR_METHOD_BOX,
BLUR_METHOD_GAUSSIAN,
BLUR_METHOD_DUAL_KAWASE,
BLUR_METHOD_ALT_KAWASE,
BLUR_METHOD_INVALID,
};
typedef struct blur_strength {
int expand;
int strength;
int iterations;
float offset;
} blur_strength_t;
typedef struct _c2_lptr c2_lptr_t;
/// Structure representing all options.
@@ -87,7 +100,7 @@ typedef struct options {
bool glx_no_stencil;
/// Whether to avoid rebinding pixmap on window damage.
bool glx_no_rebind_pixmap;
/// Length of window transitions
/// Length of window transitions
int transition_length;
/// For smoothing on the x-coordinate of window animations
float transition_pow_x;
@@ -207,6 +220,8 @@ typedef struct options {
int blur_radius;
// Standard deviation for the gaussian blur
double blur_deviation;
/// Blur strength (for kawase blur)
blur_strength_t blur_strength;
/// Whether to blur background when the window frame is not opaque.
/// Implies blur_background.
bool blur_background_frame;
@@ -255,6 +270,15 @@ typedef struct options {
// Make transparent windows clip other windows, instead of blending on top of
// them
bool transparent_clipping;
// === Rounded corners related ===
int corner_radius;
/// Rounded corners blacklist. A linked list of conditions.
c2_lptr_t *rounded_corners_blacklist;
/// Do we round the borders of rounded windows?
int round_borders;
/// Rounded borders blacklist. A linked list of conditions.
c2_lptr_t *round_borders_blacklist;
} options_t;
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
@@ -331,3 +355,42 @@ static inline bool parse_vsync(const char *str) {
}
// vim: set noet sw=8 ts=8 :
/**
* Parse a blur_strength option argument.
*/
static inline attr_pure blur_strength_t
parse_kawase_blur_strength(const int level) {
static const blur_strength_t values[20] = {
{ .expand = 10, .strength =1, .iterations = 1, .offset = 1.5 }, // 1
{ .expand = 10, .strength =2, .iterations = 1, .offset = 2.0 }, // 2
{ .expand = 20, .strength =3, .iterations = 2, .offset = 2.5 }, // 3
{ .expand = 20, .strength =4, .iterations = 2, .offset = 3.0 }, // 4
{ .expand = 50, .strength =5, .iterations = 3, .offset = 2.75 }, // 5
{ .expand = 50, .strength =6, .iterations = 3, .offset = 3.5 }, // 6
{ .expand = 50, .strength =7, .iterations = 3, .offset = 4.25 }, // 7
{ .expand = 50, .strength =8, .iterations = 3, .offset = 5.0 }, // 8
{ .expand = 150, .strength =9, .iterations = 4, .offset = 3.71429f }, // 9
{ .expand = 150, .strength =10, .iterations = 4, .offset = 4.42857f }, // 10
{ .expand = 150, .strength =11, .iterations = 4, .offset = 5.14286f }, // 11
{ .expand = 150, .strength =12, .iterations = 4, .offset = 5.85714f }, // 12
{ .expand = 150, .strength =13, .iterations = 4, .offset = 6.57143f }, // 13
{ .expand = 150, .strength =14, .iterations = 4, .offset = 7.28571f }, // 14
{ .expand = 150, .strength =15, .iterations = 4, .offset = 8.0 }, // 15
{ .expand = 400, .strength =16, .iterations = 5, .offset = 6.0 }, // 16
{ .expand = 400, .strength =17, .iterations = 5, .offset = 7.0 }, // 17
{ .expand = 400, .strength =18, .iterations = 5, .offset = 8.0 }, // 18
{ .expand = 400, .strength =19, .iterations = 5, .offset = 9.0 }, // 19
{ .expand = 400, .strength =20, .iterations = 5, .offset = 10.0 }, // 20
};
if (level < 1 || level > 20) {
log_error("(\"%d\"): Invalid blur_strength argument. Needs to be a number between 1 and 20. Will default to 5", level);
return values[5];
}
log_info("blur-strength: %d [.iter = %d, .offset = %f, .expand = %d]",
level, values[level - 1].iterations, values[level - 1].offset, values[level - 1].expand);
return values[level - 1];;
}

View File

@@ -284,6 +284,17 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
o->opacity = normalize_d(fval);
mask->opacity = true;
}
if (config_setting_lookup_int(setting, "corner-radius", &ival)) {
o->corner_radius = ival;
mask->corner_radius = true;
// log_warn("%s: corner-radius: %d", member_name, ival);
}
if (config_setting_lookup_int(setting, "round-borders", &ival)) {
o->round_borders = ival;
mask->round_borders = true;
// log_warn("%s: round_borders: %d", member_name, ival);
}
}
}
@@ -353,29 +364,31 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
// -O (fade_out_step)
if (config_lookup_float(&cfg, "fade-out-step", &dval))
opt->fade_out_step = normalize_d(dval);
// --transition-length
if (config_lookup_int(&cfg, "transition-length", &ival))
opt->transition_length = ival;
// --transition-pow-x
if (config_lookup_float(&cfg, "transition-pow-x", &dval))
opt->transition_pow_x = dval;
// --transition-pow-y
if (config_lookup_float(&cfg, "transition-pow-y", &dval))
opt->transition_pow_y = dval;
// --transition-pow-w
if (config_lookup_float(&cfg, "transition-pow-w", &dval))
opt->transition_pow_w = dval;
// --transition-pow-h
if (config_lookup_float(&cfg, "transition-pow-h", &dval))
opt->transition_pow_h = dval;
// --size-transition
lcfg_lookup_bool(&cfg, "size-transition", &opt->size_transition);
// --spawn-center-screen
lcfg_lookup_bool(&cfg, "spawn-center-screen", &opt->spawn_center_screen);
// --spawn-center
lcfg_lookup_bool(&cfg, "spawn-center", &opt->spawn_center);
// --no-scale-down
lcfg_lookup_bool(&cfg, "no-scale-down", &opt->no_scale_down);
// --transition-length
if (config_lookup_int(&cfg, "transition-length", &ival))
opt->transition_length = ival;
// --transition-pow-x
if (config_lookup_float(&cfg, "transition-pow-x", &dval))
opt->transition_pow_x = dval;
// --transition-pow-y
if (config_lookup_float(&cfg, "transition-pow-y", &dval))
opt->transition_pow_y = dval;
// --transition-pow-w
if (config_lookup_float(&cfg, "transition-pow-w", &dval))
opt->transition_pow_w = dval;
// --transition-pow-h
if (config_lookup_float(&cfg, "transition-pow-h", &dval))
opt->transition_pow_h = dval;
// --size-transition
lcfg_lookup_bool(&cfg, "size-transition", &opt->size_transition);
// --spawn-center-screen
lcfg_lookup_bool(&cfg, "spawn-center-screen", &opt->spawn_center_screen);
// --spawn-center
lcfg_lookup_bool(&cfg, "spawn-center", &opt->spawn_center);
// --no-scale-down
lcfg_lookup_bool(&cfg, "no-scale-down", &opt->no_scale_down);
// -r (shadow_radius)
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
// -o (shadow_opacity)
@@ -390,6 +403,14 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
// --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval))
opt->active_opacity = normalize_d(dval);
// --corner-radius
config_lookup_int(&cfg, "corner-radius", &opt->corner_radius);
// --rounded-corners-exclude
parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist, "rounded-corners-exclude");
// --round-borders
config_lookup_int(&cfg, "round-borders", &opt->round_borders);
// --round-borders-exclude
parse_cfg_condlst(&cfg, &opt->round_borders_blacklist, "round-borders-exclude");
// -e (frame_opacity)
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
// -c (shadow_enable)
@@ -469,6 +490,7 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
}
lcfg_lookup_bool(&cfg, "vsync", &opt->vsync);
// --backend
lcfg_lookup_bool(&cfg, "experimental-backends", &opt->experimental_backends);
if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) {
@@ -545,6 +567,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
config_lookup_int(&cfg, "blur-size", &opt->blur_radius);
// --blur-deviation
config_lookup_float(&cfg, "blur-deviation", &opt->blur_deviation);
// --blur-strength
if (config_lookup_int(&cfg, "blur-strength", &ival) && ival) {
opt->blur_strength = parse_kawase_blur_strength(ival);
}
// --blur-background
if (config_lookup_bool(&cfg, "blur-background", &ival) && ival) {
if (opt->blur_method == BLUR_METHOD_NONE) {
@@ -570,7 +596,6 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
// --glx-no-rebind-pixmap
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
lcfg_lookup_bool(&cfg, "force-win-blend", &opt->force_win_blend);
// --glx-swap-method
if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
char *endptr;
@@ -608,8 +633,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
}
// --xrender-sync
if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) {
log_error("Please use xrender-sync-fence instead of xrender-sync.");
goto err;
log_warn("Please use xrender-sync-fence instead of xrender-sync.");
opt->xrender_sync_fence = true;
}
// --xrender-sync-fence
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
@@ -617,21 +642,25 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
log_warn("\"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file");
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval)) {
log_error("\"paint-on-overlay\" has been removed as an option, and "
"the feature is enabled whenever possible");
goto err;
}
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval))
log_warn("\"paint-on-overlay\" has been removed as an option, and "
"is enabled whenever possible");
if (config_lookup_float(&cfg, "alpha-step", &dval)) {
log_error("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values");
goto err;
}
if (config_lookup_float(&cfg, "alpha-step", &dval))
log_warn("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values");
const char *deprecation_message attr_unused =
const char *deprecation_message =
"has been removed. If you encounter problems "
"without this feature, please feel free to open a bug report";
if (lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &bval) && bval) {
log_error("\"glx-use-copysubbuffermesa\" %s", deprecation_message);
return ERR_PTR(-1);
}
if (lcfg_lookup_bool(&cfg, "glx-copy-from-front", &bval) && bval) {
log_error("\"glx-copy-from-front\" %s", deprecation_message);
return ERR_PTR(-1);
}
config_setting_t *blur_cfg = config_lookup(&cfg, "blur");
if (blur_cfg) {
@@ -655,6 +684,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
}
config_setting_lookup_float(blur_cfg, "deviation", &opt->blur_deviation);
if (config_setting_lookup_int(blur_cfg, "strength", &ival) && ival) {
opt->blur_strength = parse_kawase_blur_strength(ival);
}
}
// Wintype settings

View File

@@ -100,9 +100,6 @@ static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_
}
}
#define CASESTRRET(s) \
case s: return #s;
static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
static char buf[128];
switch (ev->response_type & 0x7f) {
@@ -165,8 +162,6 @@ static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *e
return "Unknown";
}
#undef CASESTRRET
static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) {
log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_name(ev));
@@ -180,9 +175,8 @@ static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
}
static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
if (ev->parent == ps->root) {
add_win_top(ps, ev->window);
}
assert(ev->parent == ps->root);
add_win_top(ps, ev->window);
}
/// Handle configure event of a regular window
@@ -202,105 +196,122 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
auto mw = (struct managed_win *)w;
float t = get_time_ms();
if (mw->oldX == -500 && mw->oldY == -500 && mw->oldW == 0 && mw->oldH == 0) {
if (!mw->isOld) {
/* mw->isOld = true; */
float t = get_time_ms();
if (mw->oldX == -10000 && mw->oldY == -10000 && mw->oldW == 0 && mw->oldH == 0) {
if (!mw->isOld) {
/* mw->isOld = true; */
if (ps->o.spawn_center_screen) {
mw->oldX = ps->root_width/2;
mw->oldY = ps->root_height/2;
mw->oldW = 1;
mw->oldH = 1;
} else if (ps->o.spawn_center) {
mw->oldX = ce->x + ce->width/2;
mw->oldY = ce->y + ce->height/2;
mw->oldW = 1;
mw->oldH = 1;
} else {
mw->oldX = ce->x;
mw->oldY = ce->y;
mw->oldW = ce->width;
mw->oldH = ce->height;
}
} else {
mw->oldX = ce->x;
mw->oldY = ce->y;
mw->oldW = ce->width;
mw->oldH = ce->height;
}
if (ps->o.spawn_center_screen) {
mw->oldX = ps->root_width / 2;
mw->oldY = ps->root_height / 2;
mw->oldW = 1;
mw->oldH = 1;
} else if (ps->o.spawn_center) {
mw->oldX = ce->x + ce->width / 2;
mw->oldY = ce->y + ce->height / 2;
mw->oldW = 1;
mw->oldH = 1;
} else {
mw->oldX = ce->x;
mw->oldY = ce->y;
mw->oldW = ce->width;
mw->oldH = ce->height;
}
} else {
mw->oldX = ce->x;
mw->oldY = ce->y;
mw->oldW = ce->width;
mw->oldH = ce->height;
}
mw->newX = ce->x;
mw->newY = ce->y;
mw->newW = ce->width;
mw->newH = ce->height;
mw->moveTimeX = t;
mw->moveTimeY = t;
mw->moveTimeW = t;
mw->moveTimeH = t;
} else {
if (mw->newX == mw->g.x && mw->newY == mw->g.y) {
mw->oldX = mw->g.x;
mw->oldY = mw->g.y;
mw->oldW = mw->g.width;
mw->oldH = mw->g.height;
mw->moveTimeX = t;
mw->moveTimeY = t;
mw->moveTimeW = t;
mw->moveTimeH = t;
}
if (mw->newX != ce->x || mw->newY != ce->y || mw->newW != ce->width || mw->newH != ce->height) {
float moveDx = ((float) t - mw->moveTimeX) / ps->o.transition_length;
float moveDy = ((float) t - mw->moveTimeY) / ps->o.transition_length;
float moveDw = ((float) t - mw->moveTimeW) / ps->o.transition_length;
float moveDh = ((float) t - mw->moveTimeH) / ps->o.transition_length;
mw->newX = ce->x;
mw->newY = ce->y;
mw->newW = ce->width;
mw->newH = ce->height;
mw->moveTimeX = t;
mw->moveTimeY = t;
mw->moveTimeW = t;
mw->moveTimeH = t;
} else {
if (mw->newX == mw->g.x && mw->newY == mw->g.y) {
mw->oldX = mw->g.x;
mw->oldY = mw->g.y;
mw->oldW = mw->g.width;
mw->oldH = mw->g.height;
mw->moveTimeX = t;
mw->moveTimeY = t;
mw->moveTimeW = t;
mw->moveTimeH = t;
}
if (mw->newX != ce->x || mw->newY != ce->y || mw->newW != ce->width ||
mw->newH != ce->height) {
float moveDx = ((float)t - mw->moveTimeX) / ps->o.transition_length;
float moveDy = ((float)t - mw->moveTimeY) / ps->o.transition_length;
float moveDw = ((float)t - mw->moveTimeW) / ps->o.transition_length;
float moveDh = ((float)t - mw->moveTimeH) / ps->o.transition_length;
if (mw->moveTimeX != 0.0 && moveDx < 1.0 && mw->oldX != mw->newX) {
float oldMoveDx = pow((float) (mw->newX - mw->g.x) / (float) (mw->newX - ce->x), 1 / ps->o.transition_pow_x);
float fakeT = (t - oldMoveDx * (float) ps->o.transition_length);
/* printf("X: %f,%f\n", fakeT, t); */
mw->moveTimeX = isnanf(fakeT)? t : fakeT;
} else {
mw->moveTimeX = t;
}
if (mw->moveTimeY != 0.0 && moveDy < 1.0 && mw->oldY != mw->newY) {
float oldMoveDy = pow((float) (mw->newY - mw->g.y) / (float) (mw->newY - ce->y), 1 / ps->o.transition_pow_y);
float fakeT = (t - oldMoveDy * (float) ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeY = isnanf(fakeT)? t : fakeT;
} else {
mw->moveTimeY = t;
}
if (mw->moveTimeW != 0.0 && moveDw < 1.0 && mw->oldW != mw->newW) {
float oldMoveDw = pow((float) (mw->newW - mw->g.width) / (float) (mw->newW - ce->width), 1 / ps->o.transition_pow_w);
float fakeT = (t - oldMoveDw * (float) ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeW = isnanf(fakeT)? t : fakeT;
} else {
mw->moveTimeW = t;
}
if (mw->moveTimeH != 0.0 && moveDh < 1.0 && mw->oldH != mw->newH) {
float oldMoveDh = pow((float) (mw->newH - mw->g.height) / (float) (mw->newH - ce->height), 1 / ps->o.transition_pow_h);
float fakeT = (t - oldMoveDh * (float) ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeH = isnanf(fakeT)? t : fakeT;
} else {
mw->moveTimeH = t;
}
if (mw->moveTimeX != 0.0 && moveDx < 1.0 && mw->oldX != mw->newX) {
float oldMoveDx = pow(
(float)(mw->newX - mw->g.x) / (float)(mw->newX - ce->x),
1 / ps->o.transition_pow_x);
float fakeT =
(t - oldMoveDx * (float)ps->o.transition_length);
/* printf("X: %f,%f\n", fakeT, t); */
mw->moveTimeX = isnanf(fakeT) ? t : fakeT;
} else {
mw->moveTimeX = t;
}
if (mw->moveTimeY != 0.0 && moveDy < 1.0 && mw->oldY != mw->newY) {
float oldMoveDy = pow(
(float)(mw->newY - mw->g.y) / (float)(mw->newY - ce->y),
1 / ps->o.transition_pow_y);
float fakeT =
(t - oldMoveDy * (float)ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeY = isnanf(fakeT) ? t : fakeT;
} else {
mw->moveTimeY = t;
}
if (mw->moveTimeW != 0.0 && moveDw < 1.0 && mw->oldW != mw->newW) {
float oldMoveDw = pow((float)(mw->newW - mw->g.width) /
(float)(mw->newW - ce->width),
1 / ps->o.transition_pow_w);
float fakeT =
(t - oldMoveDw * (float)ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeW = isnanf(fakeT) ? t : fakeT;
} else {
mw->moveTimeW = t;
}
if (mw->moveTimeH != 0.0 && moveDh < 1.0 && mw->oldH != mw->newH) {
float oldMoveDh = pow((float)(mw->newH - mw->g.height) /
(float)(mw->newH - ce->height),
1 / ps->o.transition_pow_h);
float fakeT =
(t - oldMoveDh * (float)ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeH = isnanf(fakeT) ? t : fakeT;
} else {
mw->moveTimeH = t;
}
mw->oldX = mw->newX;
mw->oldY = mw->newY;
mw->oldW = mw->newW;
mw->oldH = mw->newH;
mw->newX = ce->x;
mw->newY = ce->y;
mw->newW = ce->width;
mw->newH = ce->height;
mw->oldX = mw->newX;
mw->oldY = mw->newY;
mw->oldW = mw->newW;
mw->oldH = mw->newH;
mw->newX = ce->x;
mw->newY = ce->y;
mw->newW = ce->width;
mw->newH = ce->height;
if (ps->o.no_scale_down && mw->newW < mw->oldW) { mw->oldW = mw->newW; }
if (ps->o.no_scale_down && mw->newH < mw->oldH) { mw->oldH = mw->newH; }
}
}
if (ps->o.no_scale_down && mw->newW < mw->oldW) {
mw->oldW = mw->newW;
}
if (ps->o.no_scale_down && mw->newH < mw->oldH) {
mw->oldH = mw->newH;
}
}
}
if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING ||
mw->state == WSTATE_DESTROYING) {
@@ -353,7 +364,7 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
ev->event, ev->window, ev->above_sibling, ev->override_redirect);
if (ev->window == ps->root) {
set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
configure_root(ps, ev->width, ev->height);
} else {
configure_win(ps, ev);
}
@@ -361,23 +372,8 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
auto w = find_win(ps, ev->window);
auto mw = find_toplevel(ps, ev->window);
if (mw && mw->client_win == mw->base.id) {
// We only want _real_ frame window
assert(&mw->base == w);
mw = NULL;
}
assert(w == NULL || mw == NULL);
if (w != NULL) {
if (w) {
auto _ attr_unused = destroy_win_start(ps, w);
} else if (mw != NULL) {
win_unmark_client(ps, mw);
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
log_debug("Received a destroy notify from an unknown window, %#010x",
ev->window);
}
}
@@ -400,7 +396,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
return;
}
win_set_flags(w, WIN_FLAGS_MAPPED);
win_queue_update(w, WIN_UPDATE_MAP);
// FocusIn/Out may be ignored when the window is unmapped, so we must
// recheck focus here
@@ -415,14 +411,8 @@ static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev)
}
static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d",
ev->window, ev->parent, ev->override_redirect);
auto w_top = find_toplevel(ps, ev->window);
if (w_top) {
win_unmark_client(ps, w_top);
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
}
log_debug("{ new_parent: %#010x, override_redirect: %d }", ev->parent,
ev->override_redirect);
if (ev->parent == ps->root) {
// X will generate reparent notifiy even if the parent didn't actually
@@ -442,11 +432,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
{
auto w = find_win(ps, ev->window);
if (w) {
auto ret = destroy_win_start(ps, w);
if (!ret && w->managed) {
auto mw = (struct managed_win *)w;
CHECK(win_skip_fading(ps, mw));
}
auto _ attr_unused = destroy_win_start(ps, w);
}
}
@@ -455,35 +441,28 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
log_debug("Window %#010x doesn't have WM_STATE property, it is "
"probably not a client window. But we will listen for "
"property change in case it gains one.",
ev->window);
xcb_change_window_attributes(
ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
XCB_EVENT_MASK_PROPERTY_CHANGE});
} else {
auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
w_real_top->state != WSTATE_UNMAPPING) {
log_debug("Mark window %#010x (%s) as having a stale "
"client",
w_real_top->base.id, w_real_top->name);
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
if (!w_real_top)
log_debug("parent %#010x not found", ev->parent);
// Check if the window is an undetected client window
// Firstly, check if it's a known client window
if (!find_toplevel(ps, ev->window)) {
// If not, look for its frame window
auto w_top = find_toplevel2(ps, ev->parent);
// If found, and the client window has not been determined, or its
// frame may not have a correct client, continue
if (w_top &&
(!w_top->client_win || w_top->client_win == w_top->base.id)) {
// If it has WM_STATE, mark it the client window
if (wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
w_top->wmwin = false;
win_unmark_client(ps, w_top);
win_mark_client(ps, w_top, ev->window);
}
// Otherwise, watch for WM_STATE on it
else {
// Window is not currently mapped, unmark its
// client to trigger a client recheck when it is
// mapped later.
win_unmark_client(ps, w_real_top);
log_debug("parent %#010x (%s) is in state %d",
w_real_top->base.id, w_real_top->name,
w_real_top->state);
xcb_change_window_attributes(
ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
XCB_EVENT_MASK_PROPERTY_CHANGE});
}
}
}
@@ -576,12 +555,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
(const uint32_t[]){determine_evmask(
ps, ev->window, WIN_EVMODE_UNKNOWN)});
auto w_top = find_managed_window_or_parent(ps, ev->window);
// ev->window might have not been managed yet, in that case w_top
// would be NULL.
if (w_top) {
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
auto w_top = find_toplevel2(ps, ev->window);
// Initialize client_win as early as possible
if (w_top &&
(!w_top->client_win || w_top->client_win == w_top->base.id) &&
wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
w_top->wmwin = false;
win_unmark_client(ps, w_top);
win_mark_client(ps, w_top, ev->window);
}
}
}
@@ -606,7 +587,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
win_update_opacity_prop(ps, w);
// we cannot receive OPACITY change when window is destroyed
assert(w->state != WSTATE_DESTROYING);
win_update_opacity_target(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w, false);
if (w->state == WSTATE_MAPPED) {
// See the winstate_t transition table
w->state = WSTATE_FADING;
}
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
}
@@ -743,14 +731,14 @@ static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev)
* if we attempt to rebuild border_size
*/
// Mark the old border_size as damaged
region_t tmp = win_get_bounding_shape_global_by_val(w);
region_t tmp = win_get_bounding_shape_global_by_val(w, true);
add_damage(ps, &tmp);
pixman_region32_fini(&tmp);
win_update_bounding_shape(ps, w);
// Mark the new border_size as damaged
tmp = win_get_bounding_shape_global_by_val(w);
tmp = win_get_bounding_shape_global_by_val(w, true);
add_damage(ps, &tmp);
pixman_region32_fini(&tmp);

View File

@@ -16,7 +16,7 @@ cflags = []
required_xcb_packages = [
'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite',
'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb-glx', 'xcb'
'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb'
]
required_packages = [
@@ -81,8 +81,8 @@ endif
host_system = host_machine.system()
if host_system == 'linux'
cflags += ['-DHAS_INOTIFY']
elif (host_system == 'freebsd' or host_system == 'netbsd' or
host_system == 'dragonfly' or host_system == 'openbsd')
elif host_system == 'freebsd' or host_system == 'netbsd' or
host_system == 'dragonfly' or host_system == 'openbsd'
cflags += ['-DHAS_KQUEUE']
endif

File diff suppressed because it is too large Load Diff

View File

@@ -36,10 +36,40 @@ typedef struct {
GLint unifm_offset_x;
/// Location of uniform "offset_y" in blur GLSL program.
GLint unifm_offset_y;
/// Location of uniform "factor_center" in blur GLSL program.
GLint unifm_factor_center;
/// Location of uniform "opacity" in conv-blur and (dual filter) kawase-blur GLSL program.
GLint unifm_opacity;
/// Location of uniform "offset" in kawase-blur GLSL program.
GLint unifm_offset;
/// Location of uniform "halfpixel" in kawase-blur GLSL program.
GLint unifm_halfpixel;
/// Location of uniform "fulltex" in kawase-blur GLSL program.
GLint unifm_fulltex;
} glx_blur_pass_t;
typedef struct {
/// Fragment shader for rounded corners.
GLuint frag_shader;
/// GLSL program for rounded corners.
GLuint prog;
/// Location of uniform "radius" in rounded-corners GLSL program.
GLint unifm_radius;
/// Location of uniform "texcoord" in rounded-corners GLSL program.
GLint unifm_texcoord;
/// Location of uniform "texsize" in rounded-corners GLSL program.
GLint unifm_texsize;
/// Location of uniform "borderw" in rounded-corners GLSL program.
GLint unifm_borderw;
/// Location of uniform "borderc" in rounded-corners GLSL program.
GLint unifm_borderc;
/// Location of uniform "resolution" in rounded-corners GLSL program.
GLint unifm_resolution;
/// Location of uniform "texture_scr" in rounded-corners GLSL program.
GLint unifm_tex_scr;
/// Location of uniform "texture_wnd" in rounded-corners GLSL program.
GLint unifm_tex_wnd;
} glx_round_pass_t;
/// Structure containing GLX-dependent data for a session.
typedef struct glx_session {
// === OpenGL related ===
@@ -49,7 +79,10 @@ typedef struct glx_session {
bool has_texture_non_power_of_two;
/// Current GLX Z value.
int z;
/// Cached blur textures for every pass
glx_blur_cache_t blur_cache;
glx_blur_pass_t *blur_passes;
glx_round_pass_t *round_passes;
} glx_session_t;
/// @brief Wrapper of a binded GLX texture.
@@ -69,8 +102,8 @@ typedef struct _glx_texture {
bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
GLfloat factor, const region_t *reg_tgt);
bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy,
int width, int height, int z, double opacity, bool argb, bool neg,
bool glx_render(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int x, int y, int dx, int dy,
int width, int height, int z, double opacity, bool argb, bool neg, int cr,
const region_t *reg_tgt, const glx_prog_main_t *pprogram);
bool glx_init(session_t *ps, bool need_render);
@@ -81,6 +114,8 @@ void glx_on_root_change(session_t *ps);
bool glx_init_blur(session_t *ps);
bool glx_init_rounded_corners(session_t *ps);
#ifdef CONFIG_OPENGL
bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram);
@@ -91,6 +126,11 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
bool glx_bind_texture(session_t *ps, glx_texture_t **pptex,
int x, int y, int width, int height, bool repeat);
void glx_release_texture(session_t *ps, glx_texture_t **ptex);
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
/**
@@ -103,7 +143,15 @@ static inline bool glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap
void glx_set_clip(session_t *ps, const region_t *reg);
bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc);
double opacity, const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst0(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int shader_idx,
int dx, int dy, int width, int height, float z, float cr,
const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst1(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int shader_idx,
int dx, int dy, int width, int height, float z, float cr,
const region_t *reg_tgt, glx_blur_cache_t *pbc);
GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
@@ -157,17 +205,19 @@ static inline void free_glx_fbo(GLuint *pfbo) {
* Free data in glx_blur_cache_t on resize.
*/
static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
free_texture_r(ps, &pbc->textures[0]);
free_texture_r(ps, &pbc->textures[1]);
pbc->width = 0;
pbc->height = 0;
for (int i = 0; i < MAX_BLUR_PASS; i++) {
free_texture_r(ps, &pbc->textures[i]);
pbc->width[i] = 0;
pbc->height[i] = 0;
}
}
/**
* Free a glx_blur_cache_t
*/
static inline void free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
free_glx_fbo(&pbc->fbo);
for (int i = 0; i < MAX_BLUR_PASS; i++)
free_glx_fbo(&pbc->fbos[i]);
free_glx_bc_resize(ps, pbc);
}
@@ -196,7 +246,6 @@ static inline void free_texture(session_t *ps, glx_texture_t **pptex) {
*/
static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->ptex);
ppaint->fbcfg = NULL;
}
/**
@@ -207,6 +256,8 @@ static inline void free_win_res_glx(session_t *ps, struct managed_win *w) {
free_paint_glx(ps, &w->shadow_paint);
#ifdef CONFIG_OPENGL
free_glx_bc(ps, &w->glx_blur_cache);
free_glx_bc(ps, &w->glx_round_cache);
free_texture(ps, &w->glx_texture_bg);
free(w->paint.fbcfg);
#endif
}

View File

@@ -114,6 +114,18 @@ static void usage(const char *argv0, int ret) {
"--active-opacity opacity\n"
" Default opacity for active windows. (0.0 - 1.0)\n"
"\n"
"--corner-radius value\n"
" Round the corners of windows. (defaults to 0)\n"
"\n"
"--rounded-corners-exclude condition\n"
" Exclude conditions for rounded corners.\n"
"\n"
"--round-borders value\n"
" When rounding corners, round the borders of windows. (defaults to 1)\n"
"\n"
"--round-borders-exclude condition\n"
" Exclude conditions for rounding borders.\n"
"\n"
"--mark-wmwin-focused\n"
" Try to detect WM windows and mark them as active.\n"
"\n"
@@ -202,8 +214,8 @@ static void usage(const char *argv0, int ret) {
"\n"
"--blur-method\n"
" The algorithm used for background bluring. Available choices are:\n"
" 'none' to disable, 'gaussian', 'box' or 'kernel' for custom\n"
" convolution blur with --blur-kern.\n"
" 'none' to disable, 'dual_kawase', 'gaussian', 'box' or 'kernel'\n"
" for custom convolution blur with --blur-kern.\n"
" Note: 'gaussian' and 'box' require --experimental-backends.\n"
"\n"
"--blur-size\n"
@@ -211,6 +223,10 @@ static void usage(const char *argv0, int ret) {
"\n"
"--blur-deviation\n"
" The standard deviation for the 'gaussian' blur method.\n"
"\n"
"--blur-strength\n"
" Only valid for '--blur-method dual_kawase'!\n"
" The strength of the kawase blur as an integer between 1 and 20. Defaults to 5.\n"
"\n"
"--blur-background\n"
" Blur background of semi-transparent / ARGB windows. Bad in\n"
@@ -227,6 +243,7 @@ static void usage(const char *argv0, int ret) {
" opacity.\n"
"\n"
"--blur-kern matrix\n"
" Only valid for '--blur-method convolution'!\n"
" Specify the blur convolution kernel, with the following format:\n"
" WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n"
" The element in the center must not be included, it will be forever\n"
@@ -398,8 +415,10 @@ static const struct option longopts[] = {
{"opengl", no_argument, NULL, 289},
{"backend", required_argument, NULL, 290},
{"glx-no-stencil", no_argument, NULL, 291},
{"glx-copy-from-front", no_argument, NULL, 292},
{"benchmark", required_argument, NULL, 293},
{"benchmark-wid", required_argument, NULL, 294},
{"glx-use-copysubbuffermesa", no_argument, NULL, 295},
{"blur-background-exclude", required_argument, NULL, 296},
{"active-opacity", required_argument, NULL, 297},
{"glx-no-rebind-pixmap", no_argument, NULL, 298},
@@ -435,6 +454,11 @@ static const struct option longopts[] = {
{"blur-method", required_argument, NULL, 328},
{"blur-size", required_argument, NULL, 329},
{"blur-deviation", required_argument, NULL, 330},
{"blur-strength", required_argument, NULL, 331},
{"corner-radius", required_argument, NULL, 332},
{"rounded-corners-exclude", required_argument, NULL, 333},
{"round-borders", required_argument, NULL, 334},
{"round-borders-exclude", required_argument, NULL, 335},
{"experimental-backends", no_argument, NULL, 733},
{"monitor-repaint", no_argument, NULL, 800},
{"diagnostics", no_argument, NULL, 801},
@@ -467,23 +491,21 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
} else if (o == 'b') {
*fork = true;
} else if (o == 'd') {
log_error("-d is removed, please use the DISPLAY "
"environment variable");
goto err;
log_warn("-d will be ignored, please use the DISPLAY "
"environment variable");
} else if (o == 314) {
*all_xerrors = true;
} else if (o == 318) {
printf("%s\n", COMPTON_VERSION);
return true;
} else if (o == 'S') {
log_error("-S is no longer available");
goto err;
log_warn("-S will be ignored");
} else if (o == 320) {
log_error("--no-name-pixmap is no longer available");
goto err;
log_warn("--no-name-pixmap will be ignored");
} else if (o == '?' || o == ':') {
usage(argv[0], 1);
goto err;
*exit_code = 1;
return true;
}
}
@@ -491,13 +513,11 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
if (optind < argc) {
// log is not initialized here yet
fprintf(stderr, "picom doesn't accept positional arguments.\n");
goto err;
*exit_code = 1;
return true;
}
return false;
err:
*exit_code = 1;
return true;
}
/**
@@ -516,10 +536,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// Parse commandline arguments. Range checking will be done later.
const char *deprecation_message attr_unused =
"has been removed. If you encounter problems "
"without this feature, please feel free to "
"open a bug report.";
const char *deprecation_message = "has been removed. If you encounter problems "
"without this feature, please feel free to "
"open a bug report.";
optind = 1;
while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
switch (o) {
@@ -645,14 +664,14 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break;
case 271:
// --alpha-step
log_error("--alpha-step has been removed, we now tries to "
log_warn("--alpha-step has been removed, we now tries to "
"make use of all alpha values");
return false;
case 272: log_error("use of --dbe is deprecated"); return false;
break;
case 272: log_warn("use of --dbe is deprecated"); break;
case 273:
log_error("--paint-on-overlay has been removed, the feature is enabled "
"whenever possible");
return false;
log_warn("--paint-on-overlay has been removed, and is enabled "
"when possible");
break;
P_CASEBOOL(274, sw_opti);
case 275:
// --vsync-aggressive
@@ -704,11 +723,19 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
exit(1);
break;
P_CASEBOOL(291, glx_no_stencil);
case 292:
log_error("--glx-copy-from-front %s", deprecation_message);
exit(1);
break;
P_CASEINT(293, benchmark);
case 294:
// --benchmark-wid
opt->benchmark_wid = (xcb_window_t)strtol(optarg, NULL, 0);
break;
case 295:
log_error("--glx-use-copysubbuffermesa %s", deprecation_message);
exit(1);
break;
case 296:
// --blur-background-exclude
condlst_add(&opt->blur_background_blacklist, optarg);
@@ -787,8 +814,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
P_CASEBOOL(311, vsync_use_glfinish);
case 312:
// --xrender-sync
log_error("Please use --xrender-sync-fence instead of --xrender-sync");
return false;
log_warn("Please use --xrender-sync-fence instead of --xrender-sync");
opt->xrender_sync_fence = true;
break;
P_CASEBOOL(313, xrender_sync_fence);
P_CASEBOOL(315, no_fading_destroyed_argb);
P_CASEBOOL(316, force_win_blend);
@@ -835,7 +863,15 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// --blur-deviation
opt->blur_deviation = atof(optarg);
break;
case 331:
// --blur-strength
opt->blur_strength = parse_kawase_blur_strength(atoi(optarg));
break;
case 332: opt->corner_radius = atoi(optarg); break;
case 333: condlst_add(&opt->rounded_corners_blacklist, optarg); break;
case 334: opt->round_borders = atoi(optarg); break;
case 335: condlst_add(&opt->round_borders_blacklist, optarg); break;
P_CASEBOOL(733, experimental_backends);
P_CASEBOOL(800, monitor_repaint);
case 801: opt->print_diagnostics = true; break;
@@ -915,6 +951,13 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
opt->track_leader = true;
}
// Blur method kawase is not compatible with the xrender backend
if (opt->backend != BKEND_GLX && (opt->blur_method == BLUR_METHOD_DUAL_KAWASE
|| opt->blur_method == BLUR_METHOD_ALT_KAWASE)) {
log_warn("Blur method 'kawase' is incompatible with the XRender backend. Fall back to default.\n");
opt->blur_method = BLUR_METHOD_KERNEL;
}
// Fill default blur kernel
if (opt->blur_method == BLUR_METHOD_KERNEL &&
(!opt->blur_kerns || !opt->blur_kerns[0])) {
@@ -924,6 +967,15 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
CHECK(opt->blur_kernel_count);
}
// override blur_kernel_count for kawase
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE ||
opt->blur_method == BLUR_METHOD_ALT_KAWASE) {
opt->blur_kernel_count = MAX_BLUR_PASS;
opt->blur_kerns = ccalloc(opt->blur_kernel_count, struct conv *);
CHECK(opt->blur_kerns);
CHECK(opt->blur_kernel_count);
}
if (opt->resize_damage < 0) {
log_warn("Negative --resize-damage will not work correctly.");
}

View File

@@ -19,7 +19,6 @@
#include <string.h>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <xcb/glx.h>
#include <xcb/present.h>
#include <xcb/randr.h>
#include <xcb/render.h>
@@ -99,9 +98,7 @@ const char *const BACKEND_STRS[] = {[BKEND_XRENDER] = "xrender",
session_t *ps_g = NULL;
void set_root_flags(session_t *ps, uint64_t flags) {
log_debug("Setting root flags: %lu", flags);
ps->root_flags |= flags;
ps->pending_updates = true;
}
void quit(session_t *ps) {
@@ -124,16 +121,15 @@ static inline void free_xinerama_info(session_t *ps) {
}
/**
* Get current system clock in 40micro second accuracy.
* Get current system clock in milliseconds.
*/
int64_t get_time_ms(void) {
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
/* return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000; //ultra high refresh */
return (int64_t)tp.tv_sec * 100 + (int64_t)tp.tv_nsec / 250000; //ultra high refresh
// return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000;
return (int64_t)tp.tv_sec * 100 + (int64_t)tp.tv_nsec / 250000;
}
// XXX Move to x.c
void cxinerama_upd_scrs(session_t *ps) {
// XXX Consider deprecating Xinerama, switch to RandR when necessary
@@ -182,7 +178,7 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t
if (!w)
w = find_toplevel(ps, wid);
if (!w)
w = find_managed_window_or_parent(ps, wid);
w = find_toplevel2(ps, wid);
return w;
}
@@ -305,9 +301,9 @@ uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode) {
struct managed_win *w = NULL;
// Check if it's a mapped frame window
if (mode == WIN_EVMODE_FRAME ||
if (WIN_EVMODE_FRAME == mode ||
((w = find_managed_win(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
if (!ps->o.use_ewmh_active_win) {
evmask |= XCB_EVENT_MASK_FOCUS_CHANGE;
}
@@ -377,219 +373,31 @@ static void recheck_focus(session_t *ps) {
}
/**
* Rebuild cached <code>screen_reg</code>.
* Look for the client window of a particular window.
*/
static void rebuild_screen_reg(session_t *ps) {
get_screen_region(ps, &ps->screen_reg);
}
/**
* Rebuild <code>shadow_exclude_reg</code>.
*/
static void rebuild_shadow_exclude_reg(session_t *ps) {
bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
if (!ret)
exit(1);
}
/// Free up all the images and deinit the backend
static void destroy_backend(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
// Wrapping up fading in progress
if (win_skip_fading(ps, w)) {
// `w` is freed by win_skip_fading
continue;
}
if (ps->backend_data) {
if (w->state == WSTATE_MAPPED) {
win_release_images(ps->backend_data, w);
} else {
assert(!w->win_image);
assert(!w->shadow_image);
}
}
free_paint(ps, &w->paint);
xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
return w;
}
if (ps->backend_data && ps->root_image) {
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
ps->root_image = NULL;
xcb_query_tree_reply_t *reply =
xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
if (!reply)
return 0;
xcb_window_t *children = xcb_query_tree_children(reply);
int nchildren = xcb_query_tree_children_length(reply);
int i;
xcb_window_t ret = 0;
for (i = 0; i < nchildren; ++i) {
if ((ret = find_client_win(ps, children[i])))
break;
}
if (ps->backend_data) {
// deinit backend
if (ps->backend_blur_context) {
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;
}
}
free(reply);
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) {
assert(!ps->backend_data);
// Reinitialize win_data
assert(backend_list[ps->o.backend]);
ps->backend_data = backend_list[ps->o.backend]->init(ps);
if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting...");
quit(ps);
return false;
}
ps->backend_data->ops = backend_list[ps->o.backend];
if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting...");
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
quit(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;
}
auto w = (struct managed_win *)_w;
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
if (w->state == WSTATE_MAPPED) {
// We need to reacquire image
log_debug("Marking window %#010x (%s) for update after "
"redirection",
w->base.id, w->name);
if (w->shadow) {
struct color c = {
.red = ps->o.shadow_red,
.green = ps->o.shadow_green,
.blue = ps->o.shadow_blue,
.alpha = ps->o.shadow_opacity,
};
win_bind_shadow(ps->backend_data, w, c,
ps->gaussian_map);
}
w->flags |= WIN_FLAGS_PIXMAP_STALE;
ps->pending_updates = true;
}
}
}
// The old backends binds pixmap lazily, nothing to do here
return true;
}
/// Handle configure event of the root window
static void configure_root(session_t *ps) {
auto r = XCB_AWAIT(xcb_get_geometry, ps->c, ps->root);
if (!r) {
log_fatal("Failed to fetch root geometry");
abort();
}
log_info("Root configuration changed, new geometry: %dx%d", r->width, r->height);
bool has_root_change = false;
if (ps->redirected) {
// On root window changes
if (ps->o.experimental_backends) {
assert(ps->backend_data);
has_root_change = ps->backend_data->ops->root_change != NULL;
} else {
// Old backend can handle root change
has_root_change = true;
}
if (!has_root_change) {
// deinit/reinit backend and free up resources if the backend
// cannot handle root change
destroy_backend(ps);
}
free_paint(ps, &ps->tgt_buffer);
}
ps->root_width = r->width;
ps->root_height = r->height;
rebuild_screen_reg(ps);
rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
if (top_w) {
rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false;
}
if (ps->redirected) {
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_clear(&ps->damage_ring[i]);
}
ps->damage = ps->damage_ring + ps->ndamage - 1;
#ifdef CONFIG_OPENGL
// GLX root change callback
if (BKEND_GLX == ps->o.backend && !ps->o.experimental_backends) {
glx_on_root_change(ps);
}
#endif
if (has_root_change) {
if (ps->backend_data != NULL) {
ps->backend_data->ops->root_change(ps->backend_data, ps);
}
// Old backend's root_change is not a specific function
} else {
if (!initialize_backend(ps)) {
log_fatal("Failed to re-initialize backend after root "
"change, aborting...");
ps->quit = true;
// TODO only event handlers should request ev_break,
// otherwise it's too hard to keep track of what can break
// the event loop
ev_break(ps->loop, EVBREAK_ALL);
return;
}
// Re-acquire the root pixmap.
root_damaged(ps);
}
force_repaint(ps);
}
return;
return ret;
}
static void handle_root_flags(session_t *ps) {
@@ -607,11 +415,6 @@ static void handle_root_flags(session_t *ps) {
}
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_SCREEN_CHANGE;
}
if ((ps->root_flags & ROOT_FLAGS_CONFIGURED) != 0) {
configure_root(ps);
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_CONFIGURED;
}
}
static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
@@ -668,8 +471,20 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
w->frame_opacity = 1.0;
}
// The below moved to it's own function:
// `win_determine_rounded_corners` (win.c)
/*
// Don't round full screen windows & excluded windows
if ((w && win_is_fullscreen(ps, w)) ||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL)) {
w->corner_radius = 0;
} else {
w->corner_radius = ps->o.corner_radius;
}
*/
// Update window mode
w->mode = win_calc_mode(w);
w->mode = win_calc_mode(ps, w);
// Destroy all reg_ignore above when frame opaque state changes on
// SOLID mode
@@ -678,56 +493,63 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
}
}
win_stack_foreach_managed(w, &ps->window_stack) {
bool posChanged = (w->oldX != -30000 && w->oldY != -30000 && w->oldW != 0 && w->oldH != 0)
&& (w->g.x != w->newX || w->g.y != w->newY || w->g.width != w->newW || w->g.height != w->newH);
win_stack_foreach_managed(w, &ps->window_stack) {
bool posChanged =
(w->oldX != -10000 && w->oldY != -10000 && w->oldW != 0 && w->oldH != 0) &&
(w->g.x != w->newX || w->g.y != w->newY || w->g.width != w->newW ||
w->g.height != w->newH);
if (posChanged) {
float t = get_time_ms();
float moveDx = (t - w->moveTimeX) / ps->o.transition_length;
float moveDy = (t - w->moveTimeY) / ps->o.transition_length;
float moveDw = (t - w->moveTimeW) / ps->o.transition_length;
float moveDh = (t - w->moveTimeH) / ps->o.transition_length;
if (moveDx >= 1.0) moveDx = 1.0;
if (moveDy >= 1.0) moveDy = 1.0;
if (moveDw >= 1.0) moveDw = 1.0;
if (moveDh >= 1.0) moveDh = 1.0;
if (posChanged) {
float t = get_time_ms();
float moveDx = (t - w->moveTimeX) / ps->o.transition_length;
float moveDy = (t - w->moveTimeY) / ps->o.transition_length;
float moveDw = (t - w->moveTimeW) / ps->o.transition_length;
float moveDh = (t - w->moveTimeH) / ps->o.transition_length;
if (moveDx >= 1.0)
moveDx = 1.0;
if (moveDy >= 1.0)
moveDy = 1.0;
if (moveDw >= 1.0)
moveDw = 1.0;
if (moveDh >= 1.0)
moveDh = 1.0;
float q = pow (moveDx, ps->o.transition_pow_x);
float k = pow (moveDy, ps->o.transition_pow_y);
float g = pow (moveDw, ps->o.transition_pow_w);
float z = pow (moveDh, ps->o.transition_pow_h);
float q = pow(moveDx, ps->o.transition_pow_x);
float k = pow(moveDy, ps->o.transition_pow_y);
float g = pow(moveDw, ps->o.transition_pow_w);
float z = pow(moveDh, ps->o.transition_pow_h);
float x = (float) w->oldX * (1-q) + (float) w->newX * q;
float y = (float) w->oldY * (1-k) + (float) w->newY * k;
float W = (float) w->oldW * (1-g) + (float) w->newW * g;
float h = (float) w->oldH * (1-z) + (float) w->newH * z;
float x = (float)w->oldX * (1 - q) + (float)w->newX * q;
float y = (float)w->oldY * (1 - k) + (float)w->newY * k;
float W = (float)w->oldW * (1 - g) + (float)w->newW * g;
float h = (float)w->oldH * (1 - z) + (float)w->newH * z;
add_damage_from_win(ps, w);
w->g.x = (int) x;
w->g.y = (int) y;
if (ps->o.size_transition) {
w->g.width = (int) W;
w->g.height = (int) h;
}
add_damage_from_win(ps, w);
w->g.x = (int)x;
w->g.y = (int)y;
if (ps->o.size_transition) {
w->g.width = (int)W;
w->g.height = (int)h;
}
/* w->to_paint = true; */
w->mode = WMODE_TRANS;
*fade_running = true;
}
// TODO
//if ((w->shadow && posChanged) || (ps->o.size_transition && w->pixmap_damaged)) {
// rc_region_unref(&w->extents);
// rc_region_unref(&w->border_size);
// w->extents = win_extents(ps, w);
// calc_win_size(ps, w);
/* w->to_paint = true; */
w->mode = WMODE_TRANS;
*fade_running = true;
}
// TODO
// if ((w->shadow && posChanged) || (ps->o.size_transition &&
// w->pixmap_damaged)) {
// rc_region_unref(&w->extents);
// rc_region_unref(&w->border_size);
// w->extents = win_extents(ps, w);
// calc_win_size(ps, w);
// if (ps->shape_exists && ps->o.shadow_ignore_shaped
// && ps->o.detect_rounded_corners && w->bounding_shaped)
// win_update_shape(ps, w);
//}
/* add_damage_win(ps, w); */
}
// if (ps->shape_exists && ps->o.shadow_ignore_shaped
// && ps->o.detect_rounded_corners && w->bounding_shaped)
// win_update_shape(ps, w);
//}
/* add_damage_win(ps, w); */
}
// Opacity will not change, from now on.
rc_region_t *last_reg_ignore = rc_region_new();
@@ -747,6 +569,11 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
rc_region_unref(&w->reg_ignore);
}
// Clear flags if we are not using experimental backends
if (!ps->o.experimental_backends) {
w->flags = 0;
}
// log_trace("%d %d %s", w->a.map_state, w->ever_damaged, w->name);
// Give up if it's not damaged or invisible, or it's unmapped and its
@@ -801,10 +628,10 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
// w->mode == WMODE_SOLID or WMODE_FRAME_TRANS
region_t *tmp = rc_region_new();
if (w->mode == WMODE_SOLID) {
*tmp = win_get_bounding_shape_global_by_val(w);
*tmp = win_get_bounding_shape_global_by_val(w, false);
} else {
// w->mode == WMODE_FRAME_TRANS
win_get_region_noframe_local(w, tmp);
win_get_region_noframe_local(w, tmp, false);
pixman_region32_intersect(tmp, tmp, &w->bounding_shape);
pixman_region32_translate(tmp, w->g.x, w->g.y);
}
@@ -894,6 +721,243 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
return bottom;
}
/**
* Rebuild cached <code>screen_reg</code>.
*/
static void rebuild_screen_reg(session_t *ps) {
get_screen_region(ps, &ps->screen_reg);
}
/**
* Rebuild <code>shadow_exclude_reg</code>.
*/
static void rebuild_shadow_exclude_reg(session_t *ps) {
bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
if (!ret)
exit(1);
}
/// Free up all the images and deinit the backend
static void destroy_backend(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
// Wrapping up fading in progress
if (win_skip_fading(ps, w)) {
// `w` is freed by win_skip_fading
continue;
}
if (ps->backend_data) {
if (w->state == WSTATE_MAPPED) {
win_release_images(ps->backend_data, w);
} else {
assert(!w->win_image);
assert(!w->shadow_image);
}
}
free_paint(ps, &w->paint);
}
if (ps->backend_data && ps->root_image) {
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
ps->root_image = NULL;
}
if (ps->backend_data) {
// deinit backend
if (ps->backend_blur_context) {
ps->backend_data->ops->destroy_blur_context(
ps->backend_data, ps->backend_blur_context);
ps->backend_blur_context = NULL;
}
if (ps->backend_round_context) {
ps->backend_data->ops->destroy_round_context(
ps->backend_data, ps->backend_round_context);
ps->backend_round_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 dual_kawase_blur_args dkargs;
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;
case BLUR_METHOD_ALT_KAWASE:
case BLUR_METHOD_DUAL_KAWASE:
dkargs.size = ps->o.blur_radius;
dkargs.strength = ps->o.blur_strength;
args = (void *)&dkargs;
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;
}
static bool initialize_round_corners(session_t *ps) {
struct round_corners_args cargs;
cargs.corner_radius = ps->o.corner_radius;
cargs.round_borders = ps->o.round_borders;
ps->backend_round_context =
ps->backend_data->ops->create_round_context(ps->backend_data, &cargs);
return ps->backend_round_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) {
assert(!ps->backend_data);
// Reinitialize win_data
assert(backend_list[ps->o.backend]);
ps->backend_data = backend_list[ps->o.backend]->init(ps);
if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting...");
quit(ps);
return false;
}
ps->backend_data->ops = backend_list[ps->o.backend];
if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting...");
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
quit(ps);
return false;
}
if (!initialize_round_corners(ps)) {
log_fatal("Failed to prepare for rounded corners, will "
"ignore...");
ps->o.corner_radius = 0;
}
// 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;
}
auto w = (struct managed_win *)_w;
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
if (w->state == WSTATE_MAPPED) {
// We need to reacquire image
log_debug("Marking window %#010x (%s) for update after "
"redirection",
w->base.id, w->name);
if (w->shadow) {
struct color c = {
.red = ps->o.shadow_red,
.green = ps->o.shadow_green,
.blue = ps->o.shadow_blue,
.alpha = ps->o.shadow_opacity,
};
win_bind_shadow(ps->backend_data, w, c,
ps->gaussian_map);
}
w->flags |= WIN_FLAGS_PIXMAP_STALE;
ps->pending_updates = true;
}
}
}
// The old backends binds pixmap lazily, nothing to do here
return true;
}
/// Handle configure event of a root window
void configure_root(session_t *ps, int width, int height) {
log_info("Root configuration changed, new geometry: %dx%d", width, height);
bool has_root_change = false;
if (ps->redirected) {
// On root window changes
if (ps->o.experimental_backends) {
assert(ps->backend_data);
has_root_change = ps->backend_data->ops->root_change != NULL;
} else {
// Old backend can handle root change
has_root_change = true;
}
if (!has_root_change) {
// deinit/reinit backend and free up resources if the backend
// cannot handle root change
destroy_backend(ps);
}
free_paint(ps, &ps->tgt_buffer);
}
ps->root_width = width;
ps->root_height = height;
rebuild_screen_reg(ps);
rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
if (top_w) {
rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false;
}
if (ps->redirected) {
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_clear(&ps->damage_ring[i]);
}
ps->damage = ps->damage_ring + ps->ndamage - 1;
#ifdef CONFIG_OPENGL
// GLX root change callback
if (BKEND_GLX == ps->o.backend && !ps->o.experimental_backends) {
glx_on_root_change(ps);
}
#endif
if (has_root_change) {
if (ps->backend_data != NULL) {
ps->backend_data->ops->root_change(ps->backend_data, ps);
}
// Old backend's root_change is not a specific function
} else {
if (!initialize_backend(ps)) {
log_fatal("Failed to re-initialize backend after root "
"change, aborting...");
ps->quit = true;
// TODO only event handlers should request ev_break,
// otherwise it's too hard to keep track of what can break
// the event loop
ev_break(ps->loop, EVBREAK_ALL);
return;
}
// Re-acquire the root pixmap.
root_damaged(ps);
}
force_repaint(ps);
}
return;
}
void root_damaged(session_t *ps) {
if (ps->root_tile_paint.pixmap) {
free_root_tile(ps);
@@ -1277,7 +1341,6 @@ static bool redirect_start(session_t *ps) {
// Re-detect driver since we now have a backend
ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
apply_driver_workarounds(ps, ps->drivers);
root_damaged(ps);
@@ -1363,6 +1426,12 @@ static void handle_new_windows(session_t *ps) {
}
static void refresh_windows(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
win_process_updates(ps, w);
}
}
static void refresh_stale_images(session_t *ps) {
win_stack_foreach_managed(w, &ps->window_stack) {
win_process_flags(ps, w);
}
@@ -1399,13 +1468,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
// Call fill_win on new windows
handle_new_windows(ps);
// Handle screen changes
// This HAS TO be called before refresh_windows, as handle_root_flags
// could call configure_root, which will release images and mark them
// stale.
handle_root_flags(ps);
// Process window flags
// Process window updates
refresh_windows(ps);
{
@@ -1417,6 +1480,12 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
free(r);
}
// Refresh pixmaps and shadows
refresh_stale_images(ps);
// Handle screen changes
handle_root_flags(ps);
e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c));
if (e) {
log_fatal_x_error(e, "failed to ungrab x server");
@@ -1670,9 +1739,11 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.randr_exists = 0,
.randr_event = 0,
.randr_error = 0,
#ifdef CONFIG_OPENGL
.glx_exists = false,
.glx_event = 0,
.glx_error = 0,
#endif
.xrfilter_convolution_exists = false,
.atoms_wintypes = {0},
@@ -1739,7 +1810,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
xcb_prefetch_extension_data(ps->c, &xcb_xinerama_id);
xcb_prefetch_extension_data(ps->c, &xcb_present_id);
xcb_prefetch_extension_data(ps->c, &xcb_sync_id);
xcb_prefetch_extension_data(ps->c, &xcb_glx_id);
ext_info = xcb_get_extension_data(ps->c, &xcb_render_id);
if (!ext_info || !ext_info->present) {
@@ -1795,13 +1865,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
XCB_XFIXES_MINOR_VERSION)
.sequence);
ext_info = xcb_get_extension_data(ps->c, &xcb_glx_id);
if (ext_info && ext_info->present) {
ps->glx_exists = true;
ps->glx_error = ext_info->first_error;
ps->glx_event = ext_info->first_event;
}
// Parse configuration file
win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}};
bool shadow_enabled = false, fading_enable = false, hasneg = false;
@@ -1869,6 +1932,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.blur_background_blacklist) &&
c2_list_postprocess(ps, ps->o.invert_color_list) &&
c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
c2_list_postprocess(ps, ps->o.round_borders_blacklist) &&
c2_list_postprocess(ps, ps->o.focus_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
"might not work");
@@ -1925,24 +1990,23 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
}
ps->sync_fence = XCB_NONE;
if (ps->xsync_exists) {
if (!ps->xsync_exists && ps->o.xrender_sync_fence) {
log_error("XSync extension not found. No XSync fence sync is "
"possible. (xrender-sync-fence can't be enabled)");
ps->o.xrender_sync_fence = false;
}
if (ps->o.xrender_sync_fence) {
ps->sync_fence = x_new_id(ps->c);
e = xcb_request_check(
ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0));
if (e) {
if (ps->o.xrender_sync_fence) {
log_error_x_error(e, "Failed to create a XSync fence. "
"xrender-sync-fence will be "
"disabled");
ps->o.xrender_sync_fence = false;
}
log_error_x_error(e, "Failed to create a XSync fence. "
"xrender-sync-fence will be disabled");
ps->o.xrender_sync_fence = false;
ps->sync_fence = XCB_NONE;
free(e);
}
} else if (ps->o.xrender_sync_fence) {
log_error("XSync extension not found. No XSync fence sync is "
"possible. (xrender-sync-fence can't be enabled)");
ps->o.xrender_sync_fence = false;
}
// Query X RandR
@@ -2014,8 +2078,12 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
}
}
// Target window must be initialized before the backend
//
// backend_operations::present == NULL means this backend doesn't need a target
// window; non experimental backends always need a target window
ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
apply_driver_workarounds(ps, ps->drivers);
// Initialize filters, must be preceded by OpenGL context creation
if (!ps->o.experimental_backends && !init_render(ps)) {
@@ -2132,7 +2200,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c));
if (e) {
log_fatal_x_error(e, "Failed to grab X server");
free(e);
goto err;
}
@@ -2151,7 +2218,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
if (e) {
log_fatal_x_error(e, "Failed to ungrab server");
free(e);
goto err;
}
ps->server_grabbed = false;
@@ -2241,6 +2307,8 @@ static void session_destroy(session_t *ps) {
free_wincondlst(&ps->o.opacity_rules);
free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist);
free_wincondlst(&ps->o.rounded_corners_blacklist);
free_wincondlst(&ps->o.round_borders_blacklist);
// Free tracked atom list
{

View File

@@ -25,11 +25,7 @@
#include "win.h"
#include "x.h"
enum root_flags {
ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we
// use this to track refresh rate changes
ROOT_FLAGS_CONFIGURED = 2 // Received configure notify on the root window
};
enum root_flags { ROOT_FLAGS_SCREEN_CHANGE = 1 };
// == Functions ==
// TODO move static inline functions that are only used in picom.c, into
@@ -42,6 +38,11 @@ void add_damage(session_t *ps, const region_t *damage);
uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode);
xcb_window_t find_client_win(session_t *ps, xcb_window_t w);
/// Handle configure event of a root window
void configure_root(session_t *ps, int width, int height);
void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce);
void update_refresh_rate(session_t *ps);

View File

@@ -48,14 +48,15 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
bool repeat, int depth, xcb_visualid_t visual, bool force) {
#ifdef CONFIG_OPENGL
// XXX This is a mess. But this will go away after the backend refactor.
static thread_local struct glx_fbconfig_info *argb_fbconfig = NULL;
if (!ppaint->pixmap)
return false;
struct glx_fbconfig_info *fbcfg;
if (!visual) {
assert(depth == 32);
if (!ps->argb_fbconfig) {
ps->argb_fbconfig =
if (!argb_fbconfig) {
argb_fbconfig =
glx_find_fbconfig(ps->dpy, ps->scr,
(struct xvisual_info){.red_size = 8,
.green_size = 8,
@@ -63,11 +64,11 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
.alpha_size = 8,
.visual_depth = 32});
}
if (!ps->argb_fbconfig) {
if (!argb_fbconfig) {
log_error("Failed to find appropriate FBConfig for 32 bit depth");
return false;
}
fbcfg = ps->argb_fbconfig;
fbcfg = argb_fbconfig;
} else {
auto m = x_get_visual_info(ps->c, visual);
if (m.visual_depth < 0) {
@@ -185,28 +186,139 @@ void free_paint(session_t *ps, paint_t *ppaint) {
ppaint->pixmap = XCB_NONE;
}
void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity,
bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram) {
uint32_t
make_circle(int cx, int cy, int radius, uint32_t max_ntraps, xcb_render_trapezoid_t traps[]) {
uint32_t n = 0, k = 0;
int y1, y2;
double w;
while (k < max_ntraps) {
y1 = (int)(-radius * cos(M_PI * k / max_ntraps));
traps[n].top = (cy + y1) << 16;
traps[n].left.p1.y = (cy + y1) << 16;
traps[n].right.p1.y = (cy + y1) << 16;
w = sqrt(radius * radius - y1 * y1) * 65536;
traps[n].left.p1.x = (int)((cx << 16) - w);
traps[n].right.p1.x = (int)((cx << 16) + w);
do {
k++;
y2 = (int)(-radius * cos(M_PI * k / max_ntraps));
} while (y1 == y2);
traps[n].bottom = (cy + y2) << 16;
traps[n].left.p2.y = (cy + y2) << 16;
traps[n].right.p2.y = (cy + y2) << 16;
w = sqrt(radius * radius - y2 * y2) * 65536;
traps[n].left.p2.x = (int)((cx << 16) - w);
traps[n].right.p2.x = (int)((cx << 16) + w);
n++;
}
return n;
}
uint32_t make_rectangle(int x, int y, int wid, int hei, xcb_render_trapezoid_t traps[]) {
traps[0].top = y << 16;
traps[0].left.p1.y = y << 16;
traps[0].left.p1.x = x << 16;
traps[0].left.p2.y = (y + hei) << 16;
traps[0].left.p2.x = x << 16;
traps[0].bottom = (y + hei) << 16;
traps[0].right.p1.x = (x + wid) << 16;
traps[0].right.p1.y = y << 16;
traps[0].right.p2.x = (x + wid) << 16;
traps[0].right.p2.y = (y + hei) << 16;
return 1;
}
uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps, int cr, int wid, int hei)
{
uint32_t n = make_circle(cr, cr, cr, max_ntraps, traps);
n += make_circle(wid - cr, cr, cr, max_ntraps, traps + n);
n += make_circle(wid - cr, hei - cr, cr, max_ntraps, traps + n);
n += make_circle(cr, hei - cr, cr, max_ntraps, traps + n);
n += make_rectangle(0, cr, cr, hei - 2 * cr, traps + n);
n += make_rectangle(cr, 0, wid - 2 * cr, cr, traps + n);
n += make_rectangle(wid - cr, cr, cr, hei - 2 * cr, traps + n);
n += make_rectangle(cr, hei - cr, wid - 2 * cr, cr, traps + n);
n += make_rectangle(cr, cr, wid - 2 * cr, hei - 2 * cr,
traps + n);
return n;
}
void render(session_t *ps, struct managed_win *w attr_unused, int x, int y,
int dx, int dy, int wid, int hei, int fullwid, int fullhei, double opacity,
bool argb, bool neg, int cr, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram, clip_t *clip) {
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
auto alpha_step = (int)(opacity * MAX_ALPHA);
xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step];
if (alpha_step != 0) {
uint8_t op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC
if (cr) {
//log_warn("f(%d, %d) wh(%d %d) xy(%d %d) dxy(%d %d)", fullwid, fullhei, wid, hei, x, y, dx, dy);
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
ps->c, ps->root, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {
.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0,
.y = 0,
.width = to_u16_checked(fullwid),
.height = to_u16_checked(fullhei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
p_tmp, trans, 1, &rect);
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, fullwid, fullhei);
xcb_render_trapezoids(
ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
0, 0, n, traps);
xcb_render_composite(
ps->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp,
ps->tgt_buffer.pict, to_i16_checked(x), to_i16_checked(y),
to_i16_checked(x), to_i16_checked(y), to_i16_checked(dx), to_i16_checked(dy),
to_u16_checked(wid), to_u16_checked(hei));
xcb_render_free_picture(ps->c, p_tmp);
} else {
xcb_render_picture_t p_tmp = alpha_pict;
if(clip){
p_tmp = x_create_picture_with_standard(ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t black = {.red = 255, .blue = 255, .green = 255, .alpha = 255};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(wid), .height = to_u16_checked(hei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, p_tmp, black, 1, &rect);
if(alpha_pict) {
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, alpha_pict, XCB_NONE, p_tmp, 0, 0, 0, 0, 0, 0, to_u16_checked(wid), to_u16_checked(hei));
}
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OUT_REVERSE, clip->pict, XCB_NONE, p_tmp, 0, 0, 0, 0, to_i16_checked(clip->x), to_i16_checked(clip->y), to_u16_checked(wid), to_u16_checked(hei));
}
uint8_t op = ((!argb && !alpha_pict && !clip) ? XCB_RENDER_PICT_OP_SRC
: XCB_RENDER_PICT_OP_OVER);
xcb_render_composite(
ps->c, op, pict, alpha_pict, ps->tgt_buffer.pict,
to_i16_checked(x), to_i16_checked(y), 0, 0, to_i16_checked(dx),
to_i16_checked(dy), to_u16_checked(wid), to_u16_checked(hei));
xcb_render_composite(
ps->c, op, pict, p_tmp/*alpha_pict*/, ps->tgt_buffer.pict,
to_i16_checked(x), to_i16_checked(y), 0, 0, to_i16_checked(dx),
to_i16_checked(dy), to_u16_checked(wid), to_u16_checked(hei));
if(clip){
xcb_render_free_picture(ps->c, p_tmp);
}
}
}
break;
}
#ifdef CONFIG_OPENGL
case BKEND_GLX:
glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb,
neg, reg_paint, pprogram);
glx_render(ps, w, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb,
neg, cr, reg_paint, pprogram);
ps->psglx->z += 1;
break;
#endif
@@ -221,21 +333,24 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, doubl
}
static inline void
paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, int hei,
paint_region(session_t *ps, struct managed_win *w, int x, int y, int wid, int hei,
double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
const int dx = (w ? w->g.x : 0) + x;
const int dy = (w ? w->g.y : 0) + y;
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w-> heightb : 0;
const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
const bool neg = (w && w->invert_color);
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict,
(w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
render(ps, w, x, y, dx, dy, wid, hei, fullwid, fullhei, opacity, argb, neg,
(w ? w->corner_radius : 0),
pict, (w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
#ifdef CONFIG_OPENGL
w ? &ps->glx_prog_win : NULL
#else
NULL
#endif
);
, XCB_NONE);
}
/**
@@ -258,6 +373,46 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
return true;
}
/**
* Rounde the corners of a window.
* Applies a fragment shader to discard corners
*
*/
static inline void
win_round_corners(session_t *ps, struct managed_win *w attr_unused, int shader_idx attr_unused, float cr attr_unused,
xcb_render_picture_t tgt_buffer attr_unused, const region_t *reg_paint) {
#ifdef CONFIG_OPENGL
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
#endif
//log_debug("x:%d y:%d w:%d h:%d", x, y, wid, hei);
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
// XRender method is implemented inside render()
} break;
#ifdef CONFIG_OPENGL
case BKEND_GLX:
if (shader_idx == 1) {
glx_round_corners_dst1(ps, w, w->glx_texture_bg, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint, &w->glx_round_cache);
} else {
glx_round_corners_dst0(ps, w, w->glx_texture_bg, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint, &w->glx_round_cache);
}
break;
#endif
default: assert(0);
}
#ifndef CONFIG_OPENGL
(void)reg_paint;
#endif
}
/**
* Paint a window itself and dim it if asked.
*/
@@ -529,8 +684,8 @@ static void paint_root(session_t *ps, const region_t *reg_paint) {
* Generate shadow <code>Picture</code> for a window.
*/
static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) {
/* const int width = w->widthb; */
/* const int height = w->heightb; */
//const int width = w->widthb;
//const int height = w->heightb;
const int width = w->newW; // TODO!
const int height = w->newH;
// log_trace("(): building shadow for %s %d %d", w->name, width, height);
@@ -613,9 +768,29 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
return;
}
render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width,
w->shadow_height, w->shadow_opacity, true, false, w->shadow_paint.pict,
w->shadow_paint.ptex, reg_paint, NULL);
xcb_render_picture_t td = XCB_NONE;
if (w->corner_radius) {
uint32_t max_ntraps = to_u32_checked(w->corner_radius);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, w->corner_radius, w->widthb, w->heightb);
td = x_create_picture_with_standard(ps->c, ps->root, w->widthb, w->heightb, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(w->widthb), .height = to_u16_checked(w->heightb)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect);
xcb_render_trapezoids(ps->c, XCB_RENDER_PICT_OP_OVER, solid_picture(ps->c, ps->root, false, 1, 0, 0, 0), td, x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, 0, n, traps);
}
clip_t clip = { .pict = td, -(w->shadow_dx), .y = -(w->shadow_dy) };
render(ps, w, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width,
w->shadow_height, w->widthb, w->heightb, w->shadow_opacity, true, false, 0, w->shadow_paint.pict,
w->shadow_paint.ptex, reg_paint, NULL, w->corner_radius ? &clip : NULL);
if(td){
xcb_render_free_picture(ps->c, td);
}
}
/**
@@ -635,7 +810,7 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
*/
static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y,
uint16_t wid, uint16_t hei, struct x_convolution_kernel **blur_kerns,
int nkernels, const region_t *reg_clip) {
int nkernels, const region_t *reg_clip, xcb_render_picture_t rounded) {
assert(blur_kerns);
assert(blur_kerns[0]);
@@ -680,7 +855,8 @@ static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t
}
if (src_pict != tgt_buffer)
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
//xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, rounded,
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded,
tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
free_picture(ps->c, &tmp_picture);
@@ -698,6 +874,7 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
const int cr = (w ? w->corner_radius : 0);
double factor_center = 1.0;
// Adjust blur strength according to window opacity, to make it appear
@@ -729,13 +906,27 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
&ps->blur_kerns_cache[i]);
}
xcb_render_picture_t td = XCB_NONE;
if (cr) {
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, wid, hei);
td = x_create_picture_with_standard(ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(wid), .height = to_u16_checked(hei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect);
xcb_render_trapezoids(ps->c, XCB_RENDER_PICT_OP_OVER, solid_picture(ps->c, ps->root, false, 1, 0, 0, 0), td, x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, 0, n, traps);
}
// Minimize the region we try to blur, if the window itself is not
// opaque, only the frame is.
region_t reg_blur = win_get_bounding_shape_global_by_val(w);
region_t reg_blur = win_get_bounding_shape_global_by_val(w, true);
if (w->mode == WMODE_FRAME_TRANS && !ps->o.force_win_blend) {
region_t reg_noframe;
pixman_region32_init(&reg_noframe);
win_get_region_noframe_local(w, &reg_noframe);
win_get_region_noframe_local(w, &reg_noframe, true);
pixman_region32_translate(&reg_noframe, w->g.x, w->g.y);
pixman_region32_subtract(&reg_blur, &reg_blur, &reg_noframe);
pixman_region32_fini(&reg_noframe);
@@ -743,14 +934,14 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
// Translate global coordinates to local ones
pixman_region32_translate(&reg_blur, -x, -y);
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
ps->o.blur_kernel_count, &reg_blur);
ps->o.blur_kernel_count, &reg_blur, td);
pixman_region32_clear(&reg_blur);
} break;
#ifdef CONFIG_OPENGL
case BKEND_GLX:
// TODO: Handle frame opacity
glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f,
(float)factor_center, reg_paint, &w->glx_blur_cache);
(float)w->opacity, reg_paint, &w->glx_blur_cache);
break;
#endif
default: assert(0);
@@ -852,7 +1043,9 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
//
// Whether this is beneficial is to be determined XXX
for (auto w = t; w; w = w->prev_trans) {
region_t bshape = win_get_bounding_shape_global_by_val(w);
region_t bshape_no_corners = win_get_bounding_shape_global_by_val(w, false);
region_t bshape_corners = win_get_bounding_shape_global_by_val(w, true);
// Painting shadow
if (w->shadow) {
// Lazy shadow building
@@ -881,7 +1074,7 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// saving GPU power and handling shaped windows (XXX
// unconfirmed)
if (!ps->o.wintype_option[w->window_type].full_shadow)
pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape_no_corners);
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
w->xinerama_scr < ps->xinerama_nscrs)
@@ -908,12 +1101,25 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Remember, reg_ignore is the union of all windows above the current
// window.
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_fini(&bshape);
pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape_corners);
pixman_region32_fini(&bshape_corners);
pixman_region32_fini(&bshape_no_corners);
reg_tmp = region;
if (pixman_region32_not_empty(&reg_tmp) || true) {
set_tgt_clip(ps, &reg_tmp);
// If rounded corners backup the region first
#ifdef CONFIG_OPENGL
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei, false);
}
#endif
// Blur window background
if (w->blur_background &&
(w->mode == WMODE_TRANS ||
@@ -923,6 +1129,12 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Painting the window
paint_one(ps, w, &reg_tmp);
// Round window corners
if (w->corner_radius > 0) {
win_round_corners(ps, w, 1, (float)w->corner_radius,
ps->tgt_buffer.pict, &reg_tmp);
}
}
}
@@ -1011,8 +1223,8 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
else
glFlush();
glXWaitX();
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
ps->root_height, 0, 1.0, false, false, &region, NULL);
glx_render(ps, t, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
ps->root_height, 0, 1.0, false, false, 0, &region, NULL);
// falls through
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
#endif
@@ -1131,13 +1343,15 @@ bool init_render(session_t *ps) {
}
// Blur filter
if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL) {
log_warn("Old backends only support blur method \"kernel\". Your blur "
if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL &&
ps->o.blur_method != BLUR_METHOD_DUAL_KAWASE && ps->o.blur_method != BLUR_METHOD_ALT_KAWASE) {
log_warn("Old backends only support blur methods \"kernel|kawase\". Your blur "
"setting will not be applied");
ps->o.blur_method = BLUR_METHOD_NONE;
}
if (ps->o.blur_method == BLUR_METHOD_KERNEL) {
if (ps->o.blur_method == BLUR_METHOD_KERNEL || ps->o.blur_method == BLUR_METHOD_DUAL_KAWASE
|| ps->o.blur_method == BLUR_METHOD_ALT_KAWASE) {
ps->blur_kerns_cache =
ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *);
@@ -1176,6 +1390,18 @@ bool init_render(session_t *ps) {
return false;
}
}
// Initialize our rounded corners fragment shader
if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
if (!glx_init_rounded_corners(ps)) {
log_error("Failed to init rounded corners shader.");
return false;
}
#else
assert(false);
#endif
}
return true;
}

View File

@@ -25,9 +25,15 @@ typedef struct paint {
#endif
} paint_t;
void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, double opacity,
bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram);
typedef struct clip {
xcb_render_picture_t pict;
int x;
int y;
} clip_t;
void render(session_t *ps, struct managed_win *, int x, int y, int dx, int dy, int w, int h, int fullw, int fullh, double opacity,
bool argb, bool neg, int cr, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram, clip_t *clip);
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint);
void paint_all(session_t *ps, struct managed_win *const t, bool ignore_damage);

View File

@@ -33,6 +33,9 @@ safe_isnan(double a) {
return __builtin_isnan(a);
}
#define CASESTRRET(s) \
case s: return #s
/// Same as assert(false), but make sure we abort _even in release builds_.
/// Silence compiler warning caused by release builds making some code paths reachable.
#define BUG() \

364
src/win.c
View File

@@ -46,6 +46,9 @@
// TODO Make more window states internal
struct managed_win_internal {
struct managed_win base;
/// A bit mask of unhandled window updates
uint_fast32_t pending_updates;
};
#define OPAQUE (0xffffffff)
@@ -53,6 +56,17 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20;
static const int ROUNDED_PIXELS = 1;
static const double ROUNDED_PERCENT = 0.05;
/// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *, bool)
#define gen_by_val_corners(fun) \
region_t fun##_by_val(const struct managed_win *w, bool include_corners) { \
region_t ret; \
pixman_region32_init(&ret); \
fun(w, &ret, include_corners); \
return ret; \
}
/// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *)
@@ -109,6 +123,19 @@ static void win_update_focused(session_t *ps, struct managed_win *w) {
w->focused = true;
}
}
// Always recalculate the window target opacity, since some opacity-related
// options depend on the output value of win_is_focused_real() instead of
// w->focused
auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w, false);
if (opacity_target_old != w->opacity_target && w->state == WSTATE_MAPPED) {
// Only MAPPED can transition to FADING
w->state = WSTATE_FADING;
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
}
/**
@@ -170,16 +197,18 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
/**
* Get a rectangular region a window occupies, excluding shadow.
*/
static void win_get_region_local(const struct managed_win *w, region_t *res) {
static void win_get_region_local(const struct managed_win *w, region_t *res, bool include_corners) {
assert(w->widthb >= 0 && w->heightb >= 0);
pixman_region32_fini(res);
pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb);
if(!include_corners) win_region_remove_corners(w, res);
}
/**
* Get a rectangular region a window occupies, excluding frame and shadow.
*/
void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
void win_get_region_noframe_local(const struct managed_win *w, region_t *res, bool include_corners) {
const margin_t extents = win_calc_frame_extents(w);
int x = extents.left;
@@ -190,10 +219,11 @@ void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
pixman_region32_fini(res);
if (width > 0 && height > 0) {
pixman_region32_init_rect(res, x, y, (uint)width, (uint)height);
if(!include_corners) win_region_remove_corners(w, res);
}
}
void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
void win_get_region_frame_local(const struct managed_win *w, region_t *res, bool include_corners) {
const margin_t extents = win_calc_frame_extents(w);
auto outer_width = extents.left + extents.right + w->g.width;
auto outer_height = extents.top + extents.bottom + w->g.height;
@@ -216,10 +246,11 @@ void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
region_t reg_win;
pixman_region32_init_rects(&reg_win, (rect_t[]){0, 0, outer_width, outer_height}, 1);
pixman_region32_intersect(res, &reg_win, res);
if(!include_corners) win_region_remove_corners(w, res);
pixman_region32_fini(&reg_win);
}
gen_by_val(win_get_region_frame_local);
gen_by_val_corners(win_get_region_frame_local);
/**
* Add a window to damaged area.
@@ -244,7 +275,6 @@ static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
if (w->win_image) {
base->ops->release_image(base, w->win_image);
w->win_image = NULL;
// Bypassing win_set_flags, because `w` might have been destroyed
w->flags |= WIN_FLAGS_PIXMAP_NONE;
}
}
@@ -254,7 +284,6 @@ static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
if (w->shadow_image) {
base->ops->release_image(base, w->shadow_image);
w->shadow_image = NULL;
// Bypassing win_set_flags, because `w` might have been destroyed
w->flags |= WIN_FLAGS_SHADOW_NONE;
}
}
@@ -275,11 +304,11 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w
b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true);
if (!w->win_image) {
log_error("Failed to bind pixmap");
win_set_flags(w, WIN_FLAGS_IMAGE_ERROR);
w->flags |= WIN_FLAGS_IMAGE_ERROR;
return false;
}
win_clear_flags(w, WIN_FLAGS_PIXMAP_NONE);
w->flags &= ~WIN_FLAGS_PIXMAP_NONE;
return true;
}
@@ -293,13 +322,13 @@ bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color
log_error("Failed to bind shadow image, shadow will be disabled for "
"%#010x (%s)",
w->base.id, w->name);
win_set_flags(w, WIN_FLAGS_SHADOW_NONE);
w->flags |= WIN_FLAGS_SHADOW_NONE;
w->shadow = false;
return false;
}
log_debug("New shadow for %#010x (%s)", w->base.id, w->name);
win_clear_flags(w, WIN_FLAGS_SHADOW_NONE);
w->flags &= ~WIN_FLAGS_SHADOW_NONE;
return true;
}
@@ -309,38 +338,40 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
// But if we are not releasing any images anyway, we don't care about the stale
// flags.
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
if ((w->flags & WIN_FLAGS_PIXMAP_NONE) == 0) {
assert((w->flags & WIN_FLAGS_PIXMAP_STALE) == 0);
win_release_pixmap(backend, w);
}
if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
assert(!win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE));
if ((w->flags & WIN_FLAGS_SHADOW_NONE) == 0) {
assert((w->flags & WIN_FLAGS_SHADOW_STALE) == 0);
win_release_shadow(backend, w);
}
}
void win_process_flags(session_t *ps, struct managed_win *w) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
map_win_start(ps, w);
win_clear_flags(w, WIN_FLAGS_MAPPED);
// Make sure all pending window updates are processed before this. Making this
// assumption simplifies some checks (e.g. whether window is mapped)
assert(((struct managed_win_internal *)w)->pending_updates == 0);
if (!w->flags || (w->flags & WIN_FLAGS_IMAGE_ERROR) != 0) {
return;
}
// Not a loop
while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
!win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
while ((w->flags & WIN_FLAGS_IMAGES_STALE) != 0) {
// Image needs to be updated, update it.
if (!ps->backend_data) {
// We are using legacy backend, nothing to do here.
break;
}
if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) {
if ((w->flags & WIN_FLAGS_PIXMAP_STALE) != 0) {
// Check to make sure the window is still mapped, otherwise we
// won't be able to rebind pixmap after releasing it, yet we might
// still need the pixmap for rendering.
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
if ((w->flags & WIN_FLAGS_PIXMAP_NONE) == 0) {
// Must release images first, otherwise breaks
// NVIDIA driver
win_release_pixmap(ps->backend_data, w);
@@ -348,8 +379,8 @@ void win_process_flags(session_t *ps, struct managed_win *w) {
win_bind_pixmap(ps->backend_data, w);
}
if (win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE)) {
if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
if ((w->flags & WIN_FLAGS_SHADOW_STALE) != 0) {
if ((w->flags & WIN_FLAGS_SHADOW_NONE) == 0) {
win_release_shadow(ps->backend_data, w);
}
if (w->shadow) {
@@ -367,14 +398,7 @@ void win_process_flags(session_t *ps, struct managed_win *w) {
}
// Clear stale image flags
if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) {
win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
}
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
win_recheck_client(ps, w);
win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
}
w->flags &= ~WIN_FLAGS_IMAGES_STALE;
}
/**
@@ -542,11 +566,15 @@ bool win_client_has_alpha(const struct managed_win *w) {
w->client_pictfmt->direct.alpha_mask;
}
winmode_t win_calc_mode(const struct managed_win *w) {
winmode_t win_calc_mode(session_t *ps, const struct managed_win *w) {
if (w->opacity < 1.0) {
return WMODE_TRANS;
}
if (ps->o.backend == BKEND_GLX && w->corner_radius > 0) {
return WMODE_TRANS;
}
if (win_has_alpha(w)) {
if (w->client_win == XCB_NONE) {
// This is a window not managed by the WM, and it has alpha,
@@ -586,17 +614,18 @@ winmode_t win_calc_mode(const struct managed_win *w) {
*
* @param ps current session
* @param w struct _win object representing the window
* @param ignore_state whether window state should be ignored in opacity calculation
*
* @return target opacity
*/
double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
double win_calc_opacity_target(session_t *ps, const struct managed_win *w, bool ignore_state) {
double opacity = 1;
if (w->state == WSTATE_UNMAPPED) {
if (w->state == WSTATE_UNMAPPED && !ignore_state) {
// be consistent
return 0;
}
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
if ((w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) && !ignore_state) {
return 0;
}
// Try obeying opacity property and window type opacity firstly
@@ -697,7 +726,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
w->shadow = shadow_new;
assert(!w->shadow_image);
assert(!w->win_image);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE));
//assert(w->flags & WIN_FLAGS_IMAGES_NONE);
return;
}
@@ -723,14 +752,14 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// asserting the existence of the shadow image.
if (w->shadow) {
// Mark the new extents as damaged if the shadow is added
assert(!w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
assert(!w->shadow_image || (w->flags & WIN_FLAGS_SHADOW_STALE) ||
!ps->o.experimental_backends);
pixman_region32_clear(&extents);
win_extents(w, &extents);
add_damage_from_win(ps, w);
} else {
// Mark the old extents as damaged if the shadow is removed
assert(w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
assert(w->shadow_image || (w->flags & WIN_FLAGS_SHADOW_STALE) ||
!ps->o.experimental_backends);
add_damage(ps, &extents);
}
@@ -740,7 +769,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// Delayed update of shadow image
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to re-create or
// release the shaodw in based on whether w->shadow is set.
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
w->flags |= WIN_FLAGS_SHADOW_STALE;
ps->pending_updates = true;
}
@@ -878,6 +907,48 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
win_set_blur_background(ps, w, blur_background_new);
}
/**
* Determine if a window should have rounded corners.
*/
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE /*|| ps->o.corner_radius == 0*/)
return;
// Don't round full screen windows & excluded windows
if ((w && win_is_fullscreen(ps, w)) ||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL)) {
w->corner_radius = 0;
//log_warn("xy(%d %d) wh(%d %d) will NOT round corners", w->g.x, w->g.y, w->widthb, w->heightb);
} else {
w->corner_radius = ps->o.corner_radius;
//log_warn("xy(%d %d) wh(%d %d) will round corners", w->g.x, w->g.y, w->widthb, w->heightb);
// HACK: we reset this so we can query the color once
// we query the color in glx_round_corners_dst0 using glReadPixels
//w->border_col = { -1., -1, -1, -1 };
w->border_col[0] = w->border_col[1] = w->border_col[2] = w->border_col[3] = -1.0;
// wintypes config section override
if (!safe_isnan(ps->o.wintype_option[w->window_type].corner_radius) &&
ps->o.wintype_option[w->window_type].corner_radius >= 0) {
w->corner_radius = ps->o.wintype_option[w->window_type].corner_radius;
//log_warn("xy(%d %d) wh(%d %d) wintypes:corner_radius: %d", w->g.x, w->g.y, w->widthb, w->heightb, w->corner_radius);
}
if (w && c2_match(ps, w, ps->o.round_borders_blacklist, NULL)) {
w->round_borders = 0;
} else {
w->round_borders = ps->o.round_borders;
// wintypes config section override
if (!safe_isnan(ps->o.wintype_option[w->window_type].round_borders) &&
ps->o.wintype_option[w->window_type].round_borders >= 0) {
w->round_borders = ps->o.wintype_option[w->window_type].round_borders;
//log_warn("wintypes:round_borders: %d", w->round_borders);
}
}
}
}
/**
* Update window opacity according to opacity rules.
*/
@@ -904,7 +975,6 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
* TODO need better name
*/
void win_on_factor_change(session_t *ps, struct managed_win *w) {
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
// Focus needs to be updated first, as other rules might depend on the focused
// state of the window
win_update_focused(ps, w);
@@ -912,8 +982,7 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
win_determine_shadow(ps, w);
win_determine_invert_color(ps, w);
win_determine_blur_background(ps, w);
w->mode = win_calc_mode(w);
log_debug("Window mode changed to %d", w->mode);
win_determine_rounded_corners(ps, w);
win_update_opacity_rule(ps, w);
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL);
@@ -921,7 +990,15 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
w->unredir_if_possible_excluded =
c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL);
win_update_opacity_target(ps, w);
auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w, false);
if (opacity_target_old != w->opacity_target && w->state == WSTATE_MAPPED) {
// Only MAPPED can transition to FADING
w->state = WSTATE_FADING;
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
w->reg_ignore_valid = false;
}
@@ -940,7 +1017,7 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
// Invalidate the shadow we built
if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING ||
w->state == WSTATE_FADING) {
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
w->flags |= WIN_FLAGS_IMAGES_STALE;
ps->pending_updates = true;
} else {
assert(w->state == WSTATE_UNMAPPED);
@@ -1014,9 +1091,9 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
win_on_factor_change(ps, w);
auto r = xcb_get_window_attributes_reply(
ps->c, xcb_get_window_attributes(ps->c, w->client_win), &e);
ps->c, xcb_get_window_attributes(ps->c, w->client_win), NULL);
if (!r) {
log_error_x_error(e, "Failed to get client window attributes");
log_error("Failed to get client window attributes");
return;
}
@@ -1032,8 +1109,6 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
*/
void win_unmark_client(session_t *ps, struct managed_win *w) {
xcb_window_t client = w->client_win;
log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
w->base.id, w->name);
w->client_win = XCB_NONE;
@@ -1043,42 +1118,13 @@ void win_unmark_client(session_t *ps, struct managed_win *w) {
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
}
/**
* Look for the client window of a particular window.
*/
static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
return w;
}
xcb_query_tree_reply_t *reply =
xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
if (!reply)
return 0;
xcb_window_t *children = xcb_query_tree_children(reply);
int nchildren = xcb_query_tree_children_length(reply);
int i;
xcb_window_t ret = 0;
for (i = 0; i < nchildren; ++i) {
if ((ret = find_client_win(ps, children[i])))
break;
}
free(reply);
return ret;
}
/**
* Recheck client window of a window.
*
* @param ps current session
* @param w struct _win of the parent window
*/
void win_recheck_client(session_t *ps, struct managed_win *w) {
assert(ps->server_grabbed);
static void win_recheck_client(session_t *ps, struct managed_win *w) {
// Initialize wmwin to false
w->wmwin = false;
@@ -1088,14 +1134,14 @@ void win_recheck_client(session_t *ps, struct managed_win *w) {
// sets override-redirect flags on all frame windows.
xcb_window_t cw = find_client_win(ps, w->base.id);
if (cw) {
log_debug("(%#010x): client %#010x", w->base.id, cw);
log_trace("(%#010x): client %#010x", w->base.id, cw);
}
// Set a window's client window to itself if we couldn't find a
// client window
if (!cw) {
cw = w->base.id;
w->wmwin = !w->a.override_redirect;
log_debug("(%#010x): client self (%s)", w->base.id,
log_trace("(%#010x): client self (%s)", w->base.id,
(w->wmwin ? "wmwin" : "override-redirected"));
}
@@ -1191,10 +1237,10 @@ struct win *fill_win(session_t *ps, struct win *w) {
.invert_color = false,
.blur_background = false,
.oldX = -10000,
.oldY = -10000,
.oldW = 0,
.oldH = 0,
.oldX = -10000,
.oldY = -10000,
.oldW = 0,
.oldH = 0,
.reg_ignore = NULL,
// The following ones are updated for other reasons
@@ -1262,6 +1308,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
// Initialized during paint
.paint = PAINT_INIT,
.shadow_paint = PAINT_INIT,
.corner_radius = 0,
};
assert(!w->destroyed);
@@ -1305,6 +1353,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
// Allocate and initialize the new win structure
auto new_internal = cmalloc(struct managed_win_internal);
auto new = (struct managed_win *)new_internal;
new_internal->pending_updates = 0;
// Fill structure
// We only need to initialize the part that are not initialized
@@ -1550,7 +1599,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
pixman_region32_clear(&w->bounding_shape);
// Start with the window rectangular region
win_get_region_local(w, &w->bounding_shape);
win_get_region_local(w, &w->bounding_shape, true);
// Only request for a bounding region if the window is shaped
// (while loop is used to avoid goto, not an actual loop)
@@ -1602,7 +1651,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
// Note we only do this when screen is redirected, because
// otherwise win_data is not valid
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
w->flags |= WIN_FLAGS_IMAGES_STALE;
ps->pending_updates = true;
}
free_paint(ps, &w->paint);
@@ -1716,7 +1765,7 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) {
free_paint(ps, &w->shadow_paint);
// Try again at binding images when the window is mapped next time
win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
w->flags &= ~WIN_FLAGS_IMAGE_ERROR;
}
/// Finish the destruction of a window (e.g. after fading has finished).
@@ -1778,7 +1827,7 @@ static void destroy_win_finish(session_t *ps, struct win *w) {
static void map_win_finish(struct managed_win *w) {
w->in_openclose = false;
w->isOld = true;
w->isOld = true;
w->state = WSTATE_MAPPED;
}
@@ -1890,15 +1939,14 @@ bool destroy_win_start(session_t *ps, struct win *w) {
}
if (w->managed) {
// Clear PIXMAP_STALE flag, since the window is destroyed there is no
// pixmap available so STALE doesn't make sense.
// Do this before changing the window state to destroying
win_clear_flags(mw, WIN_FLAGS_PIXMAP_STALE);
// Update state flags of a managed window
mw->state = WSTATE_DESTROYING;
mw->a.map_state = XCB_MAP_STATE_UNMAPPED;
mw->in_openclose = true;
// Clear PIXMAP_STALE flag, since the window is destroyed there is no
// pixmap available so STALE doesn't make sense.
mw->flags &= ~WIN_FLAGS_PIXMAP_STALE;
}
// don't need win_ev_stop because the window is gone anyway
@@ -1918,6 +1966,7 @@ bool destroy_win_start(session_t *ps, struct win *w) {
}
void unmap_win_start(session_t *ps, struct managed_win *w) {
auto internal_w = (struct managed_win_internal *)w;
assert(w);
assert(w->base.managed);
assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY);
@@ -1930,9 +1979,8 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
}
if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
// Clear the pending map as this window is now unmapped
win_clear_flags(w, WIN_FLAGS_MAPPED);
if (internal_w->pending_updates & WIN_UPDATE_MAP) {
internal_w->pending_updates &= ~(unsigned long)WIN_UPDATE_MAP;
} else {
log_warn("Trying to unmapping an already unmapped window %#010x "
"\"%s\"",
@@ -1947,12 +1995,11 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
w->a.map_state = XCB_MAP_STATE_UNMAPPED;
w->state = WSTATE_UNMAPPING;
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
w->opacity_target = win_calc_opacity_target(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w, false);
// Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap
// available so STALE doesn't make sense.
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
w->flags &= ~WIN_FLAGS_PIXMAP_STALE;
// don't care about properties anymore
win_ev_stop(ps, &w->base);
@@ -2059,7 +2106,8 @@ void map_win_start(session_t *ps, struct managed_win *w) {
}
assert(w->state == WSTATE_UNMAPPED);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE) || !ps->o.experimental_backends);
assert((w->flags & WIN_FLAGS_IMAGES_NONE) == WIN_FLAGS_IMAGES_NONE ||
!ps->o.experimental_backends);
// We stopped processing window size change when we were unmapped, refresh the
// size of the window
@@ -2087,13 +2135,14 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// XXX Can we assume map_state is always viewable?
w->a.map_state = XCB_MAP_STATE_VIEWABLE;
if (!w->isOld) {
w->oldX = -1000;
w->oldY = -1000;
if (!w->isOld) {
w->oldX = -10000;
w->oldY = -10000;
w->oldW = 0;
w->oldH = 0;
}
win_update_screen(ps, w);
// Set window event mask before reading properties so that no property
@@ -2108,7 +2157,7 @@ void map_win_start(session_t *ps, struct managed_win *w) {
}
// Update window mode here to check for ARGB windows
w->mode = win_calc_mode(w);
w->mode = win_calc_mode(ps, w);
// Detect client window here instead of in add_win() as the client
// window should have been prepared at this point
@@ -2139,13 +2188,13 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// XXX We need to make sure that win_data is available
// iff `state` is MAPPED
w->state = WSTATE_MAPPING;
w->opacity_target_old = 0;
w->opacity_target = win_calc_opacity_target(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w, false);
log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
w->opacity, w->opacity_target);
win_determine_blur_background(ps, w);
win_determine_rounded_corners(ps, w);
// Cannot set w->ever_damaged = false here, since window mapping could be
// delayed, so a damage event might have already arrived before this function
@@ -2161,7 +2210,7 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// the window's image will be bound
win_update_bounding_shape(ps, w);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_STALE));
assert((w->flags & WIN_FLAGS_IMAGES_STALE) == WIN_FLAGS_IMAGES_STALE);
#ifdef CONFIG_DBUS
// Send D-Bus signal
@@ -2175,60 +2224,6 @@ void map_win_start(session_t *ps, struct managed_win *w) {
}
}
/**
* Update target window opacity depending on the current state.
*/
void win_update_opacity_target(session_t *ps, struct managed_win *w) {
auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w);
if (opacity_target_old == w->opacity_target) {
return;
}
if (w->state == WSTATE_MAPPED) {
// Opacity target changed while MAPPED. Transition to FADING.
assert(w->opacity == opacity_target_old);
w->opacity_target_old = opacity_target_old;
w->state = WSTATE_FADING;
log_debug("Window %#010x (%s) opacity %f, opacity target %f, set "
"old target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
} else if (w->state == WSTATE_MAPPING) {
// Opacity target changed while fading in.
if (w->opacity >= w->opacity_target) {
// Already reached new target opacity. Transition to
// FADING.
map_win_finish(w);
w->opacity_target_old = fmax(opacity_target_old, w->opacity);
w->state = WSTATE_FADING;
log_debug("Window %#010x (%s) opacity %f already reached "
"new opacity target %f while mapping, set old "
"target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
}
} else if (w->state == WSTATE_FADING) {
// Opacity target changed while FADING.
if ((w->opacity < opacity_target_old && w->opacity > w->opacity_target) ||
(w->opacity > opacity_target_old && w->opacity < w->opacity_target)) {
// Changed while fading in and will fade out or while
// fading out and will fade in.
w->opacity_target_old = opacity_target_old;
log_debug("Window %#010x (%s) opacity %f already reached "
"new opacity target %f while fading, set "
"old target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
}
}
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
/**
* Find a managed window from window id in window linked list of the session.
*/
@@ -2284,13 +2279,13 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
}
/**
* Find a managed window that is, or is a parent of `wid`.
* Find out the WM frame of a client window by querying X.
*
* @param ps current session
* @param wid window ID
* @return struct _win object of the found window, NULL if not found
*/
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) {
struct managed_win *find_toplevel2(session_t *ps, xcb_window_t wid) {
// TODO this should probably be an "update tree", then find_toplevel.
// current approach is a bit more "racy"
struct win *w = NULL;
@@ -2351,35 +2346,31 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo
return false;
}
/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags) {
log_debug("Set flags %lu to window %#010x (%s)", flags, w->base.id, w->name);
if (unlikely(w->state == WSTATE_DESTROYING)) {
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
/// Queue an update on a window. A series of sanity checks are performed
void win_queue_update(struct managed_win *_w, enum win_update update) {
auto w = (struct managed_win_internal *)_w;
assert(popcount(update) == 1);
assert(update == WIN_UPDATE_MAP); // Currently the only supported update
if (unlikely(_w->state == WSTATE_DESTROYING)) {
log_error("Updates queued on a destroyed window %#010x (%s)", _w->base.id,
_w->name);
return;
}
w->flags |= flags;
w->pending_updates |= update;
}
/// Clear flags on a window. Some sanity checks are performed
void win_clear_flags(struct managed_win *w, uint64_t flags) {
log_debug("Clear flags %lu from window %#010x (%s)", flags, w->base.id, w->name);
if (unlikely(w->state == WSTATE_DESTROYING)) {
log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
w->name);
return;
/// Process pending updates on a window. Has to be called in X critical section
void win_process_updates(struct session *ps, struct managed_win *_w) {
assert(ps->server_grabbed);
auto w = (struct managed_win_internal *)_w;
if (w->pending_updates & WIN_UPDATE_MAP) {
map_win_start(ps, _w);
}
w->flags = w->flags & (~flags);
}
bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
return (w->flags & flags) != 0;
}
bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
return (w->flags & flags) == flags;
w->pending_updates = 0;
}
/**
@@ -2435,6 +2426,7 @@ win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
/// Return whether this window is mapped on the X server side
bool win_is_mapped_in_x(const struct managed_win *w) {
auto iw = (const struct managed_win_internal *)w;
return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING ||
w->state == WSTATE_MAPPED || (w->flags & WIN_FLAGS_MAPPED);
w->state == WSTATE_MAPPED || (iw->pending_updates & WIN_UPDATE_MAP);
}

View File

@@ -40,13 +40,13 @@ typedef struct _glx_texture glx_texture_t;
// it is very unideal for it to be here
typedef struct {
/// Framebuffer used for blurring.
GLuint fbo;
GLuint fbos[MAX_BLUR_PASS];
/// Textures used for blurring.
GLuint textures[2];
GLuint textures[MAX_BLUR_PASS];
/// Width of the textures.
int width;
int width[MAX_BLUR_PASS];
/// Height of the textures.
int height;
int height[MAX_BLUR_PASS];
} glx_blur_cache_t;
#endif
@@ -131,7 +131,7 @@ struct managed_win {
/// See above about coordinate systems.
region_t bounding_shape;
/// Window flags. Definitions above.
uint64_t flags;
int_fast16_t flags;
/// The region of screen that will be obscured when windows above is painted,
/// in global coordinates.
/// We use this to reduce the pixels that needed to be paint when painting
@@ -192,8 +192,6 @@ struct managed_win {
double opacity;
/// Target window opacity.
double opacity_target;
/// Previous window opacity.
double opacity_target_old;
/// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop
bool has_opacity_prop;
@@ -204,6 +202,11 @@ struct managed_win {
/// Last window opacity value set by the rules.
double opacity_set;
/// Corner radius
int corner_radius;
bool round_borders;
float border_col[4];
// Fading-related members
/// Override value of window fade state. Set by D-Bus method calls.
switch_t fade_force;
@@ -248,21 +251,29 @@ struct managed_win {
/// Whether to blur window background.
bool blur_background;
/// Animation state
int oldX; int oldY; int oldW; int oldH;
int newX; int newY; int newW; int newH;
float moveTimeX; float moveTimeY;
float moveTimeW; float moveTimeH;
bool isOld;
/// Animation state
int oldX; int oldY; int oldW; int oldH;
int newX; int newY; int newW; int newH;
float moveTimeX; float moveTimeY;
float moveTimeW; float moveTimeH;
bool isOld;
#ifdef CONFIG_OPENGL
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
glx_blur_cache_t glx_round_cache;
/// Background texture of the window
glx_texture_t *glx_texture_bg;
#endif
};
/// Process pending updates on a window. Has to be called in X critical section
void win_process_updates(struct session *ps, struct managed_win *_w);
/// Process pending images flags on a window. Has to be called in X critical section
void win_process_flags(session_t *ps, struct managed_win *w);
/// Queue an update on a window. A series of sanity checks are performed
void win_queue_update(struct managed_win *_w, enum win_update update);
/// Bind a shadow to the window, with color `c` and shadow kernel `kernel`
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct conv *kernel);
@@ -284,7 +295,7 @@ bool must_use destroy_win_start(session_t *ps, struct win *w);
void win_release_images(struct backend_base *base, struct managed_win *w);
int win_update_name(session_t *ps, struct managed_win *w);
int win_get_role(session_t *ps, struct managed_win *w);
winmode_t attr_pure win_calc_mode(const struct managed_win *w);
winmode_t attr_pure win_calc_mode(session_t *ps, const struct managed_win *w);
void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val);
void win_set_fade_force(struct managed_win *w, switch_t val);
void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val);
@@ -296,7 +307,6 @@ void win_set_focused(session_t *ps, struct managed_win *w);
bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
void win_update_prop_shadow(session_t *ps, struct managed_win *w);
void win_update_opacity_target(session_t *ps, struct managed_win *w);
void win_on_factor_change(session_t *ps, struct managed_win *w);
/**
* Update cache data in struct _win that depends on window size.
@@ -305,7 +315,6 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w);
void win_update_wintype(session_t *ps, struct managed_win *w);
void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client);
void win_unmark_client(session_t *ps, struct managed_win *w);
void win_recheck_client(session_t *ps, struct managed_win *w);
bool win_get_class(session_t *ps, struct managed_win *w);
/**
@@ -318,10 +327,12 @@ bool win_get_class(session_t *ps, struct managed_win *w);
*
* @param ps current session
* @param w struct _win object representing the window
* @param ignore_state whether window state should be ignored in opacity calculation
*
* @return target opacity
*/
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w);
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w,
bool ignore_state);
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
void win_update_screen(session_t *, struct managed_win *);
/**
@@ -362,12 +373,12 @@ void add_damage_from_win(session_t *ps, const struct managed_win *w);
*
* Return region in global coordinates.
*/
void win_get_region_noframe_local(const struct managed_win *w, region_t *);
void win_get_region_noframe_local(const struct managed_win *w, region_t *, bool include_corners);
/// Get the region for the frame of the window
void win_get_region_frame_local(const struct managed_win *w, region_t *res);
void win_get_region_frame_local(const struct managed_win *w, region_t *res, bool include_corners);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const struct managed_win *w);
region_t win_get_region_frame_local_by_val(const struct managed_win *w, bool include_corners);
/**
* Retrieve frame extents from a window.
*/
@@ -407,13 +418,13 @@ struct managed_win *find_managed_win(session_t *ps, xcb_window_t id);
struct win *find_win(session_t *ps, xcb_window_t id);
struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
/**
* Find a managed window that is, or is a parent of `wid`.
* Find out the WM frame of a client window by querying X.
*
* @param ps current session
* @param wid window ID
* @return struct _win object of the found window, NULL if not found
*/
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid);
struct managed_win *find_toplevel2(session_t *ps, xcb_window_t wid);
/**
* Check if a window is a fullscreen window.
@@ -439,22 +450,29 @@ bool win_is_mapped_in_x(const struct managed_win *w);
// Find the managed window immediately below `w` in the window stack
struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps,
const struct list_node *w);
/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags);
/// Clear flags on a window. Some sanity checks are performed
void win_clear_flags(struct managed_win *w, uint64_t flags);
/// Returns true if any of the flags in `flags` is set
bool win_check_flags_any(struct managed_win *w, uint64_t flags);
/// Returns true if all of the flags in `flags` are set
bool win_check_flags_all(struct managed_win *w, uint64_t flags);
/// Free all resources in a struct win
void free_win_res(session_t *ps, struct managed_win *w);
static inline region_t win_get_bounding_shape_global_by_val(struct managed_win *w) {
static inline void win_region_remove_corners(const struct managed_win *w, region_t *res) {
region_t corners;
pixman_region32_init_rects(
&corners,
(rect_t[]){
{.x1 = 0, .y1 = 0, .x2 = w->corner_radius, .y2 = w->corner_radius},
{.x1 = 0, .y1 = w->heightb-w->corner_radius, .x2 = w->corner_radius, .y2 = w->heightb},
{.x1 = w->widthb-w->corner_radius, .y1 = 0, .x2 = w->widthb, .y2 = w->corner_radius},
{.x1 = w->widthb-w->corner_radius, .y1 = w->heightb-w->corner_radius, .x2 = w->widthb, .y2 = w->heightb},
},
4);
pixman_region32_subtract(res, res, &corners);
}
static inline region_t win_get_bounding_shape_global_by_val(struct managed_win *w, bool include_corners) {
region_t ret;
pixman_region32_init(&ret);
pixman_region32_copy(&ret, &w->bounding_shape);
if(!include_corners) win_region_remove_corners(w, &ret);
pixman_region32_translate(&ret, w->g.x, w->g.y);
return ret;
}

View File

@@ -27,6 +27,11 @@ typedef enum {
WMODE_SOLID, // The window is opaque including the frame
} winmode_t;
/// Pending window updates
enum win_update {
WIN_UPDATE_MAP = 1,
};
/// Transition table:
/// (DESTROYED is when the win struct is destroyed and freed)
/// ('o' means in all other cases)
@@ -40,8 +45,8 @@ typedef enum {
/// | DESTROYING | - | o | - | - | - | - | Fading |
/// | | | | | | | |finished |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | MAPPING | Window | Window | o |Opacity| - | Fading | - |
/// | |unmapped |destroyed | |change | |finished| |
/// | MAPPING | Window | Window | o | - | - | Fading | - |
/// | |unmapped |destroyed | | | |finished| |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | FADING | Window | Window | - | o | - | Fading | - |
/// | |unmapped |destroyed | | | |finished| |
@@ -81,13 +86,9 @@ enum win_flags {
WIN_FLAGS_SHADOW_STALE = 8,
/// shadow has not been generated
WIN_FLAGS_SHADOW_NONE = 16,
/// the client window needs to be updated
WIN_FLAGS_CLIENT_STALE = 32,
/// the window is mapped by X, we need to call map_win_start for it
WIN_FLAGS_MAPPED = 64,
};
static const uint64_t WIN_FLAGS_IMAGES_STALE =
static const int_fast16_t WIN_FLAGS_IMAGES_STALE =
WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE;
#define WIN_FLAGS_IMAGES_NONE (WIN_FLAGS_PIXMAP_NONE | WIN_FLAGS_SHADOW_NONE)

107
src/x.c
View File

@@ -7,7 +7,6 @@
#include <pixman.h>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <xcb/glx.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb.h>
@@ -168,6 +167,15 @@ xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
}
xcb_render_pictformat_t
x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
x_get_server_pictfmts(c);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
return pictfmt->id;
}
int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
auto setup = xcb_get_setup(c);
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
@@ -231,6 +239,17 @@ x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
}
xcb_render_picture_t
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
xcb_pict_standard_t standard, uint32_t valuemask,
const xcb_render_create_picture_value_list_t *attr) {
x_get_server_pictfmts(c);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
assert(pictfmt);
return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
}
/**
* Create an picture.
*/
@@ -338,80 +357,69 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
int o = 0;
const char *name = "Unknown";
#define CASESTRRET(s) \
case s: \
name = #s; \
break
#define CASESTRRET2(s) \
case XCB_##s: name = #s; break
case s: name = #s; break
// TODO separate error code out from session_t
o = error_code - ps->xfixes_error;
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
switch (o) { CASESTRRET2(XCB_XFIXES_BAD_REGION); }
o = error_code - ps->damage_error;
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
switch (o) { CASESTRRET2(XCB_DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error;
switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE);
CASESTRRET2(RENDER_PICT_OP);
CASESTRRET2(RENDER_GLYPH_SET);
CASESTRRET2(RENDER_GLYPH);
CASESTRRET2(XCB_RENDER_PICT_FORMAT);
CASESTRRET2(XCB_RENDER_PICTURE);
CASESTRRET2(XCB_RENDER_PICT_OP);
CASESTRRET2(XCB_RENDER_GLYPH_SET);
CASESTRRET2(XCB_RENDER_GLYPH);
}
#ifdef CONFIG_OPENGL
if (ps->glx_exists) {
o = error_code - ps->glx_error;
switch (o) {
CASESTRRET2(GLX_BAD_SCREEN);
CASESTRRET2(GLX_BAD_ATTRIBUTE);
CASESTRRET2(GLX_NO_EXTENSION);
CASESTRRET2(GLX_BAD_VISUAL);
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE);
CASESTRRET2(GLX_BAD_DRAWABLE);
CASESTRRET2(GLX_BAD_PIXMAP);
CASESTRRET2(GLX_BAD_CONTEXT_TAG);
CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
CASESTRRET2(GLX_BAD_RENDER_REQUEST);
CASESTRRET2(GLX_BAD_LARGE_REQUEST);
CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
CASESTRRET2(GLX_BAD_FB_CONFIG);
CASESTRRET2(GLX_BAD_PBUFFER);
CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
CASESTRRET2(GLX_BAD_WINDOW);
CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
CASESTRRET2(GLX_BAD_VALUE);
CASESTRRET2(GLX_BAD_ENUM);
}
}
#endif
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
switch (o) {
CASESTRRET(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm);
CASESTRRET(XSyncBadFence);
CASESTRRET2(XSyncBadCounter);
CASESTRRET2(XSyncBadAlarm);
CASESTRRET2(XSyncBadFence);
}
}
switch (error_code) {
CASESTRRET2(ACCESS);
CASESTRRET2(ALLOC);
CASESTRRET2(ATOM);
CASESTRRET2(COLORMAP);
CASESTRRET2(CURSOR);
CASESTRRET2(DRAWABLE);
CASESTRRET2(FONT);
CASESTRRET2(G_CONTEXT);
CASESTRRET2(ID_CHOICE);
CASESTRRET2(IMPLEMENTATION);
CASESTRRET2(LENGTH);
CASESTRRET2(MATCH);
CASESTRRET2(NAME);
CASESTRRET2(PIXMAP);
CASESTRRET2(REQUEST);
CASESTRRET2(VALUE);
CASESTRRET2(WINDOW);
CASESTRRET2(BadAccess);
CASESTRRET2(BadAlloc);
CASESTRRET2(BadAtom);
CASESTRRET2(BadColor);
CASESTRRET2(BadCursor);
CASESTRRET2(BadDrawable);
CASESTRRET2(BadFont);
CASESTRRET2(BadGC);
CASESTRRET2(BadIDChoice);
CASESTRRET2(BadImplementation);
CASESTRRET2(BadLength);
CASESTRRET2(BadMatch);
CASESTRRET2(BadName);
CASESTRRET2(BadPixmap);
CASESTRRET2(BadRequest);
CASESTRRET2(BadValue);
CASESTRRET2(BadWindow);
}
#undef CASESTRRET
#undef CASESTRRET2
thread_local static char buffer[256];
@@ -434,9 +442,6 @@ void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t
* for multiple calls to this function,
*/
const char *x_strerror(xcb_generic_error_t *e) {
if (!e) {
return "No error";
}
return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
}

View File

@@ -172,6 +172,12 @@ x_create_picture_with_standard_and_pixmap(xcb_connection_t *, xcb_pict_standard_
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
xcb_render_picture_t
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
xcb_pict_standard_t standard, uint32_t valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
/**
* Create an picture.
*/
@@ -261,6 +267,8 @@ struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual
xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
xcb_render_pictformat_t x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen);
uint32_t attr_deprecated xcb_generate_id(xcb_connection_t *c);