From ad8211e3413611a76491b576767cdd99ee4fb657 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sat, 27 Apr 2019 13:36:42 +0100 Subject: [PATCH] event: handle reparent to the same parent When a window is reparented to the same parent again, we should just move it to the top of the stack, instead of add it again. Also a slight refactor restack_win. Fixes #164 Signed-off-by: Yuxuan Shui --- src/compton.c | 148 -------------------------------------------------- src/compton.h | 3 - src/event.c | 98 +++++++++++++++++++++++++++++++-- src/list.h | 6 ++ src/win.c | 94 ++++++++++++++++++++++++++++++-- src/win.h | 6 ++ 6 files changed, 193 insertions(+), 162 deletions(-) diff --git a/src/compton.c b/src/compton.c index 9f78afe..c4b4b30 100644 --- a/src/compton.c +++ b/src/compton.c @@ -631,71 +631,6 @@ static void rebuild_shadow_exclude_reg(session_t *ps) { exit(1); } -static void restack_win(session_t *ps, struct win *w, xcb_window_t new_above) { - xcb_window_t old_above; - - if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) { - old_above = list_next_entry(w, stack_neighbour)->id; - } else { - old_above = XCB_NONE; - } - log_debug("Restack %#010x (%s), old_above: %#010x, new_above: %#010x", w->id, - win_get_name_if_managed(w), old_above, new_above); - - struct managed_win *mw = NULL; - if (w->managed) { - mw = (struct managed_win *)w; - } - - if (old_above != new_above) { - if (mw) { - mw->reg_ignore_valid = false; - rc_region_unref(&mw->reg_ignore); - } - - auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour); - if (next_w) { - next_w->reg_ignore_valid = false; - rc_region_unref(&next_w->reg_ignore); - } - - struct list_node *new_next; - if (!new_above) { - new_next = &ps->window_stack; - } else { - struct win *tmp_w = NULL; - HASH_FIND_INT(ps->windows, &new_above, tmp_w); - - if (!tmp_w) { - log_error("(%#010x, %#010x): Failed to found new above " - "window.", - w->id, new_above); - return; - } - - new_next = &tmp_w->stack_neighbour; - } - - list_move_before(&w->stack_neighbour, new_next); - - // add damage for this window - if (mw) { - add_damage_from_win(ps, mw); - } - -#ifdef DEBUG_RESTACK - log_trace("Window stack modified. Current stack:"); - for (auto c = ps->list; c; c = c->next) { - const char *desc = ""; - if (c->state == WSTATE_DESTROYING) { - desc = "(D) "; - } - log_trace("%#010x \"%s\" %s", c->id, c->name, desc); - } -#endif - } -} - /// Free up all the images and deinit the backend static void destroy_backend(session_t *ps) { win_stack_foreach_managed_safe(w, &ps->window_stack) { @@ -826,89 +761,6 @@ void configure_root(session_t *ps, int width, int height) { return; } -/// Handle configure event of a regular window -void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { - auto w = find_win(ps, ce->window); - region_t damage; - pixman_region32_init(&damage); - - if (!w) { - return; - } - - if (!w->managed) { - restack_win(ps, w, ce->above_sibling); - return; - } - - auto mw = (struct managed_win *)w; - - if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING || - mw->state == WSTATE_DESTROYING) { - // Only restack the window to make sure we can handle future restack - // notification correctly - restack_win(ps, w, ce->above_sibling); - } else { - restack_win(ps, w, ce->above_sibling); - bool factor_change = false; - win_extents(mw, &damage); - - // If window geometry change, free old extents - if (mw->g.x != ce->x || mw->g.y != ce->y || mw->g.width != ce->width || - mw->g.height != ce->height || mw->g.border_width != ce->border_width) { - factor_change = true; - } - - mw->g.x = ce->x; - mw->g.y = ce->y; - - if (mw->g.width != ce->width || mw->g.height != ce->height || - mw->g.border_width != ce->border_width) { - log_trace("Window size changed, %dx%d -> %dx%d", mw->g.width, - mw->g.height, ce->width, ce->height); - mw->g.width = ce->width; - mw->g.height = ce->height; - mw->g.border_width = ce->border_width; - win_on_win_size_change(ps, mw); - win_update_bounding_shape(ps, mw); - } - - region_t new_extents; - pixman_region32_init(&new_extents); - win_extents(mw, &new_extents); - pixman_region32_union(&damage, &damage, &new_extents); - pixman_region32_fini(&new_extents); - - if (factor_change) { - win_on_factor_change(ps, mw); - add_damage(ps, &damage); - win_update_screen(ps, mw); - } - } - - pixman_region32_fini(&damage); - - // override_redirect flag cannot be changed after window creation, as far - // as I know, so there's no point to re-match windows here. - mw->a.override_redirect = ce->override_redirect; -} - -void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce) { - auto w = find_win(ps, ce->window); - xcb_window_t new_above; - - if (!w) - return; - - if (ce->place == PlaceOnTop) { - new_above = list_entry(ps->window_stack.next, struct win, stack_neighbour)->id; - } else { - new_above = XCB_NONE; - } - - restack_win(ps, w, new_above); -} - void root_damaged(session_t *ps) { if (ps->root_tile_paint.pixmap) { free_root_tile(ps); diff --git a/src/compton.h b/src/compton.h index 5d9ff78..8d0a79e 100644 --- a/src/compton.h +++ b/src/compton.h @@ -47,9 +47,6 @@ struct managed_win *recheck_focus(session_t *ps); /// Handle configure event of a root window void configure_root(session_t *ps, int width, int height); -/// Handle configure event of a regular window -void configure_win(session_t *ps, xcb_configure_notify_event_t *ce); - void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce); void update_ewmh_active_win(session_t *ps); diff --git a/src/event.c b/src/event.c index a5de234..afbcdde 100644 --- a/src/event.c +++ b/src/event.c @@ -173,6 +173,73 @@ static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev add_win_top(ps, ev->window); } +/// Handle configure event of a regular window +static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) { + auto w = find_win(ps, ce->window); + region_t damage; + pixman_region32_init(&damage); + + if (!w) { + return; + } + + if (!w->managed) { + restack_above(ps, w, ce->above_sibling); + return; + } + + auto mw = (struct managed_win *)w; + + if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING || + mw->state == WSTATE_DESTROYING) { + // Only restack the window to make sure we can handle future restack + // notification correctly + restack_above(ps, w, ce->above_sibling); + } else { + restack_above(ps, w, ce->above_sibling); + bool factor_change = false; + win_extents(mw, &damage); + + // If window geometry change, free old extents + if (mw->g.x != ce->x || mw->g.y != ce->y || mw->g.width != ce->width || + mw->g.height != ce->height || mw->g.border_width != ce->border_width) { + factor_change = true; + } + + mw->g.x = ce->x; + mw->g.y = ce->y; + + if (mw->g.width != ce->width || mw->g.height != ce->height || + mw->g.border_width != ce->border_width) { + log_trace("Window size changed, %dx%d -> %dx%d", mw->g.width, + mw->g.height, ce->width, ce->height); + mw->g.width = ce->width; + mw->g.height = ce->height; + mw->g.border_width = ce->border_width; + win_on_win_size_change(ps, mw); + win_update_bounding_shape(ps, mw); + } + + region_t new_extents; + pixman_region32_init(&new_extents); + win_extents(mw, &new_extents); + pixman_region32_union(&damage, &damage, &new_extents); + pixman_region32_fini(&new_extents); + + if (factor_change) { + win_on_factor_change(ps, mw); + add_damage(ps, &damage); + win_update_screen(ps, mw); + } + } + + pixman_region32_fini(&damage); + + // override_redirect flag cannot be changed after window creation, as far + // as I know, so there's no point to re-match windows here. + mw->a.override_redirect = ce->override_redirect; +} + static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) { log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }", ev->event, ev->window, ev->above_sibling, ev->override_redirect); @@ -215,8 +282,18 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t ev->override_redirect); if (ev->parent == ps->root) { - // new window - add_win_top(ps, ev->window); + // X will generate reparent notifiy even if the parent didn't actually + // change (i.e. reparent again to current parent). So we check if that's + // the case + auto w = find_win(ps, ev->window); + if (w) { + // This window has already been reparented to root before, + // so we don't need to create a new window for it, we just need to + // move it to the top + restack_top(ps, w); + } else { + add_win_top(ps, ev->window); + } } else { // otherwise, find and destroy the window first auto w = find_win(ps, ev->window); @@ -240,7 +317,8 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t auto w_top = find_toplevel2(ps, ev->parent); // If found, and the client window has not been determined, or its // frame may not have a correct client, continue - if (w_top && (!w_top->client_win || w_top->client_win == w_top->base.id)) { + if (w_top && + (!w_top->client_win || w_top->client_win == w_top->base.id)) { // If it has WM_STATE, mark it the client window if (wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; @@ -261,7 +339,16 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t } static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) { - circulate_win(ps, ev); + auto w = find_win(ps, ev->window); + + if (!w) + return; + + if (ev->place == PlaceOnTop) { + restack_top(ps, w); + } else { + restack_bottom(ps, w); + } } static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) { @@ -339,7 +426,8 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t auto w_top = find_toplevel2(ps, ev->window); // Initialize client_win as early as possible - if (w_top && (!w_top->client_win || w_top->client_win == w_top->base.id) && + if (w_top && + (!w_top->client_win || w_top->client_win == w_top->base.id) && wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; win_unmark_client(ps, w_top); diff --git a/src/list.h b/src/list.h index 3c9ee6c..19e2c2c 100644 --- a/src/list.h +++ b/src/list.h @@ -61,6 +61,12 @@ static inline void list_move_before(struct list_node *to_move, struct list_node list_insert_before(new_next, to_move); } +/// Move `to_move` so that it's after `new_prev` +static inline void list_move_after(struct list_node *to_move, struct list_node *new_prev) { + list_remove(to_move); + list_insert_after(new_prev, to_move); +} + /// Initialize a list node that's intended to be the head node static inline void list_init_head(struct list_node *head) { head->next = head->prev = head; diff --git a/src/win.c b/src/win.c index d25abb8..afd98e9 100644 --- a/src/win.c +++ b/src/win.c @@ -551,8 +551,8 @@ void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) { log_debug("Updating shadow property of window %#010x (%s) to %d", w->base.id, w->name, shadow_new); - if (ps->backend_data && - w->state != WSTATE_UNMAPPED && !(w->flags & WIN_FLAGS_IMAGE_ERROR)) { + if (ps->backend_data && w->state != WSTATE_UNMAPPED && + !(w->flags & WIN_FLAGS_IMAGE_ERROR)) { assert(!w->shadow_image); // Create shadow image w->shadow_image = ps->backend_data->ops->render_shadow( @@ -946,6 +946,7 @@ void free_win_res(session_t *ps, struct managed_win *w) { /// Insert a new window after list_node `prev` /// New window will be in unmapped state static struct win *add_win(session_t *ps, xcb_window_t id, struct list_node *prev) { + log_debug("Adding window %#010x", id); struct win *old_w = NULL; HASH_FIND_INT(ps->windows, &id, old_w); assert(old_w == NULL); @@ -1085,7 +1086,7 @@ struct win *fill_win(session_t *ps, struct win *w) { return &duplicated_win->base; } - log_debug("Adding window %#010x", w->id); + log_debug("Managing window %#010x", w->id); xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, w->id); xcb_get_window_attributes_reply_t *a = xcb_get_window_attributes_reply(ps->c, acookie, NULL); @@ -1625,6 +1626,90 @@ void destroy_unmanaged_win(session_t *ps, struct win **_w) { *_w = NULL; } +/// Move window `w` so it's before `next` in the list +static inline void restack_win(session_t *ps, struct win *w, struct list_node *next) { + struct managed_win *mw = NULL; + if (w->managed) { + mw = (struct managed_win *)w; + } + + if (mw) { + // This invalidates all reg_ignore below the new stack position of `w` + mw->reg_ignore_valid = false; + rc_region_unref(&mw->reg_ignore); + + // This invalidates all reg_ignore below the old stack position of `w` + auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour); + if (next_w) { + next_w->reg_ignore_valid = false; + rc_region_unref(&next_w->reg_ignore); + } + } + + list_move_before(&w->stack_neighbour, next); + + // add damage for this window + if (mw) { + add_damage_from_win(ps, mw); + } + +#ifdef DEBUG_RESTACK + log_trace("Window stack modified. Current stack:"); + for (auto c = ps->list; c; c = c->next) { + const char *desc = ""; + if (c->state == WSTATE_DESTROYING) { + desc = "(D) "; + } + log_trace("%#010x \"%s\" %s", c->id, c->name, desc); + } +#endif +} + +/// Move window `w` so it's right above `below` +void restack_above(session_t *ps, struct win *w, xcb_window_t below) { + xcb_window_t old_below; + + if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) { + old_below = list_next_entry(w, stack_neighbour)->id; + } else { + old_below = XCB_NONE; + } + log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id, + win_get_name_if_managed(w), old_below, below); + + if (old_below != below) { + struct list_node *new_next; + if (!below) { + new_next = &ps->window_stack; + } else { + struct win *tmp_w = NULL; + HASH_FIND_INT(ps->windows, &below, tmp_w); + + if (!tmp_w) { + log_error("(%#010x, %#010x): Failed to found new below " + "window.", w->id, below); + return; + } + + new_next = &tmp_w->stack_neighbour; + } + restack_win(ps, w, new_next); + } +} + +void restack_bottom(session_t *ps, struct win *w) { + restack_above(ps, w, 0); +} + +void restack_top(session_t *ps, struct win *w) { + log_debug("Restack %#010x (%s) to top", w->id, win_get_name_if_managed(w)); + if (&w->stack_neighbour == ps->window_stack.next) { + // already at top + return; + } + restack_win(ps, w, ps->window_stack.next); +} + void unmap_win(session_t *ps, struct managed_win **_w, bool destroy) { auto w = *_w; @@ -1768,9 +1853,6 @@ void win_update_screen(session_t *ps, struct managed_win *w) { } } -// TODO remove this -void configure_win(session_t *, xcb_configure_notify_event_t *); - /// Map an already registered window void map_win(session_t *ps, struct managed_win *w) { assert(w); diff --git a/src/win.h b/src/win.h index ca96ae0..b609d59 100644 --- a/src/win.h +++ b/src/win.h @@ -419,6 +419,12 @@ struct win *add_win_top(session_t *ps, xcb_window_t id); /// Query the Xorg for information about window `win` /// `win` pointer might become invalid after this function returns struct win *fill_win(session_t *ps, struct win *win); +/// Move window `w` to be right above `below` +void restack_above(session_t *ps, struct win *w, xcb_window_t below); +/// Move window `w` to the bottom of the stack +void restack_bottom(session_t *ps, struct win *w); +/// Move window `w` to the top of the stack +void restack_top(session_t *ps, struct win *w); /// Unmap or destroy a window void unmap_win(session_t *ps, struct managed_win **, bool destroy); /// Destroy an unmanaged window