new backend: fix background blur of window with opacity set

Only render blur with transparency when window is fading in/out.
Otherwise, a window with a set opacity will always have a completely
blurred background.

Fixes #198

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-07-26 23:40:30 +01:00
parent 79fc36b3e3
commit a229f34eaa
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
6 changed files with 77 additions and 52 deletions

View File

@ -207,12 +207,25 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
(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.
// TODO resize blur region to fix black line artifact
double blur_opacity = 1;
if (w->state == WSTATE_MAPPING) {
// Gradually increase the blur intensity during
// fading in.
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);
}
if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) { 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(
ps->backend_data, w->opacity, ps->backend_blur_context, ps->backend_data, blur_opacity, ps->backend_blur_context,
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
} else { } else {
// Window itself is solid, we only need to blur the frame // Window itself is solid, we only need to blur the frame
@ -226,7 +239,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
pixman_region32_translate(&reg_blur, w->g.x, w->g.y); pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
// make sure reg_blur \in reg_paint // make sure reg_blur \in reg_paint
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint); pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint);
ps->backend_data->ops->blur(ps->backend_data, w->opacity, ps->backend_data->ops->blur(ps->backend_data, blur_opacity,
ps->backend_blur_context, ps->backend_blur_context,
&reg_blur, &reg_visible); &reg_blur, &reg_visible);
pixman_region32_fini(&reg_blur); pixman_region32_fini(&reg_blur);

View File

@ -232,15 +232,15 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long steps) {
auto w = *_w; auto w = *_w;
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_tgt == w->opacity); assert(w->opacity_target == w->opacity);
return false; return false;
} }
if (!win_should_fade(ps, w)) { if (!win_should_fade(ps, w)) {
log_debug("Window %#010x %s doesn't need fading", w->base.id, w->name); log_debug("Window %#010x %s doesn't need fading", w->base.id, w->name);
w->opacity = w->opacity_tgt; w->opacity = w->opacity_target;
} }
if (w->opacity == w->opacity_tgt) { if (w->opacity == w->opacity_target) {
// 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
@ -249,16 +249,16 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long steps) {
} }
if (steps) { if (steps) {
if (w->opacity < w->opacity_tgt) { if (w->opacity < w->opacity_target) {
w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps, w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps,
0.0, w->opacity_tgt); 0.0, w->opacity_target);
} else { } else {
w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps, w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps,
w->opacity_tgt, 1); w->opacity_target, 1);
} }
} }
// Note even if opacity == opacity_tgt here, we still want to run preprocess one // Note even if opacity == opacity_target here, we still want to run preprocess one
// last time to finish state transition. So return true in that case too. // last time to finish state transition. So return true in that case too.
return true; return true;
} }

View File

@ -800,7 +800,7 @@ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
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, cdbus_reply_double);
cdbus_m_win_get_do(opacity_tgt, 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);

View File

@ -455,7 +455,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// See the winstate_t transition table // See the winstate_t transition table
w->state = WSTATE_FADING; w->state = WSTATE_FADING;
} }
w->opacity_tgt = win_calc_opacity_target(ps, w); w->opacity_target = win_calc_opacity_target(ps, w, false);
} }
} }

View File

