Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I fixed every single bug in wm #1334

Merged
merged 22 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ed619f3
x: wrap some libxcb functions
yshui Sep 3, 2024
a7ac0f9
wm: fix confusion caused by window ID reuse
yshui Sep 3, 2024
eb2ea15
wm: fix reparented window not being imported
yshui Sep 3, 2024
9e3b790
wm: remove a BUG_ON based on an incorrect assumption
yshui Sep 3, 2024
e8ebb52
wm: avoid ABA problem when importing a window
yshui Sep 4, 2024
0aa6202
wm: set StructureNotifyMask on window
yshui Sep 4, 2024
f36c5e3
wm: children don't get to choose who their parents are
yshui Sep 4, 2024
7738c5b
wm: remove an incorrect assertion
yshui Sep 4, 2024
5912359
wm: fix an incorrect BUG_ON
yshui Sep 4, 2024
cd231e4
wm: don't reap orphans
yshui Sep 4, 2024
c1987ab
core: don't manage window if it has changed while awaiting X reply
yshui Sep 4, 2024
7431763
wm/tree: bump node's gen if it's removed from toplevel
yshui Sep 4, 2024
e045cd0
x: simplify x_poll_for_event
yshui Sep 5, 2024
2692fe7
x: support async requests with no reply
yshui Sep 5, 2024
7b7168b
wm: make setting event masks async
yshui Sep 5, 2024
1a4948d
wm: remove unnecessary x_flush
yshui Sep 5, 2024
28281bf
wm: update client window when a window loses/gains toplevel status
yshui Sep 5, 2024
8ddab6d
wm: setting WM_STATE on toplevel should trigger refresh
yshui Sep 5, 2024
f8828eb
wm: don't report children of the orphan root as toplevel
yshui Sep 5, 2024
9ab6447
wm: update wm_is_consistent condition
yshui Sep 5, 2024
cd5b136
wm: avoid repeated unnecessary wm_tree_find
yshui Sep 5, 2024
570381f
wm: remove an unreachable branch
yshui Sep 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/atom.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <xcb/xcb.h>

#include "atom.h"
#include "common.h"
#include "compiler.h"
#include "log.h"
#include "utils/cache.h"
Expand Down
41 changes: 35 additions & 6 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@
/// returned could not be found.
static void
update_ewmh_active_win(struct x_connection * /*c*/, struct x_async_request_base *req_base,
xcb_raw_generic_event_t *reply_or_error) {
const xcb_raw_generic_event_t *reply_or_error) {
auto ps = ((struct ev_ewmh_active_win_request *)req_base)->ps;
free(req_base);

Expand All @@ -219,7 +219,7 @@
}

// Search for the window
auto reply = (xcb_get_property_reply_t *)reply_or_error;
auto reply = (const xcb_get_property_reply_t *)reply_or_error;

Check warning on line 222 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L222

