1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2024-10-27 05:24:17 -04:00

win: use the new transition mechanism for fading

Fading behavior is changed slightly when a window's opacity is changed
while the window is already fading in/out.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-03-19 16:44:02 +00:00
parent 5a5716a7b1
commit 35b85155c5
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
6 changed files with 110 additions and 146 deletions

View file

@ -10,6 +10,7 @@
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "region.h" #include "region.h"
#include "transition.h"
#include "types.h" #include "types.h"
#include "win.h" #include "win.h"
#include "x.h" #include "x.h"
@ -253,45 +254,20 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
auto real_win_mode = w->mode; auto real_win_mode = w->mode;
coord_t window_coord = {.x = w->g.x, .y = w->g.y}; coord_t window_coord = {.x = w->g.x, .y = w->g.y};
const double window_opacity = animatable_get(&w->opacity);
if (w->blur_background && if (w->blur_background &&
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS || (ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
(ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) { (ps->o.blur_background_frame && real_win_mode == WMODE_FRAME_TRANS))) {
// Minimize the region we try to blur, if the window // Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is. // itself is not opaque, only the frame is.
double blur_opacity = 1; const double blur_opacity = animatable_get(&w->blur_opacity);
if (w->opacity < (1.0 / MAX_ALPHA)) {
// Hide blur for fully transparent windows.
blur_opacity = 0;
} else 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;
}
}
assert(blur_opacity >= 0 && blur_opacity <= 1); assert(blur_opacity >= 0 && blur_opacity <= 1);
if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) { if (blur_opacity * MAX_ALPHA < 1) {
// We don't need to blur if it would be completely
// transparent
} else if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
// We need to blur the bounding shape of the window // We need to blur the bounding shape of the window
// (reg_paint_in_bound = reg_bound \cap reg_paint) // (reg_paint_in_bound = reg_bound \cap reg_paint)
ps->backend_data->ops->blur( ps->backend_data->ops->blur(
@ -404,7 +380,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
if (w->dim) { if (w->dim) {
dim_opacity = ps->o.inactive_dim; dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed) { if (!ps->o.inactive_dim_fixed) {
dim_opacity *= w->opacity; dim_opacity *= window_opacity;
} }
} }
@ -418,7 +394,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image, ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, w->win_image,
&dim_opacity); &dim_opacity);
ps->backend_data->ops->set_image_property( ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image, &w->opacity); ps->backend_data, IMAGE_PROPERTY_OPACITY, w->win_image,
&window_opacity);
ps->backend_data->ops->set_image_property( ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image, ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS, w->win_image,
(double[]){w->corner_radius}); (double[]){w->corner_radius});
@ -440,7 +417,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL); w->fg_shader ? (void *)w->fg_shader->backend_shader : NULL);
} }
if (w->opacity * MAX_ALPHA < 1) { if (window_opacity * MAX_ALPHA < 1) {
// We don't need to paint the window body itself if it's // We don't need to paint the window body itself if it's
// completely transparent. // completely transparent.
goto skip; goto skip;

View file

@ -24,6 +24,7 @@
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
#include "string_utils.h" #include "string_utils.h"
#include "transition.h"
#include "types.h" #include "types.h"
#include "uthash_extra.h" #include "uthash_extra.h"
#include "utils.h" #include "utils.h"
@ -991,14 +992,17 @@ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_m_win_get_do(class_general, cdbus_reply_string); cdbus_m_win_get_do(class_general, cdbus_reply_string);
cdbus_m_win_get_do(role, cdbus_reply_string); cdbus_m_win_get_do(role, cdbus_reply_string);
cdbus_m_win_get_do(opacity, cdbus_reply_double); cdbus_m_win_get_do(opacity.target, cdbus_reply_double);
cdbus_m_win_get_do(opacity_target, cdbus_reply_double);
cdbus_m_win_get_do(has_opacity_prop, cdbus_reply_bool); cdbus_m_win_get_do(has_opacity_prop, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32);
cdbus_m_win_get_do(opacity_is_set, cdbus_reply_bool); cdbus_m_win_get_do(opacity_is_set, cdbus_reply_bool);
cdbus_m_win_get_do(opacity_set, cdbus_reply_double); cdbus_m_win_get_do(opacity_set, cdbus_reply_double);
cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); cdbus_m_win_get_do(frame_opacity, cdbus_reply_double);
if (strcmp(target, "opacity") == 0) {
cdbus_reply_double(ps, msg, animatable_get(&w->opacity));
return true;
}
if (!strcmp("left_width", target)) { if (!strcmp("left_width", target)) {
cdbus_reply_int32(ps, msg, w->frame_extents.left); cdbus_reply_int32(ps, msg, w->frame_extents.left);
return true; return true;

View file

@ -48,6 +48,7 @@
#include "inspect.h" #include "inspect.h"
#include "kernel.h" #include "kernel.h"
#include "picom.h" #include "picom.h"
#include "transition.h"
#include "win_defs.h" #include "win_defs.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "opengl.h" #include "opengl.h"
@ -495,22 +496,25 @@ static double fade_timeout(session_t *ps) {
* @param steps steps of fading * @param steps steps of fading
* @return whether we are still in fading mode * @return whether we are still in fading mode
*/ */
static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) { static bool run_fade(session_t *ps, struct managed_win **_w, unsigned int steps) {
auto w = *_w; auto w = *_w;
log_trace("Process fading for window %s (%#010x), steps: %lld", w->name, log_trace("Process fading for window %s (%#010x), steps: %u", w->name, w->base.id,
w->base.id, steps); steps);
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
// We are not fading // We are not fading
assert(w->opacity_target == w->opacity); assert(!animatable_is_animating(&w->opacity));
assert(!animatable_is_animating(&w->blur_opacity));
log_trace("|- not fading"); log_trace("|- not fading");
return false; return false;
} }
if (!win_should_fade(ps, w)) { if (!win_should_fade(ps, w)) {
log_trace("|- in transition but doesn't need fading"); log_trace("|- in transition but doesn't need fading");
w->opacity = w->opacity_target; animatable_early_stop(&w->opacity);
animatable_early_stop(&w->blur_opacity);
} }
if (w->opacity == w->opacity_target) { if (!animatable_is_animating(&w->opacity) &&
!animatable_is_animating(&w->blur_opacity)) {
// We have reached target opacity. // We have reached target opacity.
// We don't call win_check_fade_finished here because that could destroy // We don't call win_check_fade_finished here because that could destroy
// the window, but we still need the damage info from this window // the window, but we still need the damage info from this window
@ -518,20 +522,14 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) {
return false; return false;
} }
log_trace("|- fading, opacity: %lf", w->opacity); log_trace("|- fading, opacity: %lf", animatable_get(&w->opacity));
if (steps) { animatable_step(&w->opacity, steps);
if (w->opacity < w->opacity_target) { animatable_step(&w->blur_opacity, steps);
w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps, log_trace("|- opacity updated: %lf (%u steps)", animatable_get(&w->opacity), steps);
0.0, w->opacity_target);
} else {
w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps,
w->opacity_target, 1);
}
log_trace("|- opacity updated: %lf", w->opacity);
}
// Note even if opacity == opacity_target here, we still want to run preprocess // Note even if the animatable is not animating anymore at this point, we still
// one last time to finish state transition. So return true in that case too. // want to run preprocess one last time to finish state transition. So return true
// in that case too.
return true; return true;
} }
@ -918,17 +916,19 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
*out_bottom = NULL; *out_bottom = NULL;
// Fading step calculation // Fading step calculation
long long steps = 0L; unsigned int steps = 0L;
auto now = get_time_ms(); auto now = get_time_ms();
if (ps->fade_time) { if (ps->fade_time) {
assert(now >= ps->fade_time); assert(now >= ps->fade_time);
steps = (now - ps->fade_time) / ps->o.fade_delta; auto raw_steps = (now - ps->fade_time) / ps->o.fade_delta;
assert(raw_steps <= UINT_MAX);
steps = (unsigned int)raw_steps;
ps->fade_time += raw_steps * ps->o.fade_delta;
} else { } else {
// Reset fade_time if unset // Reset fade_time if unset
ps->fade_time = get_time_ms(); ps->fade_time = get_time_ms();
steps = 0L; steps = 0L;
} }
ps->fade_time += steps * ps->o.fade_delta;
// First, let's process fading, and animated shaders // First, let's process fading, and animated shaders
// TODO(yshui) check if a window is fully obscured, and if we don't need to // TODO(yshui) check if a window is fully obscured, and if we don't need to
@ -936,7 +936,6 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
win_stack_foreach_managed_safe(w, &ps->window_stack) { win_stack_foreach_managed_safe(w, &ps->window_stack) {
const winmode_t mode_old = w->mode; const winmode_t mode_old = w->mode;
const bool was_painted = w->to_paint; const bool was_painted = w->to_paint;
const double opacity_old = w->opacity;
if (win_should_dim(ps, w) != w->dim) { if (win_should_dim(ps, w) != w->dim) {
w->dim = win_should_dim(ps, w); w->dim = win_should_dim(ps, w);
@ -948,20 +947,20 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
*animation = true; *animation = true;
} }
// Add window to damaged area if its opacity changes
// If was_painted == false, and to_paint is also false, we don't care
// If was_painted == false, but to_paint is true, damage will be added in
// the loop below
if (was_painted && animatable_is_animating(&w->opacity)) {
add_damage_from_win(ps, w);
}
// Run fading // Run fading
if (run_fade(ps, &w, steps)) { if (run_fade(ps, &w, steps)) {
*fade_running = true; *fade_running = true;
} }
// Add window to damaged area if its opacity changes if (win_maybe_finalize_fading(ps, w)) {
// If was_painted == false, and to_paint is also false, we don't care
// If was_painted == false, but to_paint is true, damage will be added in
// the loop below
if (was_painted && w->opacity != opacity_old) {
add_damage_from_win(ps, w);
}
if (win_check_fade_finished(ps, w)) {
// the window has been destroyed because fading finished // the window has been destroyed because fading finished
continue; continue;
} }
@ -994,6 +993,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
bool to_paint = true; bool to_paint = true;
// w->to_paint remembers whether this window is painted last time // w->to_paint remembers whether this window is painted last time
const bool was_painted = w->to_paint; const bool was_painted = w->to_paint;
const double window_opacity = animatable_get(&w->opacity);
// Destroy reg_ignore if some window above us invalidated it // Destroy reg_ignore if some window above us invalidated it
if (!reg_ignore_valid) { if (!reg_ignore_valid) {
@ -1027,7 +1027,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
w->g.x >= ps->root_width || w->g.y >= ps->root_height)) { w->g.x >= ps->root_width || w->g.y >= ps->root_height)) {
log_trace("|- is positioned outside of the screen"); log_trace("|- is positioned outside of the screen");
to_paint = false; to_paint = false;
} else if (unlikely((double)w->opacity * MAX_ALPHA < 1 && !w->blur_background)) { } else if (unlikely(window_opacity * MAX_ALPHA < 1 && !w->blur_background)) {
/* TODO(yshui) for consistency, even a window has 0 opacity, we /* TODO(yshui) for consistency, even a window has 0 opacity, we
* still probably need to blur its background, so to_paint * still probably need to blur its background, so to_paint
* shouldn't be false for them. */ * shouldn't be false for them. */
@ -1060,7 +1060,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
log_verbose("Window %#010x (%s) will be painted", w->base.id, w->name); log_verbose("Window %#010x (%s) will be painted", w->base.id, w->name);
// Calculate shadow opacity // Calculate shadow opacity
w->shadow_opacity = ps->o.shadow_opacity * w->opacity * ps->o.frame_opacity; w->shadow_opacity = ps->o.shadow_opacity * window_opacity * ps->o.frame_opacity;
// Generate ignore region for painting to reduce GPU load // Generate ignore region for painting to reduce GPU load
if (!w->reg_ignore) { if (!w->reg_ignore) {

View file

@ -12,6 +12,7 @@
#include "common.h" #include "common.h"
#include "options.h" #include "options.h"
#include "transition.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
#include "backend/gl/glx.h" #include "backend/gl/glx.h"
@ -436,6 +437,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
const int y = w->g.y; const int y = w->g.y;
const uint16_t wid = to_u16_checked(w->widthb); const uint16_t wid = to_u16_checked(w->widthb);
const uint16_t hei = to_u16_checked(w->heightb); const uint16_t hei = to_u16_checked(w->heightb);
const double window_opacity = animatable_get(&w->opacity);
xcb_render_picture_t pict = w->paint.pict; xcb_render_picture_t pict = w->paint.pict;
@ -472,7 +474,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
} }
if (w->frame_opacity == 1) { if (w->frame_opacity == 1) {
paint_region(ps, w, 0, 0, wid, hei, w->opacity, reg_paint, pict); paint_region(ps, w, 0, 0, wid, hei, window_opacity, reg_paint, pict);
} else { } else {
// Painting parameters // Painting parameters
const margin_t extents = win_calc_frame_extents(w); const margin_t extents = win_calc_frame_extents(w);
@ -482,8 +484,8 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
auto const r = extents.right; auto const r = extents.right;
#define COMP_BDR(cx, cy, cwid, chei) \ #define COMP_BDR(cx, cy, cwid, chei) \
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \ paint_region(ps, w, (cx), (cy), (cwid), (chei), \
reg_paint, pict) w->frame_opacity *window_opacity, reg_paint, pict)
// Sanitize the margins, in case some broken WM makes // Sanitize the margins, in case some broken WM makes
// top_width + bottom_width > height in some cases. // top_width + bottom_width > height in some cases.
@ -542,7 +544,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
// body // body
paint_region(ps, w, cleft, ctop, body_width, body_height, paint_region(ps, w, cleft, ctop, body_width, body_height,
w->opacity, reg_paint, pict); window_opacity, reg_paint, pict);
} while (0); } while (0);
} }
@ -557,7 +559,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
if (w->dim) { if (w->dim) {
double dim_opacity = ps->o.inactive_dim; double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed) { if (!ps->o.inactive_dim_fixed) {
dim_opacity *= w->opacity; dim_opacity *= window_opacity;
} }
switch (ps->o.backend) { switch (ps->o.backend) {
@ -899,12 +901,13 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
auto const wid = to_u16_checked(w->widthb); auto const wid = to_u16_checked(w->widthb);
auto const hei = to_u16_checked(w->heightb); auto const hei = to_u16_checked(w->heightb);
const int cr = w ? w->corner_radius : 0; const int cr = w ? w->corner_radius : 0;
const double window_opacity = animatable_get(&w->opacity);
double factor_center = 1.0; double factor_center = 1.0;
// Adjust blur strength according to window opacity, to make it appear // Adjust blur strength according to window opacity, to make it appear
// better during fading // better during fading
if (!ps->o.blur_background_fixed) { if (!ps->o.blur_background_fixed) {
double pct = 1.0 - w->opacity * (1.0 - 1.0 / 9.0); double pct = 1.0 - window_opacity * (1.0 - 1.0 / 9.0);
factor_center = pct * 8.0 / (1.1 - pct); factor_center = pct * 8.0 / (1.1 - pct);
} }
@ -1143,7 +1146,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
} }
// Only clip shadows above visible windows // Only clip shadows above visible windows
if (w->opacity * MAX_ALPHA >= 1) { if (animatable_get(&w->opacity) * MAX_ALPHA >= 1) {
if (w->clip_shadow_above) { if (w->clip_shadow_above) {
// Add window bounds to shadow-clip region // Add window bounds to shadow-clip region
pixman_region32_union(&reg_shadow_clip, &reg_shadow_clip, pixman_region32_union(&reg_shadow_clip, &reg_shadow_clip,

View file

@ -27,6 +27,7 @@
#include "region.h" #include "region.h"
#include "render.h" #include "render.h"
#include "string_utils.h" #include "string_utils.h"
#include "transition.h"
#include "types.h" #include "types.h"
#include "uthash_extra.h" #include "uthash_extra.h"
#include "utils.h" #include "utils.h"
@ -799,7 +800,7 @@ bool win_client_has_alpha(const struct managed_win *w) {
} }
winmode_t win_calc_mode(const struct managed_win *w) { winmode_t win_calc_mode(const struct managed_win *w) {
if (w->opacity < 1.0) { if (animatable_get(&w->opacity) < 1.0) {
return WMODE_TRANS; return WMODE_TRANS;
} }
@ -846,7 +847,7 @@ winmode_t win_calc_mode(const struct managed_win *w) {
* *
* @return target opacity * @return target opacity
*/ */
double win_calc_opacity_target(session_t *ps, const struct managed_win *w) { static double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
double opacity = 1; double opacity = 1;
if (w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_UNMAPPED) {
@ -882,6 +883,21 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
return opacity; return opacity;
} }
/// Call `animatable_set_target` on the opacity of a window, with appropriate
/// target opacity and duration.
///
/// @return duration of the fade
static inline unsigned int win_start_fade(session_t *ps, struct managed_win *w) {
double current_opacity = animatable_get(&w->opacity),
target_opacity = win_calc_opacity_target(ps, w);
double step_size =
target_opacity > current_opacity ? ps->o.fade_in_step : ps->o.fade_out_step;
unsigned int duration =
(unsigned int)(fabs(target_opacity - current_opacity) / step_size);
animatable_set_target(&w->opacity, target_opacity, duration);
return duration;
}
/** /**
* Determine whether a window is to be dimmed. * Determine whether a window is to be dimmed.
*/ */
@ -1605,8 +1621,6 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi
.cache_leader = XCB_NONE, .cache_leader = XCB_NONE,
.window_type = WINTYPE_UNKNOWN, .window_type = WINTYPE_UNKNOWN,
.focused = false, .focused = false,
.opacity = 0,
.opacity_target = 0,
.has_opacity_prop = false, .has_opacity_prop = false,
.opacity_prop = OPAQUE, .opacity_prop = OPAQUE,
.opacity_is_set = false, .opacity_is_set = false,
@ -1685,6 +1699,8 @@ struct win *attr_ret_nonnull maybe_allocate_managed_win(session_t *ps, struct wi
new->base = *w; new->base = *w;
new->base.managed = true; new->base.managed = true;
new->a = *a; new->a = *a;
new->opacity = animatable_new(0, linear_interpolator, NULL);
new->blur_opacity = animatable_new(0, linear_interpolator, NULL);
pixman_region32_init(&new->bounding_shape); pixman_region32_init(&new->bounding_shape);
free(a); free(a);
@ -2386,8 +2402,8 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
w->a.map_state = XCB_MAP_STATE_UNMAPPED; w->a.map_state = XCB_MAP_STATE_UNMAPPED;
w->state = WSTATE_UNMAPPING; w->state = WSTATE_UNMAPPING;
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old); auto duration = win_start_fade(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w); animatable_set_target(&w->blur_opacity, 0, duration);
#ifdef CONFIG_DBUS #ifdef CONFIG_DBUS
// Send D-Bus signal // Send D-Bus signal
@ -2410,13 +2426,13 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
* *
* @return whether the window is destroyed and freed * @return whether the window is destroyed and freed
*/ */
bool win_check_fade_finished(session_t *ps, struct managed_win *w) { bool win_maybe_finalize_fading(session_t *ps, struct managed_win *w) {
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
// No fading in progress // No fading in progress
assert(w->opacity_target == w->opacity); assert(!animatable_is_animating(&w->opacity));
return false; return false;
} }
if (w->opacity == w->opacity_target) { if (!animatable_is_animating(&w->opacity)) {
switch (w->state) { switch (w->state) {
case WSTATE_UNMAPPING: unmap_win_finish(ps, w); return false; case WSTATE_UNMAPPING: unmap_win_finish(ps, w); return false;
case WSTATE_DESTROYING: destroy_win_finish(ps, &w->base); return true; case WSTATE_DESTROYING: destroy_win_finish(ps, &w->base); return true;
@ -2435,12 +2451,13 @@ bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
/// @return whether the window is destroyed and freed /// @return whether the window is destroyed and freed
bool win_skip_fading(session_t *ps, struct managed_win *w) { bool win_skip_fading(session_t *ps, struct managed_win *w) {
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
assert(w->opacity_target == w->opacity); assert(!animatable_is_animating(&w->opacity));
return false; return false;
} }
log_debug("Skipping fading process of window %#010x (%s)", w->base.id, w->name); log_debug("Skipping fading process of window %#010x (%s)", w->base.id, w->name);
w->opacity = w->opacity_target; animatable_early_stop(&w->opacity);
return win_check_fade_finished(ps, w); animatable_early_stop(&w->blur_opacity);
return win_maybe_finalize_fading(ps, w);
} }
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to // TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
@ -2514,11 +2531,11 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// XXX We need to make sure that win_data is available // XXX We need to make sure that win_data is available
// iff `state` is MAPPED // iff `state` is MAPPED
w->state = WSTATE_MAPPING; w->state = WSTATE_MAPPING;
w->opacity_target_old = 0; auto duration = win_start_fade(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w); animatable_set_target(&w->blur_opacity, 1, duration);
log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id, log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
w->opacity, w->opacity_target); animatable_get(&w->opacity), w->opacity.target);
// Cannot set w->ever_damaged = false here, since window mapping could be // Cannot set w->ever_damaged = false here, since window mapping could be
// delayed, so a damage event might have already arrived before this // delayed, so a damage event might have already arrived before this
@ -2546,49 +2563,25 @@ void map_win_start(session_t *ps, struct managed_win *w) {
* Update target window opacity depending on the current state. * Update target window opacity depending on the current state.
*/ */
void win_update_opacity_target(session_t *ps, struct managed_win *w) { void win_update_opacity_target(session_t *ps, struct managed_win *w) {
auto opacity_target_old = w->opacity_target; auto duration = win_start_fade(ps, w);
w->opacity_target = win_calc_opacity_target(ps, w);
if (opacity_target_old == w->opacity_target) { if (!animatable_is_animating(&w->opacity)) {
return; return;
} }
log_debug("Window %#010x (%s) opacity %f, opacity target %f, start %f", w->base.id,
w->name, animatable_get(&w->opacity), w->opacity.target, w->opacity.start);
if (w->state == WSTATE_MAPPED) { if (w->state == WSTATE_MAPPED) {
// Opacity target changed while MAPPED. Transition to FADING. // 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; 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) { } else if (w->state == WSTATE_MAPPING) {
// Opacity target changed while fading in. // Opacity target changed while fading in, keep the blur_opacity
if (w->opacity >= w->opacity_target) { // in lock step with the opacity
// Already reached new target opacity. Transition to animatable_set_target(&w->blur_opacity, w->blur_opacity.target, duration);
// FADING. log_debug("Opacity changed while fading in");
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) { } else if (w->state == WSTATE_FADING) {
// Opacity target changed while FADING. // Opacity target changed while FADING.
if ((w->opacity < opacity_target_old && w->opacity > w->opacity_target) || log_debug("Opacity changed while already fading");
(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) { if (!ps->redirected) {

View file

@ -16,6 +16,7 @@
#include "list.h" #include "list.h"
#include "region.h" #include "region.h"
#include "render.h" #include "render.h"
#include "transition.h"
#include "types.h" #include "types.h"
#include "utils.h" #include "utils.h"
#include "win_defs.h" #include "win_defs.h"
@ -204,12 +205,12 @@ struct managed_win {
bool is_ewmh_focused; bool is_ewmh_focused;
// Opacity-related members // Opacity-related members
/// Current window opacity. /// Window opacity
double opacity; struct animatable opacity;
/// Target window opacity. /// Opacity of the window's background blur
double opacity_target; /// Used to gracefully fade in/out the window, otherwise the blur
/// Previous window opacity. /// would be at full/zero intensity immediately which will be jarring.
double opacity_target_old; struct animatable blur_opacity;
/// true if window (or client window, for broken window managers /// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_WINDOW_OPACITY value) has opacity /// not transferring client window's _NET_WM_WINDOW_OPACITY value) has opacity
/// prop /// prop
@ -326,20 +327,6 @@ bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
void win_on_factor_change(session_t *ps, struct managed_win *w); void win_on_factor_change(session_t *ps, struct managed_win *w);
void win_unmark_client(session_t *ps, struct managed_win *w); void win_unmark_client(session_t *ps, struct managed_win *w);
/**
* Calculate and return the opacity target of a window.
*
* The priority of opacity settings are:
*
* inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
* opacity-rules (if matched) > window type default opacity > active/inactive opacity
*
* @param ps current session
* @param w struct _win object representing the window
*
* @return target opacity
*/
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w);
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w); bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw); void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
@ -396,7 +383,7 @@ void restack_top(session_t *ps, struct win *w);
/** /**
* Execute fade callback of a window if fading finished. * Execute fade callback of a window if fading finished.
*/ */
bool must_use win_check_fade_finished(session_t *ps, struct managed_win *w); bool must_use win_maybe_finalize_fading(session_t *ps, struct managed_win *w);
// Stop receiving events (except ConfigureNotify, XXX why?) from a window // Stop receiving events (except ConfigureNotify, XXX why?) from a window
void win_ev_stop(session_t *ps, const struct win *w); void win_ev_stop(session_t *ps, const struct win *w);