[gtk: 3/4] gdk/toplevel: Negotiate surface size via a compute-size signal




commit 2ff74eb667c2cbe293c7309d5661fa438e8431c4
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Jul 30 23:06:59 2020 +0200

    gdk/toplevel: Negotiate surface size via a compute-size signal
    
    GTK will not up front know how to correctly calculate a size, since it
    will not be able to reliably predict the constraints that may exist
    where it will be mapped.
    
    Thus, to handle this, calculate the size of the toplevel by having GDK
    emitting a signal called 'compute-size' that will contain information
    needed for computing a toplevel window size.
    
    This signal may be emitted at any time, e.g. during
    gdk_toplevel_present(), or spontaneously if constraints change.
    
    This also drops the max size from the toplevel layout, while moving the
    min size from the toplevel layout struct to the struct passed via the
    signal,
    
    This needs changes to a test case where we make sure we process
    GDK_CONFIGURE etc, which means we also needs to show the window and
    process all pending events in the test-focus-chain test case.

 docs/reference/gdk/gdk4-sections.txt |  14 +-
 gdk/broadway/gdksurface-broadway.c   |  34 ++++-
 gdk/gdk.h                            |   1 +
 gdk/gdksurface.c                     |   8 +-
 gdk/gdksurface.h                     |   4 +-
 gdk/gdktoplevel.c                    |  62 +++++++--
 gdk/gdktoplevel.h                    |   2 -
 gdk/gdktoplevellayout.c              |  43 +-----
 gdk/gdktoplevellayout.h              |   8 +-
 gdk/gdktoplevelprivate.h             |   8 +-
 gdk/gdktoplevelsize.c                | 119 +++++++++++++++++
 gdk/gdktoplevelsize.h                |  62 +++++++++
 gdk/gdktoplevelsizeprivate.h         |  39 ++++++
 gdk/macos/gdkmacostoplevelsurface.c  |  33 ++++-
 gdk/meson.build                      |   3 +
 gdk/wayland/gdksurface-wayland.c     | 139 +++++++++++++++++---
 gdk/win32/gdksurface-win32.c         |  35 ++++-
 gdk/x11/gdksurface-x11.c             |  45 +++++--
 gtk/gtkwindow.c                      | 244 +++++++++++++++++++++++------------
 gtk/inspector/general.c              |   4 +-
 tests/rendernode.c                   |   2 +-
 tests/showrendernode.c               |   4 +-
 tests/testfullscreen.c               |  14 +-
 testsuite/gsk/compare-render.c       |   2 +-
 testsuite/gtk/defaultvalue.c         |   2 +-
 testsuite/gtk/notify.c               |   2 +-
 testsuite/gtk/shortcuts.c            |   2 +-
 testsuite/gtk/test-focus-chain.c     |  25 ++++
 28 files changed, 747 insertions(+), 213 deletions(-)
---
diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt
index a7c942a4e5..5cff88eb65 100644
--- a/docs/reference/gdk/gdk4-sections.txt
+++ b/docs/reference/gdk/gdk4-sections.txt
@@ -633,8 +633,6 @@ gdk_toplevel_layout_get_maximized
 gdk_toplevel_layout_set_fullscreen
 gdk_toplevel_layout_get_fullscreen
 gdk_toplevel_layout_get_fullscreen_monitor
-gdk_toplevel_layout_get_min_width
-gdk_toplevel_layout_get_min_height
 gdk_toplevel_layout_set_resizable
 gdk_toplevel_layout_get_resizable
 <SUBSECTION Standard>
@@ -642,6 +640,18 @@ GDK_TYPE_TOPLEVEL_LAYOUT
 gdk_toplevel_layout_get_type
 </SECTION>
 
+<SECTION>
+<FILE>gdktoplevelsize</FILE>
+<TITLE>GdkToplevelSize</TITLE>
+GdkToplevelSize
+gdk_toplevel_size_get_bounds
+gdk_toplevel_size_set_size
+gdk_toplevel_size_set_min_size
+<SUBSECTION Standard>
+GDK_TYPE_TOPLEVEL_SIZE
+gdk_toplevel_size_get_type
+</SECTION>
+
 <SECTION>
 <FILE>gdktoplevel</FILE>
 <TITLE>GdkToplevel</TITLE>
diff --git a/gdk/broadway/gdksurface-broadway.c b/gdk/broadway/gdksurface-broadway.c
index fb03675ece..7f5f9a79e4 100644
--- a/gdk/broadway/gdksurface-broadway.c
+++ b/gdk/broadway/gdksurface-broadway.c
@@ -41,6 +41,7 @@
 #include "gdktextureprivate.h"
 #include "gdktoplevelprivate.h"
 
+#include <graphene.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -1524,20 +1525,45 @@ show_surface (GdkSurface *surface)
 
 static gboolean
 gdk_broadway_toplevel_present (GdkToplevel       *toplevel,
-                               int                width,
-                               int                height,
                                GdkToplevelLayout *layout)
 {
   GdkSurface *surface = GDK_SURFACE (toplevel);
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkMonitor *monitor;
+  GdkToplevelSize size;
+  int bounds_width, bounds_height;
+  int width, height;
   GdkGeometry geometry;
   GdkSurfaceHints mask;
 
   gdk_broadway_surface_unminimize (surface);
 
+  monitor = gdk_display_get_monitor_at_surface (display, surface);
+  if (monitor)
+    {
+      GdkRectangle monitor_geometry;
+
+      gdk_monitor_get_geometry (monitor, &monitor_geometry);
+      bounds_width = monitor_geometry.width;
+      bounds_height = monitor_geometry.height;
+    }
+  else
+    {
+      bounds_width = G_MAXINT;
+      bounds_height = G_MAXINT;
+    }
+
+  gdk_toplevel_size_init (&size, bounds_width, bounds_height);
+  gdk_toplevel_notify_compute_size (toplevel, &size);
+  g_warn_if_fail (size.width > 0);
+  g_warn_if_fail (size.height > 0);
+  width = size.width;
+  height = size.height;
+
   if (gdk_toplevel_layout_get_resizable (layout))
     {
-      geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
-      geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
+      geometry.min_width = size.min_width;
+      geometry.min_height = size.min_height;
       mask = GDK_HINT_MIN_SIZE;
     }
   else
diff --git a/gdk/gdk.h b/gdk/gdk.h
index d15dd7d922..8b6489cbf0 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -70,6 +70,7 @@
 #include <gdk/gdktexture.h>
 #include <gdk/gdktoplevel.h>
 #include <gdk/gdktoplevellayout.h>
+#include <gdk/gdktoplevelsize.h>
 #include <gdk/gdktypes.h>
 #include <gdk/gdkversionmacros.h>
 #include <gdk/gdkvulkancontext.h>
diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c
index 6d8b84ee34..75c35d6d50 100644
--- a/gdk/gdksurface.c
+++ b/gdk/gdksurface.c
@@ -795,22 +795,18 @@ gdk_surface_new (GdkDisplay     *display,
 /**
  * gdk_surface_new_toplevel: (constructor)
  * @display: the display to create the surface on
- * @width: width of new surface
- * @height: height of new surface
  *
  * Creates a new toplevel surface.
  *
  * Returns: (transfer full): the new #GdkSurface
  **/
 GdkSurface *
-gdk_surface_new_toplevel (GdkDisplay *display,
-                          int         width,
-                          int         height)
+gdk_surface_new_toplevel (GdkDisplay *display)
 {
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
   return gdk_surface_new (display, GDK_SURFACE_TOPLEVEL,
-                          NULL, 0, 0, width, height);
+                          NULL, 0, 0, 0, 0);
 }
 
 /**
diff --git a/gdk/gdksurface.h b/gdk/gdksurface.h
index 077c012a3f..9b1613147f 100644
--- a/gdk/gdksurface.h
+++ b/gdk/gdksurface.h
@@ -139,9 +139,7 @@ typedef struct _GdkSurfaceClass GdkSurfaceClass;
 GDK_AVAILABLE_IN_ALL
 GType         gdk_surface_get_type              (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
-GdkSurface *   gdk_surface_new_toplevel         (GdkDisplay    *display,
-                                                 int            width,
-                                                 int            height);
+GdkSurface *   gdk_surface_new_toplevel         (GdkDisplay    *display);
 GDK_AVAILABLE_IN_ALL
 GdkSurface *   gdk_surface_new_popup            (GdkSurface    *parent,
                                                  gboolean       autohide);
diff --git a/gdk/gdktoplevel.c b/gdk/gdktoplevel.c
index d3b1d7fad7..3b8a22a611 100644
--- a/gdk/gdktoplevel.c
+++ b/gdk/gdktoplevel.c
@@ -23,6 +23,7 @@
 #include "gdk-private.h"
 #include "gdktoplevelprivate.h"
 
+#include <graphene-gobject.h>
 #include <math.h>
 
 /**
@@ -35,10 +36,17 @@
 
 G_DEFINE_INTERFACE (GdkToplevel, gdk_toplevel, GDK_TYPE_SURFACE)
 
+enum
+{
+  COMPUTE_SIZE,
+
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
 static gboolean
 gdk_toplevel_default_present (GdkToplevel       *toplevel,
-                              int                width,
-                              int                height,
                               GdkToplevelLayout *layout)
 {
   return FALSE;
@@ -86,6 +94,13 @@ gdk_toplevel_default_restore_system_shortcuts (GdkToplevel *toplevel)
 {
 }
 
+void
+gdk_toplevel_notify_compute_size (GdkToplevel     *toplevel,
+                                  GdkToplevelSize *size)
+{
+  g_signal_emit (toplevel, signals[COMPUTE_SIZE], 0, size);
+}
+
 static void
 gdk_toplevel_default_init (GdkToplevelInterface *iface)
 {
@@ -158,6 +173,37 @@ gdk_toplevel_default_init (GdkToplevelInterface *iface)
                             "Whether keyboard shortcuts are inhibited",
                             FALSE,
                             G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY));
+
+  /**
+   * GdkToplevel::compute-size:
+   * @toplevel: a #GdkToplevel
+   * @size: (type Gdk.ToplevelSize) (out caller-allocates): a #GdkToplevelSize
+   *
+   * Compute the desired size of the toplevel, given the information passed via
+   * the #GdkToplevelSize object.
+   *
+   * It will normally be emitted during or after gdk_toplevel_present(),
+   * depending on the configuration received by the windowing system. It may
+   * also be emitted at any other point in time, in response to the windowing
+   * system spontaneously changing the configuration.
+   *
+   * It is the responsibility of the GdkToplevel user to handle this signal;
+   * failing to do so will result in an arbitrary fixed size being used as a
+   * result. The signal may be emitted with the pointer to the @size being
+   * %NULL, in which case only the minimum and maximum size needs to be
+   * computed. This could happen for example if the toplevel configuration is in
+   * a state where the size is decided by the windowing system, such as
+   * maximized or fullscreen.
+   */
+  signals[COMPUTE_SIZE] =
+    g_signal_new ("compute-size",
+                  GDK_TYPE_TOPLEVEL,
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE, 1,
+                  GDK_TYPE_TOPLEVEL_SIZE | G_SIGNAL_TYPE_STATIC_SCOPE);
 }
 
 guint
