Skip to content

Commit

Permalink
wm/win: asynchronously get geometry in win_map_start
Browse files Browse the repository at this point in the history
Because we ignore any ConfigureNotify when a window is not mapped, we
should re-fetch the geometry of the window in win_map_start to make sure
its geometry is up-to-date.

Signed-off-by: Yuxuan Shui <[email protected]>
  • Loading branch information
yshui committed Jul 31, 2024
1 parent 78fa171 commit 968ff4c
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 36 deletions.
34 changes: 2 additions & 32 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,38 +382,8 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {

add_damage_from_win(ps, w);

// We check against pending_g here, because there might have been multiple
// configure notifies in this cycle, or the window could receive multiple updates
// while it's unmapped.
bool position_changed = w->pending_g.x != ce->x || w->pending_g.y != ce->y;
bool size_changed = w->pending_g.width != ce->width ||
w->pending_g.height != ce->height ||
w->pending_g.border_width != ce->border_width;
if (position_changed || size_changed) {
// Queue pending updates
win_set_flags(w, 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 (position_changed) {
log_trace("Window position changed, %dx%d -> %dx%d", w->g.x,
w->g.y, ce->x, ce->y);
w->pending_g.x = ce->x;
w->pending_g.y = ce->y;
win_set_flags(w, WIN_FLAGS_POSITION_STALE);
}

if (size_changed) {
log_trace("Window size changed, %dx%d -> %dx%d", w->g.width,
w->g.height, ce->width, ce->height);
w->pending_g.width = ce->width;
w->pending_g.height = ce->height;
w->pending_g.border_width = ce->border_width;
win_set_flags(w, WIN_FLAGS_SIZE_STALE);
}
}
ps->pending_updates |=
win_set_pending_geometry(w, win_geometry_from_configure_notify(ce));

// override_redirect flag cannot be changed after window creation, as far
// as I know, so there's no point to re-match windows here.
Expand Down
2 changes: 1 addition & 1 deletion src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1538,7 +1538,7 @@ static void handle_new_window_attributes_reply(struct x_connection * /*c*/,
auto w = win_maybe_allocate(ps, toplevel,
(xcb_get_window_attributes_reply_t *)reply_or_error);
if (w != NULL && w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
win_map_start(w);
win_map_start(ps, w);
ps->pending_updates = true;
}
}
Expand Down
87 changes: 85 additions & 2 deletions src/wm/win.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ void win_process_primary_flags(session_t *ps, struct win *w) {
win_id(w), w->name, w->to_paint, w->flags);

if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
win_map_start(w);
win_map_start(ps, w);
win_clear_flags(w, WIN_FLAGS_MAPPED);
}

Expand Down Expand Up @@ -2163,9 +2163,83 @@ int win_find_monitor(const struct x_monitors *monitors, const struct win *mw) {
return ret;
}

bool win_set_pending_geometry(struct win *w, struct win_geometry g) {
// We check against pending_g here, because there might have been multiple
// configure notifies in this cycle, or the window could receive multiple updates
// while it's unmapped. `pending_g` should be equal to `g` otherwise.
bool position_changed = w->pending_g.x != g.x || w->pending_g.y != g.y;
bool size_changed = w->pending_g.width != g.width || w->pending_g.height != g.height ||
w->pending_g.border_width != g.border_width;
if (position_changed || size_changed) {
// Queue pending updates
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);

// At least one of the following if's is true
if (position_changed) {
log_trace("Window %#010x position changed, %dx%d -> %dx%d",
win_id(w), w->g.x, w->g.y, g.x, g.y);
w->pending_g.x = g.x;
w->pending_g.y = g.y;
win_set_flags(w, WIN_FLAGS_POSITION_STALE);
}

if (size_changed) {
log_trace("Window %#010x size changed, %dx%d -> %dx%d", win_id(w),
w->g.width, w->g.height, g.width, g.height);
w->pending_g.width = g.width;
w->pending_g.height = g.height;
w->pending_g.border_width = g.border_width;
win_set_flags(w, WIN_FLAGS_SIZE_STALE);
}
}
return position_changed || size_changed;
}

