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

Eventually consistent tree #1281

Merged
merged 7 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions src/c2.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,11 +442,11 @@ TEST_CASE(c2_parse) {
TEST_STREQUAL3(str, "name = \"xterm\"", len);

struct wm *wm = wm_new();
wm_import_incomplete(wm, 1, XCB_NONE);
struct wm_ref *node = wm_new_mock_window(wm, 1);

struct win test_win = {
.name = "xterm",
.tree_ref = wm_find(wm, 1),
.tree_ref = node,
};
TEST_TRUE(c2_match(state, &test_win, cond, NULL));
c2_list_postprocess(state, NULL, cond);
Expand Down Expand Up @@ -568,6 +568,7 @@ TEST_CASE(c2_parse) {
TEST_STREQUAL3(str, rule, len);
c2_list_free(&cond, NULL);

wm_free_mock_window(wm, test_win.tree_ref);
wm_free(wm);
}

Expand Down
73 changes: 40 additions & 33 deletions src/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,14 @@
}

static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
if (wm_is_wid_masked(ps->wm, ev->parent)) {
return;
auto parent = wm_find(ps->wm, ev->parent);
if (parent == NULL) {
log_error("Create notify received for window %#010x, but its parent "

Check warning on line 208 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L208

Added line #L208 was not covered by tests
"window %#010x is not in our tree. Expect malfunction.",
ev->window, ev->parent);
assert(false);

Check warning on line 211 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L211

Added line #L211 was not covered by tests
}
wm_import_incomplete(ps->wm, ev->window, ev->parent);
wm_import_start(ps->wm, &ps->c, ps->atoms, ev->window, parent);
}

/// Handle configure event of a regular window
Expand All @@ -215,14 +219,19 @@
auto below = wm_find(ps->wm, ce->above_sibling);

