diff --git a/src/backend/backend.c b/src/backend/backend.c index bded608..910c49c 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -204,16 +204,34 @@ 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->state == WSTATE_MAPPING) { + 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. - blur_opacity = - w->opacity / win_calc_opacity_target(ps, w, true); + 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); diff --git a/src/event.c b/src/event.c index 675261d..c6e1179 100644 --- a/src/event.c +++ b/src/event.c @@ -482,14 +482,7 @@ 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); - 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)); - } + win_update_opacity_target(ps, w); } } diff --git a/src/win.c b/src/win.c index c999bbd..9b89886 100644 --- a/src/win.c +++ b/src/win.c @@ -912,18 +912,7 @@ 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); - 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 - assert(w->opacity == opacity_target_old); - w->state = WSTATE_FADING; - log_debug("Window %#010x (%s) opactiy %f, opacity target %f", w->base.id, - w->name, w->opacity, w->opacity_target); - if (!ps->redirected) { - CHECK(!win_skip_fading(ps, w)); - } - } + win_update_opacity_target(ps, w); w->reg_ignore_valid = false; } @@ -1939,6 +1928,7 @@ 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, false); // Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap @@ -2123,6 +2113,7 @@ 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, false); log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id, @@ -2158,6 +2149,60 @@ 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, false); + + 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. */ diff --git a/src/win.h b/src/win.h index 5ee4891..f5ac8bc 100644 --- a/src/win.h +++ b/src/win.h @@ -192,6 +192,8 @@ 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; @@ -287,6 +289,7 @@ 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. diff --git a/src/win_defs.h b/src/win_defs.h index f458b4b..f8494bb 100644 --- a/src/win_defs.h +++ b/src/win_defs.h @@ -40,8 +40,8 @@ typedef enum { /// | DESTROYING | - | o | - | - | - | - | Fading | /// | | | | | | | |finished | /// +-------------+---------+----------+-------+-------+--------+--------+---------+ -/// | MAPPING | Window | Window | o | - | - | Fading | - | -/// | |unmapped |destroyed | | | |finished| | +/// | MAPPING | Window | Window | o |Opacity| - | Fading | - | +/// | |unmapped |destroyed | |change | |finished| | /// +-------------+---------+----------+-------+-------+--------+--------+---------+ /// | FADING | Window | Window | - | o | - | Fading | - | /// | |unmapped |destroyed | | | |finished| |