struct win_get_geometry_request {
struct x_async_request_base base;
struct session *ps;
xcb_window_t wid;
};

static void win_handle_get_geometry_reply(struct x_connection * /*c*/,
struct x_async_request_base *req_base,
xcb_raw_generic_event_t *reply_or_error) {
auto req = (struct win_get_geometry_request *)req_base;
auto wid = req->wid;
auto ps = req->ps;
free(req);

if (reply_or_error->response_type == 0) {
log_debug("Failed to get geometry of window %#010x: %s", wid,
x_strerror((xcb_generic_error_t *)reply_or_error));
return;
}

auto cursor = wm_find(ps->wm, wid);
if (cursor == NULL) {
// Rare, window is destroyed then its ID is reused
if (wm_is_consistent(ps->wm)) {
log_error("Successfully fetched geometry of a non-existent "
"window %#010x",
wid);
assert(false);
}
return;
}

auto w = wm_ref_deref(cursor);
if (w == NULL) {
// Not yet managed. Rare, window is destroyed then its ID is reused
return;
}

auto r = (xcb_get_geometry_reply_t *)reply_or_error;
ps->pending_updates |= win_set_pending_geometry(w, win_geometry_from_get_geometry(r));
}

/// Start the mapping of a window. We cannot map immediately since we might need to fade
/// the window in.
void win_map_start(struct win *w) {
void win_map_start(struct session *ps, struct win *w) {
assert(w);

// Don't care about window mapping if it's an InputOnly window
Expand Down Expand Up @@ -2198,6 +2272,15 @@ void win_map_start(struct win *w) {

w->state = WSTATE_MAPPED;
win_set_flags(w, WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_CLIENT_STALE);

auto req = ccalloc(1, struct win_get_geometry_request);
req->base = (struct x_async_request_base){
.callback = win_handle_get_geometry_reply,
.sequence = xcb_get_geometry(ps->c.c, win_id(w)).sequence,
};
req->wid = win_id(w);
req->ps = ps;
x_await_request(&ps->c, &req->base);
}

/// Set flags on a window. Some sanity checks are performed
Expand Down
26 changes: 25 additions & 1 deletion src/wm/win.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ void unmap_win_finish(session_t *ps, struct win *w);
/// Start the destroying of a window. Windows cannot always be destroyed immediately
/// because of fading and such.
void win_destroy_start(session_t *ps, struct win *w);
void win_map_start(struct win *w);
void win_map_start(struct session *ps, struct win *w);
/// Release images bound with a window, set the *_NONE flags on the window. Only to be
/// used when de-initializing the backend outside of win.c
void win_release_images(struct backend_base *backend, struct win *w);
Expand Down Expand Up @@ -468,6 +468,30 @@ bool win_check_flags_all(struct win *w, uint64_t flags);
/// Mark properties as stale for a window
void win_set_properties_stale(struct win *w, const xcb_atom_t *prop, int nprops);

static inline struct win_geometry
win_geometry_from_configure_notify(const xcb_configure_notify_event_t *ce) {
return (struct win_geometry){
.x = ce->x,
.y = ce->y,
.width = ce->width,
.height = ce->height,
.border_width = ce->border_width,
};
}
static inline struct win_geometry
win_geometry_from_get_geometry(const xcb_get_geometry_reply_t *g) {
return (struct win_geometry){
.x = g->x,
.y = g->y,
.width = g->width,
.height = g->height,
.border_width = g->border_width,
};
}
/// Set the pending geometry of a window. And set appropriate flags when the geometry
/// changes.
/// Returns true if the geometry has changed, false otherwise.
bool win_set_pending_geometry(struct win *w, struct win_geometry g);
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct win *w);
/**
* Retrieve frame extents from a window.
Expand Down

0 comments on commit 968ff4c

Please sign in to comment.