if (!cursor) {
log_error("Configure event received for unknown window %#010x", ce->window);
if (wm_is_consistent(ps->wm)) {
log_error("Configure event received for unknown window %#010x",

Check warning on line 223 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L222-L223

Added lines #L222 - L223 were not covered by tests
ce->window);
assert(false);

Check warning on line 225 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L225

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

if (below == NULL && ce->above_sibling != XCB_NONE) {
log_error("Configure event received for window %#010x, but its sibling "
"window %#010x is not in our tree. Expect malfunction.",
ce->window, ce->above_sibling);
assert(false);

Check warning on line 234 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L234

Added line #L234 was not covered by tests
} else if (below != NULL) {
wm_stack_move_to_above(ps->wm, cursor, below);
} else {
Expand Down Expand Up @@ -288,7 +297,7 @@

if (ev->window == ps->c.screen_info->root) {
set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
} else if (!wm_is_wid_masked(ps->wm, ev->event)) {
} else {
configure_win(ps, ev);
}
}
Expand All @@ -314,15 +323,14 @@
return;
}

if (wm_is_wid_masked(ps->wm, ev->event)) {
return;
}

auto cursor = wm_find(ps->wm, ev->window);
if (cursor == NULL) {
log_error("Map event received for unknown window %#010x, overlay is "
"%#010x",
ev->window, ps->overlay);
if (wm_is_consistent(ps->wm)) {
log_debug("Map event received for unknown window %#010x, overlay "

Check warning on line 329 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L328-L329

Added lines #L328 - L329 were not covered by tests
"is %#010x",
ev->window, ps->overlay);
assert(false);

Check warning on line 332 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L332

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

Expand All @@ -349,13 +357,12 @@
return;
}

if (wm_is_wid_masked(ps->wm, ev->event)) {
return;
}

auto cursor = wm_find(ps->wm, ev->window);
if (cursor == NULL) {
log_error("Unmap event received for unknown window %#010x", ev->window);
if (wm_is_consistent(ps->wm)) {
log_error("Unmap event received for unknown window %#010x", ev->window);
assert(false);

Check warning on line 364 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L362-L364

Added lines #L362 - L364 were not covered by tests
}
return;
}
auto w = wm_ref_deref(cursor);
Expand All @@ -372,14 +379,14 @@
}

static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
if (wm_is_wid_masked(ps->wm, ev->event)) {
return;
}

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

if (cursor == NULL) {
log_error("Circulate event received for unknown window %#010x", ev->window);
if (wm_is_consistent(ps->wm)) {
log_debug("Circulate event received for unknown window %#010x",

Check warning on line 386 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L385-L386

Added lines #L385 - L386 were not covered by tests
ev->window);
assert(false);

Check warning on line 388 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L388

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

Expand Down Expand Up @@ -451,25 +458,30 @@
return;
}

if (wm_is_wid_masked(ps->wm, ev->window)) {
return;
}

ps->pending_updates = true;
auto cursor = wm_find(ps->wm, ev->window);
if (cursor == NULL) {
log_error("Property notify received for unknown window %#010x", ev->window);
if (wm_is_consistent(ps->wm)) {
log_error("Property notify received for unknown window %#010x",

Check warning on line 465 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L464-L465

Added lines #L464 - L465 were not covered by tests
ev->window);
assert(false);

Check warning on line 467 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L467

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

auto toplevel_cursor = wm_ref_toplevel_of(cursor);
auto toplevel_cursor = wm_ref_toplevel_of(ps->wm, cursor);
if (ev->atom == ps->atoms->aWM_STATE) {
log_debug("WM_STATE changed for window %#010x (%s): %s", ev->window,
ev_window_name(ps, ev->window),
ev->state == XCB_PROPERTY_DELETE ? "deleted" : "set");
wm_set_has_wm_state(ps->wm, cursor, ev->state != XCB_PROPERTY_DELETE);
}

if (toplevel_cursor == NULL) {
assert(!wm_is_consistent(ps->wm));
return;

Check warning on line 482 in src/event.c

View check run for this annotation

Codecov / codecov/patch

src/event.c#L481-L482

Added lines #L481 - L482 were not covered by tests
}

// We only care if the property is set on the toplevel itself, or on its
// client window if it has one. WM_STATE is an exception, it is handled
// always because it is what determines if a window is a client window.
Expand Down Expand Up @@ -629,10 +641,6 @@
}

void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
if (XCB_EVENT_RESPONSE_TYPE(ev) != KeymapNotify) {
x_discard_pending_errors(&ps->c, ev->full_sequence);
}

xcb_window_t wid = ev_window(ps, ev);
if (ev->response_type != ps->damage_event + XCB_DAMAGE_NOTIFY) {
log_debug("event %10.10s serial %#010x window %#010x \"%s\"",
Expand Down Expand Up @@ -702,7 +710,6 @@
case XCB_SELECTION_CLEAR:
ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
break;
case 0: x_handle_error(&ps->c, (xcb_generic_error_t *)ev); break;
default:
if (ps->shape_exists && ev->response_type == ps->shape_event) {
ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
Expand Down
1 change: 1 addition & 0 deletions src/event.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2019, Yuxuan Shui <[email protected]>

#pragma once
#include <xcb/xcb.h>

#include "common.h"
Expand Down
37 changes: 33 additions & 4 deletions src/inspect.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
return NULL;
}

auto toplevel = wm_ref_toplevel_of(cursor);
auto toplevel = wm_ref_toplevel_of(wm, cursor);
BUG_ON_NULL(toplevel);

Check warning on line 38 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L37-L38

Added lines #L37 - L38 were not covered by tests
struct win *w = ccalloc(1, struct win);
w->state = WSTATE_MAPPED;
w->tree_ref = toplevel;
log_debug("Toplevel is %#010x", wm_ref_win_id(toplevel));
log_debug("Client is %#010x", win_client_id(w, true));

Check warning on line 43 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L42-L43

Added lines #L42 - L43 were not covered by tests
win_update_wintype(c, atoms, w);
win_update_frame_extents(c, atoms, w, win_client_id(w, /*fallback_to_self=*/true),
options->frame_opacity);
Expand Down Expand Up @@ -195,14 +198,40 @@
return 1;
}

auto atoms attr_unused = init_atoms(c.c);
auto atoms = init_atoms(c.c);

Check warning on line 201 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L201

Added line #L201 was not covered by tests
auto state = c2_state_new(atoms);
options_postprocess_c2_lists(state, &c, &options);

struct wm *wm = wm_new();

wm_import_incomplete(wm, c.screen_info->root, XCB_NONE);
wm_complete_import(wm, &c, atoms);
wm_import_start(wm, &c, atoms, c.screen_info->root, NULL);

Check warning on line 207 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L207

Added line #L207 was not covered by tests
// Process events until the window tree is consistent
while (x_has_pending_requests(&c)) {
auto ev = x_poll_for_event(&c);
if (ev == NULL) {
continue;

Check warning on line 212 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L209-L212

Added lines #L209 - L212 were not covered by tests
}
switch (ev->response_type) {
case XCB_CREATE_NOTIFY:;
auto create = (xcb_create_notify_event_t *)ev;
auto parent = wm_find(wm, create->parent);
wm_import_start(wm, &c, atoms,

Check warning on line 218 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L214-L218

Added lines #L214 - L218 were not covered by tests
((xcb_create_notify_event_t *)ev)->window, parent);
break;
case XCB_DESTROY_NOTIFY:
wm_destroy(wm, ((xcb_destroy_notify_event_t *)ev)->window);
break;
case XCB_REPARENT_NOTIFY:;
auto reparent = (xcb_reparent_notify_event_t *)ev;
wm_reparent(wm, reparent->window, reparent->parent);
break;
default:

Check warning on line 228 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L220-L228

Added lines #L220 - L228 were not covered by tests
// Ignore ConfigureNotify and CirculateNotify, because we don't
// use stacking order for window rules.
break;

Check warning on line 231 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L231

Added line #L231 was not covered by tests
}
free(ev);

Check warning on line 233 in src/inspect.c

View check run for this annotation

Codecov / codecov/patch

src/inspect.c#L233

Added line #L233 was not covered by tests
}

auto target = select_window(&c);
log_info("Target window: %#x", target);
Expand Down
78 changes: 24 additions & 54 deletions src/picom.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,11 @@
return;
}

cursor = wm_ref_toplevel_of(cursor);
cursor = wm_ref_toplevel_of(ps->wm, cursor);
if (cursor == NULL) {
assert(!wm_is_consistent(ps->wm));
return;

Check warning on line 515 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L514-L515

Added lines #L514 - L515 were not covered by tests
}

// And we set the focus state here
auto w = wm_ref_deref(cursor);
Expand Down Expand Up @@ -1558,8 +1562,8 @@
}

xcb_generic_event_t *ev;
while ((ev = xcb_poll_for_event(ps->c.c))) {
ev_handle(ps, ev);
while ((ev = x_poll_for_event(&ps->c))) {
ev_handle(ps, (xcb_generic_event_t *)ev);
free(ev);
};
int err = xcb_connection_has_error(ps->c.c);
Expand All @@ -1575,10 +1579,6 @@
}

static void handle_new_windows(session_t *ps) {
while (!wm_complete_import(ps->wm, &ps->c, ps->atoms)) {
handle_x_events(ps);
}

// Check tree changes first, because later property updates need accurate
// client window information
struct win *w = NULL;
Expand Down Expand Up @@ -1666,18 +1666,17 @@
ps->server_grabbed = true;

// Catching up with X server
/// Handle all events X server has sent us so far, so that our internal state will
/// catch up with the X server's state. It only makes sense to call this function
/// in the X critical section, otherwise we will be chasing a moving goal post.
///
/// (Disappointingly, grabbing the X server doesn't actually prevent the X server
/// state from changing. It should, but in practice we have observed window still
/// being destroyed while we have the server grabbed. This is very disappointing
/// and forces us to use some hacky code to recover when we discover we are
/// out-of-sync.)
// Handle all events X server has sent us so far, so that our internal state will
// catch up with the X server's state. It only makes sense to call this function
// in the X critical section, otherwise we will be chasing a moving goal post.
//
// (Disappointingly, grabbing the X server doesn't actually prevent the X server
// state from changing. It should, but in practice we have observed window still
// being destroyed while we have the server grabbed. This is very disappointing
// and forces us to use some hacky code to recover when we discover we are
// out-of-sync.)
handle_x_events(ps);
if (ps->pending_updates || wm_has_incomplete_imports(ps->wm) ||
wm_has_tree_changes(ps->wm)) {
if (ps->pending_updates || wm_has_tree_changes(ps->wm)) {
log_debug("Delayed handling of events, entering critical section");
// Process new windows, and maybe allocate struct managed_win for them
handle_new_windows(ps);
Expand Down Expand Up @@ -1804,9 +1803,9 @@
log_fatal("Couldn't find specified benchmark window.");
exit(1);
}
w = wm_ref_toplevel_of(w);
w = wm_ref_toplevel_of(ps->wm, w);

Check warning on line 1806 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1806

Added line #L1806 was not covered by tests

auto win = wm_ref_deref(w);
auto win = w == NULL ? NULL : wm_ref_deref(w);

Check warning on line 1808 in src/picom.c

View check run for this annotation

Codecov / codecov/patch

src/picom.c#L1808

Added line #L1808 was not covered by tests
if (win != NULL) {
add_damage_from_win(ps, win);
} else {
Expand Down Expand Up @@ -1978,7 +1977,7 @@

static void x_event_callback(EV_P attr_unused, ev_io *w, int revents attr_unused) {
session_t *ps = (session_t *)w;
xcb_generic_event_t *ev = xcb_poll_for_event(ps->c.c);
xcb_generic_event_t *ev = x_poll_for_event(&ps->c);
if (ev) {
ev_handle(ps, ev);
free(ev);
Expand Down Expand Up @@ -2511,38 +2510,7 @@
}

ps->wm = wm_new();
wm_import_incomplete(ps->wm, ps->c.screen_info->root, XCB_NONE);

e = xcb_request_check(ps->c.c, xcb_grab_server_checked(ps->c.c));
if (e) {
log_fatal_x_error(e, "Failed to grab X server");
free(e);
goto err;
}

ps->server_grabbed = true;

// We are going to pull latest information from X server now, events sent by X
// earlier is irrelevant at this point.
// A better solution is probably grabbing the server from the very start. But I
// think there still could be race condition that mandates discarding the events.
x_discard_events(&ps->c);

wm_complete_import(ps->wm, &ps->c, ps->atoms);

e = xcb_request_check(ps->c.c, xcb_ungrab_server_checked(ps->c.c));
if (e) {
log_fatal_x_error(e, "Failed to ungrab server");
free(e);
goto err;
}

ps->server_grabbed = false;

log_debug("Initial stack:");
wm_stack_foreach(ps->wm, i) {
log_debug(" %#010x", wm_ref_win_id(i));
}
wm_import_start(ps->wm, &ps->c, ps->atoms, ps->c.screen_info->root, NULL);

ps->command_builder = command_builder_new();
ps->expose_rects = dynarr_new(rect_t, 0);
Expand Down Expand Up @@ -2737,8 +2705,10 @@
ev_signal_stop(ps->loop, &ps->usr1_signal);
ev_signal_stop(ps->loop, &ps->int_signal);

wm_free(ps->wm);
// The X connection could hold references to wm if there are pending async
// requests. Therefore the wm must be freed after the X connection.
free_x_connection(&ps->c);
wm_free(ps->wm);
}

/**
Expand Down
Loading