@@ -181,30 +227,28 @@ gdk_toplevel_install_properties (GObjectClass *object_class,
 /**
  * gdk_toplevel_present:
  * @toplevel: the #GdkToplevel to show
- * @width: the unconstrained toplevel width to layout
- * @height: the unconstrained toplevel height to layout
  * @layout: the #GdkToplevelLayout object used to layout
  *
  * Present @toplevel after having processed the #GdkToplevelLayout rules.
  * If the toplevel was previously not showing, it will be showed,
  * otherwise it will change layout according to @layout.
  *
+ * GDK may emit the 'compute-size' signal to let the user of this toplevel
+ * compute the preferred size of the toplevel surface. See
+ * #GdkToplevel::compute-size for details.
+ *
  * Presenting may fail.
  *
  * Returns: %FALSE if @toplevel failed to be presented, otherwise %TRUE.
  */
 gboolean
 gdk_toplevel_present (GdkToplevel       *toplevel,
-                      int                width,
-                      int                height,
                       GdkToplevelLayout *layout)
 {
   g_return_val_if_fail (GDK_IS_TOPLEVEL (toplevel), FALSE);
-  g_return_val_if_fail (width > 0, FALSE);
-  g_return_val_if_fail (height > 0, FALSE);
   g_return_val_if_fail (layout != NULL, FALSE);
 
-  return GDK_TOPLEVEL_GET_IFACE (toplevel)->present (toplevel, width, height, layout);
+  return GDK_TOPLEVEL_GET_IFACE (toplevel)->present (toplevel, layout);
 }
 
 /**
diff --git a/gdk/gdktoplevel.h b/gdk/gdktoplevel.h
index 20b3fc5d4b..cd1bb74241 100644
--- a/gdk/gdktoplevel.h
+++ b/gdk/gdktoplevel.h
@@ -37,8 +37,6 @@ G_DECLARE_INTERFACE (GdkToplevel, gdk_toplevel, GDK, TOPLEVEL, GObject)
 
 GDK_AVAILABLE_IN_ALL
 gboolean        gdk_toplevel_present            (GdkToplevel       *toplevel,
-                                                 int                width,
-                                                 int                height,
                                                  GdkToplevelLayout *layout);
 
 GDK_AVAILABLE_IN_ALL
diff --git a/gdk/gdktoplevellayout.c b/gdk/gdktoplevellayout.c
index 8f4a6b56aa..577b03be41 100644
--- a/gdk/gdktoplevellayout.c
+++ b/gdk/gdktoplevellayout.c
@@ -39,8 +39,6 @@ struct _GdkToplevelLayout
   /* < private >*/
   grefcount ref_count;
 
-  int min_width;
-  int min_height;
   guint resizable  : 1;
   guint maximized  : 1;
   guint fullscreen : 1;
@@ -53,8 +51,6 @@ G_DEFINE_BOXED_TYPE (GdkToplevelLayout, gdk_toplevel_layout,
 
 /**
  * gdk_toplevel_layout_new: (constructor)
- * @min_width: the minimum width for the layout
- * @min_height: the minimum height for the layout
  *
  * Create a toplevel layout description.
  *
@@ -67,15 +63,12 @@ G_DEFINE_BOXED_TYPE (GdkToplevelLayout, gdk_toplevel_layout,
  * Returns: (transfer full): newly created instance of #GdkToplevelLayout
  */
 GdkToplevelLayout *
-gdk_toplevel_layout_new (int min_width,
-                         int min_height)
+gdk_toplevel_layout_new (void)
 {
   GdkToplevelLayout *layout;
 
   layout = g_new0 (GdkToplevelLayout, 1);
   g_ref_count_init (&layout->ref_count);
-  layout->min_width = min_width;
-  layout->min_height = min_height;
   layout->resizable = TRUE;
   layout->maximized = FALSE;
   layout->fullscreen = FALSE;
@@ -131,8 +124,6 @@ gdk_toplevel_layout_copy (GdkToplevelLayout *layout)
   new_layout = g_new0 (GdkToplevelLayout, 1);
   g_ref_count_init (&new_layout->ref_count);
 
-  new_layout->min_width = layout->min_width;
-  new_layout->min_height = layout->min_height;
   new_layout->resizable = layout->resizable;
   new_layout->maximized = layout->maximized;
   new_layout->fullscreen = layout->fullscreen;
@@ -159,42 +150,12 @@ gdk_toplevel_layout_equal (GdkToplevelLayout *layout,
   g_return_val_if_fail (layout, FALSE);
   g_return_val_if_fail (other, FALSE);
 
-  return layout->min_width == other->min_width &&
-         layout->min_height == other->min_height &&
-         layout->resizable == other->resizable &&
+  return layout->resizable == other->resizable &&
          layout->maximized == other->maximized &&
          layout->fullscreen == other->fullscreen &&
          layout->fullscreen_monitor == other->fullscreen_monitor;
 }
 
-/**
- * gdk_toplevel_layout_get_min_width:
- * @layout: a #GdkToplevelLayout
- *
- * Returns the minimum width of the given layout.
- *
- * Returns: the minimum width of @layout
- */
-int
-gdk_toplevel_layout_get_min_width (GdkToplevelLayout *layout)
-{
-  return layout->min_width;
-}
-
-/**
- * gdk_toplevel_layout_get_min_height:
- * @layout: a #GdkToplevelLayout
- *
- * Returns the minimum height of the given layout.
- *
- * Returns: the minimum height of @layout
- */
-int
-gdk_toplevel_layout_get_min_height (GdkToplevelLayout *layout)
-{
-  return layout->min_height;
-}
-
 /**
  * gdk_toplevel_layout_set_resizable:
  * @layout: a #GdkToplevelLayout
diff --git a/gdk/gdktoplevellayout.h b/gdk/gdktoplevellayout.h
index 5be9aee83c..23b2660414 100644
--- a/gdk/gdktoplevellayout.h
+++ b/gdk/gdktoplevellayout.h
@@ -42,8 +42,7 @@ GDK_AVAILABLE_IN_ALL
 GType                   gdk_toplevel_layout_get_type    (void);
 
 GDK_AVAILABLE_IN_ALL
-GdkToplevelLayout *     gdk_toplevel_layout_new         (int min_width,
-                                                         int min_height);
+GdkToplevelLayout *     gdk_toplevel_layout_new         (void);
 
 GDK_AVAILABLE_IN_ALL
 GdkToplevelLayout *     gdk_toplevel_layout_ref         (GdkToplevelLayout *layout);
@@ -66,11 +65,6 @@ void                    gdk_toplevel_layout_set_fullscreen (GdkToplevelLayout *l
                                                             gboolean           fullscreen,
                                                             GdkMonitor        *monitor);
 
-GDK_AVAILABLE_IN_ALL
-int                     gdk_toplevel_layout_get_min_width  (GdkToplevelLayout *layout);
-GDK_AVAILABLE_IN_ALL
-int                     gdk_toplevel_layout_get_min_height (GdkToplevelLayout *layout);
-
 GDK_AVAILABLE_IN_ALL
 gboolean                gdk_toplevel_layout_get_maximized (GdkToplevelLayout *layout);
 
diff --git a/gdk/gdktoplevelprivate.h b/gdk/gdktoplevelprivate.h
index 6cdccca8b7..989ac29c46 100644
--- a/gdk/gdktoplevelprivate.h
+++ b/gdk/gdktoplevelprivate.h
@@ -2,6 +2,9 @@
 #define __GDK_TOPLEVEL_PRIVATE_H__
 
 #include "gdktoplevel.h"
+#include "gdktoplevelsizeprivate.h"
+
+#include <graphene.h>
 
 G_BEGIN_DECLS
 
@@ -11,8 +14,6 @@ struct _GdkToplevelInterface
   GTypeInterface g_iface;
 
   gboolean      (* present)             (GdkToplevel       *toplevel,
-                                         int                width,
-                                         int                height,
                                          GdkToplevelLayout *layout);
   gboolean      (* minimize)            (GdkToplevel       *toplevel);
   gboolean      (* lower)               (GdkToplevel       *toplevel);
@@ -57,6 +58,9 @@ typedef enum
 guint gdk_toplevel_install_properties (GObjectClass *object_class,
                                        guint         first_prop);
 
+void gdk_toplevel_notify_compute_size (GdkToplevel     *toplevel,
+                                       GdkToplevelSize *size);
+
 G_END_DECLS
 
 #endif /* __GDK_TOPLEVEL_PRIVATE_H__ */
diff --git a/gdk/gdktoplevelsize.c b/gdk/gdktoplevelsize.c
new file mode 100644
index 0000000000..18933523ff
--- /dev/null
+++ b/gdk/gdktoplevelsize.c
@@ -0,0 +1,119 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2020 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "gdktoplevelsizeprivate.h"
+
+/**
+ * SECTION:gdktoplevelsize
+ * @Title: GdkToplevelSize
+ * @Short_description: Information for computing toplevel size
+ *
+ * The GdkToplevelSIze struct contains information that may be useful
+ * for users of GdkToplevel to compute a surface size. It also carries
+ * information back with the computational result.
+ */
+
+G_DEFINE_POINTER_TYPE (GdkToplevelSize, gdk_toplevel_size)
+
+#define UNCONFIGURED_WIDTH 400
+#define UNCONFIGURED_HEIGHT 300
+
+void
+gdk_toplevel_size_init (GdkToplevelSize *size,
+                        int              bounds_width,
+                        int              bounds_height)
+{
+  *size = (GdkToplevelSize) { 0 };
+
+  size->bounds_width = bounds_width;
+  size->bounds_height = bounds_height;
+
+  size->width = UNCONFIGURED_WIDTH;
+  size->height = UNCONFIGURED_HEIGHT;
+}
+
+/**
+ * gdk_toplevel_size_get_bounds:
+ * @size: a #GdkToplevelSize
+ * @bounds_width: (out): return location for width
+ * @bounds_height: (out): return location for height
+ *
+ * Retrieves the bounds the toplevel is placed within.
+ *
+ * The bounds represent the largest size a toplevel may have while still being
+ * able to fit within some type of boundery. Depending on the backend, this may
+ * be equivalent to the dimensions of the work area or the monitor on which the
+ * window is being presented on, or something else that limits the way a
+ * toplevel can be presented.
+ */
+void
+gdk_toplevel_size_get_bounds (GdkToplevelSize *size,
+                              int             *bounds_width,
+                              int             *bounds_height)
+{
+  g_return_if_fail (bounds_width);
+  g_return_if_fail (bounds_height);
+
+  *bounds_width = size->bounds_width;
+  *bounds_height = size->bounds_height;
+}
+
+/**
+ * gdk_toplevel_size_set_size:
+ * @size: a #GdkToplevelSize
+ * @width: the width
+ * @height: the height
+ *
+ * Sets the size the toplevel prefers to be resized to. The size should be
+ * within the bounds (see gdk_toplevel_size_get_bounds()). The set size should
+ * be considered as a hint, and should not be assumed to be respected by the
+ * windowing system, or backend.
+ */
+void
+gdk_toplevel_size_set_size (GdkToplevelSize *size,
+                            int              width,
+                            int              height)
+{
+  size->width = width;
+  size->height = height;
+}
+
+/**
+ * gdk_toplevel_size_set_min_size:
+ * @size: a #GdkToplevelSize
+ * @min_width: the minimum width
+ * @min_height: the minimum height
+ *
+ * The minimum size corresponds to the limitations the toplevel can be shrunk
+ * to, without resulting in incorrect painting. A user of a #GdkToplevel should
+ * calculate these given both the existing size, and the bounds retrieved from
+ * the #GdkToplevelSize object.
+ *
+ * The minimum size should be within the bounds (see
+ * gdk_toplevel_size_get_bounds()).
+ */
+void
+gdk_toplevel_size_set_min_size (GdkToplevelSize *size,
+                                int              min_width,
+                                int              min_height)
+{
+  size->min_width = min_width;
+  size->min_height = min_height;
+}
diff --git a/gdk/gdktoplevelsize.h b/gdk/gdktoplevelsize.h
new file mode 100644
index 0000000000..731ea6379a
--- /dev/null
+++ b/gdk/gdktoplevelsize.h
@@ -0,0 +1,62 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2020 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GDK_TOPLEVEL_SIZE_H__
+#define __GDK_TOPLEVEL_SIZE_H__
+
+#if !defined(__GDK_H_INSIDE__) && !defined(GTK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#include <gdk/gdktypes.h>
+#include <gdk/gdkversionmacros.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GdkTopLevelSize:
+ *
+ * Struct containing information for computing the size of a #GdkToplevel.
+ */
+typedef struct _GdkToplevelSize GdkToplevelSize;
+
+#define GDK_TYPE_TOPLEVEL_SIZE (gdk_toplevel_size_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+GType                   gdk_toplevel_size_get_type      (void);
+
+void                    gdk_toplevel_size_get_bounds    (GdkToplevelSize       *size,
+                                                         int                   *bounds_width,
+                                                         int                   *bounds_height);
+
+GDK_AVAILABLE_IN_ALL
+void                    gdk_toplevel_size_set_size      (GdkToplevelSize       *size,
+                                                         int                    width,
+                                                         int                    height);
+GDK_AVAILABLE_IN_ALL
+void                    gdk_toplevel_size_set_min_size  (GdkToplevelSize       *size,
+                                                         int                    min_width,
+                                                         int                    min_height);
+GDK_AVAILABLE_IN_ALL
+void                    gdk_toplevel_size_set_max_size  (GdkToplevelSize       *size,
+                                                         int                    max_width,
+                                                         int                    max_height);
+
+G_END_DECLS
+
+#endif /* __GDK_TOPLEVEL_SIZE_H__ */
diff --git a/gdk/gdktoplevelsizeprivate.h b/gdk/gdktoplevelsizeprivate.h
new file mode 100644
index 0000000000..eaddefba0b
--- /dev/null
+++ b/gdk/gdktoplevelsizeprivate.h
@@ -0,0 +1,39 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2020 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GDK_TOPLEVEL_SIZE_PRIVATE_H__
+#define __GDK_TOPLEVEL_SIZE_PRIVATE_H__
+
+#include "gdktoplevelsize.h"
+
+struct _GdkToplevelSize
+{
+  int bounds_width;
+  int bounds_height;
+
+  int width;
+  int height;
+  int min_width;
+  int min_height;
+};
+
+void gdk_toplevel_size_init (GdkToplevelSize *size,
+                             int              bounds_width,
+                             int              bounds_height);
+
+#endif /* __GDK_TOPLEVEL_SIZE_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacostoplevelsurface.c b/gdk/macos/gdkmacostoplevelsurface.c
index a399150fe0..0e5a2a0ae7 100644
--- a/gdk/macos/gdkmacostoplevelsurface.c
+++ b/gdk/macos/gdkmacostoplevelsurface.c
@@ -93,12 +93,15 @@ _gdk_macos_toplevel_surface_unmaximize (GdkMacosToplevelSurface *self)
 
 static gboolean
 _gdk_macos_toplevel_surface_present (GdkToplevel       *toplevel,
-                                     int                width,
-                                     int                height,
                                      GdkToplevelLayout *layout)
 {
   GdkMacosToplevelSurface *self = (GdkMacosToplevelSurface *)toplevel;
   NSWindow *nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (self));
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkMonitor *monitor;
+  GdkToplevelSize size;
+  int bounds_width, bounds_height;
+  int width, height;
   GdkGeometry geometry;
   GdkSurfaceHints mask;
   NSWindowStyleMask style_mask;
@@ -108,10 +111,32 @@ _gdk_macos_toplevel_surface_present (GdkToplevel       *toplevel,
 
   style_mask = [nswindow styleMask];
 
+  monitor = gdk_display_get_monitor_at_surface (display, surface);
+  if (monitor)
+    {
+      GdkRectangle workarea;
+
+      gdk_macos_monitor_get_workarea (monitor, &workarea);
+      bounds_width = workarea.width;
+      bounds_height = workarea.height;
+    }
+  else
+    {
+      bounds_width = G_MAXINT;
+      bounds_height = G_MAXINT;
+    }
+
+  gdk_toplevel_size_init (&size, bounds_width, bounds_height);
+  gdk_toplevel_notify_compute_size (toplevel, &size);
+  g_warn_if_fail (size.width > 0);
+  g_warn_if_fail (size.height > 0);
+  width = size.width;
+  height = size.height;
+
   if (gdk_toplevel_layout_get_resizable (layout))
     {
-      geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
-      geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
+      geometry.min_width = size.min_width;
+      geometry.min_height = size.min_height;
       mask = GDK_HINT_MIN_SIZE;
 
       /* Only set 'Resizable' mask to get native resize zones if the window is
diff --git a/gdk/meson.build b/gdk/meson.build
index eebc9b2a97..76d60dd2e6 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -47,6 +47,7 @@ gdk_public_sources = files([
   'gdkprofiler.c',
   'gdkpopup.c',
   'gdktoplevellayout.c',
+  'gdktoplevelsize.c',
   'gdktoplevel.c',
   'gdkdragsurface.c',
 ])
@@ -95,6 +96,7 @@ gdk_public_headers = files([
   'gdkpopuplayout.h',
   'gdkpopup.h',
   'gdktoplevellayout.h',
+  'gdktoplevelsize.h',
   'gdktoplevel.h',
   'gdkdragsurface.h',
 ])
@@ -107,6 +109,7 @@ gdk_private_h_sources = files([
   'gdkdevicetoolprivate.h',
   'gdkmonitorprivate.h',
   'gdkseatdefaultprivate.h',
+  'gdktoplevelsizeprivate.h',
 ])
 
 gdk_gresource_xml = configure_file(output : 'gdk.gresource.xml',
diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c
index d30d7a8a12..2d6cafd187 100644
--- a/gdk/wayland/gdksurface-wayland.c
+++ b/gdk/wayland/gdksurface-wayland.c
@@ -146,11 +146,21 @@ struct _GdkWaylandSurface
   cairo_region_t *input_region;
   gboolean input_region_dirty;
 
+  GdkRectangle last_sent_window_geometry;
+  int last_sent_min_width;
+  int last_sent_min_height;
+  int last_sent_max_width;
+  int last_sent_max_height;
+
   int saved_width;
   int saved_height;
 
   gulong parent_surface_committed_handler;
 
+  struct {
+    GdkToplevelLayout *layout;
+  } toplevel;
+
   struct {
     GdkPopupLayout *layout;
     int unconstrained_width;
@@ -1112,6 +1122,9 @@ gdk_wayland_surface_sync_margin (GdkSurface *surface)
                                           &impl->geometry_hints,
                                           impl->geometry_mask);
 
+  if (gdk_rectangle_equal (&geometry, &impl->last_sent_window_geometry))
+    return;
+
   switch (display_wayland->shell_variant)
     {
     case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL:
@@ -1131,6 +1144,8 @@ gdk_wayland_surface_sync_margin (GdkSurface *surface)
     default:
       g_assert_not_reached ();
     }
+
+  impl->last_sent_window_geometry = geometry;
 }
 
 static struct wl_region *
@@ -1265,6 +1280,50 @@ gdk_wayland_surface_create_surface (GdkSurface *surface)
   impl->display_server.wl_surface = wl_surface;
 }
 
+static void
+configure_surface_geometry (GdkSurface *surface)
+{
+  GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkMonitor *monitor;
+  GdkRectangle monitor_geometry;
+  int bounds_width, bounds_height;
+  GdkToplevelSize size;
+  int width, height;
+  GdkToplevelLayout *layout;
+  GdkGeometry geometry;
+  GdkSurfaceHints mask;
+
+  monitor = g_list_model_get_item (gdk_display_get_monitors (display), 0);
+  gdk_monitor_get_geometry (monitor, &monitor_geometry);
+  bounds_width = monitor_geometry.width;
+  bounds_height = monitor_geometry.height;
+
+  gdk_toplevel_size_init (&size, bounds_width, bounds_height);
+  gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size);
+  width = size.width;
+  height = size.height;
+  g_warn_if_fail (width > 0);
+  g_warn_if_fail (height > 0);
+
+  layout = impl->toplevel.layout;
+  if (gdk_toplevel_layout_get_resizable (layout))
+    {
+      geometry.min_width = size.min_width;
+      geometry.min_height = size.min_height;
+      mask = GDK_HINT_MIN_SIZE;
+    }
+  else
+    {
+      geometry.max_width = geometry.min_width = width;
+      geometry.max_height = geometry.min_height = height;
+      mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
+    }
+  gdk_wayland_surface_set_geometry_hints (surface, &geometry, mask);
+  gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
+  gdk_wayland_surface_resize (surface, width, height, impl->scale);
+}
+
 static void
 gdk_wayland_surface_configure_toplevel (GdkSurface *surface)
 {
@@ -1322,6 +1381,10 @@ gdk_wayland_surface_configure_toplevel (GdkSurface *surface)
 
       gdk_wayland_surface_resize (surface, width, height, impl->scale);
     }
+  else
+    {
+      configure_surface_geometry (surface);
+    }
 
   GDK_DISPLAY_NOTE (gdk_surface_get_display (surface), EVENTS,
             g_message ("configure, surface %p %dx%d,%s%s%s%s",
@@ -2729,6 +2792,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
       g_slist_free (impl->display_server.outputs);
       impl->display_server.outputs = NULL;
 
+      g_clear_pointer (&impl->toplevel.layout, gdk_toplevel_layout_unref);
       g_clear_pointer (&impl->popup.layout, gdk_popup_layout_unref);
     }
 
@@ -3369,6 +3433,12 @@ gdk_wayland_surface_set_geometry_hints (GdkSurface         *surface,
       max_height = 0;
     }
 
+  if (impl->last_sent_min_width == min_width &&
+      impl->last_sent_min_height == min_height &&
+      impl->last_sent_max_width == max_width &&
+      impl->last_sent_max_height == max_height)
+    return;
+
   switch (display_wayland->shell_variant)
     {
     case GDK_WAYLAND_SHELL_VARIANT_XDG_SHELL:
@@ -3386,6 +3456,11 @@ gdk_wayland_surface_set_geometry_hints (GdkSurface         *surface,
     default:
       g_assert_not_reached ();
     }
+
+  impl->last_sent_min_width = min_width;
+  impl->last_sent_min_height = min_height;
+  impl->last_sent_max_width = max_width;
+  impl->last_sent_max_height = max_height;
 }
 
 static void
@@ -4676,37 +4751,41 @@ show_surface (GdkSurface *surface)
     gdk_surface_invalidate_rect (surface, NULL);
 }
 
+static void
+reconfigure_callback (void               *data,
+                      struct wl_callback *callback,
+                      uint32_t            time)
+{
+  gboolean *done = (gboolean *) data;
+
+  *done = TRUE;
+}
+
+static const struct wl_callback_listener reconfigure_listener = {
+  reconfigure_callback
+};
+
 static gboolean
 gdk_wayland_toplevel_present (GdkToplevel       *toplevel,
-                              int                width,
-                              int                height,
                               GdkToplevelLayout *layout)
 {
   GdkSurface *surface = GDK_SURFACE (toplevel);
   GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
-  GdkGeometry geometry;
-  GdkSurfaceHints mask;
+  GdkWaylandDisplay *display_wayland;
+  struct wl_callback *callback;
+  gboolean done = FALSE;
+  int last_configure_serial = impl->last_configure_serial;
+  gboolean needs_reconfigure = TRUE;
 
-  if (gdk_toplevel_layout_get_resizable (layout))
+  if (gdk_toplevel_layout_get_maximized (layout))
     {
-      geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
-      geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
-      mask = GDK_HINT_MIN_SIZE;
+      gdk_wayland_surface_maximize (surface);
+      needs_reconfigure = FALSE;
     }
   else
     {
-      geometry.max_width = geometry.min_width = width;
-      geometry.max_height = geometry.min_height = height;
-      mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
+      gdk_wayland_surface_unmaximize (surface);
     }
-  gdk_wayland_surface_set_geometry_hints (surface, &geometry, mask);
-  gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
-  gdk_wayland_surface_resize (surface, width, height, impl->scale);
-
-  if (gdk_toplevel_layout_get_maximized (layout))
-    gdk_wayland_surface_maximize (surface);
-  else
-    gdk_wayland_surface_unmaximize (surface);
 
   if (gdk_toplevel_layout_get_fullscreen (layout))
     {
@@ -4715,12 +4794,34 @@ gdk_wayland_toplevel_present (GdkToplevel       *toplevel,
         gdk_wayland_surface_fullscreen_on_monitor (surface, monitor);
       else
         gdk_wayland_surface_fullscreen (surface);
+      needs_reconfigure = FALSE;
     }
   else
     gdk_wayland_surface_unfullscreen (surface);
 
+  g_clear_pointer (&impl->toplevel.layout, gdk_toplevel_layout_unref);
+  impl->toplevel.layout = gdk_toplevel_layout_copy (layout);
+
   show_surface (surface);
 
+  display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (surface));
+  callback = wl_display_sync (display_wayland->wl_display);
+  wl_proxy_set_queue ((struct wl_proxy *) callback, impl->event_queue);
+  wl_callback_add_listener (callback,
+                            &reconfigure_listener,
+                            &done);
+  while (is_realized_toplevel (surface) &&
+         !impl->initial_configure_received &&
+         !done)
+    wl_display_dispatch_queue (display_wayland->wl_display, impl->event_queue);
+
+  if (needs_reconfigure &&
+      last_configure_serial == impl->last_configure_serial &&
+      !(surface->state & (GDK_SURFACE_STATE_MAXIMIZED |
+                          GDK_SURFACE_STATE_FULLSCREEN |
+                          GDK_SURFACE_STATE_TILED)))
+    configure_surface_geometry (surface);
+
   return TRUE;
 }
 
diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c
index fc52a82a5a..a8e2e125a0 100644
--- a/gdk/win32/gdksurface-win32.c
+++ b/gdk/win32/gdksurface-win32.c
@@ -4941,18 +4941,43 @@ show_surface (GdkSurface *surface)
 
 static gboolean
 gdk_win32_toplevel_present (GdkToplevel       *toplevel,
-                              int                width,
-                              int                height,
-                              GdkToplevelLayout *layout)
+                            GdkToplevelLayout *layout)
 {
   GdkSurface *surface = GDK_SURFACE (toplevel);
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkMonitor *monitor;
+  GdkToplevelSize size;
+  int bounds_width, bounds_height;
+  int width, height;
   GdkGeometry geometry;
   GdkSurfaceHints mask;
 
+  monitor = gdk_display_get_monitor_at_surface (display, surface);
+  if (monitor)
+    {
+      GdkRectangle workarea;
+
+      gdk_win32_monitor_get_workarea (monitor, &workarea);
+      bounds_width = workarea.width;
+      bounds_height = workarea.height;
+    }
+  else
+    {
+      bounds_width = G_MAXINT;
+      bounds_height = G_MAXINT;
+    }
+
+  gdk_toplevel_size_init (&size, bounds_width, bounds_height);
+  gdk_toplevel_notify_compute_size (toplevel, &size);
+  g_warn_if_fail (size.width > 0);
+  g_warn_if_fail (size.height > 0);
+  width = size.width;
+  height = size.height;
+
   if (gdk_toplevel_layout_get_resizable (layout))
     {
-      geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
-      geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
+      geometry.min_width = size.min_width;
+      geometry.min_height = size.min_height;
       mask = GDK_HINT_MIN_SIZE;
     }
   else
diff --git a/gdk/x11/gdksurface-x11.c b/gdk/x11/gdksurface-x11.c
index d797368d79..b0a49cc737 100644
--- a/gdk/x11/gdksurface-x11.c
+++ b/gdk/x11/gdksurface-x11.c
@@ -43,6 +43,7 @@
 #include "gdktextureprivate.h"
 #include "gdk-private.h"
 
+#include <graphene.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -1017,7 +1018,8 @@ _gdk_x11_display_create_surface (GdkDisplay     *display,
   impl->xid = XCreateWindow (xdisplay, xparent,
                              (surface->x + abs_x) * impl->surface_scale,
                              (surface->y + abs_y) * impl->surface_scale,
-                             surface->width * impl->surface_scale, surface->height * impl->surface_scale,
+                             MAX (1, surface->width * impl->surface_scale),
+                             MAX (1, surface->height * impl->surface_scale),
                              0, depth, class, xvisual,
                              xattributes_mask, &xattributes);
 
@@ -4830,21 +4832,46 @@ gdk_x11_toplevel_class_init (GdkX11ToplevelClass *class)
 
 static gboolean
 gdk_x11_toplevel_present (GdkToplevel       *toplevel,
-                          int                width,
-                          int                height,
                           GdkToplevelLayout *layout)
 {
   GdkSurface *surface = GDK_SURFACE (toplevel);
+  GdkDisplay *display = gdk_surface_get_display (surface);
+  GdkMonitor *monitor;
+  GdkToplevelSize size;
+  int bounds_width, bounds_height;
+  int width, height;
   GdkGeometry geometry;
   GdkSurfaceHints mask;
   gboolean was_mapped;
 
   gdk_x11_surface_unminimize (surface);
 
+  monitor = gdk_display_get_monitor_at_surface (display, surface);
+  if (monitor)
+    {
+      GdkRectangle workarea;
+
+      gdk_x11_monitor_get_workarea (monitor, &workarea);
+      bounds_width = workarea.width;
+      bounds_height = workarea.height;
+    }
+  else
+    {
+      bounds_width = G_MAXINT;
+      bounds_height = G_MAXINT;
+    }
+
+  gdk_toplevel_size_init (&size, bounds_width, bounds_height);
+  gdk_toplevel_notify_compute_size (toplevel, &size);
+  g_warn_if_fail (size.width > 0);
+  g_warn_if_fail (size.height > 0);
+  width = size.width;
+  height = size.height;
+
   if (gdk_toplevel_layout_get_resizable (layout))
     {
-      geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
-      geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
+      geometry.min_width = size.min_width;
+      geometry.min_height = size.min_height;
       mask = GDK_HINT_MIN_SIZE;
     }
   else
@@ -4864,9 +4891,11 @@ gdk_x11_toplevel_present (GdkToplevel       *toplevel,
 
   if (gdk_toplevel_layout_get_fullscreen (layout))
     {
-      GdkMonitor *monitor = gdk_toplevel_layout_get_fullscreen_monitor (layout);
-      if (monitor)
-        gdk_x11_surface_fullscreen_on_monitor (surface, monitor);
+      GdkMonitor *fullscreen_monitor =
+        gdk_toplevel_layout_get_fullscreen_monitor (layout);
+
+      if (fullscreen_monitor)
+        gdk_x11_surface_fullscreen_on_monitor (surface, fullscreen_monitor);
       else
         gdk_x11_surface_fullscreen (surface);
     }
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 58041745d3..e0f419f88c 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -471,9 +471,7 @@ static void _gtk_window_set_is_active (GtkWindow *window,
                                       gboolean   is_active);
 static void gtk_window_present_toplevel (GtkWindow *window);
 static void gtk_window_update_toplevel (GtkWindow *window);
-static GdkToplevelLayout * gtk_window_compute_layout (GtkWindow *window,
-                                                      int        min_width,
-                                                      int        min_height);
+static GdkToplevelLayout * gtk_window_compute_layout (GtkWindow *window);
 
 static void gtk_window_release_application (GtkWindow *window);
 
@@ -3839,14 +3837,12 @@ gtk_window_hide (GtkWidget *widget)
 }
 
 static GdkToplevelLayout *
-gtk_window_compute_layout (GtkWindow *window,
-                           int        min_width,
-                           int        min_height)
+gtk_window_compute_layout (GtkWindow *window)
 {
   GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
   GdkToplevelLayout *layout;
 
-  layout = gdk_toplevel_layout_new (min_width, min_height);
+  layout = gdk_toplevel_layout_new ();
 
   gdk_toplevel_layout_set_resizable (layout, priv->resizable);
   gdk_toplevel_layout_set_maximized (layout, priv->maximize_initially);
@@ -3861,23 +3857,11 @@ static void
 gtk_window_present_toplevel (GtkWindow *window)
 {
   GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
-  GdkRectangle request;
-  GdkGeometry geometry;
-  GdkSurfaceHints flags;
-
-  gtk_window_compute_configure_request (window, &request,
-                                        &geometry, &flags);
-
-  if (!(flags & GDK_HINT_MIN_SIZE))
-    geometry.min_width = geometry.min_height = 1;
 
   if (!priv->layout)
-    priv->layout = gtk_window_compute_layout (window, geometry.min_width, geometry.min_height);
+    priv->layout = gtk_window_compute_layout (window);
 
-  gdk_toplevel_present (GDK_TOPLEVEL (priv->surface),
-                        request.width,
-                        request.height,
-                        priv->layout);
+  gdk_toplevel_present (GDK_TOPLEVEL (priv->surface), priv->layout);
 }
 
 static void
@@ -3887,22 +3871,10 @@ gtk_window_update_toplevel (GtkWindow *window)
   
   if (priv->surface && gdk_surface_get_mapped (priv->surface))
     {
-      int min_width = 1;
-      int  min_height = 1;
-
-      if (priv->layout)
-        {
-          min_width = gdk_toplevel_layout_get_min_width (priv->layout);
-          min_height = gdk_toplevel_layout_get_min_height (priv->layout);
-        }
-
       g_clear_pointer (&priv->layout, gdk_toplevel_layout_unref);
-      priv->layout = gtk_window_compute_layout (window, min_width, min_height);
+      priv->layout = gtk_window_compute_layout (window);
 
-      gdk_toplevel_present (GDK_TOPLEVEL (priv->surface),
-                            gdk_surface_get_width (priv->surface),
-                            gdk_surface_get_height (priv->surface),
-                            priv->layout);
+      gdk_toplevel_present (GDK_TOPLEVEL (priv->surface), priv->layout);
     }
 }
 
@@ -3930,9 +3902,6 @@ gtk_window_map (GtkWidget *widget)
 
   gtk_window_set_theme_variant (window);
 
-  /* No longer use the default settings */
-  priv->need_default_size = FALSE;
-
   if (!disable_startup_notification)
     {
       /* Do we have a custom startup-notification id? */
@@ -4014,7 +3983,7 @@ gtk_window_guess_default_size (GtkWindow *window,
   GtkWidget *widget;
   GdkSurface *surface;
   GdkDisplay *display;
-  GdkMonitor *monitor;
+  GdkMonitor *monitor = NULL;
   GdkRectangle geometry;
   int minimum, natural;
 
@@ -4025,21 +3994,22 @@ gtk_window_guess_default_size (GtkWindow *window,
   if (surface)
     {
       monitor = gdk_display_get_monitor_at_surface (display, surface);
+      if (monitor)
+        g_object_ref (monitor);
+    }
+
+  if (!monitor)
+    monitor = g_list_model_get_item (gdk_display_get_monitors (display), 0);
+
+  if (monitor)
+    {
       gdk_monitor_get_geometry (monitor, &geometry);
+      g_object_unref (monitor);
     }
   else
     {
-      monitor = g_list_model_get_item (gdk_display_get_monitors (display), 0);
-      if (monitor)
-        {
-          gdk_monitor_get_geometry (monitor, &geometry);
-          g_object_unref (monitor);
-        }
-      else
-        {
-          geometry.width = G_MAXINT;
-          geometry.height = G_MAXINT;
-        }
+      geometry.width = G_MAXINT;
+      geometry.height = G_MAXINT;
     }
 
   *width = geometry.width;
@@ -4277,14 +4247,150 @@ update_realized_window_properties (GtkWindow *window)
     }
 }
 
+static void
+gtk_window_compute_default_size (GtkWindow *window,
+                                 int        max_width,
+                                 int        max_height,
+                                 int       *width,
+                                 int       *height)
+{
+  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+  GtkWidget *widget = GTK_WIDGET (window);
+
+  *width = max_width;
+  *height = max_height;
+  if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
+    {
+      int minimum, natural;
+
+      gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1,
+                          &minimum, &natural,
+                          NULL, NULL);
+      *height = MAX (minimum, MIN (*height, natural));
+
+      gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL,
+                          *height,
+                          &minimum, &natural,
+                          NULL, NULL);
+      *width = MAX (minimum, MIN (*width, natural));
+    }
+  else /* GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or CONSTANT_SIZE */
+    {
+      int minimum, natural;
+
+      gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
+                          &minimum, &natural,
+                          NULL, NULL);
+      *width = MAX (minimum, MIN (*width, natural));
+
+      gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL,
+                          *width,
+                          &minimum, &natural,
+                          NULL, NULL);
+      *height = MAX (minimum, MIN (*height, natural));
+    }
+
+  /* No longer use the default settings */
+  priv->need_default_size = FALSE;
+}
+
+static void
+toplevel_compute_size (GdkToplevel     *toplevel,
+                       GdkToplevelSize *size,
+                       GtkWidget       *widget)
+{
+  GtkWindow *window = GTK_WINDOW (widget);
+  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+  GtkWindowGeometryInfo *info;
+  int width, height;
+  GtkBorder shadow;
+  int min_width, min_height;
+
+  info = gtk_window_get_geometry_info (window, FALSE);
+
+  if (priv->need_default_size)
+    {
+      int remembered_width;
+      int remembered_height;
+      int bounds_width;
+      int bounds_height;
+
+      gdk_toplevel_size_get_bounds (size, &bounds_width, &bounds_height);
+
+      gtk_window_compute_default_size (window,
+                                       bounds_width, bounds_height,
+                                       &width, &height);
+      gtk_window_get_remembered_size (window,
+                                      &remembered_width, &remembered_height);
+      width = MAX (width, remembered_width);
+      height = MAX (height, remembered_height);
+
+      /* Override with default size */
+      if (info)
+        {
+          /* Take width of shadows/headerbar into account. We want to set the
+           * default size of the content area and not the window area.
+           */
+          int default_width_csd = info->default_width;
+          int default_height_csd = info->default_height;
+          gtk_window_update_csd_size (window,
+                                      &default_width_csd, &default_height_csd,
+                                      INCLUDE_CSD_SIZE);
+
+          if (info->default_width > 0)
+            width = default_width_csd;
+          if (info->default_height > 0)
+            height = default_height_csd;
+        }
+    }
+  else
+    {
+      /* Default to keeping current size */
+      gtk_window_get_remembered_size (window, &width, &height);
+    }
+
+  /* Override any size with gtk_window_resize() values */
+  if (priv->maximized || priv->fullscreen)
+    {
+      /* Unless we are maximized or fullscreen */
+      gtk_window_get_remembered_size (window, &width, &height);
+    }
+  else if (info)
+    {
+      int resize_width_csd = info->resize_width;
+      int resize_height_csd = info->resize_height;
+      gtk_window_update_csd_size (window,
+                                  &resize_width_csd, &resize_height_csd,
+                                  INCLUDE_CSD_SIZE);
+
+      if (info->resize_width > 0)
+        width = resize_width_csd;
+      if (info->resize_height > 0)
+        height = resize_height_csd;
+    }
+
+  /* Don't ever request zero width or height, it's not supported by
+     gdk. The size allocation code will round it to 1 anyway but if
+     we do it then the value returned from this function will is
+     not comparable to the size allocation read from the GtkWindow. */
+  width = MAX (width, 1);
+  height = MAX (height, 1);
+
+  gdk_toplevel_size_set_size (size, width, height);
+
+  get_shadow_width (window, &shadow);
+
+  min_width = width + shadow.left + shadow.right;
+  min_height = height + shadow.top + shadow.bottom;
+  gdk_toplevel_size_set_min_size (size, min_width, min_height);
+}
+
 static void
 gtk_window_realize (GtkWidget *widget)
 {
   GtkWindow *window = GTK_WINDOW (widget);
   GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
-  GtkAllocation allocation;
   GdkSurface *surface;
-  GtkBorder shadow;
 
   /* Create default title bar */
   if (!priv->client_decorated && gtk_window_should_use_csd (window))
@@ -4308,32 +4414,7 @@ gtk_window_realize (GtkWidget *widget)
         }
     }
 
-  get_shadow_width (window, &shadow);
-
-  /* ensure widget tree is properly size allocated */
-  if (_gtk_widget_get_alloc_needed (widget))
-    {
-      GdkRectangle request;
-
-      gtk_window_compute_configure_request (window, &request, NULL, NULL);
-
-      allocation.x = shadow.left;
-      allocation.y = shadow.top;
-      allocation.width = request.width - shadow.left - shadow.right;
-      allocation.height = request.height - shadow.top - shadow.bottom;
-
-      gtk_widget_size_allocate (widget, &allocation, -1);
-
-      gtk_widget_queue_resize (widget);
-
-      g_return_if_fail (!_gtk_widget_get_realized (widget));
-    }
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  surface = gdk_surface_new_toplevel (gtk_widget_get_display (widget),
-                                      MAX (1, allocation.width + shadow.left + shadow.right),
-                                      MAX (1, allocation.height + shadow.top + shadow.bottom));
+  surface = gdk_surface_new_toplevel (gtk_widget_get_display (widget));
   priv->surface = surface;
   gdk_surface_set_widget (surface, widget);
 
@@ -4341,6 +4422,7 @@ gtk_window_realize (GtkWidget *widget)
   g_signal_connect_swapped (surface, "size-changed", G_CALLBACK (surface_size_changed), widget);
   g_signal_connect (surface, "render", G_CALLBACK (surface_render), widget);
   g_signal_connect (surface, "event", G_CALLBACK (surface_event), widget);
+  g_signal_connect (surface, "compute-size", G_CALLBACK (toplevel_compute_size), widget);
 
   GTK_WIDGET_CLASS (gtk_window_parent_class)->realize (widget);
 
@@ -5376,7 +5458,7 @@ gtk_window_move_resize (GtkWindow *window)
     new_geometry.min_width = new_geometry.min_height = 1;
 
   g_clear_pointer (&priv->layout, gdk_toplevel_layout_unref);
-  priv->layout = gtk_window_compute_layout (window, new_geometry.min_width, new_geometry.min_height);
+  priv->layout = gtk_window_compute_layout (window);
 
   /* This check implies the invariant that we never set info->last
    * without setting the hints and sending off a configure request.
@@ -5573,9 +5655,7 @@ gtk_window_move_resize (GtkWindow *window)
       if (configure_request_pos_changed)
         g_warning ("configure request position changed. This should not happen. Ignoring the position");
 
-      gdk_toplevel_present (GDK_TOPLEVEL (priv->surface),
-                            new_request.width, new_request.height,
-                            priv->layout);
+      gdk_toplevel_present (GDK_TOPLEVEL (priv->surface), priv->layout);
     }
   else
     {
diff --git a/gtk/inspector/general.c b/gtk/inspector/general.c
index 2e610a6672..76bd0011e2 100644
--- a/gtk/inspector/general.c
+++ b/gtk/inspector/general.c
@@ -141,7 +141,7 @@ init_version (GtkInspectorGeneral *gen)
 #endif
     backend = "Unknown";
 
-  surface = gdk_surface_new_toplevel (gen->display, 10, 10);
+  surface = gdk_surface_new_toplevel (gen->display);
   gsk_renderer = gsk_renderer_new_for_surface (surface);
   if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskVulkanRenderer") == 0)
     renderer = "Vulkan";
@@ -402,7 +402,7 @@ init_vulkan (GtkInspectorGeneral *gen)
   GdkSurface *surface;
   GdkVulkanContext *context;
 
-  surface = gdk_surface_new_toplevel (gen->display, 10, 10);
+  surface = gdk_surface_new_toplevel (gen->display);
   context = gdk_surface_create_vulkan_context (surface, NULL);
   gdk_surface_destroy (surface);
 
diff --git a/tests/rendernode.c b/tests/rendernode.c
index 49a6c8601c..786cc6d050 100644
--- a/tests/rendernode.c
+++ b/tests/rendernode.c
@@ -128,7 +128,7 @@ main(int argc, char **argv)
       GdkSurface *window;
       GdkTexture *texture = NULL;
 
-      window = gdk_surface_new_toplevel (gdk_display_get_default(), 10 , 10);
+      window = gdk_surface_new_toplevel (gdk_display_get_default());
       renderer = gsk_renderer_new_for_surface (window);
 
       for (run = 0; run < runs; run++)
diff --git a/tests/showrendernode.c b/tests/showrendernode.c
index 670e776e1d..25e9960251 100644
--- a/tests/showrendernode.c
+++ b/tests/showrendernode.c
@@ -181,7 +181,7 @@ main (int argc, char **argv)
 
   if (write_to_filename != NULL)
     {
-      GdkSurface *surface = gdk_surface_new_toplevel (gdk_display_get_default(), 10 , 10);
+      GdkSurface *surface = gdk_surface_new_toplevel (gdk_display_get_default());
       GskRenderer *renderer = gsk_renderer_new_for_surface (surface);
       GdkTexture *texture = gsk_renderer_render_texture (renderer, GTK_NODE_VIEW (nodeview)->node, NULL);
 
@@ -201,7 +201,7 @@ main (int argc, char **argv)
   if (compare_node)
     {
       GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
-      GdkSurface *gdk_surface = gdk_surface_new_toplevel (gdk_display_get_default(), 10 , 10);
+      GdkSurface *gdk_surface = gdk_surface_new_toplevel (gdk_display_get_default());
       GskRenderer *renderer = gsk_renderer_new_for_surface (gdk_surface);
       GdkTexture *texture = gsk_renderer_render_texture (renderer, GTK_NODE_VIEW (nodeview)->node, NULL);
       GtkWidget *image = gtk_image_new_from_paintable (GDK_PAINTABLE (texture));
diff --git a/tests/testfullscreen.c b/tests/testfullscreen.c
index c7deda5e92..3adc82310f 100644
--- a/tests/testfullscreen.c
+++ b/tests/testfullscreen.c
@@ -34,13 +34,10 @@ set_fullscreen_monitor_cb (GtkWidget *widget, gpointer user_data)
     monitor = gdk_display_get_monitor_at_surface (display, surface);
   else
     monitor = NULL;
-  layout = gdk_toplevel_layout_new (0, 0);
+  layout = gdk_toplevel_layout_new ();
   gdk_toplevel_layout_set_resizable (layout, TRUE);
   gdk_toplevel_layout_set_fullscreen (layout, TRUE, monitor);
-  gdk_toplevel_present (GDK_TOPLEVEL (surface),
-                        gdk_surface_get_width (surface),
-                        gdk_surface_get_height (surface),
-                        layout);
+  gdk_toplevel_present (GDK_TOPLEVEL (surface), layout);
   gdk_toplevel_layout_unref (layout);
 }
 
@@ -51,13 +48,10 @@ remove_fullscreen_cb (GtkWidget *widget, gpointer user_data)
   GdkToplevelLayout *layout;
 
   surface = gtk_native_get_surface (gtk_widget_get_native (widget));
-  layout = gdk_toplevel_layout_new (0, 0);
+  layout = gdk_toplevel_layout_new ();
   gdk_toplevel_layout_set_resizable (layout, TRUE);
   gdk_toplevel_layout_set_fullscreen (layout, FALSE, NULL);
-  gdk_toplevel_present (GDK_TOPLEVEL (surface),
-                        gdk_surface_get_width (surface),
-                        gdk_surface_get_height (surface),
-                        layout);
+  gdk_toplevel_present (GDK_TOPLEVEL (surface), layout);
   gdk_toplevel_layout_unref (layout);
 }
 
diff --git a/testsuite/gsk/compare-render.c b/testsuite/gsk/compare-render.c
index c3d1c570b8..c71ee55b7c 100644
--- a/testsuite/gsk/compare-render.c
+++ b/testsuite/gsk/compare-render.c
@@ -173,7 +173,7 @@ main (int argc, char **argv)
   node_file = argv[1];
   png_file = argv[2];
 
-  window = gdk_surface_new_toplevel (gdk_display_get_default(), 10 , 10);
+  window = gdk_surface_new_toplevel (gdk_display_get_default());
   renderer = gsk_renderer_new_for_surface (window);
 
   g_print ("Node file: '%s'\n", node_file);
diff --git a/testsuite/gtk/defaultvalue.c b/testsuite/gtk/defaultvalue.c
index 4947dac52f..f1b4d1f7fc 100644
--- a/testsuite/gtk/defaultvalue.c
+++ b/testsuite/gtk/defaultvalue.c
@@ -110,7 +110,7 @@ test_type (gconstpointer data)
     instance = G_OBJECT (g_object_ref (gtk_settings_get_default ()));
   else if (g_type_is_a (type, GDK_TYPE_SURFACE))
     {
-      instance = G_OBJECT (g_object_ref (gdk_surface_new_toplevel (display, 100, 100)));
+      instance = G_OBJECT (g_object_ref (gdk_surface_new_toplevel (display)));
     }
   else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
            g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
diff --git a/testsuite/gtk/notify.c b/testsuite/gtk/notify.c
index 5dd4499577..797d05d594 100644
--- a/testsuite/gtk/notify.c
+++ b/testsuite/gtk/notify.c
@@ -430,7 +430,7 @@ test_type (gconstpointer data)
     instance = G_OBJECT (g_object_ref (gtk_settings_get_default ()));
   else if (g_type_is_a (type, GDK_TYPE_SURFACE))
     {
-      instance = G_OBJECT (g_object_ref (gdk_surface_new_toplevel (display, 100, 100)));
+      instance = G_OBJECT (g_object_ref (gdk_surface_new_toplevel (display)));
     }
   else if (g_str_equal (g_type_name (type), "GdkX11Cursor"))
     instance = g_object_new (type, "display", display, NULL);
diff --git a/testsuite/gtk/shortcuts.c b/testsuite/gtk/shortcuts.c
index e0ce9c6509..ad5e9c798b 100644
--- a/testsuite/gtk/shortcuts.c
+++ b/testsuite/gtk/shortcuts.c
@@ -322,7 +322,7 @@ test_trigger_trigger (void)
                                             g_object_ref (trigger[2]));
 
   device = gdk_seat_get_keyboard (seat);
-  surface = gdk_surface_new_toplevel (display, 100, 100);
+  surface = gdk_surface_new_toplevel (display);
 
   for (i = 0; i < G_N_ELEMENTS (tests); i++)
     {
diff --git a/testsuite/gtk/test-focus-chain.c b/testsuite/gtk/test-focus-chain.c
index 2e52388f63..033cec98b5 100644
--- a/testsuite/gtk/test-focus-chain.c
+++ b/testsuite/gtk/test-focus-chain.c
@@ -217,6 +217,16 @@ get_dir_for_file (const char *path)
   return 0;
 }
 
+static gboolean
+quit_iteration_loop (gpointer user_data)
+{
+  gboolean *keep_running = user_data;
+
+  *keep_running = FALSE;
+
+  return G_SOURCE_REMOVE;
+}
+
 static gboolean
 load_ui_file (GFile *ui_file,
               GFile *ref_file,
@@ -230,6 +240,8 @@ load_ui_file (GFile *ui_file,
   GError *error = NULL;
   GtkDirectionType dir;
   gboolean success = FALSE;
+  gboolean keep_running = TRUE;
+  guint timeout_handle_id;
 
   ui_path = g_file_get_path (ui_file);
 
@@ -238,6 +250,19 @@ load_ui_file (GFile *ui_file,
 
   g_assert (window != NULL);
 
+  gtk_widget_show (window);
+
+  timeout_handle_id = g_timeout_add (2000,
+                                     quit_iteration_loop,
+                                     &keep_running);
+  while (keep_running)
+    {
+      if (!g_main_context_iteration (NULL, FALSE))
+        break;
+    }
+  if (keep_running)
+    g_source_remove (timeout_handle_id);
+
   if (ext)
     {
       int i;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]