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 <yshuiv7@gmail.com>
This commit is contained in:
parent
cfbd1819ed
commit
ad8211e341
148
src/compton.c
148
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
98
src/event.c
98
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
94
src/win.c
94
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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue