Subject: Re: bug#60585: 30.0.50; global-text-scale-adjust shrinks window (was not before), was: Re: bug#52493: 29.0.50; Setting Inconsolata up in init.el makes default face rendered wrong

From: martin rudalics <[email-masked]>
Date: Thu, 26 Jan 2023 16:44:03 +0100

> It seems to have a different effect: the last version at least has one size where s-f-a changes the height by 2 line heights.
 >
 > See this transcript where at the end (and multiple times before that) change the frame to 118x35 (per GNOME) which is 112x35 internally, and s-f-a changes the height to 33 (per GNOME) which is 34 internally.

I suppose you're talking about this part:

x_new_font old char size 17x37 new char size 17x37 text chars 112x35 old text pixels 1904x1330 new text pixels 1904x1295
xg_frame_set_char_size old native pixels 1952x1330 new native pixels 1952x1295 outer pixels 976x713 outer rest 0x0
xg_frame_resized old native pixels 1952x1330 new native pixels 1952x1294
adjust_frame_size old native pixels 1952x1330 new native pixels 1952x1294 old text pixels 1904x1330 new text pixels 1904x1294 old text chars 112x35 new text chars 112x34

And what you probably mean here is that you've seen the GNOME window
show 118x35, then you did 'set-face-attribute' and after that the GNOME
window showed 118x33.  The explanation is probably that GNOME shows the
N from the

   frame_height = base_height + N * height_inc

calculation and after 'set-face-attribute' already removed one line,
increasing base_height to make the equation hold removed another one.

I attach a patch where I try to mimic in *foo*, after a text "WM hint",
what a WM would typically show there.  Please switch to *foo* right away
and, while you drag a border, check whether the numbers are the same
GNOME displays.  If so, please show me again the transcript of the
above.  (118 vs 112 looks slightly preposterous BTW - it would mean that
scroll bar and fringes are six characters wide.  Here both width and
height differ by 1 only.)

martin

diff --git a/lisp/window.el b/lisp/window.el
index a11293d372..706c988b2e 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -10596,6 +10596,21 @@ window-prefix-map
   "0" #'delete-windows-on)
 (define-key ctl-x-map "w" window-prefix-map)
 
+(defun foo-it (&rest rest)
+  (with-current-buffer (get-buffer-create "*foo*")
+    (goto-char (point-max))
+    (when rest
+      (if (consp (car rest))
+          (insert (format "%sx%s" (caar rest) (cdar rest)))
+        (insert (format "%s" (car rest))))
+      (setq rest (cdr rest))
+      (while rest
+        (if (consp (car rest))
+            (insert (format " %sx%s" (caar rest) (cdar rest)))
+	  (insert (format " %s" (car rest))))
+	(setq rest (cdr rest)))
+      (insert "\n"))))
+
 (provide 'window)
 
 ;;; window.el ends here
diff --git a/src/frame.c b/src/frame.c
index b57b296be5..f000540ce1 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -906,6 +906,64 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
 
   unblock_input ();
 
+  if (old_native_width != new_native_width
+      || old_native_height != new_native_height
+      || old_text_width != new_text_width
+      || old_text_height != new_text_height
+      || old_text_cols != new_text_cols
+      || old_text_lines != new_text_lines)
+    CALLN (Ffuncall, Qfoo_it, intern ("adjust_frame_size"),
+	   intern ("old native pixels"),
+	   Fcons (make_fixnum (old_native_width),
+		  make_fixnum (old_native_height)),
+	   intern ("new native pixels"),
+	   Fcons (make_fixnum (new_native_width),
+		  make_fixnum (new_native_height)),
+	   intern ("old text pixels"),
+	   Fcons (make_fixnum (old_text_width),
+		  make_fixnum (old_text_height)),
+	   intern ("new text pixels"),
+	   Fcons (make_fixnum (new_text_width),
+		  make_fixnum (new_text_height)),
+	   intern ("old text chars"),
+	   Fcons (make_fixnum (old_text_cols),
+		  make_fixnum (old_text_lines)),
+	   intern ("new text chars"),
+	   Fcons (make_fixnum (new_text_cols),
+		  make_fixnum (new_text_lines)));
+
+#ifdef USE_GTK
+  if (f->resized_once_p)
+    {
+      int outer_height
+	= FRAME_PIXEL_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
+      int outer_width = FRAME_PIXEL_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f);
+      int scale = xg_get_scale (f);
+      GdkGeometry size_hints = f->output_data.x->size_hints;
+
+      outer_height /= scale;
+      outer_width /= scale;
+
+      if (outer_width > 0 && size_hints.base_width > 0
+	  && size_hints.width_inc > 0 && outer_height > 0
+	  && size_hints.base_height > 0 && size_hints.height_inc > 0)
+	{
+	  CALLN (Ffuncall, Qfoo_it,
+		 intern ("    base_size"),
+		 Fcons (make_fixnum (size_hints.base_width),
+			make_fixnum (size_hints.base_height)),
+		 intern ("size increments"),
+		 Fcons (make_fixnum (size_hints.width_inc),
+			make_fixnum (size_hints.height_inc)),
+		 intern ("WM hint"),
+		 Fcons (make_fixnum ((outer_width - size_hints.base_width)
+				     / size_hints.width_inc),
+			make_fixnum ((outer_height - size_hints.base_height)
+				     / size_hints.height_inc)));
+	}
+    }
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
   {
     /* Adjust size of F's child frames.  */
diff --git a/src/frame.h b/src/frame.h
index d6fd62b2ac..724b652eec 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -265,6 +265,8 @@ #define EMACS_FRAME_H
      changed.  Clear the frame in clear_garbaged_frames if set.  */
   bool_bf resized_p : 1;
 
+  bool_bf resized_once_p : 1;
+
   /* Set to true if the default face for the frame has been
      realized.  Reset to zero whenever the default face changes.
      Used to see the difference between a font change and face change.  */
diff --git a/src/gtkutil.c b/src/gtkutil.c
index a6bba096a4..8946636f0b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -1149,6 +1149,14 @@ xg_frame_resized (struct frame *f, int width, int height)
 	   f->new_size_p ? f->new_height : -1);
 
       FRAME_RIF (f)->clear_under_internal_border (f);
