From 9f3d3f2fbadb5fee868bb08bb1e0ba542a3a4cb8 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 20 Sep 2019 01:59:25 +0100 Subject: [PATCH] win: add functions for delayed window updates And a window update flag for mapping the window. Also make sure related functions consider the case where the given window has pending updates. Signed-off-by: Yuxuan Shui --- src/compton.c | 22 +++++++++++++++++----- src/event.c | 4 ++-- src/win.c | 51 +++++++++++++++++++++++++++++++++++++++++++++----- src/win.h | 7 +++++++ src/win_defs.h | 5 +++++ 5 files changed, 77 insertions(+), 12 deletions(-) diff --git a/src/compton.c b/src/compton.c index 84ffb80..41dab06 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1284,6 +1284,12 @@ static void handle_new_windows(session_t *ps) { } } +static void refresh_windows(session_t *ps) { + win_stack_foreach_managed_safe(w, &ps->window_stack) { + win_process_updates(ps, w); + } +} + static void refresh_stale_images(session_t *ps) { win_stack_foreach_managed(w, &ps->window_stack) { win_process_flags(ps, w); @@ -1328,6 +1334,9 @@ static void handle_pending_updates(EV_P_ struct session *ps) { recheck_focus(ps); } + // Process window updates + refresh_windows(ps); + // Refresh pixmaps and shadows refresh_stale_images(ps); @@ -1374,11 +1383,14 @@ static void _draw_callback(EV_P_ session_t *ps, int revents attr_unused) { 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"); - bottom = paint_preprocess(ps, &fade_running); + // some of the windows (e.g. the window image might become stale). + // so we rerun _draw_callback to make sure the rendering decision we make + // is up-to-date, and all the new flags got handled. + // + // TODO This is not ideal, we should try to avoid setting window flags in + // paint_preprocess. + log_debug("Re-run _draw_callback"); + return _draw_callback(EV_A_ ps, revents); } // Start/stop fade timer depends on whether window are fading diff --git a/src/event.c b/src/event.c index 9933c6e..a92fcf9 100644 --- a/src/event.c +++ b/src/event.c @@ -546,8 +546,8 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t } static inline void repair_win(session_t *ps, struct managed_win *w) { - if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) - return; + // Only mapped window can receive damages + assert(win_is_mapped_in_x(w)); region_t parts; pixman_region32_init(&parts); diff --git a/src/win.c b/src/win.c index f384ea6..c83992b 100644 --- a/src/win.c +++ b/src/win.c @@ -1869,6 +1869,7 @@ bool destroy_win_start(session_t *ps, struct win *w) { } void unmap_win_start(session_t *ps, struct managed_win *w) { + auto internal_w = (struct managed_win_internal *)w; assert(w); assert(w->base.managed); assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY); @@ -1881,10 +1882,14 @@ void unmap_win_start(session_t *ps, struct managed_win *w) { } if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) { - log_warn("Trying to unmapping an already unmapped window %#010x " - "\"%s\"", - w->base.id, w->name); - assert(false); + if (internal_w->pending_updates & WIN_UPDATE_MAP) { + internal_w->pending_updates &= ~(unsigned long)WIN_UPDATE_MAP; + } else { + log_warn("Trying to unmapping an already unmapped window %#010x " + "\"%s\"", + w->base.id, w->name); + assert(false); + } return; } @@ -2079,7 +2084,10 @@ void map_win_start(session_t *ps, struct managed_win *w) { win_determine_blur_background(ps, w); - w->ever_damaged = false; + // Cannot set w->ever_damaged = false here, since window mapping could be + // delayed, so a damage event might have already arrived before this function + // is called. But this should be unnecessary in the first place, since + // ever_damaged is set to false in unmap_win_finish anyway. // We stopped listening on ShapeNotify events // when the window is unmapped (XXX we shouldn't), @@ -2236,6 +2244,32 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo return false; } +/// Queue an update on a window. A series of sanity checks are performed +void win_queue_update(struct managed_win *_w, enum win_update update) { + auto w = (struct managed_win_internal *)_w; + assert(popcount(update) == 1); + + if (unlikely(_w->state == WSTATE_DESTROYING)) { + log_error("Updates queued on a destroyed window %#010x (%s)", _w->base.id, + _w->name); + return; + } + + w->pending_updates |= WIN_UPDATE_MAP; +} + +/// Process pending updates on a window. Has to be called in X critical section +void win_process_updates(struct session *ps, struct managed_win *_w) { + assert(ps->server_grabbed); + auto w = (struct managed_win_internal *)_w; + + if (w->pending_updates & WIN_UPDATE_MAP) { + map_win_start(ps, _w); + } + + w->pending_updates = 0; +} + /** * Check if a window is a fullscreen window. * @@ -2267,3 +2301,10 @@ win_stack_find_next_managed(const session_t *ps, const struct list_node *i) { } return NULL; } + +/// Return whether this window is mapped on the X server side +bool win_is_mapped_in_x(const struct managed_win *w) { + auto iw = (const struct managed_win_internal *)w; + return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING || + w->state == WSTATE_MAPPED || (iw->pending_updates & WIN_UPDATE_MAP); +} diff --git a/src/win.h b/src/win.h index 5c6d851..e8dbd66 100644 --- a/src/win.h +++ b/src/win.h @@ -250,8 +250,12 @@ struct managed_win { #endif }; +/// Process pending updates on a window. Has to be called in X critical section +void win_process_updates(struct session *ps, struct managed_win *_w); /// Process pending images flags on a window. Has to be called in X critical section void win_process_flags(session_t *ps, struct managed_win *w); +/// Queue an update on a window. A series of sanity checks are performed +void win_queue_update(struct managed_win *_w, enum win_update update); /// Start the unmap of a window. We cannot unmap immediately since we might need to fade /// the window out. @@ -417,6 +421,9 @@ bool attr_pure win_has_alpha(const struct managed_win *w); /// check if reg_ignore_valid is true for all windows above us bool attr_pure win_is_region_ignore_valid(session_t *ps, const struct managed_win *w); +/// Whether a given window is mapped on the X server side +bool win_is_mapped_in_x(const struct managed_win *w); + // Find the managed window immediately below `w` in the window stack struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps, const struct list_node *w); diff --git a/src/win_defs.h b/src/win_defs.h index b86f3ce..bc43fc3 100644 --- a/src/win_defs.h +++ b/src/win_defs.h @@ -27,6 +27,11 @@ typedef enum { WMODE_SOLID, // The window is opaque including the frame } winmode_t; +/// Pending window updates +enum win_update { + WIN_UPDATE_MAP = 1, +}; + /// Transition table: /// (DESTROYED is when the win struct is destroyed and freed) /// ('o' means in all other cases)