forked from 12to11/12to11

* compositor.h (struct _Positioner): New struct. * positioner.c (struct _Positioner): Move to compositor.h. (RetainPositioner, ReleasePositioner): Remove functions. (HandleResourceDestroy): Destroy positioner instead. (XLCreateXdgPositioner): Remove reference-counting logic. (XLRetainPositioner, XLReleasePositioner): Delete functions. (XLPositionerIsReactive): Delete function. * xdg_popup.c (struct _XdgPopup): Make positioner a struct instead of a pointer. (DestroyBacking, InternalReposition, Reposition) (HandleParentConfigure, HandleParentResize, XLGetXdgPopup): Copy positioner instead of taking a reference.
899 lines
22 KiB
C
899 lines
22 KiB
C
/* Wayland compositor running on top of an X server.
|
|
|
|
Copyright (C) 2022 to various contributors.
|
|
|
|
This file is part of 12to11.
|
|
|
|
12to11 is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
option) any later version.
|
|
|
|
12to11 is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "xdg-shell.h"
|
|
|
|
#include "compositor.h"
|
|
|
|
#ifdef DEBUG_POSITIONER
|
|
#define DebugPrint(format, args...) \
|
|
fprintf (stderr, "%s: " format "\n", __FUNCTION__, ## args)
|
|
|
|
static const char *anchor_gravity_names[] =
|
|
{
|
|
"AnchorGravityNone",
|
|
"AnchorGravityTop",
|
|
"AnchorGravityBottom",
|
|
"AnchorGravityLeft",
|
|
"AnchorGravityRight",
|
|
"AnchorGravityTopLeft",
|
|
"AnchorGravityBottomLeft",
|
|
"AnchorGravityTopRight",
|
|
"AnchorGravityBottomRight",
|
|
};
|
|
|
|
#else
|
|
#define DebugPrint(fmt, ...) ((void) 0)
|
|
#endif
|
|
|
|
typedef enum _AnchorGravity Anchor;
|
|
typedef enum _AnchorGravity Gravity;
|
|
|
|
enum _AnchorGravity
|
|
{
|
|
AnchorGravityNone,
|
|
AnchorGravityTop,
|
|
AnchorGravityBottom,
|
|
AnchorGravityLeft,
|
|
AnchorGravityRight,
|
|
AnchorGravityTopLeft,
|
|
AnchorGravityBottomLeft,
|
|
AnchorGravityTopRight,
|
|
AnchorGravityBottomRight,
|
|
};
|
|
|
|
/* Surface used to handle scaling during constraint adjustment
|
|
calculation. */
|
|
static double scale_adjustment_factor;
|
|
|
|
static void
|
|
Destroy (struct wl_client *client, struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static void
|
|
SetSize (struct wl_client *client, struct wl_resource *resource,
|
|
int32_t width, int32_t height)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
if (width < 1 || height < 1)
|
|
{
|
|
wl_resource_post_error (resource, XDG_SURFACE_ERROR_INVALID_SIZE,
|
|
"invalid size %d %d", width, height);
|
|
return;
|
|
}
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->width = width;
|
|
positioner->height = height;
|
|
}
|
|
|
|
static void
|
|
SetAnchorRect (struct wl_client *client, struct wl_resource *resource,
|
|
int32_t x, int32_t y, int32_t width, int32_t height)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
if (width < 1 || height < 1)
|
|
{
|
|
wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT,
|
|
"invalid size specified (%d %d)", width, height);
|
|
return;
|
|
}
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->anchor_x = x;
|
|
positioner->anchor_y = y;
|
|
positioner->anchor_width = width;
|
|
positioner->anchor_height = height;
|
|
}
|
|
|
|
static void
|
|
SetAnchor (struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t anchor)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT)
|
|
{
|
|
wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT,
|
|
"invalid anchor specified (%"PRIu32")", anchor);
|
|
return;
|
|
}
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->anchor = anchor;
|
|
}
|
|
|
|
static void
|
|
SetGravity (struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t gravity)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
|
|
{
|
|
wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT,
|
|
"invalid gravity specified (%"PRIu32")", gravity);
|
|
return;
|
|
}
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->gravity = gravity;
|
|
}
|
|
|
|
static void
|
|
SetConstraintAdjustment (struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t constraint_adjustment)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->constraint_adjustment = constraint_adjustment;
|
|
}
|
|
|
|
static void
|
|
SetOffset (struct wl_client *client, struct wl_resource *resource,
|
|
int32_t x, int32_t y)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->offset_x = x;
|
|
positioner->offset_y = y;
|
|
}
|
|
|
|
static void
|
|
SetReactive (struct wl_client *client, struct wl_resource *resource)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->reactive = True;
|
|
}
|
|
|
|
static void
|
|
SetParentSize (struct wl_client *client, struct wl_resource *resource,
|
|
int width, int height)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
positioner->parent_width = width;
|
|
positioner->parent_height = height;
|
|
}
|
|
|
|
static void
|
|
SetParentConfigure (struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t configure)
|
|
{
|
|
/* Unused. */
|
|
return;
|
|
}
|
|
|
|
static const struct xdg_positioner_interface xdg_positioner_impl =
|
|
{
|
|
.destroy = Destroy,
|
|
.set_size = SetSize,
|
|
.set_anchor_rect = SetAnchorRect,
|
|
.set_anchor = SetAnchor,
|
|
.set_gravity = SetGravity,
|
|
.set_constraint_adjustment = SetConstraintAdjustment,
|
|
.set_offset = SetOffset,
|
|
.set_reactive = SetReactive,
|
|
.set_parent_size = SetParentSize,
|
|
.set_parent_configure = SetParentConfigure,
|
|
};
|
|
|
|
static void
|
|
HandleResourceDestroy (struct wl_resource *resource)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = wl_resource_get_user_data (resource);
|
|
XLFree (positioner);
|
|
}
|
|
|
|
static void
|
|
CalculatePosition (Positioner *positioner, int *x_out, int *y_out)
|
|
{
|
|
int x, y, anchor_x, anchor_y, anchor_width, anchor_height;
|
|
|
|
DebugPrint ("anchor: %s, gravity: %s",
|
|
anchor_gravity_names[positioner->anchor],
|
|
anchor_gravity_names[positioner->gravity]);
|
|
|
|
/* This function calculates an offset from the origin of the parent
|
|
window geometry (in the X coordinate system where the value of
|
|
the Y axis increases as the position grows downwards.) The
|
|
offset is derived by first computing an anchor point, and then
|
|
placing the surface at that anchor in accordance with the
|
|
gravity. */
|
|
|
|
x = positioner->offset_x;
|
|
y = positioner->offset_y;
|
|
|
|
anchor_x = positioner->anchor_x;
|
|
anchor_y = positioner->anchor_y;
|
|
anchor_width = positioner->anchor_width;
|
|
anchor_height = positioner->anchor_height;
|
|
|
|
/* First, compute the point at which the surface will be
|
|
anchored. */
|
|
|
|
switch (positioner->anchor)
|
|
{
|
|
case AnchorGravityTop:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityTopRight:
|
|
y += anchor_y;
|
|
break;
|
|
|
|
case AnchorGravityBottom:
|
|
case AnchorGravityBottomLeft:
|
|
case AnchorGravityBottomRight:
|
|
y += anchor_y + anchor_height;
|
|
break;
|
|
|
|
default:
|
|
y += anchor_y + anchor_height / 2;
|
|
}
|
|
|
|
switch (positioner->anchor)
|
|
{
|
|
case AnchorGravityLeft:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityBottomLeft:
|
|
x += anchor_x;
|
|
break;
|
|
|
|
case AnchorGravityRight:
|
|
case AnchorGravityTopRight:
|
|
case AnchorGravityBottomRight:
|
|
x += anchor_x + anchor_width;
|
|
break;
|
|
|
|
default:
|
|
x += anchor_x + anchor_width / 2;
|
|
}
|
|
|
|
/* Next, compute where the surface should be. positioner->gravity
|
|
specifies the corner opposite to the one that should be touching
|
|
the anchor. For example:
|
|
|
|
(anchor)
|
|
+ ------>
|
|
|
|
|
|
|
|
v
|
|
|
|
would be the directions in which the surface rectangle would
|
|
extend if the gravity were to be AnchorGravityBottomRight. And:
|
|
|
|
^
|
|
|
|
|
|
|
|
<-----+ (anchor)
|
|
|
|
would be the those directions were it AnchorGravityTopLeft. */
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityTop:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityTopRight:
|
|
y -= positioner->height;
|
|
break;
|
|
|
|
case AnchorGravityBottom:
|
|
case AnchorGravityBottomLeft:
|
|
case AnchorGravityBottomRight:
|
|
y = y;
|
|
break;
|
|
|
|
default:
|
|
y -= positioner->height / 2;
|
|
}
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityLeft:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityBottomLeft:
|
|
x -= positioner->width;
|
|
break;
|
|
|
|
case AnchorGravityRight:
|
|
case AnchorGravityTopRight:
|
|
case AnchorGravityBottomRight:
|
|
x = x;
|
|
break;
|
|
|
|
default:
|
|
x -= positioner->width / 2;
|
|
}
|
|
|
|
if (x_out)
|
|
*x_out = x;
|
|
|
|
if (y_out)
|
|
*y_out = y;
|
|
}
|
|
|
|
static int
|
|
TrySlideX (Positioner *positioner, int x, int width, int cx, int cwidth)
|
|
{
|
|
int cx1, x1;
|
|
int new_x;
|
|
|
|
cx1 = cx + cwidth - 1;
|
|
x1 = x + width - 1;
|
|
|
|
DebugPrint ("trying to slide X %d (width %d) according to"
|
|
" constraint X %d and constraint width %d", x,
|
|
width, cx, cwidth);
|
|
|
|
/* See if the rect is unconstrained on the X axis. */
|
|
|
|
if (x >= cx && x1 <= cx1)
|
|
return x;
|
|
|
|
new_x = x;
|
|
|
|
/* Which of these conditions to use first depends on the gravity.
|
|
If the gravity is leftwards, then we try to first keep new_x less
|
|
than cx1. Otherwise, we first try to keep new_x bigger than
|
|
cx. */
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityLeft:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityBottomLeft:
|
|
if (x < cx)
|
|
/* If x is less than cx, move it to cx. */
|
|
new_x = cx;
|
|
else if (x1 > cx1)
|
|
/* If x1 extends past cx1, move it back. */
|
|
new_x = x - (x1 - cx1);
|
|
break;
|
|
|
|
case AnchorGravityRight:
|
|
case AnchorGravityTopRight:
|
|
case AnchorGravityBottomRight:
|
|
/* There is no X axis gravity. Choose some arbitrary
|
|
default. */
|
|
default:
|
|
if (x1 > cx1)
|
|
/* If x1 extends past cx1, move it back. */
|
|
new_x = x - (x1 - cx1);
|
|
else if (x < cx)
|
|
/* If x is less than cx, move it to cx. */
|
|
new_x = cx;
|
|
break;
|
|
}
|
|
|
|
DebugPrint ("new X: %d", new_x);
|
|
|
|
return new_x;
|
|
}
|
|
|
|
static int
|
|
TrySlideY (Positioner *positioner, int y, int height, int cy, int cheight)
|
|
{
|
|
int cy1, y1;
|
|
int new_y;
|
|
|
|
cy1 = cy + cheight - 1;
|
|
y1 = y + height - 1;
|
|
|
|
/* See if the rect is unconstrained on the Y axis. */
|
|
|
|
if (y >= cy && y1 <= cy1)
|
|
return y;
|
|
|
|
new_y = y;
|
|
|
|
/* Which of these conditions to use first depends on the gravity.
|
|
If the gravity is topwards, then we try to first keep new_y less
|
|
than cy1. Otherwise, we first try to keep new_y bigger than
|
|
cy. */
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityTop:
|
|
case AnchorGravityTopLeft:
|
|
case AnchorGravityTopRight:
|
|
if (y < cy)
|
|
/* If y is less than cy, move it to cy. */
|
|
new_y = cy;
|
|
else if (y1 > cy1)
|
|
/* If y1 eytends past cy1, move it back. */
|
|
new_y = y - (y1 - cy1);
|
|
break;
|
|
|
|
case AnchorGravityBottom:
|
|
case AnchorGravityBottomLeft:
|
|
case AnchorGravityBottomRight:
|
|
/* When there is no Y axis gravity, choose some arbitrary
|
|
default. */
|
|
default:
|
|
if (y1 > cy1)
|
|
/* If y1 eytends past cy1, move it back. */
|
|
new_y = y - (y1 - cy1);
|
|
else if (y < cy)
|
|
/* If y is less than cy, move it to cy. */
|
|
new_y = cy;
|
|
break;
|
|
}
|
|
|
|
return new_y;
|
|
}
|
|
|
|
static int
|
|
TryFlipX (Positioner *positioner, int x, int width, int cx, int cwidth,
|
|
int offset)
|
|
{
|
|
int cx1, x1;
|
|
int new_x;
|
|
Positioner new;
|
|
|
|
cx1 = cx + cwidth - 1;
|
|
x1 = x + width - 1;
|
|
|
|
/* If the rect is unconstrained, don't flip anything. */
|
|
|
|
if (x >= cx && x1 <= cx1)
|
|
return x;
|
|
|
|
DebugPrint ("x %d width %d found to be constrained by "
|
|
"constraint x %d constraint width %d", x, width,
|
|
cx, cwidth);
|
|
|
|
/* Otherwise, create a copy of the positioner, but with the X
|
|
gravity and X anchor flipped. */
|
|
new = *positioner;
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityLeft:
|
|
new.gravity = AnchorGravityRight;
|
|
break;
|
|
|
|
case AnchorGravityTopLeft:
|
|
new.gravity = AnchorGravityTopRight;
|
|
break;
|
|
|
|
case AnchorGravityBottomLeft:
|
|
new.gravity = AnchorGravityBottomRight;
|
|
break;
|
|
|
|
case AnchorGravityRight:
|
|
new.gravity = AnchorGravityLeft;
|
|
break;
|
|
|
|
case AnchorGravityTopRight:
|
|
new.gravity = AnchorGravityTopLeft;
|
|
break;
|
|
|
|
case AnchorGravityBottomRight:
|
|
new.gravity = AnchorGravityBottomLeft;
|
|
break;
|
|
}
|
|
|
|
switch (positioner->anchor)
|
|
{
|
|
case AnchorGravityLeft:
|
|
new.anchor = AnchorGravityRight;
|
|
break;
|
|
|
|
case AnchorGravityTopLeft:
|
|
new.anchor = AnchorGravityTopRight;
|
|
break;
|
|
|
|
case AnchorGravityBottomLeft:
|
|
new.anchor = AnchorGravityBottomRight;
|
|
break;
|
|
|
|
case AnchorGravityRight:
|
|
new.anchor = AnchorGravityLeft;
|
|
break;
|
|
|
|
case AnchorGravityTopRight:
|
|
new.anchor = AnchorGravityTopLeft;
|
|
break;
|
|
|
|
case AnchorGravityBottomRight:
|
|
new.anchor = AnchorGravityBottomLeft;
|
|
break;
|
|
}
|
|
|
|
/* If neither the gravity nor the anchor changed, punt, since
|
|
flipping won't help. */
|
|
|
|
if (positioner->gravity == new.gravity
|
|
&& positioner->anchor == new.anchor)
|
|
return x;
|
|
|
|
DebugPrint ("new anchor: %s, anchor point: %d, %d; gravity: %s",
|
|
anchor_gravity_names[new.anchor],
|
|
positioner->anchor_x, positioner->anchor_y,
|
|
anchor_gravity_names[new.gravity]);
|
|
|
|
/* Otherwise, compute a new position using the new positioner. */
|
|
CalculatePosition (&new, &new_x, NULL);
|
|
|
|
/* Scale that position. */
|
|
new_x *= scale_adjustment_factor;
|
|
|
|
DebugPrint ("new x position is %d", new_x + offset);
|
|
|
|
/* If new_x is still constrained, use the previous position. */
|
|
if (new_x + offset < cx
|
|
|| new_x + offset + width - 1 > cx1)
|
|
{
|
|
DebugPrint ("position (%d) is still constrained",
|
|
new_x + offset);
|
|
return x;
|
|
}
|
|
|
|
/* Return the new X. */
|
|
return new_x + offset;
|
|
}
|
|
|
|
static int
|
|
TryFlipY (Positioner *positioner, int y, int height, int cy, int cheight,
|
|
int offset)
|
|
{
|
|
int cy1, y1;
|
|
int new_y;
|
|
Positioner new;
|
|
|
|
cy1 = cy + cheight - 1;
|
|
y1 = y + height - 1;
|
|
|
|
/* If the rect is unconstrained, don't flip anything. */
|
|
|
|
if (y >= cy && y1 <= cy1)
|
|
return y;
|
|
|
|
/* Otherwise, create a copy of the positioner, but with the Y
|
|
gravity and Y anchor flipped. */
|
|
new = *positioner;
|
|
|
|
switch (positioner->gravity)
|
|
{
|
|
case AnchorGravityTop:
|
|
new.gravity = AnchorGravityBottom;
|
|
break;
|
|
|
|
case AnchorGravityTopLeft:
|
|
new.gravity = AnchorGravityBottomLeft;
|
|
break;
|
|
|
|
case AnchorGravityTopRight:
|
|
new.gravity = AnchorGravityBottomRight;
|
|
break;
|
|
|
|
case AnchorGravityBottom:
|
|
new.gravity = AnchorGravityTop;
|
|
break;
|
|
|
|
case AnchorGravityBottomLeft:
|
|
new.gravity = AnchorGravityTopLeft;
|
|
break;
|
|
|
|
case AnchorGravityBottomRight:
|
|
new.gravity = AnchorGravityTopRight;
|
|
break;
|
|
}
|
|
|
|
switch (positioner->anchor)
|
|
{
|
|
case AnchorGravityTop:
|
|
new.anchor = AnchorGravityBottom;
|
|
break;
|
|
|
|
case AnchorGravityTopLeft:
|
|
new.anchor = AnchorGravityBottomLeft;
|
|
break;
|
|
|
|
case AnchorGravityTopRight:
|
|
new.anchor = AnchorGravityBottomRight;
|
|
break;
|
|
|
|
case AnchorGravityBottom:
|
|
new.anchor = AnchorGravityTop;
|
|
break;
|
|
|
|
case AnchorGravityBottomLeft:
|
|
new.anchor = AnchorGravityTopLeft;
|
|
break;
|
|
|
|
case AnchorGravityBottomRight:
|
|
new.anchor = AnchorGravityTopRight;
|
|
break;
|
|
}
|
|
|
|
/* If neither the gravity nor the anchor changed, punt, since
|
|
flipping won't help. */
|
|
|
|
if (positioner->gravity == new.gravity
|
|
&& positioner->anchor == new.anchor)
|
|
return y;
|
|
|
|
/* Otherwise, compute a new position using the new positioner. */
|
|
CalculatePosition (&new, NULL, &new_y);
|
|
|
|
/* Scale that position. */
|
|
new_y *= scale_adjustment_factor;
|
|
|
|
/* If new_y is still constrained, use the previous position. */
|
|
if (new_y + offset < cy
|
|
|| new_y + offset + height - 1 > cy1)
|
|
return y;
|
|
|
|
/* Return the new Y. */
|
|
return new_y + offset;
|
|
}
|
|
|
|
static void
|
|
TryResizeX (int x, int width, int cx, int cwidth, int offset,
|
|
int *new_x, int *new_width)
|
|
{
|
|
int x1, cx1, result_width, result_x;
|
|
|
|
x1 = x + width - 1;
|
|
cx1 = cx + cwidth - 1;
|
|
|
|
if (x >= cx && x1 <= cx1)
|
|
/* The popup is not constrained on the X axis. */
|
|
return;
|
|
|
|
/* Otherwise, resize the popup to fit inside cx, cx1.
|
|
If new_width ends up less than 1, punt. */
|
|
|
|
result_x = MAX (cx, x) - offset;
|
|
result_width = MIN (cx1, x1) - (*new_x + offset) + 1;
|
|
|
|
if (result_width <= 0)
|
|
return;
|
|
|
|
*new_x = result_x;
|
|
*new_width = result_width;
|
|
}
|
|
|
|
static void
|
|
TryResizeY (int y, int height, int cy, int cheight, int offset,
|
|
int *new_y, int *new_height)
|
|
{
|
|
int y1, cy1, result_height, result_y;
|
|
|
|
y1 = y + height - 1;
|
|
cy1 = cy + cheight - 1;
|
|
|
|
if (y >= cy && y1 <= cy1)
|
|
/* The popup is not constrained on the Y axis. */
|
|
return;
|
|
|
|
/* Otherwise, resize the popup to fit inside cy, cy1.
|
|
If new_height ends up less than 1, punt. */
|
|
|
|
result_y = MAX (cy, y) - offset;
|
|
result_height = MIN (cy1, y1) - (*new_y + offset) + 1;
|
|
|
|
if (result_height <= 0)
|
|
return;
|
|
|
|
*new_y = result_y;
|
|
*new_height = result_height;
|
|
}
|
|
|
|
static void
|
|
GetAdjustmentOffset (Role *parent, int *off_x, int *off_y)
|
|
{
|
|
int root_x, root_y, parent_gx, parent_gy;
|
|
|
|
XLXdgRoleGetCurrentGeometry (parent, &parent_gx,
|
|
&parent_gy, NULL, NULL);
|
|
XLXdgRoleCurrentRootPosition (parent, &root_x, &root_y);
|
|
|
|
/* Convert the gx and gy to the window coordinate system. */
|
|
TruncateSurfaceToWindow (parent->surface, parent_gx, parent_gy,
|
|
&parent_gx, &parent_gy);
|
|
|
|
*off_x = root_x + parent_gx;
|
|
*off_y = root_y + parent_gy;
|
|
}
|
|
|
|
static void
|
|
ApplyConstraintAdjustment (Positioner *positioner, Role *parent, int x,
|
|
int y, int *x_out, int *y_out, int *width_out,
|
|
int *height_out)
|
|
{
|
|
int width, height, cx, cy, cwidth, cheight, off_x, off_y;
|
|
|
|
width = positioner->width;
|
|
height = positioner->height;
|
|
|
|
/* Constraint calculations are simplest if we use scaled
|
|
coordinates, and then unscale them later. */
|
|
TruncateSurfaceToWindow (parent->surface, x, y, &x, &y);
|
|
TruncateScaleToWindow (parent->surface, width, height, &width,
|
|
&height);
|
|
|
|
/* Set the factor describing how to convert surface coordinates to
|
|
window ones. */
|
|
scale_adjustment_factor = parent->surface->factor;
|
|
|
|
if (positioner->constraint_adjustment
|
|
== XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE)
|
|
/* There is no constraint adjustment. */
|
|
goto finish;
|
|
|
|
/* Compute the current offset. */
|
|
GetAdjustmentOffset (parent, &off_x, &off_y);
|
|
|
|
if (!XLGetOutputRectAt (off_x + x, off_y + y, &cx, &cy,
|
|
&cwidth, &cheight))
|
|
/* There is no output in which to constrain this popup. */
|
|
goto finish;
|
|
|
|
#ifdef DEBUG_POSITIONER
|
|
|
|
fputs ("ApplyConstraintAdjustments: constraint adjustments are: ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X)
|
|
fputs ("SLIDE_X ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
|
|
fputs ("SLIDE_Y ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X)
|
|
fputs ("FLIP_X ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)
|
|
fputs ("FLIP_Y ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X)
|
|
fputs ("RESIZE_X ", stderr);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
|
|
fputs ("RESIZE_X ", stderr);
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
#endif
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X)
|
|
x = TryFlipX (positioner, x + off_x, width,
|
|
cx, cwidth, off_x) - off_x;
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)
|
|
y = TryFlipY (positioner, y + off_y, height,
|
|
cy, cheight, off_y) - off_y;
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X)
|
|
x = TrySlideX (positioner, x + off_x, width,
|
|
cx, cwidth) - off_x;
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
|
|
y = TrySlideY (positioner, y + off_y, height,
|
|
cy, cheight) - off_y;
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X)
|
|
TryResizeX (x + off_x, width, cx, cwidth,
|
|
off_x, &x, &width);
|
|
|
|
if (positioner->constraint_adjustment
|
|
& XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
|
|
TryResizeY (y + off_y, height, cy, cheight,
|
|
off_y, &y, &height);
|
|
|
|
finish:
|
|
/* Now, scale the coordinates back. */
|
|
TruncateWindowToSurface (parent->surface, x, y, &x, &y);
|
|
TruncateScaleToSurface (parent->surface, width, height, &width, &height);
|
|
|
|
*x_out = x;
|
|
*y_out = y;
|
|
*width_out = width;
|
|
*height_out = height;
|
|
}
|
|
|
|
void
|
|
XLPositionerCalculateGeometry (Positioner *positioner, Role *parent,
|
|
int *x_out, int *y_out, int *width_out,
|
|
int *height_out)
|
|
{
|
|
int x, y, width, height;
|
|
|
|
CalculatePosition (positioner, &x, &y);
|
|
|
|
if (parent->surface)
|
|
ApplyConstraintAdjustment (positioner, parent, x, y,
|
|
&x, &y, &width, &height);
|
|
else
|
|
width = positioner->width, height = positioner->height;
|
|
|
|
*x_out = x;
|
|
*y_out = y;
|
|
*width_out = width;
|
|
*height_out = height;
|
|
}
|
|
|
|
void
|
|
XLCreateXdgPositioner (struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t id)
|
|
{
|
|
Positioner *positioner;
|
|
|
|
positioner = XLSafeMalloc (sizeof *positioner);
|
|
|
|
if (!positioner)
|
|
{
|
|
wl_client_post_no_memory (client);
|
|
return;
|
|
}
|
|
|
|
memset (positioner, 0, sizeof *positioner);
|
|
positioner->resource
|
|
= wl_resource_create (client, &xdg_positioner_interface,
|
|
wl_resource_get_version (resource),
|
|
id);
|
|
|
|
if (!positioner->resource)
|
|
{
|
|
wl_resource_post_no_memory (resource);
|
|
XLFree (positioner);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation (positioner->resource,
|
|
&xdg_positioner_impl,
|
|
positioner,
|
|
HandleResourceDestroy);
|
|
}
|
|
|
|
void
|
|
XLCheckPositionerComplete (Positioner *positioner)
|
|
{
|
|
if (positioner->anchor_width && positioner->width)
|
|
return;
|
|
|
|
wl_resource_post_error (positioner->resource,
|
|
XDG_WM_BASE_ERROR_INVALID_POSITIONER,
|
|
"the specified positioner is incomplete");
|
|
}
|