Subject: bug#72986: Disabling menu-bar-mode changes size of new frames

From: martin rudalics <[email-masked]>
Date: Wed, 2 Oct 2024 10:53:25 +0200

> First frame with menu bar:

Whatever it is, I can't figure out what's going on.  It's basically the
same issue that's harassing me all the time.  In a frame with menu bar
we request an initial frame of size 644x696 here:

 > xg_frame_set_char_size, visible, PS=1328x1260, XS=1328x1260, DS=1328x1260
 >   +values, PS=200x195, XS=664x696

The 696 come from (/ (+ 1260 50 82) 2) where the 1260 is the native
pixel height of the frame calculated as (* 36 35) where 36 is our number
of lines and 35 is your frame character height.  50 is the height of the
menu bar, 92 the height of the tool bar and 2 is the scaling factor.  I
cannot spot any rounding problem here.

 > ConfigureNotify, PS=1328x1260, XS=1328x1258, DS=1328x1260
 > xg_frame_resized, rejected, PS=1328x1260, XS=1328x1258, DS=664x696

Now we get a ConfigureNotify event that instead of giving us a native
pixel height of 1260 gives us a height of 1258 and we reject it.  Where
did those two pixels go to?  I suppose I'll never find out.

The next two things I'd ask you to do are:

- Apply the patch gtkutil-reject-2024-10-02.diff which adds a tolerance
   of two pixels in order to avoid rejecting the ConfigureNotify and post
   the four histories as before.

- Apply the patch gtkutil-reject-2024-10-02(25).diff (from a pristine
   Emacs).  This is like the previous patch but makes the initial frame
   only 25 lines high.  Again please post the histories for all four
   cases.

Thanks, martin

diff --git a/src/frame.c b/src/frame.c
index 7f4bf274ad9..6b6f6aa3c5c 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -6769,7 +6769,7 @@ focus (where a frame immediately loses focus when it's left by the mouse
 
 The function `frame--size-history' displays the value of this variable
 in a more readable form.  */);
-    frame_size_history = Qnil;
+  frame_size_history = Fcons (make_fixnum (100), Qnil);
 
   DEFVAR_BOOL ("tooltip-reuse-hidden-frame", tooltip_reuse_hidden_frame,
 	       doc: /* Non-nil means reuse hidden tooltip frames.
diff --git a/src/gtkutil.c b/src/gtkutil.c
index d57627f152f..e5b505ab98b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -1129,11 +1129,54 @@ xg_set_geometry (struct frame *f)
     }
 }
 
+static struct frame *last_resize_frame = NULL;
+static int last_resize_height = -1;
+static int last_resize_width = -1;
+static int last_resize_count = 0;
+
 /** Function to handle resize of native frame F to WIDTH and HEIGHT
     pixels after we got a ConfigureNotify event.  */
 void
 xg_frame_resized (struct frame *f, int width, int height)
 {
+#ifndef HAVE_PGTK
+  if (f == last_resize_frame)
+    {
+      int expected_width = ((last_resize_width * xg_get_scale (f))
+			    - FRAME_TOOLBAR_WIDTH (f));
+      int expected_height = ((last_resize_height * xg_get_scale (f))
+			     - FRAME_MENUBAR_HEIGHT (f)
+			     - FRAME_TOOLBAR_HEIGHT (f));
+
+      if ((width > expected_width + 2
+	   || width < expected_width - 2
+	   || height > expected_height + 2
+	   || height < expected_height - 2)
+	  && last_resize_count <= 3)
+	/* We did not get what we wanted, retry.  */
+	{
+	  if (CONSP (frame_size_history))
+	    frame_size_history_extra
+	      (f, build_string ("xg_frame_resized, rejected"),
+	       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+	       last_resize_width, last_resize_height);
+
+	  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+			     last_resize_width, last_resize_height);
+
+	  last_resize_count++;
+
+	  return;
+	}
+      else
+	{
+	  last_resize_frame = NULL;
+	  last_resize_height = -1;
+	  last_resize_width = -1;
+	  last_resize_count = 0;
+	}
+    }
+#endif
   /* Ignore case where size of native rectangle didn't change.  */
   if (width != FRAME_PIXEL_WIDTH (f)
       || height != FRAME_PIXEL_HEIGHT (f)
@@ -1297,19 +1340,20 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   else
     {
 #ifndef HAVE_PGTK
+      last_resize_frame = f;
+      last_resize_width = outer_width;
+      last_resize_height = outer_height;
+      last_resize_count = 0;
+
       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 			 outer_width, outer_height);
 #else
       if (FRAME_GTK_OUTER_WIDGET (f))
-	{
-	  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-			     outer_width, outer_height);
-	}
+	gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+			   outer_width, outer_height);
       else
-	{
-	  gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-				       outer_width, outer_height);
-	}
+	gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+				     outer_width, outer_height);
 #endif
       fullscreen = Qnil;
     }
@@ -1327,10 +1371,17 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   if (FRAME_VISIBLE_P (f) && !was_visible)
     {
       if (CONSP (frame_size_history))
-	frame_size_history_extra
-	  (f, build_string ("xg_frame_set_char_size, visible"),
-	   FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
-	   f->new_width, f->new_height);
+	{
+	  frame_size_history_extra
+	    (f, build_string ("xg_frame_set_char_size, visible"),
+	     FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+	     f->new_width, f->new_height);
+
+	  if (gwidth > 0 || gheight > 0)
+	    frame_size_history_extra
+	      (f, build_string (" +values"), gwidth, gheight,
+	       outer_width, outer_height, -1, -1);
+	}
 
       /* Must call this to flush out events */
       (void)gtk_events_pending ();
diff --git a/src/frame.c b/src/frame.c
index 7f4bf274ad9..7ae520188c0 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5822,7 +5822,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p,
   Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
   int parent_done = -1, outer_done = -1;
   int text_width = 80 * FRAME_COLUMN_WIDTH (f);
-  int text_height = 36 * FRAME_LINE_HEIGHT (f);
+  int text_height = 25 * FRAME_LINE_HEIGHT (f);
 
   /* Window managers expect that if program-specified
      positions are not (0,0), they're intentional, not defaults.  */
@@ -6769,7 +6769,7 @@ focus (where a frame immediately loses focus when it's left by the mouse
 
 The function `frame--size-history' displays the value of this variable
 in a more readable form.  */);
