[net.emacs] Fix for display bug

thomas%UTAH-GR%utah-cs@sri-unix.UUCP (11/18/83)

From:  Spencer W. Thomas  <thomas%UTAH-GR@utah-cs>

Well, I've found fixes for 2 bugs in Gosling's Emacs.  The first bug is
in both versions, the second *seems* to be only in #264.

Bug 1 Description:
	Sometimes executing a function while in the minibuffer "erases"
the minibuffer display.  It comes back when the next key is typed.

Repeat-by:
Load this function

(defun (bug1
	      (save-excursion (error-occurred (error-message "error")))
	      (beginning-of-file) (set-mark) (end-of-file) (erase-region)
	      (insert-string "abcde")
	      (novalue)
))

(in #85, change the spelling of error-occurred)
Bind this to a convenient key (say ESC-'), then do 
	ESC-xinsert-string ESC-'
You should get either a blank minibuffer or a minibuffer with only
"abcde" in it (no insert-string prompt).  Typing any character (e.g.,
^E) should fix the display.  

Fix:
	The problem is that an error erases the minibuffer prompt
string.  Emacs has remembered it, but doesn't know it's supposed to
restore it if you catch the error (with error-occurred).  My fix is to
add an extra check for resetting the minibuffer string at the beginning
of DoDsp.  So, in window.c$DoDsp, add the following lines after
GSaveMiniBuf = SaveMiniBuf:

    /* If the minibuffer got wiped, but should be re-displayed,
     * restore it
     */
    if (MiniBuf && !GSaveMiniBuf && !*MiniBuf && ResetMiniBuf) {
	MiniBuf = ResetMiniBuf;
	if (*ResetMiniBuf == 0) ResetMiniBuf = 0;
    }


----------------------------------------------------------------
Bug 2 Description:
	This bug is apparently only in #264, but the code in #85 is
identical, as far as I can tell.  It seems to have been caused by a
change which keeps the minibuffer visible when process output occurs.
The following mlisp function illustrates the problem:

(defun
    (foo
	(save-excursion (pop-to-buffer "main")
	(erase-buffer)
	(arg 1 "long prompt for input, type something: ")
	(save-window-excursion 
	    (insert-string "this is a test\nline 2\nline 3\n")
	    (sit-for 0)
	    (next-page)
	    (sit-for 0)
	    (message "barf") (sit-for 10)
	)
    ))
)

If you load this and execute ESC-xfoo, then type <cr> to the argument
prompt, you get the following sequence in the minibuffer:

long prompt for input, type something:
barf prompt for input, type something:
: (foo) => 10or input, type something:

If you change it so it only inserts two lines, then all is well, i.e.,
you get

long prompt for input, type something:
barf
: (foo) => 10

Fix:

	This one is somewhat complicated (as you might expect, it
turned out to be in the skull-and-crossbones section, display.c).  If
more than 3 lines may have changed, then emacs performs the full-blown
redisplay algorithm (this is why the bug doesn't appear with only two
lines inserted).  (Note: Take the following with a grain of salt, it is
my interpretation of the code, and may be wrong, but it did lead to an
apparently correct solution.)  If the minibuffer hasn't changed, and
the redisplay is caused by a function call (like sit-for), then emacs
does not redraw the minibuffer, so the DesiredScreen lines for the
minibuffer contents are null (not just empty).  For normal screen
lines (in a buffer, e.g.), it is not clear that this can ever happen.
It will happen if a one-line optimization is being done, but then the
slow update won't be performed, so there's no problem. Now, if a
DesiredScreen line is null, it is set to the corresponding PhysScreen
line, to indicate that there is no change.  Then, down in the bowels of
CalcID, the following sequence occurs:
	(line i from the previous screen is being mapped onto line j of
the new screen)
	1. Remember the old line i.
	2. If the desired line i is the same, forget it.
	3. Forget the PhysScreen copy of the old line i.
	   Assuming a null DesiredScreen line, both PhysScreen[i]
	   and DesiredScreen[i] are null now.  This assumption takes us
	   to.  Also assume (valid in minibuffer) that lines i and j are
	   really the same line.
	4. If the old line is not the same line (structure) as PhysScreen[j]
	   and DesiredScreen[j], forget it.

Now, with our assumptions, we have that
	1. PhysScreen[j] is null
	2. Line j on the screen is NOT blank, but emacs thinks it is
	   because of 1.

Next time we put something into the minibuffer (message "barf"), emacs
thinks that line is blank and just writes "barf" on top of it.

The fix just sets PhysScreen[j] to its old value when i == j and the
desired line is identical to the current line.

Even if you didn't follow all that, here is a fix.  In
display.c$UpdateScreen, make the following change:

diff -c1 -r3.3 display.c
*** /tmp/,RCSt1006077	Fri Nov 11 23:41:53 1983
--- display.c	Fri Nov 11 23:22:46 1983
***************
*** 451,453
  		CalcID (ni, nj, 0);
! 		if (InputPending && !DoneEarly) {
  		    if (PhysScreen[j] != old)

--- 451,455 -----
  		CalcID (ni, nj, 0);
! 		if ( i == j && !PhysScreen[i] && !DesiredScreen[i] )
! 		    PhysScreen[i] = old;
! 		else if (InputPending && !DoneEarly) {
  		    if (PhysScreen[j] != old)

----------------------------------------------------------------
Finally, here is a fix to #85 to make the minibuffer stick around, even
when process output occurs.  This is something I came up with for #85,
and does not need to be put into #264 which does the same thing
differently:

in window.c$DoDsp, near the end (shortly after the label update:),
comment out the lines below.

  	}
! /*	else if (!GSaveMiniBuf)
! 	    MiniBuf = *MiniBuf ? "" : 0;*/
      }

In keyboard.c$GetChar, add the lines indicated below.  The context is
what it looks like in the original #85 and #264 versions, as far as I
can tell.
  	c = getc(stdin);
  	InputPending = stdin->_cnt>0;
      }
  #endif
+     if (MiniBuf && (!InMiniBuf || *MiniBuf) && !ResetMiniBuf)
+ 	MiniBuf = *MiniBuf ? "" : 0;	/* Only reset minibuf w/ kbd input */
      if(c<0) return -1;
  HaveCharacter:

Sorry about the length of this message, seems I had a lot to say.

=Spencer