1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-04-14 17:53:25 -04:00

wm/tree: split wm_tree_new_window and wm_tree_reparent

wm_tree_new_window is splitted into 3 steps: creating the window object
(wm_tree_new_window), adding it to the hash table (wm_tree_add_window),
and attaching it to its parent (wm_tree_attach).

wm_tree_reparent is splitted into wm_tree_detach and then
wm_tree_attach.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-06-25 18:51:27 +01:00
parent de52cbd0e6
commit 7d216657d0
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
3 changed files with 70 additions and 69 deletions

View file

@ -234,33 +234,19 @@ void wm_tree_set_wm_state(struct wm_tree *tree, struct wm_tree_node *node, bool
}
}
struct wm_tree_node *
wm_tree_new_window(struct wm_tree *tree, xcb_window_t id, struct wm_tree_node *parent) {
struct wm_tree_node *wm_tree_new_window(struct wm_tree *tree, xcb_window_t id) {
auto node = ccalloc(1, struct wm_tree_node);
node->id.x = id;
node->id.gen = tree->gen++;
node->has_wm_state = false;
list_init_head(&node->children);
BUG_ON(parent == NULL && tree->nodes != NULL); // Trying to create a second
// root window
HASH_ADD_INT(tree->nodes, id.x, node);
node->parent = parent;
if (parent != NULL) {
list_insert_after(&parent->children, &node->siblings);
if (parent->parent == NULL) {
// Parent is root, this is a new toplevel window
wm_tree_enqueue_change(tree, (struct wm_tree_change){
.toplevel = node->id,
.type = WM_TREE_CHANGE_TOPLEVEL_NEW,
.new_ = node,
});
}
}
return node;
}
void wm_tree_add_window(struct wm_tree *tree, struct wm_tree_node *node) {
HASH_ADD_INT(tree->nodes, id.x, node);
}
static void
wm_tree_refresh_client_and_queue_change(struct wm_tree *tree, struct wm_tree_node *toplevel) {
BUG_ON_NULL(toplevel);
@ -307,6 +293,30 @@ void wm_tree_detach(struct wm_tree *tree, struct wm_tree_node *subroot) {
.killed = zombie,
});
}
subroot->parent = NULL;
}
void wm_tree_attach(struct wm_tree *tree, struct wm_tree_node *child,
struct wm_tree_node *parent) {
BUG_ON_NULL(parent);
BUG_ON(child->parent != NULL); // Trying to attach a window that's already
// attached
child->parent = parent;
list_insert_after(&parent->children, &child->siblings);
auto toplevel = wm_tree_find_toplevel_for(child);
if (child == toplevel) {
// This node could have a stale `->win` if it was a toplevel at
// some point in the past.
child->win = NULL;
wm_tree_enqueue_change(tree, (struct wm_tree_change){
.toplevel = child->id,
.type = WM_TREE_CHANGE_TOPLEVEL_NEW,
.new_ = child,
});
} else {
wm_tree_refresh_client_and_queue_change(tree, toplevel);
}
}
void wm_tree_destroy_window(struct wm_tree *tree, struct wm_tree_node *node) {
@ -386,38 +396,6 @@ void wm_tree_move_to_above(struct wm_tree *tree, struct wm_tree_node *node,
}
}
void wm_tree_reparent(struct wm_tree *tree, struct wm_tree_node *node,
struct wm_tree_node *new_parent) {
BUG_ON(node == NULL);
BUG_ON(new_parent == NULL); // Trying make `node` a root window
if (node->parent == new_parent) {
// Reparent to the same parent moves the window to the top of the stack
wm_tree_move_to_end(tree, node, false);
return;
}
wm_tree_detach(tree, node);
// Reparented window always becomes the topmost child of the new parent
list_insert_after(&new_parent->children, &node->siblings);
node->parent = new_parent;
auto toplevel = wm_tree_find_toplevel_for(node);
if (node == toplevel) {
// This node could have a stale `->win` if it was a toplevel at
// some point in the past.
node->win = NULL;
wm_tree_enqueue_change(tree, (struct wm_tree_change){
.toplevel = node->id,
.type = WM_TREE_CHANGE_TOPLEVEL_NEW,
.new_ = node,
});
} else {
wm_tree_refresh_client_and_queue_change(tree, toplevel);
}
}
void wm_tree_clear(struct wm_tree *tree) {
struct wm_tree_node *cur, *tmp;
HASH_ITER(hh, tree->nodes, cur, tmp) {
@ -438,7 +416,7 @@ TEST_CASE(tree_manipulation) {
struct wm_tree tree;
wm_tree_init(&tree);
wm_tree_new_window(&tree, 1, NULL);
wm_tree_add_window(&tree, wm_tree_new_window(&tree, 1));
auto root = wm_tree_find(&tree, 1);
assert(root != NULL);
assert(root->parent == NULL);
@ -446,9 +424,11 @@ TEST_CASE(tree_manipulation) {
auto change = wm_tree_dequeue_change(&tree);
assert(change.type == WM_TREE_CHANGE_NONE);
wm_tree_new_window(&tree, 2, root);
auto node2 = wm_tree_find(&tree, 2);
auto node2 = wm_tree_new_window(&tree, 2);
wm_tree_add_window(&tree, node2);
wm_tree_attach(&tree, node2, root);
assert(node2 != NULL);
assert(node2 == wm_tree_find(&tree, 2));
assert(node2->parent == root);
change = wm_tree_dequeue_change(&tree);
@ -456,15 +436,16 @@ TEST_CASE(tree_manipulation) {
assert(change.type == WM_TREE_CHANGE_TOPLEVEL_NEW);
assert(wm_treeid_eq(node2->id, change.toplevel));
wm_tree_new_window(&tree, 3, root);
auto node3 = wm_tree_find(&tree, 3);
assert(node3 != NULL);
auto node3 = wm_tree_new_window(&tree, 3);
wm_tree_add_window(&tree, node3);
wm_tree_attach(&tree, node3, root);
change = wm_tree_dequeue_change(&tree);
assert(change.toplevel.x == 3);
assert(change.type == WM_TREE_CHANGE_TOPLEVEL_NEW);
wm_tree_reparent(&tree, node2, node3);
wm_tree_detach(&tree, node2);
wm_tree_attach(&tree, node2, node3);
assert(node2->parent == node3);
assert(node3->children.next == &node2->siblings);
@ -481,8 +462,9 @@ TEST_CASE(tree_manipulation) {
assert(wm_treeid_eq(change.client.old, WM_TREEID_NONE));
assert(change.client.new_.x == 2);
wm_tree_new_window(&tree, 4, node3);
auto node4 = wm_tree_find(&tree, 4);
auto node4 = wm_tree_new_window(&tree, 4);
wm_tree_add_window(&tree, node4);
wm_tree_attach(&tree, node4, node3);
change = wm_tree_dequeue_change(&tree);
assert(change.type == WM_TREE_CHANGE_NONE);
@ -500,7 +482,9 @@ TEST_CASE(tree_manipulation) {
// Test window ID reuse
wm_tree_destroy_window(&tree, node4);
node4 = wm_tree_new_window(&tree, 4, node3);
node4 = wm_tree_new_window(&tree, 4);
wm_tree_add_window(&tree, node4);
wm_tree_attach(&tree, node4, node3);
wm_tree_set_wm_state(&tree, node4, true);
change = wm_tree_dequeue_change(&tree);
@ -509,7 +493,9 @@ TEST_CASE(tree_manipulation) {
assert(change.client.old.x == 4);
assert(change.client.new_.x == 4);
auto node5 = wm_tree_new_window(&tree, 5, root);
auto node5 = wm_tree_new_window(&tree, 5);
wm_tree_add_window(&tree, node5);
wm_tree_attach(&tree, node5, root);
wm_tree_destroy_window(&tree, node5);
change = wm_tree_dequeue_change(&tree);
assert(change.type == WM_TREE_CHANGE_NONE); // Changes cancelled out

View file

@ -286,7 +286,16 @@ void wm_reparent(struct wm *wm, xcb_window_t wid, xcb_window_t parent) {
return;
}
wm_tree_reparent(&wm->tree, window, new_parent);
if (new_parent == window->parent) {
log_debug("Reparenting window %#010x to its current parent %#010x, "
"moving it to the top.",
wid, parent);
wm_tree_move_to_end(&wm->tree, window, false);
return;
}
wm_tree_detach(&wm->tree, window);
wm_tree_attach(&wm->tree, window, new_parent);
}
void wm_set_has_wm_state(struct wm *wm, struct wm_ref *cursor, bool has_wm_state) {
@ -313,7 +322,9 @@ void wm_import_incomplete(struct wm *wm, xcb_window_t wid, xcb_window_t parent)
}
}
log_debug("Importing window %#010x with parent %#010x", wid, parent);
auto new = wm_tree_new_window(&wm->tree, wid, parent_cursor);
auto new = wm_tree_new_window(&wm->tree, wid);
wm_tree_add_window(&wm->tree, new);
wm_tree_attach(&wm->tree, new, parent_cursor);
dynarr_push(wm->incompletes, new);
if (parent == XCB_NONE) {
BUG_ON(wm->root != NULL); // Can't have more than one root
@ -383,7 +394,9 @@ static bool wm_complete_import_subtree(struct wm *wm, struct x_connection *c,
children[i], curr->id.x);
wm_tree_destroy_window(&wm->tree, existing);
}
existing = wm_tree_new_window(&wm->tree, children[i], curr);
existing = wm_tree_new_window(&wm->tree, children[i]);
wm_tree_add_window(&wm->tree, existing);
wm_tree_attach(&wm->tree, existing, curr);
wm_complete_import_single(wm, c, atoms, existing);
}
free(tree);

View file

@ -24,6 +24,7 @@ struct wm_tree {
uint64_t gen;
/// wm tree nodes indexed by their X window ID.
struct wm_tree_node *nodes;
struct wm_tree_node *root;
struct list_node changes;
struct list_node free_changes;
@ -85,15 +86,16 @@ struct wm_tree_node *attr_pure wm_tree_next(struct wm_tree_node *node,
/// permitted, and the root window cannot be destroyed once created, until
/// `wm_tree_clear` is called. If `parent` is not NULL, the new node will be put at the
/// top of the stacking order among its siblings.
struct wm_tree_node *
wm_tree_new_window(struct wm_tree *tree, xcb_window_t id, struct wm_tree_node *parent);
struct wm_tree_node *wm_tree_new_window(struct wm_tree *tree, xcb_window_t id);
void wm_tree_add_window(struct wm_tree *tree, struct wm_tree_node *node);
void wm_tree_destroy_window(struct wm_tree *tree, struct wm_tree_node *node);
/// Detach the subtree rooted at `subroot` from `tree`. The subtree root is removed from
/// its parent, and the disconnected tree nodes won't be able to be found via
/// `wm_tree_find`. Relevant events will be generated.
void wm_tree_detach(struct wm_tree *tree, struct wm_tree_node *subroot);
void wm_tree_reparent(struct wm_tree *tree, struct wm_tree_node *node,
struct wm_tree_node *new_parent);
/// Attach `node` to `parent`. `node` becomes the topmost child of `parent`.
void wm_tree_attach(struct wm_tree *tree, struct wm_tree_node *child,
struct wm_tree_node *parent);
void wm_tree_move_to_above(struct wm_tree *tree, struct wm_tree_node *node,
struct wm_tree_node *other);
/// Move `node` to the top or the bottom of its parent's child window stack.