2019-02-03 13:47:36 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
2019-11-09 21:31:55 -05:00
|
|
|
#include <xcb/sync.h>
|
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"
|
2019-08-09 19:56:04 -04:00
|
|
|
#include "types.h"
|
2019-02-09 10:50:40 -05:00
|
|
|
#include "win.h"
|
2019-11-09 21:30:56 -05:00
|
|
|
#include "x.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-08-04 12:52:45 -04:00
|
|
|
extern struct backend_operations xrender_ops, dummy_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;
|
2022-09-29 02:55:25 -04:00
|
|
|
extern struct backend_operations egl_ops;
|
2019-03-07 16:08:37 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
struct backend_operations *backend_list[NUM_BKEND] = {
|
|
|
|
[BKEND_XRENDER] = &xrender_ops,
|
2019-08-04 12:52:45 -04:00
|
|
|
[BKEND_DUMMY] = &dummy_ops,
|
2019-03-07 16:08:37 -05:00
|
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
[BKEND_GLX] = &glx_ops,
|
2022-09-29 02:55:25 -04:00
|
|
|
[BKEND_EGL] = &egl_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;
|
2021-11-25 03:21:41 -05:00
|
|
|
log_trace("damage index: %d, damage ring offset: %td", 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
|
|
|
|
2022-02-08 03:09:22 -05:00
|
|
|
void handle_device_reset(session_t *ps) {
|
|
|
|
log_error("Device reset detected");
|
|
|
|
// Wait for reset to complete
|
2022-02-08 03:32:33 -05:00
|
|
|
// Although ideally the backend should return DEVICE_STATUS_NORMAL after a reset
|
|
|
|
// is completed, it's not always possible.
|
|
|
|
//
|
|
|
|
// According to ARB_robustness (emphasis mine):
|
|
|
|
//
|
|
|
|
// "If a reset status other than NO_ERROR is returned and subsequent
|
|
|
|
// calls return NO_ERROR, the context reset was encountered and
|
|
|
|
// completed. If a reset status is repeatedly returned, the context **may**
|
|
|
|
// be in the process of resetting."
|
|
|
|
//
|
|
|
|
// Which means it may also not be in the process of resetting. For example on
|
|
|
|
// AMDGPU devices, Mesa OpenGL always return CONTEXT_RESET after a reset has
|
|
|
|
// started, completed or not.
|
|
|
|
//
|
|
|
|
// So here we blindly wait 5 seconds and hope ourselves best of the luck.
|
|
|
|
sleep(5);
|
2022-02-08 03:09:22 -05:00
|
|
|
|
|
|
|
// Reset picom
|
|
|
|
log_info("Resetting picom after device reset");
|
|
|
|
ev_break(ps->loop, EVBREAK_ALL);
|
|
|
|
}
|
|
|
|
|
2019-01-27 14:29:02 -05:00
|
|
|
/// paint all windows
|
2022-12-12 05:41:54 -05:00
|
|
|
void paint_all_new(session_t *ps, struct managed_win *t) {
|
2022-02-08 03:09:22 -05:00
|
|
|
if (ps->backend_data->ops->device_status &&
|
|
|
|
ps->backend_data->ops->device_status(ps->backend_data) != DEVICE_STATUS_NORMAL) {
|
|
|
|
return handle_device_reset(ps);
|
|
|
|
}
|
2020-04-22 15:24:15 -04:00
|
|
|
if (ps->o.xrender_sync_fence) {
|
2019-11-09 15:01:38 -05:00
|
|
|
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.");
|
|
|
|
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
|
|
|
|
ps->sync_fence = XCB_NONE;
|
|
|
|
ps->o.xrender_sync_fence = false;
|
|
|
|
ps->xsync_exists = false;
|
|
|
|
}
|
|
|
|
}
|
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;
|
2022-12-12 05:41:54 -05:00
|
|
|
reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
|
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-11-30 16:22:28 -05:00
|
|
|
// <damage-note>
|
|
|
|
// If use_damage is enabled, we MUST make sure only the damaged regions of the
|
|
|
|
// screen are ever touched by the compositor. The reason is that at the beginning
|
|
|
|
// of each render, we clear the damaged regions with the wallpaper, and nothing
|
|
|
|
// else. If later during the render we changed anything outside the damaged
|
|
|
|
// region, that won't be cleared by the next render, and will thus accumulate.
|
|
|
|
// (e.g. if shadow is drawn outside the damaged region, it will become thicker and
|
|
|
|
// thicker over time.)
|
|
|
|
|
|
|
|
/// The adjusted damaged regions
|
2019-07-25 19:00:51 -04:00
|
|
|
region_t reg_paint;
|
|
|
|
assert(ps->o.blur_method != BLUR_METHOD_INVALID);
|
|
|
|
if (ps->o.blur_method != BLUR_METHOD_NONE && ps->backend_data->ops->get_blur_size) {
|
|
|
|
int blur_width, blur_height;
|
|
|
|
ps->backend_data->ops->get_blur_size(ps->backend_blur_context,
|
|
|
|
&blur_width, &blur_height);
|
2019-07-26 13:28:22 -04:00
|
|
|
|
|
|
|
// The region of screen a given window influences will be smeared
|
|
|
|
// out by blur. With more windows on top of the given window, the
|
|
|
|
// influences region will be smeared out more.
|
|
|
|
//
|
|
|
|
// Also, blurring requires data slightly outside the area that needs
|
|
|
|
// to be blurred. The more semi-transparent windows are stacked on top
|
|
|
|
// of each other, the larger the area will be.
|
|
|
|
//
|
|
|
|
// Instead of accurately calculate how much bigger the damage
|
|
|
|
// region will be because of blur, we assume the worst case here.
|
|
|
|
// That is, the damaged window is at the bottom of the stack, and
|
|
|
|
// all other windows have semi-transparent background
|
2022-12-14 02:36:07 -05:00
|
|
|
//
|
|
|
|
// TODO(yshui): maybe we don't need to resize reg_damage, only reg_paint?
|
2019-07-26 13:28:22 -04:00
|
|
|
int resize_factor = 1;
|
2019-07-25 19:00:51 -04:00
|
|
|
if (t) {
|
2019-07-26 13:28:22 -04:00
|
|
|
resize_factor = t->stacking_rank;
|
2019-07-25 19:00:51 -04:00
|
|
|
}
|
2019-07-26 13:28:22 -04:00
|
|
|
resize_region_in_place(®_damage, blur_width * resize_factor,
|
|
|
|
blur_height * resize_factor);
|
|
|
|
reg_paint = resize_region(®_damage, blur_width * resize_factor,
|
|
|
|
blur_height * resize_factor);
|
2019-07-25 19:00:51 -04:00
|
|
|
pixman_region32_intersect(®_paint, ®_paint, &ps->screen_reg);
|
2019-07-26 13:28:22 -04:00
|
|
|
pixman_region32_intersect(®_damage, ®_damage, &ps->screen_reg);
|
2019-07-25 19:00:51 -04:00
|
|
|
} else {
|
|
|
|
pixman_region32_init(®_paint);
|
|
|
|
pixman_region32_copy(®_paint, ®_damage);
|
|
|
|
}
|
|
|
|
|
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-11-30 16:24:22 -05:00
|
|
|
if (t && !ps->o.transparent_clipping) {
|
2019-01-02 15:34:07 -05:00
|
|
|
// 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-11-30 16:24:22 -05:00
|
|
|
//
|
|
|
|
// NOTE If transparent_clipping is enabled, transparent windows are
|
|
|
|
// included in the reg_ignore, but we still want to have the wallpaper
|
|
|
|
// beneath them, so we don't use reg_ignore for wallpaper in that case.
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_subtract(®_visible, ®_visible, t->reg_ignore);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
|
|
|
|
2021-07-09 13:17:23 -04:00
|
|
|
// Region on screen we don't want any shadows on
|
|
|
|
region_t reg_shadow_clip;
|
|
|
|
pixman_region32_init(®_shadow_clip);
|
|
|
|
|
2023-06-10 09:08:49 -04:00
|
|
|
struct timespec now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
auto now_us = (uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000);
|
|
|
|
if (ps->next_render > 0) {
|
|
|
|
log_trace("Render schedule deviation: %ld us (%s) %ld %ld",
|
|
|
|
labs((int64_t)now_us - (int64_t)ps->next_render),
|
|
|
|
now_us < ps->next_render ? "early" : "late", now_us,
|
|
|
|
ps->next_render);
|
|
|
|
ps->last_schedule_delay = 0;
|
|
|
|
if (now_us > ps->next_render) {
|
|
|
|
ps->last_schedule_delay = now_us - ps->next_render;
|
|
|
|
}
|
|
|
|
}
|
2023-06-10 09:11:30 -04:00
|
|
|
ps->did_render = true;
|
2023-06-10 09:08:49 -04:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
if (ps->backend_data->ops->prepare) {
|
2019-07-25 19:00:51 -04:00
|
|
|
ps->backend_data->ops->prepare(ps->backend_data, ®_paint);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->root_image) {
|
2022-08-24 02:35:49 -04:00
|
|
|
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
|
|
|
|
(coord_t){0}, NULL, (coord_t){0},
|
2019-07-25 19:00:51 -04:00
|
|
|
®_paint, ®_visible);
|
2020-04-02 12:37:52 -04:00
|
|
|
} else {
|
|
|
|
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
|
|
|
|
®_paint);
|
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
|
2019-04-17 18:14:45 -04:00
|
|
|
for (auto 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-09-19 13:48:12 -04:00
|
|
|
assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE));
|
|
|
|
assert(!(w->flags & WIN_FLAGS_PIXMAP_NONE));
|
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);
|
2022-08-24 03:12:59 -04:00
|
|
|
auto reg_bound_no_corner =
|
|
|
|
win_get_bounding_shape_global_without_corners_by_val(w);
|
2022-08-24 02:35:49 -04:00
|
|
|
|
2022-08-26 05:41:13 -04:00
|
|
|
if (!w->mask_image && (w->bounding_shaped || w->corner_radius != 0)) {
|
2022-08-25 12:59:19 -04:00
|
|
|
win_bind_mask(ps->backend_data, w);
|
2022-08-24 02:35:49 -04:00
|
|
|
}
|
2019-03-04 16:37:22 -05:00
|
|
|
|
|
|
|
// The clip region for the current window, in global/target coordinates
|
2019-07-25 19:00:51 -04:00
|
|
|
// reg_paint_in_bound \in reg_paint
|
|
|
|
region_t reg_paint_in_bound;
|
|
|
|
pixman_region32_init(®_paint_in_bound);
|
|
|
|
pixman_region32_intersect(®_paint_in_bound, ®_bound, ®_paint);
|
2019-11-30 16:24:22 -05:00
|
|
|
if (ps->o.transparent_clipping) {
|
|
|
|
// <transparent-clipping-note>
|
|
|
|
// If transparent_clipping is enabled, we need to be SURE that
|
|
|
|
// things are not drawn inside reg_ignore, because otherwise they
|
|
|
|
// will appear underneath transparent windows.
|
|
|
|
// So here we have make sure reg_paint_in_bound \in reg_visible
|
|
|
|
// There are a few other places below where this is needed as
|
|
|
|
// well.
|
|
|
|
pixman_region32_intersect(®_paint_in_bound,
|
|
|
|
®_paint_in_bound, ®_visible);
|
|
|
|
}
|
2019-03-04 16:37:22 -05:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// Blur window background
|
2020-08-30 10:25:58 -04:00
|
|
|
/* TODO(yshui) since the backend might change the content of the window
|
|
|
|
* (e.g. with shaders), we should consult the backend whether the window
|
|
|
|
* is transparent or not. for now we will just rely on the force_win_blend
|
|
|
|
* option */
|
2019-07-13 07:29:39 -04:00
|
|
|
auto real_win_mode = w->mode;
|
2022-08-24 03:35:24 -04:00
|
|
|
coord_t window_coord = {.x = w->g.x, .y = w->g.y};
|
2019-07-13 07:29:39 -04:00
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
if (w->blur_background &&
|
2019-07-13 07:29:39 -04:00
|
|
|
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
|
|
|
|
(ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) {
|
2019-02-26 18:52:37 -05:00
|
|
|
// Minimize the region we try to blur, if the window
|
|
|
|
// itself is not opaque, only the frame is.
|
2019-07-26 18:40:30 -04:00
|
|
|
|
|
|
|
double blur_opacity = 1;
|
2019-12-20 14:17:38 -05:00
|
|
|
if (w->opacity < (1.0 / MAX_ALPHA)) {
|
|
|
|
// Hide blur for fully transparent windows.
|
|
|
|
blur_opacity = 0;
|
|
|
|
} else if (w->state == WSTATE_MAPPING) {
|
2019-07-26 18:40:30 -04:00
|
|
|
// Gradually increase the blur intensity during
|
|
|
|
// fading in.
|
2019-12-20 14:17:38 -05:00
|
|
|
assert(w->opacity <= w->opacity_target);
|
2019-07-26 18:40:30 -04:00
|
|
|
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.
|
2019-12-20 14:17:38 -05:00
|
|
|
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;
|
|
|
|
}
|
2019-07-26 18:40:30 -04:00
|
|
|
}
|
2020-04-04 10:55:30 -04:00
|
|
|
assert(blur_opacity >= 0 && blur_opacity <= 1);
|
2019-07-26 18:40:30 -04:00
|
|
|
|
2019-07-13 07:29:39 -04:00
|
|
|
if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
|
2019-03-04 16:37:22 -05:00
|
|
|
// We need to blur the bounding shape of the window
|
2019-07-25 19:00:51 -04:00
|
|
|
// (reg_paint_in_bound = reg_bound \cap reg_paint)
|
|
|
|
ps->backend_data->ops->blur(
|
2022-08-24 03:35:24 -04:00
|
|
|
ps->backend_data, blur_opacity,
|
|
|
|
ps->backend_blur_context, w->mask_image, window_coord,
|
2019-07-25 19:00:51 -04:00
|
|
|
®_paint_in_bound, ®_visible);
|
2019-07-13 07:29:39 -04:00
|
|
|
} else {
|
2019-03-05 15:27:04 -05:00
|
|
|
// Window itself is solid, we only need to blur the frame
|
|
|
|
// region
|
2019-07-13 07:29:39 -04:00
|
|
|
|
|
|
|
// Readability assertions
|
|
|
|
assert(ps->o.blur_background_frame);
|
|
|
|
assert(real_win_mode == WMODE_FRAME_TRANS);
|
|
|
|
|
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-07-25 19:00:51 -04:00
|
|
|
// make sure reg_blur \in reg_paint
|
|
|
|
pixman_region32_intersect(®_blur, ®_blur, ®_paint);
|
2019-11-30 16:24:22 -05:00
|
|
|
if (ps->o.transparent_clipping) {
|
|
|
|
// ref: <transparent-clipping-note>
|
|
|
|
pixman_region32_intersect(®_blur, ®_blur,
|
|
|
|
®_visible);
|
|
|
|
}
|
2022-08-24 03:35:24 -04:00
|
|
|
ps->backend_data->ops->blur(
|
|
|
|
ps->backend_data, blur_opacity, ps->backend_blur_context,
|
|
|
|
w->mask_image, window_coord, ®_blur, ®_visible);
|
2019-02-26 18:52:37 -05:00
|
|
|
pixman_region32_fini(®_blur);
|
|
|
|
}
|
|
|
|
}
|
2019-07-29 16:18:55 -04:00
|
|
|
|
|
|
|
// Draw shadow on target
|
|
|
|
if (w->shadow) {
|
2019-09-19 13:48:12 -04:00
|
|
|
assert(!(w->flags & WIN_FLAGS_SHADOW_NONE));
|
2019-07-29 16:18:55 -04:00
|
|
|
// Clip region for the shadow
|
|
|
|
// reg_shadow \in reg_paint
|
|
|
|
auto reg_shadow = win_extents_by_val(w);
|
|
|
|
pixman_region32_intersect(®_shadow, ®_shadow, ®_paint);
|
|
|
|
|
|
|
|
// Mask out the region we don't want shadow on
|
|
|
|
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
|
|
|
|
pixman_region32_subtract(®_shadow, ®_shadow,
|
|
|
|
&ps->shadow_exclude_reg);
|
|
|
|
}
|
2021-07-09 13:17:23 -04:00
|
|
|
if (pixman_region32_not_empty(®_shadow_clip)) {
|
|
|
|
pixman_region32_subtract(®_shadow, ®_shadow,
|
|
|
|
®_shadow_clip);
|
|
|
|
}
|
2019-07-29 16:18:55 -04:00
|
|
|
|
2023-01-13 06:27:45 -05:00
|
|
|
if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 &&
|
|
|
|
w->randr_monitor < ps->randr_nmonitors) {
|
|
|
|
// There can be a window where number of monitors is
|
|
|
|
// updated, but the monitor number attached to the window
|
2019-07-29 16:18:55 -04:00
|
|
|
// have not.
|
|
|
|
//
|
2023-01-13 06:27:45 -05:00
|
|
|
// Window monitor number will be updated eventually, so
|
2019-07-29 16:18:55 -04:00
|
|
|
// here we just check to make sure we don't access out of
|
|
|
|
// bounds.
|
|
|
|
pixman_region32_intersect(
|
|
|
|
®_shadow, ®_shadow,
|
2023-01-13 06:27:45 -05:00
|
|
|
&ps->randr_monitor_regs[w->randr_monitor]);
|
2019-07-29 16:18:55 -04:00
|
|
|
}
|
|
|
|
|
2019-11-30 16:24:22 -05:00
|
|
|
if (ps->o.transparent_clipping) {
|
|
|
|
// ref: <transparent-clipping-note>
|
|
|
|
pixman_region32_intersect(®_shadow, ®_shadow,
|
|
|
|
®_visible);
|
|
|
|
}
|
|
|
|
|
2019-07-29 16:18:55 -04:00
|
|
|
assert(w->shadow_image);
|
2021-07-09 14:57:51 -04:00
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->shadow_image,
|
|
|
|
&w->opacity);
|
2022-08-24 02:35:49 -04:00
|
|
|
coord_t shadow_coord = {.x = w->g.x + w->shadow_dx,
|
|
|
|
.y = w->g.y + w->shadow_dy};
|
|
|
|
|
|
|
|
auto inverted_mask = NULL;
|
|
|
|
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
2022-08-24 03:12:59 -04:00
|
|
|
pixman_region32_subtract(®_shadow, ®_shadow,
|
|
|
|
®_bound_no_corner);
|
2022-08-24 02:35:49 -04:00
|
|
|
if (w->mask_image) {
|
|
|
|
inverted_mask = w->mask_image;
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
|
|
|
inverted_mask, (bool[]){true});
|
|
|
|
}
|
|
|
|
}
|
2021-07-09 14:57:51 -04:00
|
|
|
ps->backend_data->ops->compose(
|
2022-08-24 02:35:49 -04:00
|
|
|
ps->backend_data, w->shadow_image, shadow_coord,
|
|
|
|
inverted_mask, window_coord, ®_shadow, ®_visible);
|
|
|
|
if (inverted_mask) {
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
|
|
|
inverted_mask, (bool[]){false});
|
|
|
|
}
|
2019-07-29 16:18:55 -04:00
|
|
|
pixman_region32_fini(®_shadow);
|
|
|
|
}
|
|
|
|
|
2021-07-09 14:57:51 -04:00
|
|
|
// Update image properties
|
|
|
|
{
|
|
|
|
double dim_opacity = 0.0;
|
|
|
|
if (w->dim) {
|
|
|
|
dim_opacity = ps->o.inactive_dim;
|
|
|
|
if (!ps->o.inactive_dim_fixed) {
|
|
|
|
dim_opacity *= w->opacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image,
|
|
|
|
&ps->o.max_brightness);
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_INVERTED, w->win_image,
|
|
|
|
&w->invert_color);
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image,
|
|
|
|
&dim_opacity);
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &w->opacity);
|
2021-10-23 07:52:50 -04:00
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image,
|
|
|
|
(double[]){w->corner_radius});
|
2022-01-12 08:34:37 -05:00
|
|
|
if (w->corner_radius) {
|
2022-01-13 06:31:31 -05:00
|
|
|
int border_width = w->g.border_width;
|
|
|
|
if (border_width == 0) {
|
|
|
|
// Some WM has borders implemented as WM frames
|
|
|
|
border_width = min3(w->frame_extents.left,
|
|
|
|
w->frame_extents.right,
|
|
|
|
w->frame_extents.bottom);
|
|
|
|
}
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
|
|
|
|
w->win_image, &border_width);
|
2022-01-12 08:34:37 -05:00
|
|
|
}
|
2022-07-17 09:24:18 -04:00
|
|
|
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_CUSTOM_SHADER, w->win_image,
|
|
|
|
w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL);
|
2021-07-09 14:57:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (w->opacity * MAX_ALPHA < 1) {
|
|
|
|
// We don't need to paint the window body itself if it's
|
|
|
|
// completely transparent.
|
|
|
|
goto skip;
|
|
|
|
}
|
2019-11-06 19:19:20 -05:00
|
|
|
|
2021-07-09 13:17:23 -04:00
|
|
|
if (w->clip_shadow_above) {
|
|
|
|
// Add window bounds to shadow-clip region
|
|
|
|
pixman_region32_union(®_shadow_clip, ®_shadow_clip, ®_bound);
|
|
|
|
} else {
|
|
|
|
// Remove overlapping window bounds from shadow-clip region
|
|
|
|
pixman_region32_subtract(®_shadow_clip, ®_shadow_clip, ®_bound);
|
|
|
|
}
|
|
|
|
|
2019-02-26 18:52:37 -05:00
|
|
|
// Draw window on target
|
2021-07-09 14:57:51 -04:00
|
|
|
if (w->frame_opacity == 1) {
|
2019-07-25 19:00:51 -04:00
|
|
|
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
2022-08-24 02:35:49 -04:00
|
|
|
window_coord, NULL, window_coord,
|
2019-07-25 19:00:51 -04:00
|
|
|
®_paint_in_bound, ®_visible);
|
2021-07-09 14:57:51 -04:00
|
|
|
} else {
|
2019-11-30 16:22:28 -05:00
|
|
|
// For window image processing, we don't have to limit the process
|
|
|
|
// region to damage for correctness. (see <damager-note> for
|
|
|
|
// details)
|
2019-03-04 16:37:22 -05:00
|
|
|
|
2021-07-09 14:57:51 -04:00
|
|
|
// 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
|
2019-03-04 16:37:22 -05:00
|
|
|
region_t reg_visible_local;
|
2022-08-25 12:59:19 -04:00
|
|
|
region_t reg_bound_local;
|
2021-07-09 14:57:51 -04:00
|
|
|
{
|
|
|
|
// The bounding shape, in window local coordinates
|
2022-08-25 12:59:19 -04:00
|
|
|
pixman_region32_init(®_bound_local);
|
|
|
|
pixman_region32_copy(®_bound_local, ®_bound);
|
|
|
|
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
2021-07-09 14:57:51 -04:00
|
|
|
|
|
|
|
pixman_region32_init(®_visible_local);
|
|
|
|
pixman_region32_intersect(®_visible_local,
|
|
|
|
®_visible, ®_paint);
|
|
|
|
pixman_region32_translate(®_visible_local, -w->g.x,
|
|
|
|
-w->g.y);
|
|
|
|
// Data outside of the bounding shape won't be visible,
|
|
|
|
// but it is not necessary to limit the image operations
|
|
|
|
// to the bounding shape yet. So pass that as the visible
|
|
|
|
// region, not the clip region.
|
|
|
|
pixman_region32_intersect(
|
|
|
|
®_visible_local, ®_visible_local, ®_bound_local);
|
|
|
|
}
|
2019-03-04 16:37:22 -05:00
|
|
|
|
2021-06-13 20:51:09 -04:00
|
|
|
auto new_img = ps->backend_data->ops->clone_image(
|
2019-03-04 16:37:22 -05:00
|
|
|
ps->backend_data, w->win_image, ®_visible_local);
|
2021-07-09 14:57:51 -04:00
|
|
|
auto reg_frame = win_get_region_frame_local_by_val(w);
|
|
|
|
ps->backend_data->ops->image_op(
|
|
|
|
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, ®_frame,
|
|
|
|
®_visible_local, (double[]){w->frame_opacity});
|
|
|
|
pixman_region32_fini(®_frame);
|
2022-08-24 02:35:49 -04:00
|
|
|
ps->backend_data->ops->compose(ps->backend_data, new_img,
|
|
|
|
window_coord, NULL, window_coord,
|
|
|
|
®_paint_in_bound, ®_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);
|
2022-08-25 12:59:19 -04:00
|
|
|
pixman_region32_fini(®_bound_local);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
2021-07-09 14:57:51 -04:00
|
|
|
skip:
|
2019-03-04 16:37:22 -05:00
|
|
|
pixman_region32_fini(®_bound);
|
2022-08-24 03:12:59 -04:00
|
|
|
pixman_region32_fini(®_bound_no_corner);
|
2019-07-25 19:00:51 -04:00
|
|
|
pixman_region32_fini(®_paint_in_bound);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
2019-07-25 19:00:51 -04:00
|
|
|
pixman_region32_fini(®_paint);
|
2021-07-09 13:17:23 -04:00
|
|
|
pixman_region32_fini(®_shadow_clip);
|
2019-03-05 15:27:01 -05:00
|
|
|
|
2019-03-07 16:08:37 -05:00
|
|
|
if (ps->o.monitor_repaint) {
|
2021-07-09 14:57:51 -04:00
|
|
|
const struct color DEBUG_COLOR = {0.5, 0, 0, 0.5};
|
2019-07-25 19:00:51 -04:00
|
|
|
auto reg_damage_debug = get_damage(ps, false);
|
2021-07-09 14:57:51 -04:00
|
|
|
ps->backend_data->ops->fill(ps->backend_data, DEBUG_COLOR, ®_damage_debug);
|
2019-07-25 19:00:51 -04:00
|
|
|
pixman_region32_fini(®_damage_debug);
|
2019-03-05 15:27:04 -05:00
|
|
|
}
|
|
|
|
|
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-07-25 19:00:51 -04:00
|
|
|
ps->backend_data->ops->present(ps->backend_data, ®_damage);
|
2019-01-02 15:34:07 -05:00
|
|
|
}
|
|
|
|
|
2019-07-25 19:00:51 -04:00
|
|
|
pixman_region32_fini(®_damage);
|
|
|
|
|
2019-01-02 15:34:07 -05:00
|
|
|
#ifdef DEBUG_REPAINT
|
|
|
|
struct timespec now = get_time_timespec();
|
|
|
|
struct timespec diff = {0};
|
|
|
|
timespec_subtract(&diff, &now, &last_paint);
|
2019-05-05 20:24:38 -04:00
|
|
|
log_trace("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec);
|
2019-01-02 15:34:07 -05:00
|
|
|
last_paint = now;
|
2019-05-05 20:24:38 -04:00
|
|
|
log_trace("paint:");
|
2019-01-02 15:34:07 -05:00
|
|
|
for (win *w = t; w; w = w->prev_trans)
|
2019-05-05 20:24:38 -04:00
|
|
|
log_trace(" %#010lx", w->id);
|
2019-01-02 15:34:07 -05:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// vim: set noet sw=8 ts=8 :
|