From 5b8b505af5d5925ae9e617ee8f3c7a0f9c43409d Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 23 Nov 2023 10:09:48 -0500 Subject: [PATCH] input: Query scene graph for relevant surface/node intersections --- include/sway/scene_descriptor.h | 3 + include/sway/tree/container.h | 13 -- include/sway/tree/root.h | 4 + sway/input/cursor.c | 194 ++++++++--------------- sway/tree/container.c | 265 +------------------------------- sway/tree/root.c | 19 ++- sway/tree/view.c | 7 + 7 files changed, 93 insertions(+), 412 deletions(-) diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h index 057993ec6..8af812192 100644 --- a/include/sway/scene_descriptor.h +++ b/include/sway/scene_descriptor.h @@ -12,6 +12,9 @@ enum sway_scene_descriptor_type { SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_NON_INTERACTIVE, + SWAY_SCENE_DESC_CONTAINER, + SWAY_SCENE_DESC_VIEW, SWAY_SCENE_DESC_DRAG_ICON, }; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ee22a0d02..6f72439cb 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -172,19 +172,6 @@ void container_begin_destroy(struct sway_container *con); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); -/** - * Find a container at the given coordinates. Returns the surface and - * surface-local coordinates of the given layout coordinates if the container - * is a view and the view contains a surface at those coordinates. - */ -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy); - -struct sway_container *tiling_container_at( - struct sway_node *parent, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 0aae89387..003606aab 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -35,6 +35,10 @@ struct sway_root { // staging node will be visible. struct wlr_scene_tree *staging; + // tree containing all layers the compositor will render. Cursor handling + // will end up iterating this tree. + struct wlr_scene_tree *layer_tree; + struct { struct wlr_scene_tree *tiling; struct wlr_scene_tree *floating; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 107424c9a..79373e408 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -37,43 +37,6 @@ static uint32_t get_current_time_msec(void) { return now.tv_sec * 1000 + now.tv_nsec / 1000000; } -static struct wlr_surface *layer_surface_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub) { - return sub; - } - } - return NULL; -} - -static bool surface_is_xdg_popup(struct wlr_surface *surface) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface != NULL && xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && - xdg_surface->popup != NULL; -} - -static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub && surface_is_xdg_popup(sub)) { - return sub; - } - } - return NULL; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -81,119 +44,82 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - // find the output the cursor is on + struct wlr_scene_node *scene_node = NULL; + + struct wlr_scene_node *node; + wl_list_for_each_reverse(node, &root->layer_tree->children, link) { + struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); + + bool non_interactive = scene_descriptor_try_get(&layer->node, + SWAY_SCENE_DESC_NON_INTERACTIVE); + if (non_interactive) { + continue; + } + + scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); + if (scene_node) { + break; + } + } + + if (scene_node) { + // determine what wlr_surface we clicked on + if (scene_node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(scene_node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + + if (scene_surface) { + *surface = scene_surface->surface; + } + } + + // determine what container we clicked on + struct wlr_scene_node *current = scene_node; + while (true) { + struct sway_container *con = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_CONTAINER); + if (!con) { + struct sway_view *view = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_VIEW); + if (view) { + con = view->container; + } + } + + if (con) { + if (!con->view || con->view->surface) { + return &con->node; + } + } + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } + } + + // if we aren't on a container, determine what workspace we are on struct wlr_output *wlr_output = wlr_output_layout_output_at( root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } + struct sway_output *output = wlr_output->data; if (!output || !output->enabled) { // output is being destroyed or is being enabled return NULL; } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); - // layer surfaces on the overlay layer are rendered on top - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy))) { - return NULL; - } - - // check for unmanaged views -#if HAVE_XWAYLAND - struct wl_list *unmanaged = &root->xwayland_unmanaged; - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - - double _sx = lx - unmanaged_surface->lx; - double _sy = ly - unmanaged_surface->ly; - if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { - *surface = xsurface->surface; - *sx = _sx; - *sy = _sy; - return NULL; - } - } -#endif - - if (root->fullscreen_global) { - // Try fullscreen container - struct sway_container *con = tiling_container_at( - &root->fullscreen_global->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - - // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); if (!ws) { return NULL; } - if (ws->fullscreen) { - // Try transient containers - for (int i = 0; i < ws->floating->length; ++i) { - struct sway_container *floater = ws->floating->items[i]; - if (container_is_transient_for(floater, ws->fullscreen)) { - struct sway_container *con = tiling_container_at( - &floater->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - } - } - // Try fullscreen container - struct sway_container *con = - tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - - struct sway_container *c; - if ((c = container_at(ws, lx, ly, surface, sx, sy))) { - return &c->node; - } - - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - return &ws->node; } diff --git a/sway/tree/container.c b/sway/tree/container.c index 307bf9633..8fca6a12d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -16,6 +16,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/scene_descriptor.h" #include "sway/output.h" #include "sway/server.h" #include "sway/surface.h" @@ -94,6 +95,11 @@ struct sway_container *container_create(struct sway_view *view) { c->border.right = alloc_rect_node(c->border.tree, &failed); } + if (!failed && !scene_descriptor_assign(&c->scene_tree->node, + SWAY_SCENE_DESC_CONTAINER, c)) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&c->scene_tree->node); free(c); @@ -239,265 +245,6 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(con->view, "Expected a view")) { - return NULL; - } - struct sway_view *view = con->view; - double view_sx = lx - con->surface_x + view->geometry.x; - double view_sy = ly - con->surface_y + view->geometry.y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - switch (view->type) { -#if HAVE_XWAYLAND - case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(view->surface, - view_sx, view_sy, &_sx, &_sy); - break; -#endif - case SWAY_VIEW_XDG_SHELL: - _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_toplevel->base, - view_sx, view_sy, &_sx, &_sy); - break; - } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return con; - } - return NULL; -} - -/** - * container_at for a container with layout L_TABBED. - */ -static struct sway_container *container_at_tabbed(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - if (!children->length) { - return NULL; - } - - // Tab titles - int title_height = container_titlebar_height(); - if (ly < box.y + title_height) { - int tab_width = box.width / children->length; - int child_index = (lx - box.x) / tab_width; - if (child_index >= children->length) { - child_index = children->length - 1; - } - struct sway_container *child = children->items[child_index]; - return child; - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_STACKED. - */ -static struct sway_container *container_at_stacked(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - - // Title bars - int title_height = container_titlebar_height(); - if (title_height > 0) { - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; - } - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_HORIZ or L_VERT. - */ -static struct sway_container *container_at_linear(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - list_t *children = node_get_children(parent); - for (int i = 0; i < children->length; ++i) { - struct sway_container *child = children->items[i]; - struct sway_container *container = - tiling_container_at(&child->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - return NULL; -} - -static struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - // For outputs with floating containers that overhang the output bounds, - // those at the end of the output list appear on top of floating - // containers from other outputs, so iterate the list in reverse. - for (int i = root->outputs->length - 1; i >= 0; --i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->workspaces->length; ++j) { - struct sway_workspace *ws = output->workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - // Items at the end of the list are on top, so iterate the list in - // reverse. - for (int k = ws->floating->length - 1; k >= 0; --k) { - struct sway_container *floater = ws->floating->items[k]; - struct sway_container *container = - tiling_container_at(&floater->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - } - } - return NULL; -} - -static struct sway_container *view_container_content_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.content_x, - .y = container->pending.content_y, - .width = container->pending.content_width, - .height = container->pending.content_height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -static struct sway_container *view_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.x, - .y = container->pending.y, - .width = container->pending.width, - .height = container->pending.height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -struct sway_container *tiling_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (node_is_view(parent)) { - return view_container_at(parent, lx, ly, surface, sx, sy); - } - if (!node_get_children(parent)) { - return NULL; - } - switch (node_get_layout(parent)) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_NONE: - return NULL; - } - return NULL; -} - -static bool surface_is_popup(struct wlr_surface *surface) { - while (wlr_xdg_surface_try_from_wlr_surface(surface) == NULL) { - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(surface); - if (subsurface == NULL) { - return false; - } - surface = subsurface->parent; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL; -} - -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c; - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - bool is_floating = focus && container_is_floating_or_child(focus); - // Focused view's popups - if (focus && focus->view) { - c = surface_at_view(focus, lx, ly, surface, sx, sy); - if (c && surface_is_popup(*surface)) { - return c; - } - *surface = NULL; - } - // Floating - if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { - return c; - } - // Tiling (focused) - if (focus && focus->view && !is_floating) { - if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { - return c; - } - } - // Tiling (non-focused) - if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { - return c; - } - return NULL; -} - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { diff --git a/sway/tree/root.c b/sway/tree/root.c index e49415663..fbdd9a966 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -9,6 +9,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -44,13 +45,19 @@ struct sway_root *root_create(struct wl_display *wl_display) { bool failed = false; root->staging = alloc_scene_tree(&root_scene->tree, &failed); + root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.tiling = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.floating = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); - root->layers.session_lock = alloc_scene_tree(&root_scene->tree, &failed); + root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); + root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); + root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); + root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); + root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); + root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); + + if (!failed && !scene_descriptor_assign(&root->layers.seat->node, + SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { + failed = true; + } if (failed) { wlr_scene_node_destroy(&root_scene->tree.node); diff --git a/sway/tree/view.c b/sway/tree/view.c index bc968edc9..d349e5faf 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -24,6 +24,7 @@ #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/input/seat.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" #include "sway/surface.h" #include "sway/tree/arrange.h" @@ -40,6 +41,12 @@ bool view_init(struct sway_view *view, enum sway_view_type type, bool failed = false; view->scene_tree = alloc_scene_tree(root->staging, &failed); view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + + if (!failed && !scene_descriptor_assign(&view->scene_tree->node, + SWAY_SCENE_DESC_VIEW, view)) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&view->scene_tree->node); return false;