From e84166a734c5c05b5b473520ffe82360aa95e1e2 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 2 Jun 2018 12:58:24 +1000 Subject: [PATCH] Fix popup rendering order and popup container_at issues This adds proper support for popups overhanging their swayc bounds. For locating a surface at a given coordinate, it iterates all the floating popups first, then floating toplevels and decorations, then tiled popups, then tiled toplevels and decorations. For rendering popups, it renders all toplevels and decorations first, then popups afterwards. --- include/sway/tree/container.h | 12 +-- sway/desktop/output.c | 58 ++++++++++++++- sway/input/cursor.c | 2 +- sway/tree/container.c | 133 +++++++++++++++++++++++++--------- 4 files changed, 159 insertions(+), 46 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 7ed6aab1a..f37bbd793 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -169,16 +169,16 @@ struct sway_container *container_parent(struct sway_container *container, * 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_container *container, - double ox, double oy, struct wlr_surface **surface, +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); /** - * Same as container_at, but only checks floating views and expects coordinates - * to be layout coordinates, as that's what floating views use. + * Same as container_at, but only checks for popups only. */ -struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); +struct sway_container *popup_at(struct sway_container *workspace, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy); /** * Apply the function for each descendant of the container breadth first. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index acc9caaec..09e5424f4 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -70,6 +70,7 @@ struct render_data { struct sway_output *output; pixman_region32_t *damage; float alpha; + bool render_popups; }; static bool get_surface_box(struct root_geometry *geo, @@ -217,9 +218,32 @@ damage_finish: pixman_region32_fini(&damage); } +static bool surface_is_popup(struct wlr_surface *surface) { + if (wlr_surface_is_xwayland_surface(surface)) { + struct wlr_xwayland_surface *xsurface = + wlr_xwayland_surface_from_wlr_surface(surface); + return wlr_xwayland_surface_is_unmanaged(xsurface); + } + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; + } + if (wlr_surface_is_xdg_surface_v6(surface)) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + return xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP; + } + // Layer surface + return false; +} + static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *_data) { struct render_data *data = _data; + if (surface_is_popup(surface) != data->render_popups) { + return; + } struct wlr_output *wlr_output = data->output->wlr_output; float rotation = data->root_geo.rotation; pixman_region32_t *output_damage = data->damage; @@ -311,11 +335,13 @@ static void premultiply_alpha(float color[4], float opacity) { } static void render_view_surfaces(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct sway_output *output, pixman_region32_t *damage, + float alpha, bool popups) { struct render_data data = { .output = output, .damage = damage, .alpha = alpha, + .render_popups = popups, }; output_view_for_each_surface( view, &data.root_geo, render_surface_iterator, &data); @@ -327,7 +353,7 @@ static void render_view_surfaces(struct sway_view *view, static void render_view(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, struct border_colors *colors) { struct sway_view *view = con->sway_view; - render_view_surfaces(view, output, damage, view->swayc->alpha); + render_view_surfaces(view, output, damage, view->swayc->alpha, false); struct wlr_box box; float output_scale = output->wlr_output->scale; @@ -833,6 +859,26 @@ static struct sway_container *output_get_active_workspace( return workspace; } +static void render_popups(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *container) { + if (container->type == C_VIEW) { + render_view_surfaces(container->sway_view, output, damage, 1.0, true); + return; + } + if (container->layout == L_TABBED || container->layout == L_STACKED) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *child = seat_get_active_child(seat, container); + if (child) { + render_popups(output, damage, child); + } + } else { + for (int i = 0; i < container->children->length; ++i) { + struct sway_container *child = container->children->items[i]; + render_popups(output, damage, child); + } + } +} + static void render_output(struct sway_output *output, struct timespec *when, pixman_region32_t *damage) { struct wlr_output *wlr_output = output->wlr_output; @@ -877,8 +923,10 @@ static void render_output(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - render_view_surfaces( - workspace->sway_workspace->fullscreen, output, damage, 1.0f); + render_view_surfaces(workspace->sway_workspace->fullscreen, + output, damage, 1.0f, false); + render_view_surfaces(workspace->sway_workspace->fullscreen, + output, damage, 1.0f, true); if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, @@ -902,7 +950,9 @@ static void render_output(struct sway_output *output, struct timespec *when, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); render_container(output, damage, workspace, focus == workspace); + render_popups(output, damage, workspace); render_floating(output, damage); + render_popups(output, damage, workspace->sway_workspace->floating); render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d6e17ae14..1ca1bd171 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -108,7 +108,7 @@ static struct sway_container *container_at_coords( } struct sway_container *c; - if ((c = floating_container_at(lx, ly, surface, sx, sy))) { + if ((c = popup_at(ws, lx, ly, surface, sx, sy))) { return c; } if ((c = container_at(ws, lx, ly, surface, sx, sy))) { diff --git a/sway/tree/container.c b/sway/tree/container.c index d0d266317..fdeae2b56 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -474,9 +474,9 @@ struct sway_container *container_parent(struct sway_container *container, return container; } -static struct sway_container *container_at_view(struct sway_container *swayc, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { +static struct sway_container *surface_at_view(struct sway_container *swayc, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy, bool only_popups) { if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { return NULL; } @@ -490,6 +490,13 @@ static struct sway_container *container_at_view(struct sway_container *swayc, case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(sview->surface, view_sx, view_sy, &_sx, &_sy); + if (_surface && only_popups) { + struct wlr_xwayland_surface *xsurface = + wlr_xwayland_surface_from_wlr_surface(_surface); + if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { + return NULL; + } + } break; case SWAY_VIEW_XDG_SHELL_V6: // the top left corner of the sway container is the @@ -500,6 +507,13 @@ static struct sway_container *container_at_view(struct sway_container *swayc, _surface = wlr_xdg_surface_v6_surface_at( sview->wlr_xdg_surface_v6, view_sx, view_sy, &_sx, &_sy); + if (_surface && only_popups) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(_surface); + if (xdg_surface_v6->role != WLR_XDG_SURFACE_V6_ROLE_POPUP) { + return NULL; + } + } break; case SWAY_VIEW_XDG_SHELL: // the top left corner of the sway container is the @@ -510,6 +524,13 @@ static struct sway_container *container_at_view(struct sway_container *swayc, _surface = wlr_xdg_surface_surface_at( sview->wlr_xdg_surface, view_sx, view_sy, &_sx, &_sy); + if (_surface && only_popups) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(_surface); + if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) { + return NULL; + } + } break; } if (_surface) { @@ -517,9 +538,16 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sy = _sy; *surface = _surface; } + if (only_popups && !_surface) { + return NULL; + } return swayc; } +static struct sway_container *container_at_container( + struct sway_container *parent, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + /** * container_at for a container with layout L_TABBED. */ @@ -546,7 +574,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return container_at_container(current, lx, ly, surface, sx, sy); } /** @@ -571,7 +599,7 @@ static struct sway_container *container_at_stacked( // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return container_at_container(current, lx, ly, surface, sx, sy); } /** @@ -589,21 +617,17 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(child, lx, ly, surface, sx, sy); + return container_at_container(child, lx, ly, surface, sx, sy); } } return NULL; } -struct sway_container *container_at(struct sway_container *parent, - double lx, double ly, +static struct sway_container *container_at_container( + struct sway_container *parent, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(parent->type >= C_WORKSPACE, - "Expected workspace or deeper")) { - return NULL; - } if (parent->type == C_VIEW) { - return container_at_view(parent, lx, ly, surface, sx, sy); + return surface_at_view(parent, lx, ly, surface, sx, sy, false); } if (!parent->children->length) { return NULL; @@ -623,38 +647,77 @@ struct sway_container *container_at(struct sway_container *parent, case L_NONE: return NULL; } - return NULL; } -struct sway_container *floating_container_at(double lx, double ly, +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - for (int j = 0; j < output->children->length; ++j) { - struct sway_container *workspace = output->children->items[j]; - struct sway_workspace *ws = workspace->sway_workspace; - if (!workspace_is_visible(workspace)) { - continue; - } - for (int k = 0; k < ws->floating->children->length; ++k) { - struct sway_container *floater = - ws->floating->children->items[k]; - struct wlr_box box = { - .x = floater->x, - .y = floater->y, - .width = floater->width, - .height = floater->height, - }; - if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(floater, lx, ly, surface, sx, sy); - } + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *floating = workspace->sway_workspace->floating; + struct sway_container *c; + // Floating + for (int i = 0; i < floating->children->length; ++i) { + struct sway_container *floater = floating->children->items[i]; + if ((c = container_at_container(floater, lx, ly, surface, sx, sy))) { + return c; + } + } + // Tiling + if ((c = container_at_container(workspace, lx, ly, surface, sx, sy))) { + return c; + } + return NULL; +} + +static struct sway_container *popup_at_container(struct sway_container *parent, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy) { + if (parent->type == C_VIEW) { + return surface_at_view(parent, lx, ly, surface, sx, sy, true); + } + if (parent->layout == L_TABBED || parent->layout == L_STACKED) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *child = seat_get_active_child(seat, parent); + struct sway_container *c = + popup_at_container(child, lx, ly, surface, sx, sy); + if (c) { + return c; + } + } else { + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct sway_container *c = + popup_at_container(child, lx, ly, surface, sx, sy); + if (c) { + return c; } } } return NULL; } +struct sway_container *popup_at(struct sway_container *workspace, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *c; + // Floating popups + if ((c = popup_at_container(workspace->sway_workspace->floating, lx, ly, + surface, sx, sy))) { + return c; + } + // Tiling poups + if ((c = popup_at_container(workspace, lx, ly, surface, sx, sy))) { + return c; + } + return NULL; +} + void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) {