Subject: Re: bug#52493: 29.0.50; Setting Inconsolata up in init.el makes default face rendered wrong

From: martin rudalics <[email-masked]>
Date: Sat, 7 Jan 2023 10:15:38 +0100

> - Width shrinks only once (at certain rare widths), and after that it is stable.

   x_new_font old char size 21x45 new char size 17x37 text chars 113x36 old text pixels 2380x1628 new text pixels 1921x1332
   xg_wm_set_size_hint scale 2 char width 17 toolbar 0 vscroll 32 fringes 16 borders 0 base width 32 width inc 8
       char height 37 menubar 50 toolbar 82 hscroll 0 borders 0 base height 84 height inc 18
   xg_frame_set_char_size old native pixels 2428x1628 new native pixels 1969x1332 outer pixels 984x732 outer rest 0x0
   xg_frame_resized old native pixels 2428x1628 new native pixels 1968x1332
   adjust_frame_size old native pixels 2428x1628 new native pixels 1968x1332 old text pixels 2380x1628 new text pixels 1920x1332 old text chars 113x36 new text chars 112x36

Here we calculate outer_width as (/ (+ 1921 32 16) 2) that is 984 and
outer_height as (/ (+ 1332 50 82) 2) that is 732.  Since 1921 is impair
we lose one pixel due to scaling.

Now width_rest calculated as (% (- 984 32) 8) and height_rest calculated
as (% (- 732 84) 18) are both zero so we do not do any compensating and
lose one column after resizing.

I attach a version to handle this particular case.  Let's see whether it
breaks something else.

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..d7d069d908 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -906,6 +906,32 @@ 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 HAVE_WINDOW_SYSTEM
   {
     /* Adjust size of F's child frames.  */
diff --git a/src/gtkutil.c b/src/gtkutil.c
index a6bba096a4..167329ccb8 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,8 +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);
+  outer_height /= scale;
+  outer_width /= scale;
+
+  if (scale != 1 && !frame_resize_pixelwise)
+    {
+      int base_width_unscaled = (FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1)
+				 + FRAME_TOOLBAR_WIDTH (f));
+      int base_width_scaled = base_width_unscaled / scale;
+      int base_height_unscaled = (FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
+				  + FRAME_MENUBAR_HEIGHT (f)
+				  + FRAME_TOOLBAR_HEIGHT (f));
+      int base_height_scaled = base_height_unscaled / scale;
+      int width_inc = FRAME_COLUMN_WIDTH (f) / scale;
+      int height_inc = FRAME_LINE_HEIGHT (f) / scale;
+      int width_rest, height_rest;
+
+      if (width_inc < 1)
+	width_inc = 1;
+
+      if (height_inc < 1)
+	height_inc = 1;
+
+      if (outer_width < base_width_scaled)
+	outer_width = base_width_scaled;
+
+      if (outer_height < base_height_scaled)
+	outer_height = base_height_scaled;
+
+      width_rest = (outer_width - base_width_scaled) % width_inc;
+      height_rest = (outer_height - base_height_scaled) % height_inc;
+
+      if (width_rest > 0 || (base_width_unscaled % scale) > 0)
+	outer_width = outer_width + width_inc - width_rest;
+      if (height_rest > 0 || (base_height_unscaled % scale) > 0)
+	outer_height = outer_height + height_inc - height_rest;
+    }
 
   xg_wm_set_size_hint (f, 0, 0);
 
@@ -1317,6 +1361,30 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
   SET_FRAME_GARBAGED (f);
   cancel_mouse_face (f);
 
+  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)));
+  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.
@@ -2028,6 +2096,24 @@ xg_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
 		 &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 ("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 ("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/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/xterm.c b/src/xterm.c
index 7eaf59d54b..b2e689fe84 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)