+
+      CALLN (Ffuncall, Qfoo_it, intern ("xg_frame_resized"),
+	     intern ("old native pixels"),
+	     Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		    make_fixnum (FRAME_PIXEL_HEIGHT (f))),
+	     intern ("new native pixels"),
+	     Fcons (make_fixnum (width), make_fixnum (height)));
+
       change_frame_size (f, width, height, false, true, false);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
@@ -1174,6 +1182,8 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   int outer_width = width + FRAME_TOOLBAR_WIDTH (f);
   bool was_visible = false;
   bool hide_child_frame;
+  int scale = xg_get_scale (f);
+  GdkGeometry size_hints = f->output_data.xp->size_hints;
 
 #ifndef HAVE_PGTK
   gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
@@ -1196,10 +1206,42 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   /* Do this before resize, as we don't know yet if we will be resized.  */
   FRAME_RIF (f)->clear_under_internal_border (f);
 
-  outer_height /= xg_get_scale (f);
-  outer_width /= xg_get_scale (f);
+  if (scale != 1 && !frame_resize_pixelwise
+      /* Don't bother the WM when the number of text columns or text
+	 lines wouldn't change and only the pixel sizes would.  But make
+	 sure first that these sizes are "out of synch" due to a
+	 preceding "imprecise by scaling" operation like a mouse drag.
+	 Otherwise, we might end up with a frame that doesn't get a
+	 suitable initial size.  */
+      && (FRAME_PIXEL_TO_TEXT_WIDTH (f, width) / FRAME_COLS (f)
+	  == FRAME_TEXT_WIDTH (f) / FRAME_COLS (f))
+      && FRAME_TEXT_WIDTH (f) != FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f)
+      && (FRAME_PIXEL_TO_TEXT_HEIGHT (f, height) / FRAME_LINES (f)
+	  == FRAME_TEXT_HEIGHT (f) / FRAME_LINES (f))
+      && FRAME_TEXT_HEIGHT (f) != FRAME_LINES (f) * FRAME_LINE_HEIGHT (f))
+    {
+      CALLN (Ffuncall, Qfoo_it, intern ("xg_frame_set_char_size_return"),
+	     intern ("char size"),
+	     Fcons (make_fixnum (FRAME_COLUMN_WIDTH (f)),
+		    make_fixnum (FRAME_LINE_HEIGHT (f))),
+	     intern ("text columns/lines"),
+	     Fcons (make_fixnum (FRAME_COLS (f)),
+		    make_fixnum (FRAME_LINES (f))),
+	     intern ("pixels to text width/height"),
+	     Fcons (make_fixnum (FRAME_PIXEL_TO_TEXT_WIDTH (f, width)),
+		    make_fixnum (FRAME_PIXEL_TO_TEXT_HEIGHT (f, height))),
+	     intern ("text width/height"),
+	     Fcons (make_fixnum (FRAME_TEXT_WIDTH (f)),
+		    make_fixnum (FRAME_TEXT_HEIGHT (f))));
+
+      return;
+    }
 
