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.
This commit is contained in:
Ryan Dwyer 2018-06-02 12:58:24 +10:00
parent 0f71547032
commit e84166a734
4 changed files with 159 additions and 46 deletions

View file

@ -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.

View file

@ -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);

View file

@ -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))) {

View file

@ -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) {