Merge pull request #546 from yshui/window-state-update
win: rework how events for unmapped windows are handled
This commit is contained in:
85
src/event.c
85
src/event.c
@@ -201,58 +201,55 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||
|
||||
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);
|
||||
restack_above(ps, w, ce->above_sibling);
|
||||
|
||||
// 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) {
|
||||
// We don't mark the old region as damaged if we have stale
|
||||
// shape/size/position information. The old region should have
|
||||
// already been add to damage when the information became stale.
|
||||
if (!win_check_flags_any(
|
||||
mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
|
||||
// Mark the old extents as damaged.
|
||||
// The new extents will be marked damaged when processing
|
||||
// window flags.
|
||||
// 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) {
|
||||
// We don't mark the old region as damaged if we have stale
|
||||
// shape/size/position information. The old region should have
|
||||
// already been add to damage when the information became stale.
|
||||
if (!win_check_flags_any(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE)) {
|
||||
if (mw->state != WSTATE_UNMAPPED && mw->state != WSTATE_UNMAPPING &&
|
||||
mw->state != WSTATE_DESTROYING) {
|
||||
// Mark the old extents as damaged. The new extents will
|
||||
// be marked damaged when processing window flags.
|
||||
// If the window is not mapped, we don't care
|
||||
region_t damage;
|
||||
pixman_region32_init(&damage);
|
||||
win_extents(mw, &damage);
|
||||
add_damage(ps, &damage);
|
||||
pixman_region32_fini(&damage);
|
||||
}
|
||||
|
||||
// Queue pending updates
|
||||
win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
|
||||
ps->pending_updates = true;
|
||||
|
||||
// At least one of the following if's is true
|
||||
if (mw->g.x != ce->x || mw->g.y != ce->y) {
|
||||
log_trace("Window position changed, %dx%d -> %dx%d",
|
||||
mw->g.x, mw->g.y, ce->x, ce->y);
|
||||
mw->g.x = ce->x;
|
||||
mw->g.y = ce->y;
|
||||
win_set_flags(mw, WIN_FLAGS_POSITION_STALE);
|
||||
}
|
||||
|
||||
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_set_flags(mw, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
|
||||
// Recalculate which screen this window is on
|
||||
win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, mw);
|
||||
}
|
||||
|
||||
// Queue pending updates
|
||||
win_set_flags(mw, WIN_FLAGS_FACTOR_CHANGED);
|
||||
// TODO(yshui) don't set pending_updates if the window is not
|
||||
// visible/mapped
|
||||
ps->pending_updates = true;
|
||||
|
||||
// At least one of the following if's is true
|
||||
if (mw->g.x != ce->x || mw->g.y != ce->y) {
|
||||
log_trace("Window position changed, %dx%d -> %dx%d", mw->g.x,
|
||||
mw->g.y, ce->x, ce->y);
|
||||
mw->g.x = ce->x;
|
||||
mw->g.y = ce->y;
|
||||
win_set_flags(mw, WIN_FLAGS_POSITION_STALE);
|
||||
}
|
||||
|
||||
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_set_flags(mw, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
|
||||
// Recalculate which screen this window is on
|
||||
win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, mw);
|
||||
}
|
||||
|
||||
// override_redirect flag cannot be changed after window creation, as far
|
||||
|
||||
48
src/picom.c
48
src/picom.c
@@ -402,12 +402,20 @@ static void destroy_backend(session_t *ps) {
|
||||
}
|
||||
|
||||
if (ps->backend_data) {
|
||||
if (w->state == WSTATE_MAPPED) {
|
||||
win_release_images(ps->backend_data, w);
|
||||
} else {
|
||||
assert(!w->win_image);
|
||||
assert(!w->shadow_image);
|
||||
// Unmapped windows could still have shadow images, but not pixmap
|
||||
// images
|
||||
assert(!w->win_image || w->state != WSTATE_UNMAPPED);
|
||||
if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
|
||||
w->state == WSTATE_MAPPED) {
|
||||
log_warn("Stale flags set for mapped window %#010x "
|
||||
"during backend destruction",
|
||||
w->base.id);
|
||||
assert(false);
|
||||
}
|
||||
// Unmapped windows can still have stale flags set, because their
|
||||
// stale flags aren't handled until they are mapped.
|
||||
win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
win_release_images(ps->backend_data, w);
|
||||
}
|
||||
free_paint(ps, &w->paint);
|
||||
}
|
||||
@@ -495,25 +503,12 @@ static bool initialize_backend(session_t *ps) {
|
||||
}
|
||||
auto w = (struct managed_win *)_w;
|
||||
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
|
||||
if (w->state == WSTATE_MAPPED) {
|
||||
// We need to reacquire image
|
||||
log_debug("Marking window %#010x (%s) for update after "
|
||||
"redirection",
|
||||
w->base.id, w->name);
|
||||
if (w->shadow) {
|
||||
struct color c = {
|
||||
.red = ps->o.shadow_red,
|
||||
.green = ps->o.shadow_green,
|
||||
.blue = ps->o.shadow_blue,
|
||||
.alpha = ps->o.shadow_opacity,
|
||||
};
|
||||
win_bind_shadow(ps->backend_data, w, c,
|
||||
ps->gaussian_map);
|
||||
}
|
||||
|
||||
w->flags |= WIN_FLAGS_PIXMAP_STALE;
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
// We need to reacquire image
|
||||
log_debug("Marking window %#010x (%s) for update after "
|
||||
"redirection",
|
||||
w->base.id, w->name);
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1354,10 +1349,7 @@ static void handle_new_windows(session_t *ps) {
|
||||
}
|
||||
auto mw = (struct managed_win *)new_w;
|
||||
if (mw->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
// Have to map immediately instead of queue window update
|
||||
// because we need the window's extent right now.
|
||||
// We can do this because we are in the critical section.
|
||||
map_win_start(ps, mw);
|
||||
win_set_flags(mw, WIN_FLAGS_MAPPED);
|
||||
|
||||
// This window might be damaged before we called fill_win
|
||||
// and created the damage handle. And there is no way for
|
||||
|
||||
232
src/win.c
232
src/win.c
@@ -125,8 +125,9 @@ static void win_update_focused(session_t *ps, struct managed_win *w) {
|
||||
(ps->o.mark_wmwin_focused && w->wmwin) ||
|
||||
(ps->o.mark_ovredir_focused && w->base.id == w->client_win && !w->wmwin) ||
|
||||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
||||
c2_match(ps, w, ps->o.focus_blacklist, NULL)))
|
||||
c2_match(ps, w, ps->o.focus_blacklist, NULL))) {
|
||||
w->focused = true;
|
||||
}
|
||||
|
||||
// If window grouping detection is enabled, mark the window active if
|
||||
// its group is
|
||||
@@ -411,6 +412,12 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
win_clear_flags(w, WIN_FLAGS_MAPPED);
|
||||
}
|
||||
|
||||
if (w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYING ||
|
||||
w->state == WSTATE_UNMAPPING) {
|
||||
// Flags of invisible windows are processed when they are mapped
|
||||
return;
|
||||
}
|
||||
|
||||
// Check client first, because later property updates need accurate client window
|
||||
// information
|
||||
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
|
||||
@@ -452,6 +459,12 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
void win_process_image_flags(session_t *ps, struct managed_win *w) {
|
||||
assert(!win_check_flags_all(w, WIN_FLAGS_MAPPED));
|
||||
|
||||
if (w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYING ||
|
||||
w->state == WSTATE_UNMAPPING) {
|
||||
// Flags of invisible windows are processed when they are mapped
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a loop
|
||||
while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
|
||||
!win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
|
||||
@@ -804,15 +817,9 @@ static 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 (w->state == WSTATE_UNMAPPED) {
|
||||
// No need to add damage or update shadow
|
||||
// Unmapped window shouldn't have any images
|
||||
w->shadow = shadow_new;
|
||||
assert(!w->shadow_image);
|
||||
assert(!w->win_image);
|
||||
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE));
|
||||
return;
|
||||
}
|
||||
// We don't handle property updates of non-visible windows until they are mapped.
|
||||
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
|
||||
w->state != WSTATE_UNMAPPING);
|
||||
|
||||
// Keep a copy of window extent before the shadow change. Will be used for
|
||||
// calculation of damaged region
|
||||
@@ -856,6 +863,9 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
|
||||
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to
|
||||
// re-create or release the shaodw in based on whether w->shadow is set.
|
||||
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
|
||||
|
||||
// Only set pending_updates if we are redirected. Otherwise change of a
|
||||
// shadow won't have influence on whether we should redirect.
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
|
||||
@@ -991,8 +1001,9 @@ win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_backgrou
|
||||
*/
|
||||
static void win_determine_blur_background(session_t *ps, struct managed_win *w) {
|
||||
log_debug("Determining blur-background of window %#010x (%s)", w->base.id, w->name);
|
||||
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
|
||||
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool blur_background_new = ps->o.blur_method != BLUR_METHOD_NONE;
|
||||
if (blur_background_new) {
|
||||
@@ -1071,14 +1082,13 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
||||
w->shadow_width = w->widthb + ps->o.shadow_radius * 2;
|
||||
w->shadow_height = w->heightb + ps->o.shadow_radius * 2;
|
||||
|
||||
// We don't handle property updates of non-visible windows until they are mapped.
|
||||
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
|
||||
w->state != WSTATE_UNMAPPING);
|
||||
|
||||
// Invalidate the shadow we built
|
||||
if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING ||
|
||||
w->state == WSTATE_FADING) {
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
ps->pending_updates = true;
|
||||
} else {
|
||||
assert(w->state == WSTATE_UNMAPPED);
|
||||
}
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
ps->pending_updates = true;
|
||||
free_paint(ps, &w->shadow_paint);
|
||||
}
|
||||
|
||||
@@ -1190,8 +1200,9 @@ static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
|
||||
|
||||
xcb_query_tree_reply_t *reply =
|
||||
xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
|
||||
if (!reply)
|
||||
if (!reply) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
xcb_window_t *children = xcb_query_tree_children(reply);
|
||||
int nchildren = xcb_query_tree_children_length(reply);
|
||||
@@ -1199,8 +1210,9 @@ static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
|
||||
xcb_window_t ret = 0;
|
||||
|
||||
for (i = 0; i < nchildren; ++i) {
|
||||
if ((ret = find_client_win(ps, children[i])))
|
||||
if ((ret = find_client_win(ps, children[i]))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(reply);
|
||||
@@ -1237,8 +1249,9 @@ void win_recheck_client(session_t *ps, struct managed_win *w) {
|
||||
}
|
||||
|
||||
// Unmark the old one
|
||||
if (w->client_win && w->client_win != cw)
|
||||
if (w->client_win && w->client_win != cw) {
|
||||
win_unmark_client(ps, w);
|
||||
}
|
||||
|
||||
// Mark the new one
|
||||
win_mark_client(ps, w, cw);
|
||||
@@ -1455,16 +1468,40 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
|
||||
free(a);
|
||||
|
||||
// Create Damage for window (if not Input Only)
|
||||
new->damage = x_new_id(ps->c);
|
||||
xcb_generic_error_t *e = xcb_request_check(
|
||||
ps->c, xcb_damage_create_checked(ps->c, new->damage, w->id,
|
||||
XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
|
||||
if (e) {
|
||||
xcb_generic_error_t *e;
|
||||
auto g = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, w->id), &e);
|
||||
if (!g) {
|
||||
log_error_x_error(e, "Failed to get geometry of window %#010x", w->id);
|
||||
free(e);
|
||||
free(new);
|
||||
return w;
|
||||
}
|
||||
new->g = *g;
|
||||
free(g);
|
||||
|
||||
win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, new);
|
||||
|
||||
// Create Damage for window (if not Input Only)
|
||||
new->damage = x_new_id(ps->c);
|
||||
e = xcb_request_check(
|
||||
ps->c, xcb_damage_create_checked(ps->c, new->damage, w->id,
|
||||
XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to create damage");
|
||||
free(e);
|
||||
free(new);
|
||||
return w;
|
||||
}
|
||||
|
||||
// Set window event mask
|
||||
xcb_change_window_attributes(
|
||||
ps->c, new->base.id, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, new->base.id, WIN_EVMODE_FRAME)});
|
||||
|
||||
// Get notification when the shape of a window changes
|
||||
if (ps->shape_exists) {
|
||||
xcb_shape_select_input(ps->c, new->base.id, 1);
|
||||
}
|
||||
|
||||
new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
|
||||
new->client_pictfmt = NULL;
|
||||
@@ -1475,6 +1512,20 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
assert(replaced == w);
|
||||
free(w);
|
||||
|
||||
// Set all the stale flags on this new window, so it's properties will get updated
|
||||
// when it's mapped
|
||||
win_set_flags(new, WIN_FLAGS_CLIENT_STALE | WIN_FLAGS_SIZE_STALE |
|
||||
WIN_FLAGS_POSITION_STALE | WIN_FLAGS_PROPERTY_STALE |
|
||||
WIN_FLAGS_FACTOR_CHANGED);
|
||||
xcb_atom_t init_stale_props[] = {
|
||||
ps->atoms->a_NET_WM_WINDOW_TYPE, ps->atoms->a_NET_WM_WINDOW_OPACITY,
|
||||
ps->atoms->a_NET_FRAME_EXTENTS, ps->atoms->aWM_NAME,
|
||||
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
|
||||
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
|
||||
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
|
||||
};
|
||||
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
if (ps->o.dbus) {
|
||||
@@ -1692,6 +1743,10 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
||||
w->bounding_shaped = win_bounding_shaped(ps, w->base.id);
|
||||
}
|
||||
|
||||
// We don't handle property updates of non-visible windows until they are mapped.
|
||||
assert(w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
|
||||
w->state != WSTATE_UNMAPPING);
|
||||
|
||||
pixman_region32_clear(&w->bounding_shape);
|
||||
// Start with the window rectangular region
|
||||
win_get_region_local(w, &w->bounding_shape);
|
||||
@@ -1743,13 +1798,9 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// Window shape changed, we should free old wpaint and shadow pict
|
||||
// log_trace("free out dated pict");
|
||||
if (w->state != WSTATE_UNMAPPED) {
|
||||
// Note we only do this when screen is redirected, because
|
||||
// otherwise win_data is not valid
|
||||
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
ps->pending_updates = true;
|
||||
|
||||
free_paint(ps, &w->paint);
|
||||
free_paint(ps, &w->shadow_paint);
|
||||
|
||||
@@ -1856,7 +1907,11 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// We are in unmap_win, this window definitely was viewable
|
||||
if (ps->backend_data) {
|
||||
win_release_images(ps->backend_data, w);
|
||||
// Only the pixmap needs to be freed and reacquired when mapping.
|
||||
// Shadow image can be preserved.
|
||||
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
||||
win_release_pixmap(ps->backend_data, w);
|
||||
}
|
||||
} else {
|
||||
assert(!w->win_image);
|
||||
assert(!w->shadow_image);
|
||||
@@ -1889,6 +1944,12 @@ static void destroy_win_finish(session_t *ps, struct win *w) {
|
||||
unmap_win_finish(ps, mw);
|
||||
}
|
||||
|
||||
// Unmapping preserves the shadow image, so free it here
|
||||
if (!win_check_flags_all(mw, WIN_FLAGS_SHADOW_NONE)) {
|
||||
assert(mw->shadow_image != NULL);
|
||||
win_release_shadow(ps->backend_data, mw);
|
||||
}
|
||||
|
||||
// Invalidate reg_ignore of windows below this one
|
||||
// TODO(yshui) what if next_w is not mapped??
|
||||
/* TODO(yshui) seriously figure out how reg_ignore behaves.
|
||||
@@ -2117,13 +2178,6 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
|
||||
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
|
||||
w->opacity_target = win_calc_opacity_target(ps, w);
|
||||
|
||||
// Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap
|
||||
// available so STALE doesn't make sense.
|
||||
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||
|
||||
// don't care about properties anymore
|
||||
win_ev_stop(ps, &w->base);
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
if (ps->o.dbus) {
|
||||
@@ -2226,23 +2280,6 @@ void map_win_start(session_t *ps, struct managed_win *w) {
|
||||
}
|
||||
|
||||
assert(w->state == WSTATE_UNMAPPED);
|
||||
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE) || !ps->o.experimental_backends);
|
||||
|
||||
// We stopped processing window size change when we were unmapped, refresh the
|
||||
// size of the window
|
||||
xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(ps->c, w->base.id);
|
||||
xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(ps->c, gcookie, NULL);
|
||||
|
||||
if (!g) {
|
||||
log_error("Failed to get the geometry of window %#010x", w->base.id);
|
||||
return;
|
||||
}
|
||||
|
||||
w->g = *g;
|
||||
free(g);
|
||||
|
||||
win_on_win_size_change(ps, w);
|
||||
log_trace("Window size: %dx%d", w->g.width, w->g.height);
|
||||
|
||||
// Rant: window size could change after we queried its geometry here and before
|
||||
// we get its pixmap. Later, when we get back to the event processing loop, we
|
||||
@@ -2254,48 +2291,11 @@ void map_win_start(session_t *ps, struct managed_win *w) {
|
||||
// XXX Can we assume map_state is always viewable?
|
||||
w->a.map_state = XCB_MAP_STATE_VIEWABLE;
|
||||
|
||||
win_update_screen(ps->xinerama_nscrs, ps->xinerama_scr_regs, w);
|
||||
|
||||
// Set window event mask before reading properties so that no property
|
||||
// changes are lost
|
||||
xcb_change_window_attributes(
|
||||
ps->c, w->base.id, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, w->base.id, WIN_EVMODE_FRAME)});
|
||||
|
||||
// Get notification when the shape of a window changes
|
||||
if (ps->shape_exists) {
|
||||
xcb_shape_select_input(ps->c, w->base.id, 1);
|
||||
}
|
||||
|
||||
// Update window mode here to check for ARGB windows
|
||||
w->mode = win_calc_mode(w);
|
||||
|
||||
// Detect client window here instead of in add_win() as the client
|
||||
// window should have been prepared at this point
|
||||
if (!w->client_win) {
|
||||
win_recheck_client(ps, w);
|
||||
} else {
|
||||
// Re-mark client window here
|
||||
win_mark_client(ps, w, w->client_win);
|
||||
}
|
||||
assert(w->client_win);
|
||||
|
||||
log_debug("Window (%#010x) has type %s", w->base.id, WINTYPES[w->window_type]);
|
||||
|
||||
// TODO(yshui) can we just replace calls below with win_on_factor_change?
|
||||
|
||||
// Update window focus state
|
||||
win_update_focused(ps, w);
|
||||
|
||||
// Update opacity and dim state
|
||||
win_update_opacity_prop(ps, w);
|
||||
|
||||
// Check for _COMPTON_SHADOW
|
||||
win_update_prop_shadow_raw(ps, w);
|
||||
|
||||
// Many things above could affect shadow
|
||||
win_determine_shadow(ps, w);
|
||||
|
||||
// XXX We need to make sure that win_data is available
|
||||
// iff `state` is MAPPED
|
||||
w->state = WSTATE_MAPPING;
|
||||
@@ -2305,23 +2305,15 @@ void map_win_start(session_t *ps, struct managed_win *w) {
|
||||
log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
|
||||
w->opacity, w->opacity_target);
|
||||
|
||||
win_determine_blur_background(ps, w);
|
||||
|
||||
// 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),
|
||||
// so the shape of the window might have changed,
|
||||
// update. (Issue #35)
|
||||
//
|
||||
// Also this sets the WIN_FLAGS_IMAGES_STALE flag so later in the critical section
|
||||
// Sets the WIN_FLAGS_IMAGES_STALE flag so later in the critical section
|
||||
// the window's image will be bound
|
||||
win_update_bounding_shape(ps, w);
|
||||
|
||||
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_STALE));
|
||||
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
@@ -2538,19 +2530,33 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) {
|
||||
w->flags = w->flags & (~flags);
|
||||
}
|
||||
|
||||
void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
||||
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *props, int nprops) {
|
||||
const auto bits_per_element = sizeof(*w->stale_props) * 8;
|
||||
if (prop >= w->stale_props_capacity * bits_per_element) {
|
||||
const auto new_size = prop / bits_per_element + 1;
|
||||
w->stale_props = realloc(w->stale_props, new_size * sizeof(*w->stale_props));
|
||||
size_t new_capacity = w->stale_props_capacity;
|
||||
|
||||
// Calculate the new capacity of the properties array
|
||||
for (int i = 0; i < nprops; i++) {
|
||||
if (props[i] >= new_capacity * bits_per_element) {
|
||||
new_capacity = props[i] / bits_per_element + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Reallocate if necessary
|
||||
if (new_capacity > w->stale_props_capacity) {
|
||||
w->stale_props =
|
||||
realloc(w->stale_props, new_capacity * sizeof(*w->stale_props));
|
||||
|
||||
// Clear the content of the newly allocated bytes
|
||||
memset(w->stale_props + w->stale_props_capacity, 0,
|
||||
(new_size - w->stale_props_capacity) * sizeof(*w->stale_props));
|
||||
w->stale_props_capacity = new_size;
|
||||
(new_capacity - w->stale_props_capacity) * sizeof(*w->stale_props));
|
||||
w->stale_props_capacity = new_capacity;
|
||||
}
|
||||
|
||||
w->stale_props[prop / bits_per_element] |= 1UL << (prop % bits_per_element);
|
||||
// Set the property bits
|
||||
for (int i = 0; i < nprops; i++) {
|
||||
w->stale_props[props[i] / bits_per_element] |=
|
||||
1UL << (props[i] % bits_per_element);
|
||||
}
|
||||
win_set_flags(w, WIN_FLAGS_PROPERTY_STALE);
|
||||
}
|
||||
|
||||
|
||||
@@ -428,8 +428,12 @@ void win_clear_flags(struct managed_win *w, uint64_t flags);
|
||||
bool win_check_flags_any(struct managed_win *w, uint64_t flags);
|
||||
/// Returns true if all of the flags in `flags` are set
|
||||
bool win_check_flags_all(struct managed_win *w, uint64_t flags);
|
||||
/// Mark a property as stale for a window
|
||||
void win_set_property_stale(struct managed_win *w, xcb_atom_t prop);
|
||||
/// Mark properties as stale for a window
|
||||
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *prop, int nprops);
|
||||
|
||||
static inline attr_unused void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
||||
return win_set_properties_stale(w, (xcb_atom_t[]){prop}, 1);
|
||||
}
|
||||
|
||||
/// Free all resources in a struct win
|
||||
void free_win_res(session_t *ps, struct managed_win *w);
|
||||
|
||||
@@ -17,4 +17,6 @@ eval `dbus-launch --sh-syntax`
|
||||
./run_one_test.sh $exe /dev/null testcases/issue299.py
|
||||
./run_one_test.sh $exe configs/issue465.conf testcases/issue465.py
|
||||
./run_one_test.sh $exe configs/clear_shadow_unredirected.conf testcases/clear_shadow_unredirected.py
|
||||
./run_one_test.sh $exe configs/clear_shadow_unredirected.conf testcases/redirect_when_unmapped_window_has_shadow.py
|
||||
./run_one_test.sh $exe configs/issue394.conf testcases/issue394.py
|
||||
./run_one_test.sh $exe configs/issue239.conf testcases/issue525.py
|
||||
|
||||
36
tests/testcases/issue525.py
Executable file
36
tests/testcases/issue525.py
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import xcffib.xproto as xproto
|
||||
import xcffib
|
||||
import time
|
||||
from common import set_window_name
|
||||
|
||||
conn = xcffib.connect()
|
||||
setup = conn.get_setup()
|
||||
root = setup.roots[0].root
|
||||
visual = setup.roots[0].root_visual
|
||||
depth = setup.roots[0].root_depth
|
||||
|
||||
# issue 525 happens when a window is unmapped with pixmap stale flag set
|
||||
wid = conn.generate_id()
|
||||
print("Window id is ", hex(wid))
|
||||
|
||||
# Create a window
|
||||
conn.core.CreateWindowChecked(depth, wid, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
|
||||
|
||||
# Map the window
|
||||
print("mapping")
|
||||
conn.core.MapWindowChecked(wid).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# change window size, invalidate the pixmap
|
||||
conn.core.ConfigureWindow(wid, xproto.ConfigWindow.X | xproto.ConfigWindow.Width, [100, 200])
|
||||
|
||||
# unmap the window immediately after
|
||||
conn.core.UnmapWindowChecked(wid).check()
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Destroy the window
|
||||
conn.core.DestroyWindowChecked(wid).check()
|
||||
65
tests/testcases/redirect_when_unmapped_window_has_shadow.py
Executable file
65
tests/testcases/redirect_when_unmapped_window_has_shadow.py
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import xcffib.xproto as xproto
|
||||
import xcffib
|
||||
import time
|
||||
from common import set_window_name
|
||||
|
||||
conn = xcffib.connect()
|
||||
setup = conn.get_setup()
|
||||
root = setup.roots[0].root
|
||||
visual = setup.roots[0].root_visual
|
||||
depth = setup.roots[0].root_depth
|
||||
|
||||
name = "_NET_WM_STATE"
|
||||
name_atom = conn.core.InternAtom(False, len(name), name).reply().atom
|
||||
atom = "ATOM"
|
||||
atom_atom = conn.core.InternAtom(False, len(atom), atom).reply().atom
|
||||
fs = "_NET_WM_STATE_FULLSCREEN"
|
||||
fs_atom = conn.core.InternAtom(False, len(fs), fs).reply().atom
|
||||
|
||||
wid1 = conn.generate_id()
|
||||
print("Window 1 id is ", hex(wid1))
|
||||
|
||||
# Create a window
|
||||
conn.core.CreateWindowChecked(depth, wid1, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
|
||||
|
||||
# Map the window
|
||||
print("mapping 1")
|
||||
conn.core.MapWindowChecked(wid1).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
print("unmapping 1")
|
||||
# Unmap the window
|
||||
conn.core.UnmapWindowChecked(wid1).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# create and map a second window
|
||||
wid2 = conn.generate_id()
|
||||
print("Window 2 id is ", hex(wid2))
|
||||
conn.core.CreateWindowChecked(depth, wid2, root, 200, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
|
||||
print("mapping 2")
|
||||
conn.core.MapWindowChecked(wid2).check()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Set fullscreen property on the second window, causing screen to be unredirected
|
||||
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid2, name_atom, atom_atom, 32, 1, [fs_atom]).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# Unset fullscreen property on the second window, causing screen to be redirected
|
||||
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid2, name_atom, atom_atom, 32, 0, []).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# map the first window again
|
||||
print("mapping 1")
|
||||
conn.core.MapWindowChecked(wid1).check()
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# Destroy the windows
|
||||
conn.core.DestroyWindowChecked(wid1).check()
|
||||
conn.core.DestroyWindowChecked(wid2).check()
|
||||
Reference in New Issue
Block a user