-  xg_wm_set_size_hint (f, 0, 0);
+  outer_height /= scale;
+  outer_width /= scale;
+
+  xg_wm_set_size_hint (f, 0, 0, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+		       FRAME_PIXEL_TO_TEXT_HEIGHT (f, height));
 
   /* Resize the top level widget so rows and columns remain constant.
 
@@ -1317,6 +1359,50 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   SET_FRAME_GARBAGED (f);
   cancel_mouse_face (f);
 
+  f->resized_once_p = true;
+
+  size_hints = f->output_data.xp->size_hints;
+  if (outer_width > 0 && size_hints.base_width > 0
+      && size_hints.width_inc > 0 && outer_height > 0
+      && size_hints.base_height > 0 && size_hints.height_inc > 0)
+    {
+
+      CALLN (Ffuncall, Qfoo_it, intern ("xg_frame_set_char_size"),
+	     intern ("old native pixels"),
+	     Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		    make_fixnum (FRAME_PIXEL_HEIGHT (f))),
+	     intern ("new native pixels"),
+	     Fcons (make_fixnum (width), make_fixnum (height)),
+	     intern ("outer pixels"),
+	     Fcons (make_fixnum (outer_width), make_fixnum (outer_height)),
+	     intern ("outer rest"),
+	     Fcons (make_fixnum ((outer_width - size_hints.base_width)
+				 % size_hints.width_inc),
+		    make_fixnum ((outer_height - size_hints.base_height)
+				 % size_hints.height_inc)));
+      CALLN (Ffuncall, Qfoo_it,
+	     intern ("    base_size"),
+	     Fcons (make_fixnum (size_hints.base_width),
+		    make_fixnum (size_hints.base_height)),
+	     intern ("size increments"),
+	     Fcons (make_fixnum (size_hints.width_inc),
+		    make_fixnum (size_hints.height_inc)),
+	     intern ("WM hint"),
+	     Fcons (make_fixnum ((outer_width - size_hints.base_width)
+				 / size_hints.width_inc),
+		    make_fixnum ((outer_height - size_hints.base_height)
+				 / size_hints.height_inc)));
+    }
+  else
+    CALLN (Ffuncall, Qfoo_it, intern ("xg_frame_set_char_size"),
+	   intern ("old native pixels"),
+	   Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		  make_fixnum (FRAME_PIXEL_HEIGHT (f))),
+	   intern ("new native pixels"),
+	   Fcons (make_fixnum (width), make_fixnum (height)),
+	   intern ("outer pixels"),
+	   Fcons (make_fixnum (outer_width), make_fixnum (outer_height)));
+
   /* We can not call change_frame_size for a mapped frame,
      we can not set pixel width/height either.  The window manager may
      override our resize request, XMonad does this all the time.
@@ -1360,21 +1446,6 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
     }
 }
 
-/* Handle height/width changes (i.e. add/remove/move menu/toolbar).
-   The policy is to keep the number of editable lines.  */
-
-#if 0
-static void
-xg_height_or_width_changed (struct frame *f)
-{
-  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                     FRAME_TOTAL_PIXEL_WIDTH (f),
-                     FRAME_TOTAL_PIXEL_HEIGHT (f));
-  f->output_data.xp->hint_flags = 0;
-  x_wm_set_size_hint (f, 0, 0);
-}
-#endif
-
 #ifndef HAVE_PGTK
 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
    Must be done like this, because GtkWidget:s can have "hidden"
@@ -1917,7 +1988,8 @@ xg_free_frame_widgets (struct frame *f)
    flag (this is useful when FLAGS is 0).  */
 
 void
-xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
+xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position,
+		     int text_width, int text_height)
 {
   /* Must use GTK routines here, otherwise GTK resets the size hints
      to its own defaults.  */
@@ -1964,21 +2036,33 @@ xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
   hint_flags = f->output_data.xp->hint_flags;
 
   hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
-  size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
-  size_hints.height_inc = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
+  size_hints.width_inc
+    = frame_resize_pixelwise ? 1 : (FRAME_COLUMN_WIDTH (f) / scale);
+  size_hints.height_inc
+    = frame_resize_pixelwise ? 1 : (FRAME_LINE_HEIGHT (f) / scale);
 
   hint_flags |= GDK_HINT_BASE_SIZE;
   /* Use one row/col here so base_height/width does not become zero.
      Gtk+ and/or Unity on Ubuntu 12.04 can't handle it.
      Obviously this makes the row/col value displayed off by 1.  */
-  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
-  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
-    + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
+  base_width = ((FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1)
+		 + FRAME_TOOLBAR_WIDTH (f)) / scale);
+  base_height = ((FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
+		  + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
+		 / scale);
+
+  if (text_width < 0)
+    text_width = FRAME_TEXT_WIDTH (f);
+  text_width /= scale;
+
+  if (text_height < 0)
+    text_height = FRAME_TEXT_HEIGHT (f);
+  text_height /= scale;
 
-  size_hints.base_width = base_width;
-  size_hints.base_height = base_height;
-  size_hints.min_width  = base_width;
-  size_hints.min_height = base_height;
+  size_hints.base_width = base_width + (text_width % size_hints.width_inc);
+  size_hints.base_height = base_height + (text_height % size_hints.height_inc);
+  size_hints.min_width = size_hints.base_width;
+  size_hints.min_height = size_hints.base_height;
 
   /* These currently have a one to one mapping with the X values, but I
      don't think we should rely on that.  */
@@ -2018,16 +2102,31 @@ xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
       hint_flags |= GDK_HINT_USER_POS;
     }
 