@ -443,31 +443,25 @@ winmode_t win_calc_mode(const struct managed_win *w) {
/** /**
* Calculate and return the opacity target of a window. * Calculate and return the opacity target of a window.
* *
* If window is inactive and inactive_opacity_override is set, the * The priority of opacity settings are:
* priority is: (Simulates the old behavior)
* *
* inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) * inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
* > window type default opacity * opacity-rules (if matched) > window type default opacity > active/inactive opacity
*
* Otherwise:
*
* _NET_WM_WINDOW_OPACITY (if not opaque)
* > window type default opacity (if not opaque)
* > inactive_opacity
* *
* @param ps current session * @param ps current session
* @param w struct _win object representing the window * @param w struct _win object representing the window
* @param ignore_state whether window state should be ignored in opacity calculation
* *
* @return target opacity * @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; double opacity = 1;
if (w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_UNMAPPED && !ignore_state) {
// be consistent // be consistent
return 0; return 0;
} }
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) { if ((w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) && !ignore_state) {
return 0; return 0;
} }
// Try obeying opacity property and window type opacity firstly // Try obeying opacity property and window type opacity firstly
@ -487,8 +481,9 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
} }
// respect inactive override // respect inactive override
if (ps->o.inactive_opacity_override && !w->focused) if (ps->o.inactive_opacity_override && !w->focused) {
opacity = ps->o.inactive_opacity; opacity = ps->o.inactive_opacity;
}
return opacity; return opacity;
} }
@ -705,7 +700,8 @@ void win_determine_invert_color(session_t *ps, struct managed_win *w) {
win_set_invert_color(ps, w, invert_color_new); win_set_invert_color(ps, w, invert_color_new);
} }
static void win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) { static void
win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) {
if (w->blur_background == blur_background_new) if (w->blur_background == blur_background_new)
return; return;
@ -1069,7 +1065,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
.wmwin = false, .wmwin = false,
.focused = false, .focused = false,
.opacity = 0, .opacity = 0,
.opacity_tgt = 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,
@ -1201,12 +1197,10 @@ void win_update_focused(session_t *ps, struct managed_win *w) {
// Always recalculate the window target opacity, since some opacity-related // Always recalculate the window target opacity, since some opacity-related
// options depend on the output value of win_is_focused_real() instead of // options depend on the output value of win_is_focused_real() instead of
// w->focused // w->focused
double opacity_tgt_old = w->opacity_tgt; auto opacity_target_old = w->opacity_target;
w->opacity_tgt = win_calc_opacity_target(ps, w); w->opacity_target = win_calc_opacity_target(ps, w, false);
if (opacity_tgt_old != w->opacity_tgt && w->state == WSTATE_MAPPED) { if (opacity_target_old != w->opacity_target && w->state == WSTATE_MAPPED) {
// Only MAPPED can transition to FADING // Only MAPPED can transition to FADING
assert(w->state != WSTATE_DESTROYING && w->state != WSTATE_UNMAPPING &&
w->state != WSTATE_UNMAPPED);
w->state = WSTATE_FADING; w->state = WSTATE_FADING;
} }
} }
@ -1796,7 +1790,7 @@ void unmap_win(session_t *ps, struct managed_win **_w, bool destroy) {
w->a.map_state = XCB_MAP_STATE_UNMAPPED; w->a.map_state = XCB_MAP_STATE_UNMAPPED;
w->state = target_state; w->state = target_state;
w->opacity_tgt = win_calc_opacity_target(ps, w); w->opacity_target = win_calc_opacity_target(ps, w, false);
w->in_openclose = destroy; w->in_openclose = destroy;
@ -1828,10 +1822,10 @@ void win_check_fade_finished(session_t *ps, struct managed_win **_w) {
auto w = *_w; auto w = *_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_tgt == w->opacity); assert(w->opacity_target == w->opacity);
return; return;
} }
if (w->opacity == w->opacity_tgt) { if (w->opacity == w->opacity_target) {
switch (w->state) { switch (w->state) {
case WSTATE_UNMAPPING: return finish_unmap_win(ps, _w); case WSTATE_UNMAPPING: return finish_unmap_win(ps, _w);
case WSTATE_DESTROYING: return finish_destroy_win(ps, _w); case WSTATE_DESTROYING: return finish_destroy_win(ps, _w);
@ -1847,11 +1841,11 @@ void win_check_fade_finished(session_t *ps, struct managed_win **_w) {
void win_skip_fading(session_t *ps, struct managed_win **_w) { void win_skip_fading(session_t *ps, struct managed_win **_w) {
auto w = *_w; auto w = *_w;
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
assert(w->opacity_tgt == w->opacity); assert(w->opacity_target == w->opacity);
return; return;
} }
log_trace("Skipping fading process of window %#010x (%s)", w->base.id, w->name); log_trace("Skipping fading process of window %#010x (%s)", w->base.id, w->name);
w->opacity = w->opacity_tgt; w->opacity = w->opacity_target;
win_check_fade_finished(ps, _w); win_check_fade_finished(ps, _w);
} }
@ -1973,10 +1967,10 @@ void map_win(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_tgt = 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, log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
w->opacity, w->opacity_tgt); w->opacity, w->opacity_target);
win_determine_blur_background(ps, w); win_determine_blur_background(ps, w);
@ -2136,8 +2130,10 @@ static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid
/** /**
* Check if a window is fulscreen using EWMH * Check if a window is fulscreen using EWMH
*/ */
static inline bool win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) { static inline bool
xcb_get_property_cookie_t prop = xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12); win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
xcb_get_property_cookie_t prop =
xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL); xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
if (!reply) if (!reply)
return false; return false;

View File

@ -263,7 +263,7 @@ struct managed_win {
/// Current window opacity. /// Current window opacity.
double opacity; double opacity;
/// Target window opacity. /// Target window opacity.
double opacity_tgt; double opacity_target;
/// 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_OPACITY value) has opacity prop /// not transferring client window's _NET_WM_OPACITY value) has opacity prop
bool has_opacity_prop; bool has_opacity_prop;
@ -360,7 +360,23 @@ void win_unmark_client(session_t *ps, struct managed_win *w);
void win_recheck_client(session_t *ps, struct managed_win *w); void win_recheck_client(session_t *ps, struct managed_win *w);
xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions); xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions);
bool win_get_class(session_t *ps, struct managed_win *w); bool win_get_class(session_t *ps, struct managed_win *w);
double attr_pure win_calc_opacity_target(session_t *ps, const 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
* @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,
bool ignore_state);
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_screen(session_t *, struct managed_win *); void win_update_screen(session_t *, struct managed_win *);
/// Prepare window for fading because opacity target changed /// Prepare window for fading because opacity target changed