Added line #L222 was not covered by tests
if (reply->type == XCB_NONE || xcb_get_property_value_length(reply) < 4) {
log_debug("EWMH _NET_ACTIVE_WINDOW not set.");
return;
Expand Down Expand Up @@ -248,7 +248,7 @@
* @return struct _win of currently focused window, NULL if not found
*/
static void recheck_focus(struct x_connection * /*c*/, struct x_async_request_base *req_base,
xcb_raw_generic_event_t *reply_or_error) {
const xcb_raw_generic_event_t *reply_or_error) {
auto ps = ((struct ev_ewmh_active_win_request *)req_base)->ps;
free(req_base);

Expand All @@ -268,7 +268,7 @@
return;
}

auto reply = (xcb_get_input_focus_reply_t *)reply_or_error;
auto reply = (const xcb_get_input_focus_reply_t *)reply_or_error;
xcb_window_t wid = reply->focus;
log_debug("Current focused window is %#010x", wid);
if (wid == XCB_NONE || wid == XCB_INPUT_FOCUS_POINTER_ROOT ||
Expand Down Expand Up @@ -399,6 +399,10 @@
return;
}

if (ev->window == ev->event) {
return;
}

if (ev->window == ps->c.screen_info->root) {
configure_root(ps);
} else {
Expand All @@ -408,10 +412,20 @@

static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
log_debug("{ event: %#010x, id: %#010x }", ev->event, ev->window);
wm_destroy(ps->wm, ev->window);
// If we hit an ABA problem, it is possible to get a DestroyNotify event from a
// parent for its child, but not from the child for itself.
if (ev->event != ev->window) {
wm_disconnect(ps->wm, ev->window, ev->event, XCB_NONE);
} else {
wm_destroy(ps->wm, ev->window);
}
}

static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
if (ev->window == ev->event) {
return;
}

// Unmap overlay window if it got mapped but we are currently not
// in redirected state.
if (ps->overlay && ev->window == ps->overlay) {
Expand Down Expand Up @@ -461,6 +475,10 @@
return;
}

if (ev->event == ev->window) {
return;
}

auto cursor = wm_find(ps->wm, ev->window);
if (cursor == NULL) {
if (wm_is_consistent(ps->wm)) {
Expand All @@ -479,10 +497,21 @@
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d, "
"send_event: %#010x",
ev->window, ev->parent, ev->override_redirect, ev->event);
wm_reparent(ps->wm, ev->window, ev->parent);
if (ev->event == ev->window) {
return;
}
if (ev->parent != ev->event) {
wm_disconnect(ps->wm, ev->window, ev->event, ev->parent);
} else {
wm_reparent(ps->wm, &ps->c, ps->atoms, ev->window, ev->parent);
}
}

static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
if (ev->event == ev->window) {
return;

Check warning on line 512 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L511-L512

Added lines #L511 - L512 were not covered by tests
}

auto cursor = wm_find(ps->wm, ev->window);

if (cursor == NULL) {
Expand Down
33 changes: 21 additions & 12 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1488,16 +1488,16 @@
struct new_window_attributes_request {
struct x_async_request_base base;
struct session *ps;
xcb_window_t wid;
wm_treeid id;
};

static void handle_new_window_attributes_reply(struct x_connection * /*c*/,
struct x_async_request_base *req_base,
xcb_raw_generic_event_t *reply_or_error) {
const xcb_raw_generic_event_t *reply_or_error) {
auto req = (struct new_window_attributes_request *)req_base;
auto ps = req->ps;
auto wid = req->wid;
auto new_window = wm_find(ps->wm, wid);
auto id = req->id;
auto new_window = wm_find(ps->wm, id.x);
free(req);

if (reply_or_error == NULL) {
Expand All @@ -1508,7 +1508,7 @@
if (reply_or_error->response_type == 0) {
log_debug("Failed to get window attributes for newly created window "
"%#010x",
wid);
id.x);
return;
}

Expand All @@ -1517,17 +1517,26 @@
// created with the same window ID before this request completed, and the
// latter window isn't in our tree yet.
if (wm_is_consistent(ps->wm)) {
log_error("Newly created window %#010x is not in the window tree", wid);
log_error("Newly created window %#010x is not in the window tree",

Check warning on line 1520 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1520

Added line #L1520 was not covered by tests
id.x);
assert(false);
}
return;
}

auto current_id = wm_ref_treeid(new_window);
if (!wm_treeid_eq(current_id, id)) {
log_debug("Window %#010x was not the window we queried anymore. Current "

Check warning on line 1529 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1529

Added line #L1529 was not covered by tests
"gen %" PRIu64 ", expected %" PRIu64,
id.x, current_id.gen, id.gen);
return;

Check warning on line 1532 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1532

Added line #L1532 was not covered by tests
}

auto toplevel = wm_ref_toplevel_of(ps->wm, new_window);
if (toplevel != new_window) {
log_debug("Newly created window %#010x was moved away from toplevel "
"while we were waiting for its attributes",
wid);
id.x);
return;
}
if (wm_ref_deref(toplevel) != NULL) {
Expand All @@ -1538,12 +1547,12 @@
// toplevel. But there is another get attributes request sent the
// second time it became a toplevel. When we get the reply for the second
// request, we will reach here.
log_debug("Newly created window %#010x is already managed", wid);
log_debug("Newly created window %#010x is already managed", id.x);

Check warning on line 1550 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1550

Added line #L1550 was not covered by tests
return;
}

auto w = win_maybe_allocate(ps, toplevel,
(xcb_get_window_attributes_reply_t *)reply_or_error);
auto w = win_maybe_allocate(
ps, toplevel, (const xcb_get_window_attributes_reply_t *)reply_or_error);
if (w != NULL && w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
win_set_flags(w, WIN_FLAGS_MAPPED);
ps->pending_updates = true;
Expand All @@ -1566,11 +1575,11 @@
// number of things could happen before we get the reply. The
// window can be reparented, destroyed, then get its window ID
// reused, etc.
req->wid = wm_ref_win_id(wm_change.toplevel);
req->id = wm_ref_treeid(wm_change.toplevel);
req->ps = ps;
req->base.callback = handle_new_window_attributes_reply,
req->base.sequence =
xcb_get_window_attributes(ps->c.c, req->wid).sequence;
xcb_get_window_attributes(ps->c.c, req->id.x).sequence;
x_await_request(&ps->c, &req->base);
break;
case WM_TREE_CHANGE_TOPLEVEL_KILLED:
Expand Down
2 changes: 1 addition & 1 deletion src/region.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ static inline void _resize_region(const region_t *region, region_t *output, int
int nrects;
int nnewrects = 0;
const rect_t *rects = pixman_region32_rectangles((region_t *)region, &nrects);
auto newrects = ccalloc(nrects, rect_t);
rect_t *newrects = calloc((size_t)nrects, sizeof(rect_t));
for (int i = 0; i < nrects; i++) {
int x1 = rects[i].x1 - dx;
int y1 = rects[i].y1 - dy;
Expand Down
25 changes: 19 additions & 6 deletions src/wm/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@

/// Find a client window under a toplevel window. If there are multiple windows with
/// `WM_STATE` set under the toplevel window, we will return an arbitrary one.
static struct wm_tree_node *attr_pure wm_tree_find_client(struct wm_tree_node *subroot) {
struct wm_tree_node *attr_pure wm_tree_find_client(struct wm_tree_node *subroot) {
if (subroot->has_wm_state) {
log_debug("Toplevel %#010x has WM_STATE set, weird. Using itself as its "
"client window.",
Expand Down Expand Up @@ -250,14 +250,13 @@
node->has_wm_state = has_wm_state;
BUG_ON(node->parent == NULL); // Trying to set WM_STATE on the root window

struct wm_tree_node *toplevel;
for (auto cur = node; cur->parent != NULL; cur = cur->parent) {
toplevel = cur;
struct wm_tree_node *toplevel = wm_tree_find_toplevel_for(tree, node);
if (toplevel == NULL) {
return;

Check warning on line 255 in src/wm/tree.c

View check run for this annotation

Codecov / codecov/patch

src/wm/tree.c#L255

Added line #L255 was not covered by tests
}

if (toplevel == node) {
log_debug("Setting WM_STATE on a toplevel window %#010x, weird.", node->id.x);
return;
}

if (!has_wm_state && toplevel->client_window == node) {
Expand All @@ -284,6 +283,9 @@
node->id.x = id;
node->id.gen = tree->gen++;
node->has_wm_state = false;
node->receiving_events = false;
node->is_zombie = false;
node->visited = false;
node->leader = id;
list_init_head(&node->children);
return node;
Expand All @@ -307,6 +309,9 @@
if (new_client != NULL) {
new_client_id = new_client->id;
}
log_debug("Toplevel window %#010x had client window %#010x, now has "
"%#010x.",
toplevel->id.x, old_client_id.x, new_client_id.x);
toplevel->client_window = new_client;
wm_tree_enqueue_client_change(tree, toplevel, old_client_id, new_client_id);
}
Expand All @@ -325,6 +330,7 @@
}
} else {
// Detached a toplevel, create a zombie for it
log_debug("Detaching toplevel window %#010x.", subroot->id.x);
zombie = ccalloc(1, struct wm_tree_node);
zombie->parent = subroot->parent;
zombie->id = subroot->id;
Expand All @@ -334,6 +340,12 @@
if (wm_tree_enqueue_toplevel_killed(tree, subroot->id, zombie)) {
zombie = NULL;
}

// Gen bump must happen after enqueuing the change, because otherwise the
// kill change won't cancel out a previous new change because the IDs will
// be different.
subroot->id.gen = tree->gen++;
subroot->client_window = NULL;
}
subroot->parent = NULL;
return zombie;
Expand All @@ -356,7 +368,8 @@
auto toplevel = wm_tree_find_toplevel_for(tree, child);
if (child == toplevel) {
wm_tree_enqueue_toplevel_new(tree, child);
} else if (toplevel != NULL) {
}
if (toplevel != NULL) {
wm_tree_refresh_client_and_queue_change(tree, toplevel);
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/wm/win.c
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,7 @@ void free_win_res(session_t *ps, struct win *w) {
/// Query the Xorg for information about window `win`, and assign a window to `cursor` if
/// this window should be managed.
struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
xcb_get_window_attributes_reply_t *attrs) {
const xcb_get_window_attributes_reply_t *attrs) {
static const struct win win_def = {
.frame_opacity = 1.0,
.in_openclose = true, // set to false after first map is done,
Expand Down Expand Up @@ -1364,8 +1364,9 @@ struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
}

// Set window event mask
uint32_t frame_event_mask =
XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
uint32_t frame_event_mask = XCB_EVENT_MASK_PROPERTY_CHANGE |
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_STRUCTURE_NOTIFY;
if (!ps->o.use_ewmh_active_win) {
frame_event_mask |= XCB_EVENT_MASK_FOCUS_CHANGE;
}
Expand Down Expand Up @@ -2087,7 +2088,7 @@ struct win_get_geometry_request {

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) {
const 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;
Expand Down Expand Up @@ -2122,7 +2123,7 @@ static void win_handle_get_geometry_reply(struct x_connection * /*c*/,
return;
}

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

Expand Down
2 changes: 1 addition & 1 deletion src/wm/win.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ region_t win_get_region_frame_local_by_val(const struct win *w);
/// Query the Xorg for information about window `win`
/// `win` pointer might become invalid after this function returns
struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
xcb_get_window_attributes_reply_t *attrs);
const xcb_get_window_attributes_reply_t *attrs);

/**
* Release a destroyed window that is no longer needed.
Expand Down
Loading