-  size_hints.base_width /= scale;
-  size_hints.base_height /= scale;
-  size_hints.width_inc /= scale;
-  size_hints.height_inc /= scale;
-
   if (hint_flags != f->output_data.xp->hint_flags
       || memcmp (&size_hints,
 		 &f->output_data.xp->size_hints,
 		 sizeof (size_hints)) != 0)
     {
+      CALLN (Ffuncall, Qfoo_it, intern ("xg_wm_set_size_hint"),
+	     intern ("scale"), make_fixnum (scale),
+	     intern ("char width"), make_fixnum (FRAME_COLUMN_WIDTH (f)),
+	     intern ("toolbar"), make_fixnum (FRAME_TOOLBAR_WIDTH (f)),
+	     intern ("vscroll"), make_fixnum (FRAME_SCROLL_BAR_AREA_WIDTH (f)),
+	     intern ("fringes"), make_fixnum (FRAME_TOTAL_FRINGE_WIDTH (f)),
+	     intern ("borders"), make_fixnum (2 * FRAME_INTERNAL_BORDER_WIDTH (f)),
+	     intern ("text width"), make_fixnum (text_width),
+	     intern ("base width"), make_fixnum (size_hints.base_width),
+	     intern ("width inc"), make_fixnum (size_hints.width_inc));
+      CALLN (Ffuncall, Qfoo_it, intern ("   "),
+	     intern ("char height"), make_fixnum (FRAME_LINE_HEIGHT (f)),
+	     intern ("menubar"), make_fixnum (FRAME_MENUBAR_HEIGHT (f)),
+	     intern ("toolbar"), make_fixnum (FRAME_TOOLBAR_HEIGHT (f)),
+	     intern ("hscroll"), make_fixnum (FRAME_SCROLL_BAR_AREA_HEIGHT (f)),
+	     intern ("borders"), make_fixnum (2 * FRAME_INTERNAL_BORDER_WIDTH (f)),
+	     intern ("text height"), make_fixnum (text_height),
+	     intern ("base height"), make_fixnum (size_hints.base_height),
+	     intern ("height inc"), make_fixnum (size_hints.height_inc));
+
       block_input ();
       gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                                      NULL, &size_hints, hint_flags);
diff --git a/src/gtkutil.h b/src/gtkutil.h
index 190d662831..21245dde5f 100644
--- a/src/gtkutil.h
+++ b/src/gtkutil.h
@@ -153,7 +153,7 @@ #define XG_ITEM_DATA "emacs_menuitem"
 extern int xg_get_default_scrollbar_width (struct frame *f);
 extern int xg_get_default_scrollbar_height (struct frame *f);
 
-extern void xg_wm_set_size_hint (struct frame *, long int, bool);
+extern void xg_wm_set_size_hint (struct frame *, long int, bool, int, int);
 
 extern void update_frame_tool_bar (struct frame *f);
 extern void free_frame_tool_bar (struct frame *f);
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
index a32067af81..a391541e10 100644
--- a/src/pgtkfns.c
+++ b/src/pgtkfns.c
@@ -1662,7 +1662,8 @@ #define INSTALL_CURSOR(FIELD, NAME) \
      badly we want them.  This should be done after we have the menu
      bar so that its size can be taken into account.  */
   block_input ();
-  xg_wm_set_size_hint (f, window_prompting, false);
+  xg_wm_set_size_hint (f, window_prompting, false,
+		       FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
   unblock_input ();
 
   adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 13f6c6c3c4..8b077be178 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -658,7 +658,7 @@ pgtk_set_offset (struct frame *f, int xoff, int yoff, int change_gravity)
   pgtk_calc_absolute_position (f);
 
   block_input ();
-  xg_wm_set_size_hint (f, 0, false);
+  xg_wm_set_size_hint (f, 0, false, -1, -1);
 
   if (change_gravity != 0)
     {
@@ -705,7 +705,8 @@ pgtk_set_window_size (struct frame *f, bool change_gravity,
 
   f->output_data.pgtk->preferred_width = pixelwidth;
   f->output_data.pgtk->preferred_height = pixelheight;
-  xg_wm_set_size_hint (f, 0, 0);
+  xg_wm_set_size_hint (f, 0, 0, FRAME_PIXEL_TO_TEXT_WIDTH (pixelwidth),
+		       FRAME_PIXEL_TO_TEXT_HEIGHT (pixelheight));
   xg_frame_set_char_size (f, pixelwidth, pixelheight);
   gtk_widget_queue_resize (FRAME_WIDGET (f));
 
@@ -993,7 +994,10 @@ pgtk_set_parent_frame (struct frame *f, Lisp_Object new_value,
 			      fixed, TRUE, TRUE, 0);
 	  f->output_data.pgtk->preferred_width = alloc.width;
 	  f->output_data.pgtk->preferred_height = alloc.height;
-	  xg_wm_set_size_hint (f, 0, 0);
+	  xg_wm_set_size_hint (f, 0, 0, FRAME_PIXEL_TO_TEXT_WIDTH (alloc.width),
+			       FRAME_PIXEL_TO_TEXT_HEIGHT (alloc.height));
+	  /* Why convert here?  xg_frame_set_char_size wants native
+	     pixels.  */
 	  xg_frame_set_char_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, alloc.width),
 				  FRAME_PIXEL_TO_TEXT_HEIGHT (f, alloc.height));
 	  gtk_widget_queue_resize (FRAME_WIDGET (f));
diff --git a/src/widget.c b/src/widget.c
index aaab33b6d8..7468bf63fc 100644
--- a/src/widget.c
+++ b/src/widget.c
@@ -32,6 +32,7 @@
 #include "sysstdio.h"
 #include "xterm.h"
 #include "frame.h"
+#include "blockinput.h"
 
 #include <X11/StringDefs.h>
 #include <X11/IntrinsicP.h>
@@ -154,15 +155,6 @@ emacsFrameClass (void)
   return (WidgetClass) &emacsFrameClassRec;
 }
 
-static void
-get_default_char_pixel_size (EmacsFrame ew, int *pixel_width, int *pixel_height)
-{
-  struct frame *f = ew->emacs_frame.frame;
-
-  *pixel_width = FRAME_COLUMN_WIDTH (f);
-  *pixel_height = FRAME_LINE_HEIGHT (f);
-}
-
 static void
 pixel_to_char_size (EmacsFrame ew, Dimension pixel_width,
 		    Dimension pixel_height, int *char_width, int *char_height)
