mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2025-12-20 03:30:19 +01:00
libweston/desktop: Support constraining popups to monitor region
Doesn't yet deal with work area (monitor area excluding panels etc), or reconstraining while moving, but it's a start. It's more or less based on the mutter implementation. While the mutter implementation is GPL, it was authored by me, thus grant permission to relicense to MIT. Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
This commit is contained in:
parent
7c8b745249
commit
cdb011ee53
1 changed files with 353 additions and 5 deletions
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include <libweston/desktop.h>
|
||||
#include "internal.h"
|
||||
#include "libweston-internal.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
/************************************************************************************
|
||||
|
|
@ -141,11 +142,40 @@ struct weston_desktop_xdg_popup {
|
|||
sizeof(struct weston_desktop_xdg_popup))
|
||||
#define weston_desktop_surface_configure_biggest_size weston_desktop_surface_role_biggest_size
|
||||
|
||||
static bool
|
||||
intersect_rectangles(struct weston_geometry *dst,
|
||||
const struct weston_geometry *src1,
|
||||
const struct weston_geometry *src2)
|
||||
{
|
||||
int dst_x, dst_y;
|
||||
int dst_w, dst_h;
|
||||
|
||||
dst_x = MAX(src1->x, src2->x);
|
||||
dst_y = MAX(src1->y, src2->y);
|
||||
dst_w = MIN(src1->x + src1->width, src2->x + src2->width) - dst_x;
|
||||
dst_h = MIN(src1->y + src1->height, src2->y + src2->height) - dst_y;
|
||||
|
||||
if (dst_w > 0 && dst_h > 0) {
|
||||
*dst = (struct weston_geometry) {
|
||||
.x = dst_x,
|
||||
.y = dst_y,
|
||||
.width = dst_w,
|
||||
.height = dst_h,
|
||||
};
|
||||
return true;
|
||||
} else {
|
||||
*dst = (struct weston_geometry) {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct weston_geometry
|
||||
weston_desktop_xdg_positioner_get_geometry(struct weston_desktop_xdg_positioner *positioner,
|
||||
struct weston_desktop_surface *dsurface,
|
||||
struct weston_desktop_surface *parent)
|
||||
process_positioner(struct weston_desktop_xdg_positioner *positioner)
|
||||
{
|
||||
struct weston_geometry geometry = {
|
||||
.x = positioner->offset.x,
|
||||
|
|
@ -214,10 +244,328 @@ weston_desktop_xdg_positioner_get_geometry(struct weston_desktop_xdg_positioner
|
|||
geometry.x -= geometry.width / 2;
|
||||
}
|
||||
|
||||
if (positioner->constraint_adjustment == XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE)
|
||||
return geometry;
|
||||
}
|
||||
|
||||
static void
|
||||
positioner_flip_vertically(struct weston_desktop_xdg_positioner *positioner)
|
||||
{
|
||||
switch (positioner->anchor) {
|
||||
case XDG_POSITIONER_ANCHOR_TOP:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_BOTTOM;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_TOP;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_TOP_LEFT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_TOP_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_TOP_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_NONE:
|
||||
case XDG_POSITIONER_ANCHOR_LEFT:
|
||||
case XDG_POSITIONER_ANCHOR_RIGHT:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (positioner->gravity) {
|
||||
case XDG_POSITIONER_GRAVITY_TOP:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_BOTTOM;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_TOP;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_TOP_LEFT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_TOP_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_TOP_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_NONE:
|
||||
case XDG_POSITIONER_GRAVITY_LEFT:
|
||||
case XDG_POSITIONER_GRAVITY_RIGHT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
positioner_flip_horizontally(struct weston_desktop_xdg_positioner *positioner)
|
||||
{
|
||||
switch (positioner->anchor) {
|
||||
case XDG_POSITIONER_ANCHOR_NONE:
|
||||
case XDG_POSITIONER_ANCHOR_TOP:
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM:
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_TOP_LEFT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_TOP_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_TOP_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_LEFT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_ANCHOR_RIGHT:
|
||||
positioner->anchor = XDG_POSITIONER_ANCHOR_LEFT;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (positioner->gravity) {
|
||||
case XDG_POSITIONER_GRAVITY_NONE:
|
||||
case XDG_POSITIONER_GRAVITY_TOP:
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM:
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_TOP_LEFT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_TOP_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_TOP_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_LEFT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_RIGHT;
|
||||
break;
|
||||
case XDG_POSITIONER_GRAVITY_RIGHT:
|
||||
positioner->gravity = XDG_POSITIONER_GRAVITY_LEFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
is_constraint_satisfied(struct weston_geometry *surface_geometry,
|
||||
struct weston_geometry *output_geometry,
|
||||
struct weston_geometry *intersection)
|
||||
{
|
||||
intersect_rectangles(intersection, surface_geometry, output_geometry);
|
||||
return (intersection->width == surface_geometry->width &&
|
||||
intersection->height == surface_geometry->height);
|
||||
}
|
||||
|
||||
static void
|
||||
local_geometry_to_global(struct weston_geometry *geometry,
|
||||
struct weston_view *parent_view,
|
||||
struct weston_geometry *global_geometry)
|
||||
{
|
||||
pixman_box32_t geometry_rect;
|
||||
pixman_box32_t global_rect;
|
||||
|
||||
geometry_rect = (pixman_box32_t) {
|
||||
.x1 = geometry->x,
|
||||
.y1 = geometry->y,
|
||||
.x2 = geometry->x + geometry->width,
|
||||
.y2 = geometry->y + geometry->height,
|
||||
};
|
||||
global_rect = weston_matrix_transform_rect(&parent_view->transform.matrix,
|
||||
geometry_rect);
|
||||
global_geometry->x = global_rect.x1;
|
||||
global_geometry->y = global_rect.y1;
|
||||
global_geometry->width = global_rect.x2 - global_rect.x1;
|
||||
global_geometry->height = global_rect.y2 - global_rect.y1;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_geometry_satisfied(struct weston_view *parent_view,
|
||||
struct weston_geometry *geometry,
|
||||
struct weston_output *output,
|
||||
struct weston_geometry *intersection)
|
||||
{
|
||||
struct weston_geometry global_geometry;
|
||||
struct weston_geometry output_geometry;
|
||||
struct weston_geometry global_intersection;
|
||||
struct weston_coord_surface surface_coord;
|
||||
struct weston_coord_global tmp_g;
|
||||
bool is_satisfied;
|
||||
|
||||
local_geometry_to_global(geometry, parent_view, &global_geometry);
|
||||
|
||||
output_geometry = (struct weston_geometry) {
|
||||
.x = output->pos.c.x,
|
||||
.y = output->pos.c.y,
|
||||
.width = output->width,
|
||||
.height = output->height,
|
||||
};
|
||||
|
||||
is_satisfied = is_constraint_satisfied(&global_geometry,
|
||||
&output_geometry,
|
||||
&global_intersection);
|
||||
|
||||
tmp_g.c = weston_coord(global_intersection.x,
|
||||
global_intersection.y);
|
||||
surface_coord = weston_coord_global_to_surface(parent_view, tmp_g);
|
||||
intersection->x = round(surface_coord.c.x);
|
||||
intersection->y = round(surface_coord.c.y);
|
||||
|
||||
tmp_g.c = weston_coord(global_intersection.x +
|
||||
global_intersection.width,
|
||||
global_intersection.y +
|
||||
global_intersection.height);
|
||||
surface_coord = weston_coord_global_to_surface(parent_view, tmp_g);
|
||||
|
||||
intersection->width = round(surface_coord.c.x) - intersection->x;
|
||||
intersection->height = round(surface_coord.c.y) - intersection->y;
|
||||
|
||||
return is_satisfied;
|
||||
}
|
||||
|
||||
static void
|
||||
try_flip_position(struct weston_desktop_surface *surface,
|
||||
struct weston_view *parent_view,
|
||||
struct weston_output *output,
|
||||
struct weston_geometry *geometry,
|
||||
struct weston_desktop_xdg_positioner *positioner,
|
||||
enum xdg_positioner_constraint_adjustment constraint_adjustment)
|
||||
{
|
||||
struct weston_desktop_xdg_positioner flipped_positioner = *positioner;
|
||||
struct weston_geometry flipped_geometry;
|
||||
struct weston_geometry flipped_intersection;
|
||||
|
||||
switch (constraint_adjustment) {
|
||||
case XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X:
|
||||
positioner_flip_horizontally(&flipped_positioner);
|
||||
break;
|
||||
case XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y:
|
||||
positioner_flip_vertically(&flipped_positioner);
|
||||
break;
|
||||
default:
|
||||
assert(!"Invalid constraint adjustment");
|
||||
}
|
||||
|
||||
flipped_geometry = process_positioner(&flipped_positioner);
|
||||
is_geometry_satisfied(parent_view, &flipped_geometry, output,
|
||||
&flipped_intersection);
|
||||
|
||||
if (constraint_adjustment ==
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) {
|
||||
*positioner = flipped_positioner;
|
||||
geometry->x = flipped_geometry.x;
|
||||
} else if (constraint_adjustment ==
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) {
|
||||
*positioner = flipped_positioner;
|
||||
geometry->y = flipped_geometry.y;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
constrain_to_output(struct weston_desktop_surface *surface,
|
||||
struct weston_view *parent_view,
|
||||
struct weston_output *output,
|
||||
struct weston_geometry *geometry,
|
||||
struct weston_desktop_xdg_positioner *positioner)
|
||||
{
|
||||
struct weston_geometry global_geometry;
|
||||
int output_x2;
|
||||
int output_y2;
|
||||
struct weston_geometry intersection;
|
||||
|
||||
local_geometry_to_global(geometry, parent_view, &global_geometry);
|
||||
output_x2 = output->pos.c.x + output->width;
|
||||
output_y2 = output->pos.c.y + output->height;
|
||||
|
||||
if (is_geometry_satisfied(parent_view, geometry, output, &intersection))
|
||||
return;
|
||||
|
||||
if (geometry->width != intersection.width &&
|
||||
(positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X)) {
|
||||
try_flip_position(surface,
|
||||
parent_view,
|
||||
output,
|
||||
geometry,
|
||||
positioner,
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X);
|
||||
}
|
||||
if (geometry->height != intersection.height &&
|
||||
(positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) {
|
||||
try_flip_position(surface,
|
||||
parent_view,
|
||||
output,
|
||||
geometry,
|
||||
positioner,
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y);
|
||||
}
|
||||
|
||||
if (is_geometry_satisfied(parent_view, geometry, output, &intersection))
|
||||
return;
|
||||
|
||||
if (positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X &&
|
||||
global_geometry.x + global_geometry.width > output_x2) {
|
||||
if (geometry->x < intersection.x)
|
||||
geometry->x = intersection.x;
|
||||
else
|
||||
geometry->x -= geometry->width - intersection.width;
|
||||
}
|
||||
if (positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y &&
|
||||
global_geometry.y + global_geometry.height > output_y2) {
|
||||
if (geometry->y < intersection.y)
|
||||
geometry->y = intersection.y;
|
||||
else
|
||||
geometry->y -= geometry->height - intersection.height;
|
||||
}
|
||||
|
||||
if (is_geometry_satisfied(parent_view, geometry, output, &intersection))
|
||||
return;
|
||||
|
||||
if (positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) {
|
||||
geometry->x = intersection.x;
|
||||
geometry->width = intersection.width;
|
||||
}
|
||||
if (positioner->constraint_adjustment &
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) {
|
||||
geometry->y = intersection.y;
|
||||
geometry->height = intersection.height;
|
||||
}
|
||||
}
|
||||
|
||||
static struct weston_geometry
|
||||
weston_desktop_xdg_positioner_get_geometry(struct weston_desktop_xdg_positioner *positioner,
|
||||
struct weston_desktop_surface *dsurface,
|
||||
struct weston_desktop_surface *parent)
|
||||
{
|
||||
struct weston_geometry geometry;
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *parent_view;
|
||||
|
||||
geometry = process_positioner(positioner);
|
||||
|
||||
if (positioner->constraint_adjustment ==
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE)
|
||||
return geometry;
|
||||
|
||||
/* TODO: add compositor policy configuration and the code here */
|
||||
surface = weston_desktop_surface_get_surface(parent);
|
||||
wl_list_for_each(parent_view, &surface->views, surface_link) {
|
||||
struct weston_output *output = parent_view->output;
|
||||
|
||||
constrain_to_output(dsurface, parent_view,
|
||||
output, &geometry, positioner);
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue