2019-02-03 13:47:36 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <xcb/xcb.h>
|
|
|
|
|
2019-03-05 15:27:04 -05:00
|
|
|
#include "backend/backend.h"
|
2018-12-31 09:27:18 -05:00
|
|
|
#include "common.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "compiler.h"
|
2019-02-09 10:50:40 -05:00
|
|
|
#include "config.h"
|
2019-03-04 16:37:22 -05:00
|
|
|
#include "log.h"
|
2019-02-09 10:50:40 -05:00
|
|
|
#include "region.h"
|
|
|
|
#include "win.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-07 16:08:37 -05:00
|
|
|
extern struct backend_operations xrender_ops;
|
2019-01-27 14:28:15 -05:00
|
|
|
#ifdef CONFIG_OPENGL
|
2019-03-07 16:08:37 -05:00
|
|
|
extern struct backend_operations glx_ops;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct backend_operations *backend_list[NUM_BKEND] = {
|
|
|
|
[BKEND_XRENDER] = &xrender_ops,
|
|
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
[BKEND_GLX] = &glx_ops,
|
2019-01-27 14:28:15 -05:00
|
|
|
#endif
|
|
|
|
};
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-05 15:27:04 -05:00
|
|
|
/**
|
|
|
|
* @param all_damage if true ignore damage and repaint the whole screen
|
|
|
|
*/
|
|
|
|
region_t get_damage(session_t *ps, bool all_damage) {
|
2019-01-27 14:29:02 -05:00
|
|
|
region_t region;
|
2019-02-26 18:52:37 -05:00
|
|
|
auto buffer_age_fn = ps->backend_data->ops->buffer_age;
|
|
|
|
int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1;
|
2019-01-27 14:32:00 -05:00
|
|
|
|
2019-03-05 15:27:04 -05:00
|
|
|
if (all_damage) {
|
|
|
|
buffer_age = -1;
|
|
|
|
}
|
|
|
|
|
2019-01-27 14:29:02 -05:00
|
|
|
pixman_region32_init(®ion);
|
|
|
|
if (buffer_age == -1 || buffer_age > ps->ndamage) {
|
|
|
|
pixman_region32_copy(®ion, &ps->screen_reg);
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < buffer_age; i++) {
|
2019-03-30 05:07:21 -04:00
|
|
|
auto curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage;
|
|
|
|
log_trace("damage index: %d, damage ring offset: %ld", i, curr);
|
2019-03-27 11:41:02 -04:00
|
|
|
dump_region(&ps->damage_ring[curr]);
|
2019-01-27 14:29:02 -05:00
|
|
|
pixman_region32_union(®ion, ®ion, &ps->damage_ring[curr]);
|
|
|
|
}
|
|
|
|
pixman_region32_intersect(®ion, ®ion, &ps->screen_reg);
|
|
|
|
}
|
|
|
|
return region;
|
|
|
|
}
|
2019-01-02 15:34:07 -05:00
|
|
|
|
2019-01-27 14:29:02 -05:00
|
|
|
/// paint all windows
|
|
|
|
void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
|
2019-03-04 16:37:22 -05:00
|
|
|
// All painting will be limited to the damage, if _some_ of
|
|
|
|
// the paints bleed out of the damage region, it will destroy
|
|
|
|
// part of the image we want to reuse
|
|
|
|
region_t reg_damage;
|
2019-01-27 14:29:02 -05:00
|
|
|
if (!ignore_damage) {
|
2019-03-16 08:12:54 -04:00
|
|
|
reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
|
2018-12-31 18:54:06 -05:00
|
|
|
} else {
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_init(®_damage);
|
|
|
|
pixman_region32_copy(®_damage, &ps->screen_reg);
|
2019-01-27 14:29:02 -05:00
|
|
|
}
|
|
|
|
|
2019-03-04 16:37:22 -05:00
|
|
|
if (!pixman_region32_not_empty(®_damage)) {
|
|
|
|
pixman_region32_fini(®_damage);
|
2019-01-27 14:29:02 -05:00
|
|
|
return;
|
2018-12-31 18:54:06 -05:00
|
|
|
}
|
|
|
|
|
2019-01-02 15:34:07 -05:00
|
|
|
#ifdef DEBUG_REPAINT
|
|
|
|
static struct timespec last_paint = {0};
|
|
|
|
#endif
|
|
|
|
|
2019-03-04 16:37:22 -05:00
|
|
|
// A hint to backend, the region that will be visible on screen
|
|
|
|
// backend can optimize based on this info
|
|
|
|
region_t reg_visible;
|
|
|
|
pixman_region32_init(®_visible);
|
|
|
|
pixman_region32_copy(®_visible, &ps->screen_reg);
|
2019-01-02 15:34:07 -05:00
|
|
|
if (t) {
|
|
|
|
// Calculate the region upon which the root window (wallpaper) is to be
|
|
|
|
// painted based on the ignore region of the lowest window, if available
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_subtract(®_visible, ®_visible, t->reg_ignore);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// TODO Bind root pixmap
|
|
|
|
|
|
|
|
if (ps->backend_data->ops->prepare) {
|
2019-03-05 15:27:03 -05:00
|
|
|
ps->backend_data->ops->prepare(ps->backend_data, ®_damage);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->root_image) {
|
|
|
|
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
|
2019-03-04 16:37:22 -05:00
|
|
|
®_damage, ®_visible);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
2019-01-02 15:34:07 -05:00
|
|
|
|
|
|
|
// Windows are sorted from bottom to top
|
|
|
|
// Each window has a reg_ignore, which is the region obscured by all the windows
|
|
|
|
// on top of that window. This is used to reduce the number of pixels painted.
|
|
|
|
//
|
|
|
|
// Whether this is beneficial is to be determined XXX
|
|
|
|
for (win *w = t; w; w = w->prev_trans) {
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_subtract(®_visible, &ps->screen_reg, w->reg_ignore);
|
2019-03-24 00:47:25 -04:00
|
|
|
assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
|
2019-02-26 18:52:37 -05:00
|
|
|
|
2019-03-04 16:37:22 -05:00
|
|
|
// The bounding shape of the window, in global/target coordinates
|
|
|
|
// reminder: bounding shape contains the WM frame
|
2019-02-26 18:52:37 -05:00
|
|
|
auto reg_bound = win_get_bounding_shape_global_by_val(w);
|
2019-03-04 16:37:22 -05:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// Draw shadow on target
|
|
|
|
if (w->shadow) {
|
2019-03-04 16:37:22 -05:00
|
|
|
// Clip region for the shadow
|
|
|
|
// reg_shadow \in reg_damage
|
2019-02-26 18:52:37 -05:00
|
|
|
auto reg_shadow = win_extents_by_val(w);
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_intersect(®_shadow, ®_shadow, ®_damage);
|
2019-02-26 18:52:37 -05:00
|
|
|
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
|
|
|
pixman_region32_subtract(®_shadow, ®_shadow, ®_bound);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// Mask out the region we don't want shadow on
|
|
|
|
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_subtract(®_shadow, ®_shadow,
|
2019-02-26 18:52:37 -05:00
|
|
|
&ps->shadow_exclude_reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
|
|
|
|
w->xinerama_scr < ps->xinerama_nscrs) {
|
|
|
|
// There can be a window where number of screens is
|
|
|
|
// updated, but the screen number attached to the windows
|
|
|
|
// have not.
|
|
|
|
//
|
|
|
|
// Window screen number will be updated eventually, so
|
|
|
|
// here we just check to make sure we don't access out of
|
|
|
|
// bounds.
|
|
|
|
pixman_region32_intersect(
|
|
|
|
®_shadow, ®_shadow,
|
|
|
|
&ps->xinerama_scr_regs[w->xinerama_scr]);
|
|
|
|
}
|
2019-01-02 15:34:07 -05:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
assert(w->shadow_image);
|
2019-03-10 15:56:55 -04:00
|
|
|
if (w->opacity == 1) {
|
|
|
|
ps->backend_data->ops->compose(
|
|
|
|
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
|
|
|
|
w->g.y + w->shadow_dy, ®_shadow, ®_visible);
|
|
|
|
} else {
|
|
|
|
auto new_img = ps->backend_data->ops->copy(
|
|
|
|
ps->backend_data, w->shadow_image, ®_visible);
|
|
|
|
ps->backend_data->ops->image_op(
|
|
|
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
|
|
|
|
NULL, ®_visible, (double[]){w->opacity});
|
|
|
|
ps->backend_data->ops->compose(
|
|
|
|
ps->backend_data, new_img, w->g.x + w->shadow_dx,
|
|
|
|
w->g.y + w->shadow_dy, ®_shadow, ®_visible);
|
|
|
|
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
|
|
|
}
|
2019-02-26 18:52:37 -05:00
|
|
|
pixman_region32_fini(®_shadow);
|
|
|
|
}
|
|
|
|
|
2019-03-04 16:37:22 -05:00
|
|
|
// The clip region for the current window, in global/target coordinates
|
|
|
|
// reg_paint \in reg_damage
|
|
|
|
region_t reg_paint;
|
|
|
|
pixman_region32_init(®_paint);
|
|
|
|
pixman_region32_intersect(®_paint, ®_bound, ®_damage);
|
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// Blur window background
|
|
|
|
bool win_transparent = ps->backend_data->ops->is_image_transparent(
|
|
|
|
ps->backend_data, w->win_image);
|
|
|
|
bool frame_transparent = w->frame_opacity != 1;
|
|
|
|
if (w->blur_background &&
|
|
|
|
(win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
|
|
|
|
// Minimize the region we try to blur, if the window
|
|
|
|
// itself is not opaque, only the frame is.
|
2019-03-04 16:37:22 -05:00
|
|
|
// TODO resize blur region to fix black line artifact
|
|
|
|
if (!win_is_solid(ps, w)) {
|
|
|
|
// We need to blur the bounding shape of the window
|
|
|
|
// (reg_paint = reg_bound \cap reg_damage)
|
|
|
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
|
|
|
®_paint, ®_visible);
|
|
|
|
} else if (frame_transparent && ps->o.blur_background_frame) {
|
2019-03-05 15:27:04 -05:00
|
|
|
// Window itself is solid, we only need to blur the frame
|
|
|
|
// region
|
2019-03-04 16:37:22 -05:00
|
|
|
auto reg_blur = win_get_region_frame_local_by_val(w);
|
2019-02-26 18:52:37 -05:00
|
|
|
pixman_region32_translate(®_blur, w->g.x, w->g.y);
|
2019-03-04 16:37:22 -05:00
|
|
|
// make sure reg_blur \in reg_damage
|
|
|
|
pixman_region32_intersect(®_blur, ®_blur, ®_damage);
|
2019-02-26 18:52:37 -05:00
|
|
|
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
|
2019-03-04 16:37:22 -05:00
|
|
|
®_blur, ®_visible);
|
2019-02-26 18:52:37 -05:00
|
|
|
pixman_region32_fini(®_blur);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Draw window on target
|
|
|
|
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data->ops->compose(ps->backend_data, w->win_image, w->g.x,
|
|
|
|
w->g.y, ®_paint, ®_visible);
|
2019-02-26 18:52:37 -05:00
|
|
|
} else {
|
2019-03-04 16:37:22 -05:00
|
|
|
// For window image processing, we don't need to limit the process
|
2019-03-05 15:27:04 -05:00
|
|
|
// region to damage, since the window image data is independent
|
|
|
|
// from the target image data, which we want to protect.
|
2019-03-04 16:37:22 -05:00
|
|
|
|
|
|
|
// The bounding shape, in window local coordinates
|
|
|
|
region_t reg_bound_local;
|
|
|
|
pixman_region32_init(®_bound_local);
|
|
|
|
pixman_region32_copy(®_bound_local, ®_bound);
|
|
|
|
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
|
|
|
|
|
|
|
// The visible region, in window local coordinates
|
|
|
|
// Although we don't limit process region to damage, we provide
|
|
|
|
// that info in reg_visible as a hint. Since window image data
|
|
|
|
// outside of the damage region won't be painted onto target
|
|
|
|
region_t reg_visible_local;
|
|
|
|
pixman_region32_init(®_visible_local);
|
|
|
|
pixman_region32_intersect(®_visible_local, ®_visible, ®_damage);
|
|
|
|
pixman_region32_translate(®_visible_local, -w->g.x, -w->g.y);
|
|
|
|
// Data outside of the bounding shape won't be visible, but it is
|
2019-03-05 15:27:04 -05:00
|
|
|
// not necessary to limit the image operations to the bounding
|
|
|
|
// shape yet. So pass that as the visible region, not the clip
|
|
|
|
// region.
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_intersect(®_visible_local, ®_visible_local,
|
|
|
|
®_bound_local);
|
|
|
|
|
|
|
|
// A region covers the entire window
|
2019-02-26 18:52:37 -05:00
|
|
|
auto new_img = ps->backend_data->ops->copy(
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data, w->win_image, ®_visible_local);
|
2019-02-26 18:52:37 -05:00
|
|
|
if (w->invert_color) {
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data->ops->image_op(
|
2019-03-09 08:37:32 -05:00
|
|
|
ps->backend_data, IMAGE_OP_INVERT_COLOR_ALL, new_img,
|
|
|
|
NULL, ®_visible_local, NULL);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
if (w->dim) {
|
|
|
|
double dim_opacity = ps->o.inactive_dim;
|
|
|
|
if (!ps->o.inactive_dim_fixed) {
|
|
|
|
dim_opacity *= w->opacity;
|
|
|
|
}
|
|
|
|
ps->backend_data->ops->image_op(
|
2019-03-09 08:37:32 -05:00
|
|
|
ps->backend_data, IMAGE_OP_DIM_ALL, new_img, NULL,
|
2019-03-04 16:37:22 -05:00
|
|
|
®_visible_local, (double[]){dim_opacity});
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
if (w->frame_opacity != 1) {
|
2019-03-04 16:37:22 -05:00
|
|
|
auto reg_frame = win_get_region_frame_local_by_val(w);
|
2019-02-26 18:52:37 -05:00
|
|
|
ps->backend_data->ops->image_op(
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, ®_frame,
|
|
|
|
®_visible_local, (double[]){w->frame_opacity});
|
|
|
|
pixman_region32_fini(®_frame);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
if (w->opacity != 1) {
|
|
|
|
ps->backend_data->ops->image_op(
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
|
|
|
|
NULL, ®_visible_local, (double[]){w->opacity});
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
|
2019-03-04 16:37:22 -05:00
|
|
|
w->g.y, ®_paint, ®_visible);
|
2019-02-26 18:52:37 -05:00
|
|
|
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_fini(®_visible_local);
|
|
|
|
pixman_region32_fini(®_bound_local);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_fini(®_bound);
|
|
|
|
pixman_region32_fini(®_paint);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
2019-03-05 15:27:01 -05:00
|
|
|
pixman_region32_fini(®_damage);
|
|
|
|
|
2019-03-07 16:08:37 -05:00
|
|
|
if (ps->o.monitor_repaint) {
|
2019-03-05 15:27:04 -05:00
|
|
|
reg_damage = get_damage(ps, false);
|
2019-03-10 15:56:55 -04:00
|
|
|
ps->backend_data->ops->fill(ps->backend_data, 0.5, 0, 0, 0.5, ®_damage);
|
2019-03-05 15:27:04 -05:00
|
|
|
pixman_region32_fini(®_damage);
|
|
|
|
}
|
|
|
|
|
2019-03-05 15:27:01 -05:00
|
|
|
// Move the head of the damage ring
|
|
|
|
ps->damage = ps->damage - 1;
|
|
|
|
if (ps->damage < ps->damage_ring) {
|
|
|
|
ps->damage = ps->damage_ring + ps->ndamage - 1;
|
|
|
|
}
|
|
|
|
pixman_region32_clear(ps->damage);
|
2019-01-02 15:34:07 -05:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
if (ps->backend_data->ops->present) {
|
2019-01-02 15:34:07 -05:00
|
|
|
// Present the rendered scene
|
|
|
|
// Vsync is done here
|
2019-02-26 18:52:37 -05:00
|
|
|
ps->backend_data->ops->present(ps->backend_data);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_REPAINT
|
|
|
|
print_timestamp(ps);
|
|
|
|
struct timespec now = get_time_timespec();
|
|
|
|
struct timespec diff = {0};
|
|
|
|
timespec_subtract(&diff, &now, &last_paint);
|
|
|
|
printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
|
|
|
|
last_paint = now;
|
|
|
|
printf("paint:");
|
|
|
|
for (win *w = t; w; w = w->prev_trans)
|
|
|
|
printf(" %#010lx", w->id);
|
|
|
|
putchar('\n');
|
|
|
|
fflush(stdout);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// vim: set noet sw=8 ts=8 :
|