@@ -207,120 +199,97 @@ get_wm_shell (Widget w)
   return (WMShellWidget) wmshell;
 }
 
-#if 0 /* Currently not used.  */
-
-static void
-mark_shell_size_user_specified (Widget wmshell)
-{
-  if (! XtIsWMShell (wmshell)) emacs_abort ();
-  /* This is kind of sleazy, but I can't see how else to tell it to make it
-     mark the WM_SIZE_HINTS size as user specified when appropriate. */
-  ((WMShellWidget) wmshell)->wm.size_hints.flags |= USSize;
-}
-
-#endif
-
-
 static void
 set_frame_size (EmacsFrame ew)
 {
-  /* The widget hierarchy is
-
-	argv[0]			emacsShell	pane	Frame-NAME
-	ApplicationShell	EmacsShell	Paned	EmacsFrame
-
-     We accept geometry specs in this order:
-
-	*Frame-NAME.geometry
-	*EmacsFrame.geometry
-	Emacs.geometry
-
-     Other possibilities for widget hierarchies might be
-
-	argv[0]			frame		pane	Frame-NAME
-	ApplicationShell	EmacsShell	Paned	EmacsFrame
-     or
-	argv[0]			Frame-NAME	pane	Frame-NAME
-	ApplicationShell	EmacsShell	Paned	EmacsFrame
-     or
-	argv[0]			Frame-NAME	pane	emacsTextPane
-	ApplicationShell	EmacsFrame	Paned	EmacsTextPane
-
-     With the current setup, the text-display-area is the part which is
-     an emacs "frame", since that's the only part managed by emacs proper
-     (the menubar and the parent of the menubar and all that sort of thing
-     are managed by lwlib.)
-
-     The EmacsShell widget is simply a replacement for the Shell widget
-     which is able to deal with using an externally-supplied window instead
-     of always creating its own.  It is not actually emacs specific, and
-     should possibly have class "Shell" instead of "EmacsShell" to simplify
-     the resources.
-
-   */
-
   struct frame *f = ew->emacs_frame.frame;
 
   ew->core.width = FRAME_PIXEL_WIDTH (f);
   ew->core.height = FRAME_PIXEL_HEIGHT (f);
 
-  if (CONSP (frame_size_history))
-    frame_size_history_plain
-      (f, build_string ("set_frame_size"));
+  CALLN (Ffuncall, Qfoo_it, build_string ("set_frame_size"),
+	 build_string ("native pixels"),
+	 Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		make_fixnum (FRAME_PIXEL_HEIGHT (f))));
 }
 
 static bool
-update_wm_hints (WMShellWidget wmshell, EmacsFrame ew)
+update_wm_hints (WMShellWidget wmshell, EmacsFrame ew, int width, int height)
 {
-  int cw;
-  int ch;
-  Dimension rounded_width;
-  Dimension rounded_height;
-  int char_width;
-  int char_height;
-  int base_width;
-  int base_height;
-  char buffer[sizeof wmshell->wm.size_hints];
-  char *hints_ptr;
-
-  /* Copy the old size hints to the buffer.  */
-  memcpy (buffer, &wmshell->wm.size_hints,
-	  sizeof wmshell->wm.size_hints);
-
-  pixel_to_char_size (ew, ew->core.width, ew->core.height,
-		      &char_width, &char_height);
-  char_to_pixel_size (ew, char_width, char_height,
-		      &rounded_width, &rounded_height);
-  get_default_char_pixel_size (ew, &cw, &ch);
-
-  base_width = (wmshell->core.width - ew->core.width
-		+ (rounded_width - (char_width * cw)));
-  base_height = (wmshell->core.height - ew->core.height
-		 + (rounded_height - (char_height * ch)));
+  struct frame *f = ew->emacs_frame.frame;
+  int char_width = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
+  int char_height = frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f);
+  int base_width
+    = (FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1)
+       + (((width < 0) ? FRAME_TEXT_WIDTH (f) : width) % char_width));
+  int base_height
+    = (FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
+       + FRAME_MENUBAR_HEIGHT (f)
+       + (((height < 0) ? FRAME_TEXT_HEIGHT (f) : height) % char_height));
+  int min_width = base_width;
+  int min_height = base_height;
+  bool value;
+
+  int old_base_width, old_base_height;
+  int old_char_width, old_char_height;
+  int old_min_width, old_min_height;
+
+  block_input ();
+  XtVaGetValues ((Widget) wmshell,
+		 XtNbaseWidth, &old_base_width,
+		 XtNbaseHeight, &old_base_height,
+		 XtNwidthInc, &old_char_width,
+		 XtNheightInc, &old_char_height,
+		 XtNminWidth, &old_min_width,
+		 XtNminHeight, &old_min_height,
+		 NULL);
+  unblock_input ();
 
   XtVaSetValues ((Widget) wmshell,
 		 XtNbaseWidth, (XtArgVal) base_width,
 		 XtNbaseHeight, (XtArgVal) base_height,
-		 XtNwidthInc, (XtArgVal) (frame_resize_pixelwise ? 1 : cw),
-		 XtNheightInc, (XtArgVal) (frame_resize_pixelwise ? 1 : ch),
-		 XtNminWidth, (XtArgVal) base_width,
-		 XtNminHeight, (XtArgVal) base_height,
+		 XtNwidthInc, (XtArgVal) char_width,
+		 XtNheightInc, (XtArgVal) char_height,
+		 XtNminWidth, (XtArgVal) min_width,
+		 XtNminHeight, (XtArgVal) min_height,
 		 NULL);
 
