core: re-run paint_preprocess after redirecting

paint_preprocess calls redir_start if it decides to redirect the screen.
redir_start might change the state of the windows. For example, when the
image of the window fails to bind, redir_start sets the error flag. And
those state changes might exclude the window from being rendered. Thus,
paint_preprocess should be re-run to make sure the new states are
honoured.

Fixes a crash when window images fail to bind after redirecting.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-03-24 04:47:25 +00:00
parent a3f41bdc25
commit f501d35ba9
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
3 changed files with 84 additions and 67 deletions

View File

@ -98,6 +98,7 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
// Whether this is beneficial is to be determined XXX // Whether this is beneficial is to be determined XXX
for (win *w = t; w; w = w->prev_trans) { for (win *w = t; w; w = w->prev_trans) {
pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore); pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
// The bounding shape of the window, in global/target coordinates // The bounding shape of the window, in global/target coordinates
// reminder: bounding shape contains the WM frame // reminder: bounding shape contains the WM frame

View File

@ -24,8 +24,8 @@
#include <xcb/sync.h> #include <xcb/sync.h>
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include <test.h>
#include <ev.h> #include <ev.h>
#include <test.h>
#include "common.h" #include "common.h"
#include "compiler.h" #include "compiler.h"
@ -605,12 +605,13 @@ static win *paint_preprocess(session_t *ps, bool *fade_running) {
rc_region_unref(&last_reg_ignore); rc_region_unref(&last_reg_ignore);
// If possible, unredirect all windows and stop painting // If possible, unredirect all windows and stop painting
if (ps->o.redirected_force != UNSET) if (ps->o.redirected_force != UNSET) {
unredir_possible = !ps->o.redirected_force; unredir_possible = !ps->o.redirected_force;
else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) } else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) {
// If there's no window to paint, and the screen isn't redirected, // If there's no window to paint, and the screen isn't redirected,
// don't redirect it. // don't redirect it.
unredir_possible = true; unredir_possible = true;
}
if (unredir_possible) { if (unredir_possible) {
if (ps->redirected) { if (ps->redirected) {
if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit)
@ -623,8 +624,10 @@ static win *paint_preprocess(session_t *ps, bool *fade_running) {
} }
} else { } else {
ev_timer_stop(ps->loop, &ps->unredir_timer); ev_timer_stop(ps->loop, &ps->unredir_timer);
if (!redir_start(ps)) { if (!ps->redirected) {
return NULL; if (!redir_start(ps)) {
return NULL;
}
} }
} }
@ -1315,46 +1318,45 @@ static bool init_overlay(session_t *ps) {
* @return whether the operation succeeded or not * @return whether the operation succeeded or not
*/ */
static bool redir_start(session_t *ps) { static bool redir_start(session_t *ps) {
if (!ps->redirected) { assert(!ps->redirected);
log_debug("Screen redirected."); log_debug("Redirecting the screen.");
// Map overlay window. Done firstly according to this: // Map overlay window. Done firstly according to this:
// https://bugzilla.gnome.org/show_bug.cgi?id=597014 // https://bugzilla.gnome.org/show_bug.cgi?id=597014
if (ps->overlay) { if (ps->overlay) {
xcb_map_window(ps->c, ps->overlay); xcb_map_window(ps->c, ps->overlay);
}
xcb_composite_redirect_subwindows(ps->c, ps->root,
XCB_COMPOSITE_REDIRECT_MANUAL);
x_sync(ps->c);
if (!initialize_backend(ps)) {
return false;
}
if (ps->o.experimental_backends) {
ps->ndamage = ps->backend_data->ops->max_buffer_age;
} else {
ps->ndamage = maximum_buffer_age(ps);
}
ps->damage_ring = ccalloc(ps->ndamage, region_t);
ps->damage = ps->damage_ring + ps->ndamage - 1;
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_init(&ps->damage_ring[i]);
}
// Must call XSync() here
x_sync(ps->c);
ps->redirected = true;
root_damaged(ps);
// Repaint the whole screen
force_repaint(ps);
} }
xcb_composite_redirect_subwindows(ps->c, ps->root, XCB_COMPOSITE_REDIRECT_MANUAL);
x_sync(ps->c);
if (!initialize_backend(ps)) {
return false;
}
if (ps->o.experimental_backends) {
ps->ndamage = ps->backend_data->ops->max_buffer_age;
} else {
ps->ndamage = maximum_buffer_age(ps);
}
ps->damage_ring = ccalloc(ps->ndamage, region_t);
ps->damage = ps->damage_ring + ps->ndamage - 1;
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_init(&ps->damage_ring[i]);
}
// Must call XSync() here
x_sync(ps->c);
ps->redirected = true;
root_damaged(ps);
// Repaint the whole screen
force_repaint(ps);
log_debug("Screen redirected.");
return true; return true;
} }
@ -1362,30 +1364,29 @@ static bool redir_start(session_t *ps) {
* Unredirect all windows. * Unredirect all windows.
*/ */
static void redir_stop(session_t *ps) { static void redir_stop(session_t *ps) {
if (ps->redirected) { assert(ps->redirected);
log_debug("Screen unredirected."); log_debug("Unredirecting the screen.");
destroy_backend(ps); destroy_backend(ps);
xcb_composite_unredirect_subwindows(ps->c, ps->root, xcb_composite_unredirect_subwindows(ps->c, ps->root, XCB_COMPOSITE_REDIRECT_MANUAL);
XCB_COMPOSITE_REDIRECT_MANUAL); // Unmap overlay window
// Unmap overlay window if (ps->overlay)
if (ps->overlay) xcb_unmap_window(ps->c, ps->overlay);
xcb_unmap_window(ps->c, ps->overlay);
// Free the damage ring // Free the damage ring
for (int i = 0; i < ps->ndamage; ++i) { for (int i = 0; i < ps->ndamage; ++i) {
pixman_region32_fini(&ps->damage_ring[i]); pixman_region32_fini(&ps->damage_ring[i]);
}
ps->ndamage = 0;
free(ps->damage_ring);
ps->damage_ring = ps->damage = NULL;
// Must call XSync() here
x_sync(ps->c);
ps->redirected = false;
} }
ps->ndamage = 0;
free(ps->damage_ring);
ps->damage_ring = ps->damage = NULL;
// Must call XSync() here
x_sync(ps->c);
ps->redirected = false;
log_debug("Screen unredirected.");
} }
// Handle queued events before we go to sleep // Handle queued events before we go to sleep
@ -1443,14 +1444,27 @@ static void _draw_callback(EV_P_ session_t *ps, int revents) {
handle_root_flags(ps); handle_root_flags(ps);
// TODO have a stripped down version of paint_preprocess that is used when screen
// is not redirected. its sole purpose should be to decide whether the screen
// should be redirected.
bool fade_running = false; bool fade_running = false;
bool was_redirected = ps->redirected;
win *t = paint_preprocess(ps, &fade_running); win *t = paint_preprocess(ps, &fade_running);
ps->tmout_unredir_hit = false; ps->tmout_unredir_hit = false;
if (!was_redirected && ps->redirected) {
// paint_preprocess redirected the screen, which might change the state of
// some of the windows (e.g. the window image might fail to bind, and the
// window would be put into an error state). so we rerun paint_preprocess
// here to make sure the rendering decision we make is up-to-date
log_debug("Re-run paint_preprocess");
t = paint_preprocess(ps, &fade_running);
}
// Start/stop fade timer depends on whether window are fading // Start/stop fade timer depends on whether window are fading
if (!fade_running && ev_is_active(&ps->fade_timer)) if (!fade_running && ev_is_active(&ps->fade_timer)) {
ev_timer_stop(ps->loop, &ps->fade_timer); ev_timer_stop(ps->loop, &ps->fade_timer);
else if (fade_running && !ev_is_active(&ps->fade_timer)) { } else if (fade_running && !ev_is_active(&ps->fade_timer)) {
ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0); ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0);
ev_timer_start(ps->loop, &ps->fade_timer); ev_timer_start(ps->loop, &ps->fade_timer);
} }
@ -2144,7 +2158,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
* @param ps session to destroy * @param ps session to destroy
*/ */
static void session_destroy(session_t *ps) { static void session_destroy(session_t *ps) {
redir_stop(ps); if (ps->redirected) {
redir_stop(ps);
}
// Stop listening to events on root window // Stop listening to events on root window
xcb_change_window_attributes(ps->c, ps->root, XCB_CW_EVENT_MASK, xcb_change_window_attributes(ps->c, ps->root, XCB_CW_EVENT_MASK,

View File

@ -196,7 +196,7 @@ _win_bind_image(session_t *ps, win *w, void **win_image, void **shadow_image) {
auto e = xcb_request_check( auto e = xcb_request_check(
ps->c, xcb_composite_name_window_pixmap_checked(ps->c, w->id, pixmap)); ps->c, xcb_composite_name_window_pixmap_checked(ps->c, w->id, pixmap));
if (e) { if (e) {
log_error("Failed to get named pixmap"); log_error("Failed to get named pixmap for window %#010x(%s)", w->id, w->name);
free(e); free(e);
return false; return false;
} }