Subject: bug#78474: 31.0.50; Wrong char insertion in rxvt

From: Manuel Giraud <[email-masked]>
Date: Sat, 02 Aug 2025 15:17:35 +0200

Robert Pluim <[email-masked]> writes:

>>>>>> On Fri, 25 Jul 2025 13:08:07 +0200, Manuel Giraud <[email-masked]> said:
>
>     Manuel> Samuel Thibault <[email-masked]> writes:
>     Manuel> [...]
>
>     >>> Here are my results with this new benchmark:
>     >> 
>     >> (Several measurements are really needed because of variability)
>
>     Manuel> Why not.  So, here is the benchmark code I used:
>
> So it looks to me like not emitting \t\b is slightly faster. Could you
> repost your patch with a user option to enable/disable it? Then we can
> discuss what the default should be. Iʼm leaning towards the default
> being not emitting it, since the resulting redisplay will be correct
> in any case.

Hi Robert and all,

Here it is.  I'm not quite sure about the user option name, its
documentation and NEWS entry.  Samuel could you test if this new default
works for screen readers and also if setting
`inhibit-overshoot-in-tty-motions' to nil make them unwork.

FWIW, here are the results for Robert's benchmark (xterm only) with this
patch:

* inhibit-overshoot-in-tty-motions is t (default)
** xterm -geometry 80x50
0) GCs: 116 Elapsed time: 62.708855 seconds
1) GCs: 116 Elapsed time: 70.723578 seconds
2) GCs: 115 Elapsed time: 70.099679 seconds
3) GCs: 115 Elapsed time: 70.479095 seconds
4) GCs: 115 Elapsed time: 70.143764 seconds

* (setopt inhibit-overshoot-in-tty-motions nil)
** xterm -geometry 80x50
0) GCs: 117 Elapsed time: 62.697354 seconds
1) GCs: 115 Elapsed time: 71.844141 seconds
2) GCs: 116 Elapsed time: 71.422948 seconds
3) GCs: 115 Elapsed time: 71.161357 seconds
4) GCs: 115 Elapsed time: 70.877772 seconds
>From 019be1f9c795224f7aafcbabb3e772bdbec69d36 Mon Sep 17 00:00:00 2001
From: Manuel Giraud <[email-masked]>
Date: Tue, 22 Jul 2025 15:15:39 +0200
Subject: [PATCH] User option to control overshoot and backup for tty motions

* src/term.c (syms_of_term): New user option.
* src/cm.c (calccost): Control overshooting with new option.
* etc/NEWS: Document new option.
---
 etc/NEWS   |  8 ++++++++
 src/cm.c   | 45 ++++++++++++++++++++++++++-------------------
 src/term.c |  9 +++++++++
 3 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b469b22aacf..46f414d82d1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -85,6 +85,14 @@ the 'standard-display-table's extra slots with Unicode characters.
 Please see the documentation of that function to see which slots of the
 display table it changes.
 
+---
+** New user option 'inhibit-overshoot-in-tty-motions'.
+The display optimization where the combination TAB characters +
+BACKSPACE to move to a position on a TTY frame is now disabled by
+default and controlled by this user option.  This option can be set
+customized to nil to keep the old behavior.  This is to accomodate to
+some software such as screen readers.
+
 +++
 ** Child frames are now supported on TTY frames.
 This supports use-cases like Posframe, Corfu, and child frames acting
diff --git a/src/cm.c b/src/cm.c
index 150d1c9a580..0f6a692ee48 100644
--- a/src/cm.c
+++ b/src/cm.c
@@ -187,9 +187,9 @@ calccost (struct tty_display_info *tty,
                     c,
                     totalcost;
     int     ntabs,
-            n2tabs,
-            tabx,
-            tab2x,
+	    n2tabs,
+	    tabx,
+	    tab2x,
             tabcost;
     register const char *p;
 
@@ -229,36 +229,43 @@ calccost (struct tty_display_info *tty,
 	goto olddelta;		/* forget it! */
 
     /*
-     * ntabs is # tabs towards but not past dstx; n2tabs is one more
-     * (ie past dstx), but this is only valid if that is not past the
-     * right edge of the screen.  We can check that at the same time
-     * as we figure out where we would be if we use the tabs (which
-     * we will put into tabx (for ntabs) and tab2x (for n2tabs)).
+     * ntabs is # tabs towards but not past dstx.  tabx is where we
+     * would be if we put those ntabs tabulations.
      */
 
     ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth;
-    n2tabs = ntabs + 1;
     tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth;
-    tab2x = tabx + tty->Wcm->cm_tabwidth;
 
-    if (tab2x >= tty->Wcm->cm_cols)	/* too far (past edge) */
+    if (!inhibit_overshoot_in_tty_motions) {
+      /*
+       * n2tabs is one more tab than ntabs (i.e. past dstx), but this is
+       * only valid if that is not past the right edge of the screen.
+       * tab2x is where we would be if we put those n2tabs tabulations.
+       */
+      n2tabs = ntabs + 1;
+      tab2x = tabx + tty->Wcm->cm_tabwidth;
+      if (tab2x >= tty->Wcm->cm_cols)	/* too far (past edge) */
 	n2tabs = 0;
 
+      /*
+       * Set c to the cost for using n2tabs
+       */
+                /* cost for n2tabs          +    cost for left motion */
+      c = n2tabs ? n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
+	: BIG;
+    }
+
     /*
-     * Now set tabcost to the cost for using ntabs, and c to the cost
-     * for using n2tabs, then pick the minimum.
+     * Set tabcost to the cost for using ntabs.
      */
 
 		   /* cost for ntabs           +    cost for right motion */
     tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
 		    : BIG;
 
-		   /* cost for n2tabs          +    cost for left motion */
-    c = n2tabs  ?    n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
-		: BIG;
-
-    if (c < tabcost)		/* then cheaper to overshoot & back up */
-	ntabs = n2tabs, tabcost = c, tabx = tab2x;
+    /* Is it cheaper to overshoot & back up? */
+    if (!inhibit_overshoot_in_tty_motions && (c < tabcost))
+      ntabs = n2tabs, tabcost = c, tabx = tab2x;
 
     if (tabcost >= BIG)		/* caint use tabs */
 	goto newdelta;
diff --git a/src/term.c b/src/term.c
index 8aa47322d19..26d6df3b7fc 100644
--- a/src/term.c
+++ b/src/term.c
@@ -5167,6 +5167,15 @@ syms_of_term (void)
 trigger redisplay.  */);
   tty_menu_calls_mouse_position_function = 0;
 
+  DEFVAR_BOOL ("inhibit-overshoot-in-tty-motions", inhibit_overshoot_in_tty_motions,
+    doc: /* Whether TTY frames may use the combination TAB + BACKSPACE for moving around.
+On TTY frames, as a display optimization, Emacs may move to a position
+by "overshooting" with TAB characters and one BACKSPACE character, when
+this is more efficient.  This combination can interfere with the
+functioning of some software, such as screen readers.  Set this to nil
+to enable this optimization.  */);
+  inhibit_overshoot_in_tty_motions = 1;
+
   defsubr (&Stty_display_color_p);
   defsubr (&Stty_display_color_cells);
   defsubr (&Stty_no_underline);
-- 
2.50.1

-- 
Manuel Giraud