-  /* Return if size hints really changed.  If they did not, then Xt
-     probably didn't set them either (or take the flags into
-     account.)  */
-  hints_ptr = (char *) &wmshell->wm.size_hints;
+  value = (base_width != old_base_width || base_height != old_base_height
+	   || char_width != old_char_width || char_height != old_char_height
+	   || min_width != old_min_width || min_height != old_min_height);
 
-  /* Skip flags, which is unsigned long.  */
-  return memcmp (hints_ptr + sizeof (long), buffer + sizeof (long),
-		 sizeof wmshell->wm.wm_hints - sizeof (long));
+  if (value)
+    {
+      CALLN (Ffuncall, Qfoo_it, build_string ("update_wm_hints"),
+	     build_string ("char width"), make_fixnum (FRAME_COLUMN_WIDTH (f)),
+	     build_string ("old char width"), make_fixnum (old_char_width),
+	     build_string ("vscroll"), make_fixnum (FRAME_SCROLL_BAR_AREA_WIDTH (f)),
+	     build_string ("fringes"), make_fixnum (FRAME_TOTAL_FRINGE_WIDTH (f)),
+	     build_string ("borders"), make_fixnum (2 * FRAME_INTERNAL_BORDER_WIDTH (f)),
+	     build_string ("base width"), make_fixnum (base_width),
+	     build_string ("old_base width"), make_fixnum (old_base_width),
+	     build_string ("min width"), make_fixnum (min_width),
+	     build_string ("old min width"), make_fixnum (old_min_width));
+      CALLN (Ffuncall, Qfoo_it, build_string ("   "),
+	     build_string ("char height"), make_fixnum (FRAME_LINE_HEIGHT (f)),
+	     build_string ("old char height"), make_fixnum (old_char_height),
+	     build_string ("menubar"), make_fixnum (FRAME_MENUBAR_HEIGHT (f)),
+	     build_string ("hscroll"), make_fixnum (FRAME_SCROLL_BAR_AREA_HEIGHT (f)),
+	     build_string ("borders"), make_fixnum (2 * FRAME_INTERNAL_BORDER_WIDTH (f)),
+	     build_string ("base height"), make_fixnum (base_height),
+	     build_string ("old base height"), make_fixnum (old_base_height),
+	     build_string ("min height"), make_fixnum (min_height),
+	     build_string ("old min height"), make_fixnum (old_min_height));
+    }
+
+  return value;
 }
 
 bool
-widget_update_wm_size_hints (Widget widget, Widget frame)
+widget_update_wm_size_hints (Widget widget, Widget frame, int width, int height)
 {
-  return update_wm_hints ((WMShellWidget) widget, (EmacsFrame) frame);
+  return update_wm_hints ((WMShellWidget) widget, (EmacsFrame) frame,
+			  width, height);
 }
 
 static void
@@ -337,7 +306,7 @@ update_from_various_frame_slots (EmacsFrame ew)
   struct frame *f = ew->emacs_frame.frame;
   struct x_output *x = f->output_data.x;
 
-  ew->core.height = FRAME_PIXEL_HEIGHT (f) - x->menubar_height;
+  ew->core.height = FRAME_PIXEL_HEIGHT (f); // - x->menubar_height;
   ew->core.width = FRAME_PIXEL_WIDTH (f);
   ew->core.background_pixel = FRAME_BACKGROUND_PIXEL (f);
   ew->emacs_frame.internal_border_width = f->internal_border_width;
@@ -345,12 +314,11 @@ update_from_various_frame_slots (EmacsFrame ew)
   ew->emacs_frame.cursor_color = x->cursor_pixel;
   ew->core.border_pixel = x->border_pixel;
 
-  if (CONSP (frame_size_history))
-    frame_size_history_extra
-      (f, build_string ("update_from_various_frame_slots"),
-       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
-       ew->core.width, ew->core.height,
-       f->new_width, f->new_height);
+  CALLN (Ffuncall, Qfoo_it,
+	 build_string ("update_from_various_frame_slots"),
+	 build_string ("native pixels"),
+	 (Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		 make_fixnum (FRAME_PIXEL_HEIGHT (f)))));
 }
 
 static void
@@ -384,7 +352,6 @@ EmacsFrameRealize (Widget widget, XtValueMask *mask,
 		   XSetWindowAttributes *attrs)
 {
   EmacsFrame ew = (EmacsFrame) widget;
-  struct frame *f = ew->emacs_frame.frame;
 
   /* This used to contain SubstructureRedirectMask, but this turns out
      to be a problem with XIM on Solaris, and events from that mask
@@ -399,12 +366,8 @@ EmacsFrameRealize (Widget widget, XtValueMask *mask,
      make sure we get them all.  Seen with xfcwm4 for example.  */
   XtAddRawEventHandler (widget, StructureNotifyMask, False, resize_cb, NULL);
 
-  if (CONSP (frame_size_history))
-    frame_size_history_plain
-      (f, build_string ("EmacsFrameRealize"));
-
   if (get_wm_shell (widget))
-    update_wm_hints (get_wm_shell (widget), ew);
+    update_wm_hints (get_wm_shell (widget), ew, -1, -1);
 }
 
 static void