-    frame_size_history = Qnil;
+  frame_size_history = Fcons (make_fixnum (100), Qnil);
 
   DEFVAR_BOOL ("tooltip-reuse-hidden-frame", tooltip_reuse_hidden_frame,
 	       doc: /* Non-nil means reuse hidden tooltip frames.
diff --git a/src/gtkutil.c b/src/gtkutil.c
index d57627f152f..e5b505ab98b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -1129,11 +1129,54 @@ xg_set_geometry (struct frame *f)
     }
 }
 
+static struct frame *last_resize_frame = NULL;
+static int last_resize_height = -1;
+static int last_resize_width = -1;
+static int last_resize_count = 0;
+
 /** Function to handle resize of native frame F to WIDTH and HEIGHT
     pixels after we got a ConfigureNotify event.  */
 void
 xg_frame_resized (struct frame *f, int width, int height)
 {
+#ifndef HAVE_PGTK
+  if (f == last_resize_frame)
+    {
+      int expected_width = ((last_resize_width * xg_get_scale (f))
+			    - FRAME_TOOLBAR_WIDTH (f));
+      int expected_height = ((last_resize_height * xg_get_scale (f))
+			     - FRAME_MENUBAR_HEIGHT (f)
+			     - FRAME_TOOLBAR_HEIGHT (f));
+
+      if ((width > expected_width + 2
+	   || width < expected_width - 2
+	   || height > expected_height + 2
+	   || height < expected_height - 2)
+	  && last_resize_count <= 3)
+	/* We did not get what we wanted, retry.  */
+	{
+	  if (CONSP (frame_size_history))
+	    frame_size_history_extra
+	      (f, build_string ("xg_frame_resized, rejected"),
+	       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+	       last_resize_width, last_resize_height);
+
+	  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+			     last_resize_width, last_resize_height);
+
+	  last_resize_count++;
+
+	  return;
+	}
+      else
+	{
+	  last_resize_frame = NULL;
+	  last_resize_height = -1;
+	  last_resize_width = -1;
+	  last_resize_count = 0;
+	}
+    }
+#endif
   /* Ignore case where size of native rectangle didn't change.  */
   if (width != FRAME_PIXEL_WIDTH (f)
       || height != FRAME_PIXEL_HEIGHT (f)
@@ -1297,19 +1340,20 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   else
     {
 #ifndef HAVE_PGTK
+      last_resize_frame = f;
+      last_resize_width = outer_width;
+      last_resize_height = outer_height;
+      last_resize_count = 0;
+
       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 			 outer_width, outer_height);
 #else
       if (FRAME_GTK_OUTER_WIDGET (f))
-	{
-	  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-			     outer_width, outer_height);
-	}
+	gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+			   outer_width, outer_height);
       else
-	{
-	  gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-				       outer_width, outer_height);
-	}
+	gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+				     outer_width, outer_height);
 #endif
       fullscreen = Qnil;
     }
@@ -1327,10 +1371,17 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   if (FRAME_VISIBLE_P (f) && !was_visible)
     {
       if (CONSP (frame_size_history))
-	frame_size_history_extra
-	  (f, build_string ("xg_frame_set_char_size, visible"),
-	   FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
-	   f->new_width, f->new_height);
+	{
+	  frame_size_history_extra
+	    (f, build_string ("xg_frame_set_char_size, visible"),
+	     FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+	     f->new_width, f->new_height);
+
+	  if (gwidth > 0 || gheight > 0)
+	    frame_size_history_extra
+	      (f, build_string (" +values"), gwidth, gheight,
+	       outer_width, outer_height, -1, -1);
+	}
 
       /* Must call this to flush out events */
       (void)gtk_events_pending ();