diff options
author | Jonas Ã…dahl <jadahl@gmail.com> | 2020-02-16 12:59:24 +0100 |
---|---|---|
committer | Jonas Ã…dahl <jadahl@gmail.com> | 2020-02-19 09:47:18 +0100 |
commit | ca71119a40ac9b196700855dac6484f4e198bdb1 (patch) | |
tree | 7d5a2cc24a6d0f9ee5ffb7712e5860a0becca9d3 /gdk/wayland | |
parent | 37f4c644d34e77d076a818854e2c3467b2b14b54 (diff) | |
download | gtk+-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.c | 2 | ||||
-rw-r--r-- | gdk/wayland/gdksurface-wayland.c | 794 |
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; |