@@ -419,18 +382,23 @@ EmacsFrameResize (Widget widget)
   EmacsFrame ew = (EmacsFrame) widget;
   struct frame *f = ew->emacs_frame.frame;
 
-  if (CONSP (frame_size_history))
-    frame_size_history_extra
-      (f, build_string ("EmacsFrameResize"),
-       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
-       ew->core.width, ew->core.height,
-       f->new_width, f->new_height);
+  if (FRAME_PIXEL_WIDTH (f) != ew->core.width
+      || FRAME_PIXEL_HEIGHT (f)	!= ew->core.height)
+    CALLN (Ffuncall, Qfoo_it, build_string ("EmacsFrameResize"),
+	   build_string ("old native pixels"),
+	   Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+		  make_fixnum (FRAME_PIXEL_HEIGHT (f))),
+	   build_string ("new native pixels"),
+	   Fcons (make_fixnum (ew->core.width),
+		  make_fixnum (ew->core.height)));
 
   change_frame_size (f, ew->core.width, ew->core.height,
 		     false, true, false);
 
   if (get_wm_shell (widget))
-    update_wm_hints (get_wm_shell (widget), ew);
+    update_wm_hints (get_wm_shell (widget), ew,
+		     FRAME_PIXEL_TO_TEXT_WIDTH (f, ew->core.width),
+		     FRAME_PIXEL_TO_TEXT_HEIGHT (f, ew->core.height));
   update_various_frame_slots (ew);
 
   cancel_mouse_face (f);
@@ -472,13 +440,6 @@ EmacsFrameSetCharSize (Widget widget, int columns, int rows)
   EmacsFrame ew = (EmacsFrame) widget;
   struct frame *f = ew->emacs_frame.frame;
 
-  if (CONSP (frame_size_history))
-    frame_size_history_extra
-      (f, build_string ("EmacsFrameSetCharSize"),
-       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
-       columns, rows,
-       f->new_width, f->new_height);
-
   if (!frame_inhibit_resize (f, 0, Qfont)
       && !frame_inhibit_resize (f, 1, Qfont))
     x_set_window_size (f, 0, columns * FRAME_COLUMN_WIDTH (f),
diff --git a/src/widget.h b/src/widget.h
index cf83cb1078..03bc809c41 100644
--- a/src/widget.h
+++ b/src/widget.h
@@ -97,6 +97,6 @@ #define XtCInitialGeometry "InitialGeometry"
 /* Special entry points */
 void EmacsFrameSetCharSize (Widget, int, int);
 void widget_store_internal_border (Widget widget);
-bool widget_update_wm_size_hints (Widget widget, Widget frame);
+bool widget_update_wm_size_hints (Widget widget, Widget frame, int width, int height);
 
 #endif /* _EmacsFrame_h */
diff --git a/src/window.c b/src/window.c
index f116b9a9d7..c090f29461 100644
--- a/src/window.c
+++ b/src/window.c
@@ -8394,6 +8394,7 @@ syms_of_window (void)
   DEFSYM (Qheader_line_format, "header-line-format");
   DEFSYM (Qtab_line_format, "tab-line-format");
   DEFSYM (Qno_other_window, "no-other-window");
+  DEFSYM (Qfoo_it, "foo-it");
 
   DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
 	       doc: /* Non-nil means call as function to display a help buffer.
diff --git a/src/xfns.c b/src/xfns.c
index 36b51a3011..25e1af279d 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -4575,7 +4575,7 @@ DEFUN ("x-wm-set-size-hint", Fx_wm_set_size_hint, Sx_wm_set_size_hint,
   struct frame *f = decode_window_system_frame (frame);
 
   block_input ();
-  x_wm_set_size_hint (f, 0, false);
+  x_wm_set_size_hint (f, 0, false, -1, -1);
   unblock_input ();
   return Qnil;
 }
@@ -5097,7 +5097,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
      badly we want them.  This should be done after we have the menu
      bar so that its size can be taken into account.  */
   block_input ();
-  x_wm_set_size_hint (f, window_prompting, false);
+  x_wm_set_size_hint (f, window_prompting, false,
+		      FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f));
   unblock_input ();
 
   adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
