summaryrefslogtreecommitdiff
path: root/gdk/wayland
diff options
context:
space:
mode:
authorJonas Ã…dahl <jadahl@gmail.com>2020-02-16 12:59:24 +0100
committerJonas Ã…dahl <jadahl@gmail.com>2020-02-19 09:47:18 +0100
commitca71119a40ac9b196700855dac6484f4e198bdb1 (patch)
tree7d5a2cc24a6d0f9ee5ffb7712e5860a0becca9d3 /gdk/wayland
parent37f4c644d34e77d076a818854e2c3467b2b14b54 (diff)
downloadgtk+-ca71119a40ac9b196700855dac6484f4e198bdb1.tar.gz
gdk/surface: Replace move_to_rect() with GdkPopupLayout based API
Replace the gdk_surface_move_to_rect() API with a new GdkSurface method called gdk_surface_present_popup() taking a new GdkPopupLayout object describing how they should be laid out on screen. The layout properties provided are the same as the ones used with gdk_surface_move_to_rect(), except they are now set up using GdkPopupLayout. Calling gdk_surface_present_popup() will either show the popup at the position described using the popup layout object and a new unconstrained size, or reposition it accordingly. In some situations, such as when a popup is set to autohide, presenting may immediately fail, in case the grab was not granted by the display server. After a successful present, the result of the layout can be queried using the following methods: * gdk_surface_get_position() - to get the position relative to its parent * gdk_surface_get_width() - to get the current width * gdk_surface_get_height() - to get the current height * gdk_surface_get_rect_anchor() - to get the anchor point on the anchor rectangle the popup was effectively positioned against given constraints defined by the environment and the layout rules provided via GdkPopupLayout. * gdk_surface_get_surface_anchor() - the same as the one above but for the surface anchor. A new signal replaces the old "moved-to-rect" one - "popup-layout-changed". However, it is only intended to be emitted when the layout changes implicitly by the windowing system, for example if the monitor resolution changed, or the parent window moved.
Diffstat (limited to 'gdk/wayland')
-rw-r--r--gdk/wayland/gdkdevice-wayland.c2
-rw-r--r--gdk/wayland/gdksurface-wayland.c794
2 files changed, 497 insertions, 299 deletions
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index 8ef0a3ae03..814c180386 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -4593,8 +4593,6 @@ gdk_wayland_seat_grab (GdkSeat *seat,
if (!gdk_surface_is_visible (surface))
{
gdk_wayland_seat_set_grab_surface (wayland_seat, NULL);
- g_critical ("Surface %p has not been made visible in GdkSeatGrabPrepareFunc",
- surface);
return GDK_GRAB_NOT_VIEWABLE;
}
diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c
index d9302c75e8..5a1b767089 100644
--- a/gdk/wayland/gdksurface-wayland.c
+++ b/gdk/wayland/gdksurface-wayland.c
@@ -52,6 +52,13 @@ static guint signals[LAST_SIGNAL];
#define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */
+typedef enum _PopupState
+{
+ POPUP_STATE_IDLE,
+ POPUP_STATE_WAITING_FOR_CONFIGURE,
+ POPUP_STATE_WAITING_FOR_FRAME,
+} PopupState;
+
struct _GdkWaylandSurface
{
GdkSurface parent_instance;
@@ -83,6 +90,8 @@ struct _GdkWaylandSurface
EGLSurface egl_surface;
EGLSurface dummy_egl_surface;
+ PopupState popup_state;
+
unsigned int initial_configure_received : 1;
unsigned int mapped : 1;
unsigned int pending_commit : 1;
@@ -91,7 +100,6 @@ struct _GdkWaylandSurface
GdkSurfaceTypeHint hint;
GdkSurface *transient_for;
GdkSurface *popup_parent;
- gboolean has_layout_data;
int pending_buffer_offset_x;
int pending_buffer_offset_y;
@@ -137,13 +145,10 @@ struct _GdkWaylandSurface
gulong parent_surface_committed_handler;
struct {
- GdkRectangle rect;
- GdkGravity rect_anchor;
- GdkGravity surface_anchor;
- GdkAnchorHints anchor_hints;
- gint rect_anchor_dx;
- gint rect_anchor_dy;
- } pending_move_to_rect;
+ GdkPopupLayout *layout;
+ int unconstrained_width;
+ int unconstrained_height;
+ } popup;
struct {
struct {
@@ -160,8 +165,11 @@ struct _GdkWaylandSurface
} popup;
uint32_t serial;
+ gboolean is_dirty;
} pending;
+ int state_freeze_count;
+
struct {
GdkWaylandSurfaceExported callback;
gpointer user_data;
@@ -199,15 +207,12 @@ static void gdk_wayland_surface_move_resize (GdkSurface *surface,
gint width,
gint height);
-static void calculate_moved_to_rect_result (GdkSurface *surface,
- int x,
- int y,
- int width,
- int height,
- GdkRectangle *flipped_rect,
- GdkRectangle *final_rect,
- gboolean *flipped_x,
- gboolean *flipped_y);
+static void update_popup_layout_state (GdkSurface *surface,
+ int x,
+ int y,
+ int width,
+ int height,
+ GdkPopupLayout *layout);
static gboolean gdk_wayland_surface_is_exported (GdkSurface *surface);
@@ -224,6 +229,32 @@ gdk_wayland_surface_init (GdkWaylandSurface *impl)
}
static void
+gdk_wayland_surface_freeze_state (GdkSurface *surface)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ impl->state_freeze_count++;
+}
+
+static void
+gdk_wayland_surface_thaw_state (GdkSurface *surface)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ g_assert (impl->state_freeze_count > 0);
+
+ impl->state_freeze_count--;
+
+ if (impl->state_freeze_count > 0)
+ return;
+
+ if (impl->pending.is_dirty)
+ gdk_wayland_surface_configure (surface);
+
+ g_assert (!impl->display_server.xdg_popup);
+}
+
+static void
_gdk_wayland_screen_add_orphan_dialog (GdkSurface *surface)
{
GdkWaylandDisplay *display_wayland =
@@ -341,6 +372,44 @@ fill_presentation_time_from_frame_time (GdkFrameTimings *timings,
}
}
+static GdkSurface *
+get_popup_toplevel (GdkSurface *surface)
+{
+ if (surface->parent)
+ return get_popup_toplevel (surface->parent);
+ else
+ return surface;
+}
+
+static void
+freeze_popup_toplevel_state (GdkSurface *surface)
+{
+ GdkSurface *toplevel;
+
+ toplevel = get_popup_toplevel (surface);
+ gdk_wayland_surface_freeze_state (toplevel);
+}
+
+static void
+thaw_popup_toplevel_state (GdkSurface *surface)
+{
+ GdkSurface *toplevel;
+
+ toplevel = get_popup_toplevel (surface);
+ gdk_wayland_surface_thaw_state (toplevel);
+}
+
+static void
+finish_pending_relayout (GdkSurface *surface)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ g_assert (impl->popup_state == POPUP_STATE_WAITING_FOR_FRAME);
+ impl->popup_state = POPUP_STATE_IDLE;
+
+ thaw_popup_toplevel_state (surface);
+}
+
static void
frame_callback (void *data,
struct wl_callback *callback,
@@ -364,6 +433,18 @@ frame_callback (void *data,
if (!impl->awaiting_frame)
return;
+ switch (impl->popup_state)
+ {
+ case POPUP_STATE_IDLE:
+ case POPUP_STATE_WAITING_FOR_CONFIGURE:
+ break;
+ case POPUP_STATE_WAITING_FOR_FRAME:
+ finish_pending_relayout (surface);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
impl->awaiting_frame = FALSE;
if (impl->awaiting_frame_frozen)
{
@@ -464,11 +545,8 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- if (impl->pending_commit)
+ if (impl->pending_commit && surface->update_freeze_count == 0)
{
- if (surface->update_freeze_count > 0)
- return;
-
gdk_wayland_surface_request_frame (surface);
/* From this commit forward, we can't write to the buffer,
@@ -671,6 +749,8 @@ gdk_wayland_surface_dispose (GObject *object)
impl = GDK_WAYLAND_SURFACE (surface);
+ g_clear_object (&impl->popup_parent);
+
if (impl->event_queue)
{
GdkWaylandDisplay *display_wayland =
@@ -1202,12 +1282,6 @@ gdk_wayland_surface_configure_popup (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
int x, y, width, height;
- GdkRectangle flipped_rect;
- GdkRectangle final_rect;
- gboolean flipped_x;
- gboolean flipped_y;
-
- g_return_if_fail (impl->transient_for);
if (impl->display_server.xdg_popup)
{
@@ -1220,6 +1294,18 @@ gdk_wayland_surface_configure_popup (GdkSurface *surface)
impl->pending.serial);
}
+ switch (impl->popup_state)
+ {
+ case POPUP_STATE_WAITING_FOR_CONFIGURE:
+ impl->popup_state = POPUP_STATE_WAITING_FOR_FRAME;
+ break;
+ case POPUP_STATE_IDLE:
+ case POPUP_STATE_WAITING_FOR_FRAME:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
x = impl->pending.popup.x;
y = impl->pending.popup.y;
width = impl->pending.popup.width;
@@ -1227,20 +1313,12 @@ gdk_wayland_surface_configure_popup (GdkSurface *surface)
gdk_wayland_surface_resize (surface, width, height, impl->scale);
- calculate_moved_to_rect_result (surface,
- x, y,
- width, height,
- &flipped_rect,
- &final_rect,
- &flipped_x,
- &flipped_y);
+ update_popup_layout_state (surface,
+ x, y,
+ width, height,
+ impl->popup.layout);
- g_signal_emit_by_name (surface,
- "moved-to-rect",
- &flipped_rect,
- &final_rect,
- flipped_x,
- flipped_y);
+ gdk_surface_invalidate_rect (surface, NULL);
}
static void
@@ -1260,6 +1338,8 @@ gdk_wayland_surface_configure (GdkSurface *surface)
gdk_wayland_surface_configure_toplevel (surface);
else
g_warn_if_reached ();
+
+ memset (&impl->pending, 0, sizeof (impl->pending));
}
static void
@@ -1268,8 +1348,12 @@ gdk_wayland_surface_handle_configure (GdkSurface *surface,
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+ impl->pending.is_dirty = TRUE;
impl->pending.serial = serial;
+ if (impl->state_freeze_count > 0)
+ return;
+
gdk_wayland_surface_configure (surface);
}
@@ -1776,70 +1860,28 @@ gdk_wayland_surface_announce_csd (GdkSurface *surface)
ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT);
}
-static GdkSurface *
-get_real_parent_and_translate (GdkSurface *surface,
- gint *x,
- gint *y)
-{
- GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- GdkSurface *parent = impl->transient_for;
-
- return parent;
-}
-
-static void
-translate_to_real_parent_surface_geometry (GdkSurface *surface,
- gint *x,
- gint *y)
-{
- GdkSurface *parent;
-
- parent = get_real_parent_and_translate (surface, x, y);
-
- *x -= parent->shadow_left;
- *y -= parent->shadow_top;
-}
-
-static GdkSurface *
-translate_from_real_parent_surface_geometry (GdkSurface *surface,
- gint *x,
- gint *y)
-{
- GdkSurface *parent;
- gint dx = 0;
- gint dy = 0;
-
- parent = get_real_parent_and_translate (surface, &dx, &dy);
-
- *x -= dx - parent->shadow_left;
- *y -= dy - parent->shadow_top;
-
- return parent;
-}
-
static void
-calculate_popup_rect (GdkSurface *surface,
- GdkGravity rect_anchor,
- GdkGravity surface_anchor,
- GdkRectangle *out_rect)
+calculate_popup_rect (GdkSurface *surface,
+ GdkPopupLayout *layout,
+ GdkRectangle *out_rect)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- GdkRectangle geometry;
+ int width, height;
GdkRectangle anchor_rect;
+ int dx, dy;
int x = 0, y = 0;
- gdk_wayland_surface_get_window_geometry (surface, &geometry);
+ width = (impl->popup.unconstrained_width -
+ (impl->margin_left + impl->margin_right));
+ height = (impl->popup.unconstrained_height -
+ (impl->margin_top + impl->margin_bottom));
- anchor_rect = (GdkRectangle) {
- .x = (impl->pending_move_to_rect.rect.x +
- impl->pending_move_to_rect.rect_anchor_dx),
- .y = (impl->pending_move_to_rect.rect.y +
- impl->pending_move_to_rect.rect_anchor_dy),
- .width = impl->pending_move_to_rect.rect.width,
- .height = impl->pending_move_to_rect.rect.height
- };
+ anchor_rect = *gdk_popup_layout_get_anchor_rect (layout);
+ gdk_popup_layout_get_offset (layout, &dx, &dy);
+ anchor_rect.x += dx;
+ anchor_rect.y += dy;
- switch (rect_anchor)
+ switch (gdk_popup_layout_get_rect_anchor (layout))
{
default:
case GDK_GRAVITY_STATIC:
@@ -1881,137 +1923,71 @@ calculate_popup_rect (GdkSurface *surface,
break;
}
- switch (surface_anchor)
+ switch (gdk_popup_layout_get_surface_anchor (layout))
{
default:
case GDK_GRAVITY_STATIC:
case GDK_GRAVITY_NORTH_WEST:
break;
case GDK_GRAVITY_NORTH:
- x -= geometry.width / 2;
+ x -= width / 2;
break;
case GDK_GRAVITY_NORTH_EAST:
- x -= geometry.width;
+ x -= width;
break;
case GDK_GRAVITY_WEST:
- y -= geometry.height / 2;
+ y -= height / 2;
break;
case GDK_GRAVITY_CENTER:
- x -= geometry.width / 2;
- y -= geometry.height / 2;
+ x -= width / 2;
+ y -= height / 2;
break;
case GDK_GRAVITY_EAST:
- x -= geometry.width;
- y -= geometry.height / 2;
+ x -= width;
+ y -= height / 2;
break;
case GDK_GRAVITY_SOUTH_WEST:
- y -= geometry.height;
+ y -= height;
break;
case GDK_GRAVITY_SOUTH:
- x -= geometry.width / 2;
- y -= geometry.height;
+ x -= width / 2;
+ y -= height;
break;
case GDK_GRAVITY_SOUTH_EAST:
- x -= geometry.width;
- y -= geometry.height;
+ x -= width;
+ y -= height;
break;
}
*out_rect = (GdkRectangle) {
.x = x,
.y = y,
- .width = geometry.width,
- .height = geometry.height
+ .width = width,
+ .height = height
};
}
-static GdkGravity
-flip_anchor_horizontally (GdkGravity anchor)
-{
- switch (anchor)
- {
- default:
- case GDK_GRAVITY_STATIC:
- case GDK_GRAVITY_NORTH_WEST:
- return GDK_GRAVITY_NORTH_EAST;
- case GDK_GRAVITY_NORTH:
- return GDK_GRAVITY_NORTH;
- case GDK_GRAVITY_NORTH_EAST:
- return GDK_GRAVITY_NORTH_WEST;
- case GDK_GRAVITY_WEST:
- return GDK_GRAVITY_EAST;
- case GDK_GRAVITY_CENTER:
- return GDK_GRAVITY_CENTER;
- case GDK_GRAVITY_EAST:
- return GDK_GRAVITY_WEST;
- case GDK_GRAVITY_SOUTH_WEST:
- return GDK_GRAVITY_SOUTH_EAST;
- case GDK_GRAVITY_SOUTH:
- return GDK_GRAVITY_SOUTH;
- case GDK_GRAVITY_SOUTH_EAST:
- return GDK_GRAVITY_SOUTH_WEST;
- }
-
- g_assert_not_reached ();
-}
-
-static GdkGravity
-flip_anchor_vertically (GdkGravity anchor)
-{
- switch (anchor)
- {
- default:
- case GDK_GRAVITY_STATIC:
- case GDK_GRAVITY_NORTH_WEST:
- return GDK_GRAVITY_SOUTH_WEST;
- case GDK_GRAVITY_NORTH:
- return GDK_GRAVITY_SOUTH;
- case GDK_GRAVITY_NORTH_EAST:
- return GDK_GRAVITY_SOUTH_EAST;
- case GDK_GRAVITY_WEST:
- return GDK_GRAVITY_WEST;
- case GDK_GRAVITY_CENTER:
- return GDK_GRAVITY_CENTER;
- case GDK_GRAVITY_EAST:
- return GDK_GRAVITY_EAST;
- case GDK_GRAVITY_SOUTH_WEST:
- return GDK_GRAVITY_NORTH_WEST;
- case GDK_GRAVITY_SOUTH:
- return GDK_GRAVITY_NORTH;
- case GDK_GRAVITY_SOUTH_EAST:
- return GDK_GRAVITY_NORTH_EAST;
- }
-
- g_assert_not_reached ();
-}
-
static void
-calculate_moved_to_rect_result (GdkSurface *surface,
- int x,
- int y,
- int width,
- int height,
- GdkRectangle *flipped_rect,
- GdkRectangle *final_rect,
- gboolean *flipped_x,
- gboolean *flipped_y)
+update_popup_layout_state (GdkSurface *surface,
+ int x,
+ int y,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
{
- GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- GdkSurface *parent;
gint surface_x, surface_y;
gint surface_width, surface_height;
GdkRectangle best_rect;
+ GdkRectangle flipped_rect;
+ GdkGravity rect_anchor;
+ GdkGravity surface_anchor;
+ GdkAnchorHints anchor_hints;
- parent = translate_from_real_parent_surface_geometry (surface, &x, &y);
- *final_rect = (GdkRectangle) {
- .x = x,
- .y = y,
- .width = width,
- .height = height,
- };
+ x += surface->parent->shadow_left;
+ y += surface->parent->shadow_top;
- surface_x = parent->x + x;
- surface_y = parent->y + y;
+ surface_x = x;
+ surface_y = y;
surface_width = width + surface->shadow_left + surface->shadow_right;
surface_height = height + surface->shadow_top + surface->shadow_bottom;
@@ -2019,79 +1995,117 @@ calculate_moved_to_rect_result (GdkSurface *surface,
surface_x, surface_y,
surface_width, surface_height);
- calculate_popup_rect (surface,
- impl->pending_move_to_rect.rect_anchor,
- impl->pending_move_to_rect.surface_anchor,
- &best_rect);
+ rect_anchor = gdk_popup_layout_get_rect_anchor (layout);
+ surface_anchor = gdk_popup_layout_get_surface_anchor (layout);
+ anchor_hints = gdk_popup_layout_get_anchor_hints (layout);
- *flipped_rect = best_rect;
+ calculate_popup_rect (surface, layout, &best_rect);
+
+ flipped_rect = best_rect;
if (x != best_rect.x &&
- impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X)
+ anchor_hints & GDK_ANCHOR_FLIP_X)
{
GdkRectangle flipped_x_rect;
GdkGravity flipped_rect_anchor;
GdkGravity flipped_surface_anchor;
-
- flipped_rect_anchor =
- flip_anchor_horizontally (impl->pending_move_to_rect.rect_anchor);
- flipped_surface_anchor =
- flip_anchor_horizontally (impl->pending_move_to_rect.surface_anchor),
+ GdkPopupLayout *flipped_layout;
+
+ flipped_rect_anchor = gdk_gravity_flip_horizontally (rect_anchor);
+ flipped_surface_anchor = gdk_gravity_flip_horizontally (surface_anchor);
+ flipped_layout = gdk_popup_layout_copy (layout);
+ gdk_popup_layout_set_rect_anchor (flipped_layout,
+ flipped_rect_anchor);
+ gdk_popup_layout_set_surface_anchor (flipped_layout,
+ flipped_surface_anchor);
calculate_popup_rect (surface,
- flipped_rect_anchor,
- flipped_surface_anchor,
+ flipped_layout,
&flipped_x_rect);
+ gdk_popup_layout_unref (flipped_layout);
if (flipped_x_rect.x == x)
- flipped_rect->x = x;
+ flipped_rect.x = x;
}
if (y != best_rect.y &&
- impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y)
+ anchor_hints & GDK_ANCHOR_FLIP_Y)
{
GdkRectangle flipped_y_rect;
GdkGravity flipped_rect_anchor;
GdkGravity flipped_surface_anchor;
-
- flipped_rect_anchor =
- flip_anchor_vertically (impl->pending_move_to_rect.rect_anchor);
- flipped_surface_anchor =
- flip_anchor_vertically (impl->pending_move_to_rect.surface_anchor),
+ GdkPopupLayout *flipped_layout;
+
+ flipped_rect_anchor = gdk_gravity_flip_vertically (rect_anchor);
+ flipped_surface_anchor = gdk_gravity_flip_vertically (surface_anchor);
+ flipped_layout = gdk_popup_layout_copy (layout);
+ gdk_popup_layout_set_rect_anchor (flipped_layout,
+ flipped_rect_anchor);
+ gdk_popup_layout_set_surface_anchor (flipped_layout,
+ flipped_surface_anchor);
calculate_popup_rect (surface,
- flipped_rect_anchor,
- flipped_surface_anchor,
+ flipped_layout,
&flipped_y_rect);
+ gdk_popup_layout_unref (flipped_layout);
if (flipped_y_rect.y == y)
- flipped_rect->y = y;
+ flipped_rect.y = y;
}
- *flipped_x = flipped_rect->x != best_rect.x;
- *flipped_y = flipped_rect->y != best_rect.y;
+ if (flipped_rect.x != best_rect.x)
+ {
+ rect_anchor = gdk_gravity_flip_horizontally (rect_anchor);
+ surface_anchor = gdk_gravity_flip_horizontally (surface_anchor);
+ }
+ if (flipped_rect.y != best_rect.y)
+ {
+ rect_anchor = gdk_gravity_flip_vertically (rect_anchor);
+ surface_anchor = gdk_gravity_flip_vertically (surface_anchor);
+ }
+
+ surface->popup.rect_anchor = rect_anchor;
+ surface->popup.surface_anchor = surface_anchor;
}
static gpointer
-create_dynamic_positioner (GdkSurface *surface)
+create_dynamic_positioner (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+ GdkSurface *parent = surface->parent;
GdkWaylandDisplay *display =
GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
GdkRectangle geometry;
uint32_t constraint_adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE;
+ const GdkRectangle *anchor_rect;
gint real_anchor_rect_x, real_anchor_rect_y;
gint anchor_rect_width, anchor_rect_height;
+ int rect_anchor_dx;
+ int rect_anchor_dy;
+ GdkGravity rect_anchor;
+ GdkGravity surface_anchor;
+ GdkAnchorHints anchor_hints;
- g_warn_if_fail (impl->has_layout_data);
+ geometry = (GdkRectangle) {
+ .x = impl->margin_left,
+ .y = impl->margin_top,
+ .width = width - (impl->margin_left + impl->margin_right),
+ .height = height - (impl->margin_top + impl->margin_bottom),
+ };
- gdk_wayland_surface_get_window_geometry (surface, &geometry);
+ anchor_rect = gdk_popup_layout_get_anchor_rect (layout);
+ real_anchor_rect_x = anchor_rect->x - parent->shadow_left;
+ real_anchor_rect_y = anchor_rect->y - parent->shadow_top;
+
+ anchor_rect_width = anchor_rect->width;
+ anchor_rect_height = anchor_rect->height;
- real_anchor_rect_x = impl->pending_move_to_rect.rect.x;
- real_anchor_rect_y = impl->pending_move_to_rect.rect.y;
- translate_to_real_parent_surface_geometry (surface,
- &real_anchor_rect_x,
- &real_anchor_rect_y);
+ gdk_popup_layout_get_offset (layout, &rect_anchor_dx, &rect_anchor_dy);
- anchor_rect_width = impl->pending_move_to_rect.rect.width;
- anchor_rect_height = impl->pending_move_to_rect.rect.height;
+ rect_anchor = gdk_popup_layout_get_rect_anchor (layout);
+ surface_anchor = gdk_popup_layout_get_surface_anchor (layout);
+
+ anchor_hints = gdk_popup_layout_get_anchor_hints (layout);
switch (display->shell_variant)
{
@@ -2109,27 +2123,25 @@ create_dynamic_positioner (GdkSurface *surface)
real_anchor_rect_y,
anchor_rect_width,
anchor_rect_height);
- xdg_positioner_set_offset (positioner,
- impl->pending_move_to_rect.rect_anchor_dx,
- impl->pending_move_to_rect.rect_anchor_dy);
+ xdg_positioner_set_offset (positioner, rect_anchor_dx, rect_anchor_dy);
- anchor = rect_anchor_to_anchor (impl->pending_move_to_rect.rect_anchor);
+ anchor = rect_anchor_to_anchor (rect_anchor);
xdg_positioner_set_anchor (positioner, anchor);
- gravity = surface_anchor_to_gravity (impl->pending_move_to_rect.surface_anchor);
+ gravity = surface_anchor_to_gravity (surface_anchor);
xdg_positioner_set_gravity (positioner, gravity);
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X)
+ if (anchor_hints & GDK_ANCHOR_FLIP_X)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y)
+ if (anchor_hints & GDK_ANCHOR_FLIP_Y)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_X)
+ if (anchor_hints & GDK_ANCHOR_SLIDE_X)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_Y)
+ if (anchor_hints & GDK_ANCHOR_SLIDE_Y)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_X)
+ if (anchor_hints & GDK_ANCHOR_RESIZE_X)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_Y)
+ if (anchor_hints & GDK_ANCHOR_RESIZE_Y)
constraint_adjustment |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
xdg_positioner_set_constraint_adjustment (positioner,
constraint_adjustment);
@@ -2151,26 +2163,26 @@ create_dynamic_positioner (GdkSurface *surface)
anchor_rect_width,
anchor_rect_height);
zxdg_positioner_v6_set_offset (positioner,
- impl->pending_move_to_rect.rect_anchor_dx,
- impl->pending_move_to_rect.rect_anchor_dy);
+ rect_anchor_dx,
+ rect_anchor_dy);
- anchor = rect_anchor_to_anchor_legacy (impl->pending_move_to_rect.rect_anchor);
+ anchor = rect_anchor_to_anchor_legacy (rect_anchor);
zxdg_positioner_v6_set_anchor (positioner, anchor);
- gravity = surface_anchor_to_gravity_legacy (impl->pending_move_to_rect.surface_anchor);
+ gravity = surface_anchor_to_gravity_legacy (surface_anchor);
zxdg_positioner_v6_set_gravity (positioner, gravity);
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X)
+ if (anchor_hints & GDK_ANCHOR_FLIP_X)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y)
+ if (anchor_hints & GDK_ANCHOR_FLIP_Y)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_X)
+ if (anchor_hints & GDK_ANCHOR_SLIDE_X)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_Y)
+ if (anchor_hints & GDK_ANCHOR_SLIDE_Y)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_X)
+ if (anchor_hints & GDK_ANCHOR_RESIZE_X)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X;
- if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_Y)
+ if (anchor_hints & GDK_ANCHOR_RESIZE_Y)
constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
zxdg_positioner_v6_set_constraint_adjustment (positioner,
constraint_adjustment);
@@ -2202,7 +2214,10 @@ can_map_grabbing_popup (GdkSurface *surface,
static void
gdk_wayland_surface_create_xdg_popup (GdkSurface *surface,
GdkSurface *parent,
- GdkWaylandSeat *grab_input_seat)
+ GdkWaylandSeat *grab_input_seat,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
{
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
@@ -2235,7 +2250,7 @@ gdk_wayland_surface_create_xdg_popup (GdkSurface *surface,
gdk_surface_freeze_updates (surface);
- positioner = create_dynamic_positioner (surface);
+ positioner = create_dynamic_positioner (surface, width, height, layout);
switch (display->shell_variant)
{
@@ -2302,7 +2317,14 @@ gdk_wayland_surface_create_xdg_popup (GdkSurface *surface,
gdk_profiler_add_mark (g_get_monotonic_time (), 0, "wayland", "surface commit");
wl_surface_commit (impl->display_server.wl_surface);
- impl->popup_parent = parent;
+ if (surface->surface_type == GDK_SURFACE_POPUP)
+ {
+ g_assert (impl->popup_state == POPUP_STATE_IDLE);
+ impl->popup_state = POPUP_STATE_WAITING_FOR_CONFIGURE;
+ freeze_popup_toplevel_state (surface);
+ }
+
+ g_set_object (&impl->popup_parent, parent);
display->current_popups = g_list_append (display->current_popups, surface);
if (grab_input_seat)
{
@@ -2360,10 +2382,9 @@ should_map_as_popup (GdkSurface *surface)
}
static void
-gdk_wayland_surface_map (GdkSurface *surface)
+gdk_wayland_surface_map_toplevel (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- GdkSurface *parent = NULL;
if (!should_be_mapped (surface))
return;
@@ -2371,25 +2392,7 @@ gdk_wayland_surface_map (GdkSurface *surface)
if (impl->mapped)
return;
- if (should_map_as_popup (surface))
- {
- GdkWaylandSeat *grab_input_seat;
-
- parent = surface->parent;
- if (!parent)
- {
- g_warning ("Couldn't map as surface %p as popup because it doesn't have a parent",
- surface);
- return;
- }
-
- grab_input_seat = find_grab_input_seat (surface, parent);
- gdk_wayland_surface_create_xdg_popup (surface, parent, grab_input_seat);
- }
- else
- {
- gdk_wayland_surface_create_xdg_toplevel (surface);
- }
+ gdk_wayland_surface_create_xdg_toplevel (surface);
impl->mapped = TRUE;
}
@@ -2400,10 +2403,12 @@ gdk_wayland_surface_show (GdkSurface *surface,
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+ g_return_if_fail (!should_map_as_popup (surface));
+
if (!impl->display_server.wl_surface)
gdk_wayland_surface_create_surface (surface);
- gdk_wayland_surface_map (surface);
+ gdk_wayland_surface_map_toplevel (surface);
}
static void
@@ -2517,6 +2522,23 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
gdk_surface_thaw_updates (surface);
}
+ if (surface->surface_type == GDK_SURFACE_POPUP)
+ {
+ switch (impl->popup_state)
+ {
+ case POPUP_STATE_WAITING_FOR_CONFIGURE:
+ case POPUP_STATE_WAITING_FOR_FRAME:
+ thaw_popup_toplevel_state (surface);
+ break;
+ case POPUP_STATE_IDLE:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ impl->popup_state = POPUP_STATE_IDLE;
+ }
+
if (impl->display_server.gtk_surface)
{
gtk_surface1_destroy (impl->display_server.gtk_surface);
@@ -2533,6 +2555,8 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG && !impl->transient_for)
display_wayland->orphan_dialogs =
g_list_remove (display_wayland->orphan_dialogs, surface);
+
+ g_clear_pointer (&impl->popup.layout, gdk_popup_layout_unref);
}
unset_transient_for_exported (surface);
@@ -2606,41 +2630,217 @@ gdk_wayland_surface_toplevel_resize (GdkSurface *surface,
impl->scale);
}
-/* Avoid zero width/height as this is a protocol error */
+static gboolean
+is_fallback_relayout_possible (GdkSurface *surface)
+{
+ GList *l;
+
+ for (l = surface->children; l; l = l->next)
+ {
+ GdkSurface *child = l->data;
+
+ if (GDK_WAYLAND_SURFACE (child)->mapped)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+queue_relayout_fallback (GdkSurface *surface,
+ GdkPopupLayout *layout)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ if (!is_fallback_relayout_possible (surface))
+ return;
+
+ gdk_wayland_surface_hide_surface (surface);
+ gdk_surface_present_popup (surface,
+ impl->popup.unconstrained_width,
+ impl->popup.unconstrained_height,
+ layout);
+}
+
+static void
+do_queue_relayout (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ g_assert (is_realized_popup (surface));
+ g_assert (impl->popup_state == POPUP_STATE_IDLE ||
+ impl->popup_state == POPUP_STATE_WAITING_FOR_FRAME);
+
+ g_clear_pointer (&impl->popup.layout, gdk_popup_layout_unref);
+ impl->popup.layout = gdk_popup_layout_copy (layout);
+ impl->popup.unconstrained_width = width;
+ impl->popup.unconstrained_height = height;
+
+ queue_relayout_fallback (surface, layout);
+}
+
+static gboolean
+is_relayout_finished (GdkSurface *surface)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ if (!impl->initial_configure_received)
+ return FALSE;
+
+ return TRUE;
+}
+
static void
-sanitize_anchor_rect (GdkSurface *surface,
- GdkRectangle *rect)
+gdk_wayland_surface_map_popup (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
{
- gint original_width = rect->width;
- gint original_height = rect->height;
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+ GdkSurface *parent;
+ GdkWaylandSeat *grab_input_seat;
+
+ if (!should_be_mapped (surface))
+ return;
- rect->width = MAX (1, rect->width);
- rect->height = MAX (1, rect->height);
- rect->x = MAX (rect->x + original_width - rect->width, 0);
- rect->y = MAX (rect->y + original_height - rect->height, 0);
+ if (impl->mapped)
+ return;
+
+ parent = surface->parent;
+ if (!parent)
+ {
+ g_warning ("Couldn't map as surface %p as popup because it doesn't have a parent",
+ surface);
+ return;
+ }
+
+ if (surface->autohide)
+ grab_input_seat = find_grab_input_seat (surface, parent);
+ else
+ grab_input_seat = NULL;
+ gdk_wayland_surface_create_xdg_popup (surface,
+ parent,
+ grab_input_seat,
+ width, height,
+ layout);
+
+ impl->popup.layout = gdk_popup_layout_copy (layout);
+ impl->popup.unconstrained_width = width;
+ impl->popup.unconstrained_height = height;
+ impl->mapped = TRUE;
}
static void
-gdk_wayland_surface_move_to_rect (GdkSurface *surface,
- const GdkRectangle *rect,
- GdkGravity rect_anchor,
- GdkGravity surface_anchor,
- GdkAnchorHints anchor_hints,
- gint rect_anchor_dx,
- gint rect_anchor_dy)
+show_popup (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
- impl->pending_move_to_rect.rect = *rect;
- sanitize_anchor_rect (surface, &impl->pending_move_to_rect.rect);
+ if (!impl->display_server.wl_surface)
+ gdk_wayland_surface_create_surface (surface);
+
+ gdk_wayland_surface_map_popup (surface, width, height, layout);
+}
- impl->pending_move_to_rect.rect_anchor = rect_anchor;
- impl->pending_move_to_rect.surface_anchor = surface_anchor;
- impl->pending_move_to_rect.anchor_hints = anchor_hints;
- impl->pending_move_to_rect.rect_anchor_dx = rect_anchor_dx;
- impl->pending_move_to_rect.rect_anchor_dy = rect_anchor_dy;
+typedef struct
+{
+ int width;
+ int height;
+ GdkPopupLayout *layout;
+} GrabPrepareData;
+
+static void
+show_grabbing_popup (GdkSeat *seat,
+ GdkSurface *surface,
+ gpointer user_data)
+{
+ GrabPrepareData *data = user_data;
- impl->has_layout_data = TRUE;
+ show_popup (surface, data->width, data->height, data->layout);
+}
+
+static void
+reposition_popup (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
+{
+ GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+
+ switch (impl->popup_state)
+ {
+ case POPUP_STATE_IDLE:
+ case POPUP_STATE_WAITING_FOR_FRAME:
+ do_queue_relayout (surface, width, height, layout);
+ break;
+ case POPUP_STATE_WAITING_FOR_CONFIGURE:
+ g_warn_if_reached ();
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+gdk_wayland_surface_present_popup (GdkSurface *surface,
+ int width,
+ int height,
+ GdkPopupLayout *layout)
+{
+ GdkWaylandDisplay *display_wayland =
+ GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
+ GdkWaylandSurface *impl;
+
+ g_return_val_if_fail (should_map_as_popup (surface), FALSE);
+
+ impl = GDK_WAYLAND_SURFACE (surface);
+
+ if (!impl->mapped)
+ {
+ if (surface->autohide)
+ {
+ GrabPrepareData data;
+
+ data = (GrabPrepareData) {
+ .width = width,
+ .height = height,
+ .layout = layout,
+ };
+ gdk_seat_grab (gdk_display_get_default_seat (surface->display),
+ surface,
+ GDK_SEAT_CAPABILITY_ALL,
+ TRUE,
+ NULL, NULL,
+ show_grabbing_popup, &data);
+ }
+ else
+ {
+ show_popup (surface, width, height, layout);
+ }
+ }
+ else
+ {
+ reposition_popup (surface, width, height, layout);
+ }
+
+ while (impl->display_server.xdg_popup && !is_relayout_finished (surface))
+ wl_display_dispatch_queue (display_wayland->wl_display, impl->event_queue);
+
+ if (impl->display_server.xdg_popup)
+ {
+ gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
+ gdk_surface_invalidate_rect (surface, NULL);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
}
static void
@@ -3697,7 +3897,7 @@ gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *klass)
impl_class->lower = gdk_wayland_surface_lower;
impl_class->restack_toplevel = gdk_wayland_surface_restack_toplevel;
impl_class->toplevel_resize = gdk_wayland_surface_toplevel_resize;
- impl_class->move_to_rect = gdk_wayland_surface_move_to_rect;
+ impl_class->present_popup = gdk_wayland_surface_present_popup;
impl_class->get_geometry = gdk_wayland_surface_get_geometry;
impl_class->get_root_coords = gdk_wayland_surface_get_root_coords;
impl_class->get_device_state = gdk_wayland_surface_get_device_state;