[gnu.bash.bug] Bad cursor optimization

cudcv@warwick.ac.uk (Rob McMahon) (07/15/89)

Having noticed the cursor `bouncing' on the first character of a line, I
decided to investigate:

Here's the TERMCAP, I've chopped it into short lines for news:

Mu|sun:am:bs:km:mi:ms:pt:li#34:co#80:cl=^L:cm=\E[%i%d;%dH:ce=\E[K:cd=\E[J:\
	:so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:rs=\E[s:md=\E[1m:mr=\E[7m:me=\E[m:\
	:al=\E[L:dl=\E[M:im=:ei=:ic=\E[@:dc=\E[P:AL=\E[%dL:DL=\E[%dM:\
	:IC=\E[%d@:DC=\E[%dP:up=\E[A:nd=\E[C:ku=\E[A:kd=\E[B:kr=\E[C:kl=\E[D:\
	:k1=\E[224z:k2=\E[225z:k3=\E[226z:k4=\E[227z:k5=\E[228z:k6=\E[229z:\
	:k7=\E[230z:k8=\E[231z:k9=\E[232z:

I typed `echo^M^P^Aecho ^M^D' to bash, and this is an `od -c' of the
typescript:

(... this bit's boring ...)
0000120    v  \r  \n   b   a   s   h   $       e  \b   e   c   h   o  \r
first character `bounces'		       ^^^^^^^^^
0000140   \r  \n  \r  \n   b   a   s   h   $       e   c   h   o  \b  \b
0000160   \b  \b  \b  \b  \b  \b  \b  \b 033   [   2   @   *   b   a   s
		  ^^^^^^^^^^^^^^^^^^^^^^
very slow way to get back to left of line
0000200    h   $       e 033   [   1   @   c   e  \b 033   [   1   @   h
				   ^
(unnecessary `1' here, not very important)
0000220    e  \b 033   [   1   @   o   e  \b 033   [   1   @       e  \b
				   ^^^^^^^^^
every time a character is inserted, the one after is redrawn
0000240   \r  \r  \n   e   c   h   o  \r  \n   b   a   s   h   $      \r
	  ^^^^^^
(always get 2 carriage returns, not very important)
0000260   \r  \n  \n   s   c   r   i   p   t       d   o   n   e       o
(... etc ...)

The two carriage returns I take to be because the shell doesn't turn off
output translations, nor check to see whether they are enabled or not.  The
first is probably a feature, the second doesn't seem worth fixing.

I believe the problems in readline.c are
1) in rl_redisplay the first characters of the visible and invisible buffers
   are compared before the invisible one is filled in when dealing with the
   first redisplay on the line.
2) in update_line, if the loop finding the last same character ends because of
   meeting the first difference, rather than because of finding two characters
   that don't match, the pointers already point at the last same, and don't
   need incrementing.
3) (I've made an optimization to move_cursor_relative to move backwards by
   doing a carriage return and then moving forwards if that is faster.)
4) In passing I noticed that term_im is not output when using the multiple
   insert, I assume it shoule be ?  Else end_insert should not output term_ei
   if start_insert used term_IC ?  I don't suppose any terminal really has
   both im and IC ...
5) I changed start_insert to use term_ic rather than term_IC if only one
   character is to be inserted.

The patches at the end seem to fix all but the double cr problem, make of them
what you will, I don't seem to have broken anything as far as I can tell.  The
resulting `od -c' is:

0000120    v  \r  \n   b   a   s   h   $       e   c   h   o  \r  \r  \n
no bounce				       ^
still got double cr					      ^^^^^^
0000140   \r  \n   b   a   s   h   $       e   c   h   o  \b  \b  \b  \b
0000160   \r 033   [   2   @   *   b   a   s   h   $       e 033   [   @
	  ^^
use cr to get to left of line
0000200    c 033   [   @   h 033   [   @   o 033   [   @      \r  \r  \n
	     ^^^^^^^^^^^
use ic rather than IC when inserting one character
			   ^
no redrawing and moving back over character following insertion
0000220    e   c   h   o  \r  \n   b   a   s   h   $      \r  \r  \n  \n

RCS file: readline.c,v
retrieving revision 1.1
diff -c -r1.1 readline.c
*** /tmp/,RCSt1a02097	Sat Jul 15 16:57:12 1989
--- readline.c	Sat Jul 15 16:25:59 1989
***************
*** 794,805 ****
        line[out] = '\0';
      }
  
-   /* If someone thought that the redisplay was handled, but the currently
-      visible line has a different modification state than the one about
-      to become visible, then correct the callers misconception. */
-   if (visible_line[0] != invisible_line[0])
-     rl_display_fixed = 0;
-   
    strncpy (line + out,  rl_display_prompt, strlen (rl_display_prompt));
    out += strlen (rl_display_prompt);
    line[out] = '\0';
--- 794,799 ----
***************
*** 847,852 ****
--- 841,852 ----
    if (c_pos < 0)
      c_pos = out;
    
+   /* If someone thought that the redisplay was handled, but the currently
+      visible line has a different modification state than the one about
+      to become visible, then correct the callers misconception. */
+   if (visible_line[0] != invisible_line[0])
+     rl_display_fixed = 0;
+   
    /* PWP: now is when things get a bit hairy.  The visible and invisible
       line buffers are really multiple lines, which would wrap every
       (screenwidth - 1) characters.  Go through each in turn, finding
***************
*** 1001,1007 ****
        ols = oe;
        nls = ne;
      }
!   else
      {
        if (*ols)			/* don't step past the NUL */
  	ols++;
--- 1001,1007 ----
        ols = oe;
        nls = ne;
      }
!   else if (*ols != *nls)
      {
        if (*ols)			/* don't step past the NUL */
  	ols++;
***************
*** 1112,1118 ****
--- 1112,1126 ----
       char *data;
  {
    register int i;
+   static void output_character_function ();
  
+   /* may be faster to do cr then move forwards than to move backwards. */
+   if (new + 1 < last_c_pos - new)
+     {
+       tputs (term_cr, 1, output_character_function);
+       last_c_pos = 0;
+     }
+ 
    if (last_c_pos == new) return;
  
    if (last_c_pos < new)
***************
*** 1128,1134 ****
  	 data is underneath the cursor. */
  #ifdef HACK_TERMCAP_MOTION
        extern char *term_forward_char;
-       static void output_character_function ();
  
        if (term_forward_char)
  	for (i = last_c_pos; i < new; i++)
--- 1136,1141 ----
***************
*** 1398,1404 ****
  start_insert (count)
       int count;
  {
!   if (term_IC && *term_IC)
      {
        char *tgoto (), *buffer;
        buffer = tgoto (term_IC, 0, count);
--- 1405,1415 ----
  start_insert (count)
       int count;
  {
!   if (term_im && *term_im)
!     tputs (term_im, 1, output_character_function);
! 
!   if (term_IC && *term_IC
!       && (count > 1 || !term_ic || !*term_ic))
      {
        char *tgoto (), *buffer;
        buffer = tgoto (term_IC, 0, count);
***************
*** 1406,1414 ****
      }
    else
      {
-       if (term_im && *term_im)
- 	tputs (term_im, 1, output_character_function);
- 
        if (term_ic && *term_ic)
  	while (count--)
  	  tputs (term_ic, 1, output_character_function);
--- 1417,1422 ----

Rob
-- 
UUCP:   ...!mcvax!ukc!warwick!cudcv	PHONE:  +44 203 523037
JANET:  cudcv@uk.ac.warwick             ARPA:   cudcv@warwick.ac.uk
Rob McMahon, Computing Services, Warwick University, Coventry CV4 7AL, England