diff --git a/src/xterm.c b/src/xterm.c
index 7eaf59d54b..b1d65ce59d 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -26160,6 +26160,8 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
 {
   struct font *font = XFONT_OBJECT (font_object);
   int unit, font_ascent, font_descent;
+  int old_width = FRAME_COLUMN_WIDTH (f);
+  int old_height = FRAME_LINE_HEIGHT (f);
 
   if (fontset < 0)
     fontset = fontset_from_font (font_object);
@@ -26197,9 +26199,27 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
      because it's done in Fx_show_tip, and it leads to problems because
      the tip frame has no widget.  */
   if (FRAME_X_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
-    adjust_frame_size
-      (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
-       FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, false, Qfont);
+    {
+      CALLN (Ffuncall, Qfoo_it, intern ("\nx_new_font"),
+	     intern ("old char size"),
+	     Fcons (make_fixnum (old_width), make_fixnum (old_height)),
+	     intern ("new char size"),
+	     Fcons (make_fixnum (FRAME_COLUMN_WIDTH (f)),
+		    make_fixnum (FRAME_LINE_HEIGHT (f))),
+	     intern ("text chars"),
+	     Fcons (make_fixnum (FRAME_COLS (f)),
+		    make_fixnum (FRAME_LINES (f))),
+	     intern ("old text pixels"),
+	     Fcons (make_fixnum (FRAME_TEXT_WIDTH (f)),
+		    make_fixnum (FRAME_TEXT_HEIGHT (f))),
+	     intern ("new text pixels"),
+	     Fcons (make_fixnum (FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f)),
+		    make_fixnum (FRAME_LINES (f) * FRAME_LINE_HEIGHT (f))));
+
+      adjust_frame_size
+	(f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+	 FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, false, Qfont);
+    }
 
 #ifdef HAVE_X_I18N
   if (FRAME_XIC (f)
@@ -26589,7 +26609,7 @@ x_set_offset (struct frame *f, int xoff, int yoff, int change_gravity)
   x_calc_absolute_position (f);
 
   block_input ();
-  x_wm_set_size_hint (f, 0, false);
+  x_wm_set_size_hint (f, 0, false, -1, -1);
 
 #ifdef USE_GTK
   if (x_gtk_use_window_move)
@@ -27279,7 +27299,7 @@ x_check_fullscreen (struct frame *f)
 	  emacs_abort ();
         }
 
-      x_wm_set_size_hint (f, 0, false);
+      x_wm_set_size_hint (f, 0, false, -1, -1);
 
       XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
 		     width, height);
@@ -27453,7 +27473,8 @@ x_set_window_size_1 (struct frame *f, bool change_gravity,
 {
   if (change_gravity)
     f->win_gravity = NorthWestGravity;
-  x_wm_set_size_hint (f, 0, false);
+  x_wm_set_size_hint (f, 0, false, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+		      FRAME_PIXEL_TO_TEXT_HEIGHT (f, height));
 
   XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
 		 width, height + FRAME_MENUBAR_HEIGHT (f));
@@ -28243,7 +28264,7 @@ x_make_frame_invisible (struct frame *f)
      program-specified, so that when the window is mapped again, it will be
      placed at the same location, without forcing the user to position it
      by hand again (they have already done that once for this window.)  */
-  x_wm_set_size_hint (f, 0, true);
+  x_wm_set_size_hint (f, 0, true, -1, -1);
 
 #ifdef USE_GTK
   if (FRAME_GTK_OUTER_WIDGET (f))
@@ -28896,7 +28917,8 @@ x_embed_frame (struct x_display_info *dpyinfo, struct frame *f)
    The GTK version is in gtkutils.c.  */
 
 void
-x_wm_set_size_hint (struct frame *f, long flags, bool user_position)
+x_wm_set_size_hint (struct frame *f, long flags, bool user_position,
+		    int width, int height)
 {
 #ifndef USE_GTK
   XSizeHints size_hints;
@@ -28935,10 +28957,12 @@ x_wm_set_size_hint (struct frame *f, long flags, bool user_position)
 #ifndef USE_MOTIF
       hints_changed
 	= widget_update_wm_size_hints (f->output_data.x->widget,
-				       f->output_data.x->edit_widget);
+				       f->output_data.x->edit_widget,
+				       width, height);
 #else
       widget_update_wm_size_hints (f->output_data.x->widget,
-				   f->output_data.x->edit_widget);
+				   f->output_data.x->edit_widget,
+				   width, height);
 
       /* Do this all over again for the benefit of Motif, which always
 	 knows better than the programmer.  */
@@ -29012,8 +29036,16 @@ x_wm_set_size_hint (struct frame *f, long flags, bool user_position)
   {
     int base_width, base_height;
 
-    base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
-    base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0);
+    if (width == -1)
+      width = FRAME_TEXT_WIDTH (f);
+
+    if (height == -1)
+      height = FRAME_TEXT_HEIGHT (f);
+
+    base_width = (FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0)
+		  + width % FRAME_COLUMN_WIDTH (f));
+    base_height = (FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0)
+		   + height % FRAME_LINE_HEIGHT (f));
 
     /* The window manager uses the base width hints to calculate the
        current number of rows and columns in the frame while
@@ -29075,7 +29107,7 @@ x_wm_set_size_hint (struct frame *f, long flags, bool user_position)
 
   XSetWMNormalHints (FRAME_X_DISPLAY (f), window, &size_hints);
 #else
-  xg_wm_set_size_hint (f, flags, user_position);
+  xg_wm_set_size_hint (f, flags, user_position, width, height);
 #endif /* USE_GTK */
 }
 
diff --git a/src/xterm.h b/src/xterm.h
index ee429e9c68..61512f1117 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1653,7 +1653,7 @@ #define SELECTION_EVENT_TIME(eventp)	\
 extern void x_make_frame_invisible (struct frame *);
 extern void x_iconify_frame (struct frame *);
 extern void x_free_frame_resources (struct frame *);
-extern void x_wm_set_size_hint (struct frame *, long, bool);
+extern void x_wm_set_size_hint (struct frame *, long, bool, int, int);
 #if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK \
   && defined HAVE_CLOCK_GETTIME
 extern void x_sync_init_fences (struct frame *);