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

From: martin rudalics <[email-masked]>
Date: Sat, 30 Nov 2024 20:00:55 +0100

> Assuming that this problem affects all or most users of recent GNOME, it
 > would be fantastic to have this patch installed. It's a very unsatisfactory
 > fix (because we don't understand what the problem really is, or even where,
 > I think?), but the current situation is quite annoying (every time I open a
 > new Emacs frame, I first have to resize it before I can use it).
 > Thanks again to Martin; having myself had to do quite a lot of proxy
 > debugging of weird effects on systems I don't use, I appreciate the
 > patience and hard thought that goes into trying to diagnose and fix this
 > sort of bug.

I now added a variable called 'x-gtk-insist-on-requested-size'.  If
non-nil, it should do what its doc-string says.  The rest of the
attached patch is mostly what was in a diff I earlier posted as
gtkutil-reject-2024-10-02.diff.  Please try it.

Thanks, martin

diff --git a/src/gtkutil.c b/src/gtkutil.c
index d57627f152f..1496b761550 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -1129,11 +1129,59 @@ xg_set_geometry (struct frame *f)
     }
 }
 
+#ifndef HAVE_PGTK
+static struct frame *last_resize_frame = NULL;
+static int last_resize_height = -1;
+static int last_resize_width = -1;
+static bool last_resize_insist = false;
+#endif
+
 /** 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
+  bool reject = false;
+
+  if (last_resize_insist && 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));
+
+      /* Fuzzyness: Reject the new size if the deviation is more than
+	 two pixels in either direction.  */
+      if (width > expected_width + 2
+	  || width < expected_width - 2
+	  || height > expected_height + 2
+	  || height < expected_height - 2)
+	/* 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);
+
+	  /* Resend the previous request.  */
+	  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+			     last_resize_width, last_resize_height);
+	  reject = true;
+	}
+
+      /* Reset the insistence values.  */
+      last_resize_frame = NULL;
+      last_resize_height = -1;
+      last_resize_width = -1;
+      last_resize_insist = false;
+
+      if (reject)
+	return;
+    }
+#endif
   /* Ignore case where size of native rectangle didn't change.  */
   if (width != FRAME_PIXEL_WIDTH (f)
       || height != FRAME_PIXEL_HEIGHT (f)
@@ -1297,19 +1345,23 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   else
     {
 #ifndef HAVE_PGTK
+      if (x_gtk_insist_on_requested_size)
+	{
+	  last_resize_frame = f;
+	  last_resize_width = outer_width;
+	  last_resize_height = outer_height;
+	  last_resize_insist = true;
+	}
+
       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;
     }
diff --git a/src/xterm.c b/src/xterm.c
index 0c20d38b0f7..00dc976e2ff 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -32762,6 +32762,23 @@ syms_of_xterm (void)
 always uses gtk_window_move and ignores the value of this variable.  */);
   x_gtk_use_window_move = true;
 
+#if defined USE_GTK && !defined HAVE_PGTK
+  DEFVAR_BOOL ("x-gtk-insist-on-requested-size", x_gtk_insist_on_requested_size,
+    doc: /* Non-nil means try harder to obtain requested size for new frames.
+If this variable is non-nil, a GTK build on X will reject once a frame
+size it is offered that does not match the one previously requested by
+Emacs.  Rather, Emacs will reissue the previous request to size the
+frame.  If this variable is nil, Emacs will accept any frame size it is
+offered.
+
+Setting this to a non-nil value can be necessary when running on a GNOME
+desktop with the mutter window manager under GTK3.  These may offer
+Emacs a much smaller frame size for the initial as well as for any
+additional frames.  For the initial frame it will be necessary to set
+this in the `early-init.el' file.  */);
+  x_gtk_insist_on_requested_size = false;
+#endif
+
   DEFVAR_LISP ("x-scroll-event-delta-factor", Vx_scroll_event_delta_factor,
 	       doc: /* A scale to apply to pixel deltas reported in scroll events.
 This option is only effective when Emacs is built with XInput 2