Subject: bug#73022: 31.0.50; Crash in build_frame_matrix_from_leaf_window after C-x 2 and reducing terminal size

From: martin rudalics <[email-masked]>
Date: Thu, 12 Sep 2024 11:49:36 +0200

>> It _might_ be incorrect when we allow FRAME_TOTAL_LINES (f) to exceed
 >> FrameRows (FRAME_TTY (f)) because we refuse to shrink a frame below some
 >> height.  That's why I used the term "probably".  If I knew what that
 >> code does in all consequences, I could tell you more.  But I don't know.
 >
 > If FRAME_TOTAL_LINES is different from FrameRows at that spot, it's a
 > bug, isn't it?

It depends.  On a GUI frame do the following: In *scratch* insert the
two lines

(insert (format "%s" (frame-native-height)))
(insert (format "%s" (window-pixel-height (frame-root-window))))

and then do C-x 2 three times so yet get four windows.  Now with the
mouse drag the lower border upwards as far as you can.  The minibuffer
window should have disappeared by now.  Evaluate the two lines above by
putting point at their end and typing C-x e.  You will see that the
second value exceeds the first one - the root window got larger than
its frame.  So the window dimensions as Emacs can reasonably draw them
exceed the dimensions of the window as the WM gives them to us.

The same thing happens on TTY frames.  With split windows, the size of
the root window may exceed the size of the terminal window.  Whether we
now synch FRAME_TOTAL_LINES and FrameRows is just a matter of taste.

 > I still don't understand what is supposed to happen when we shrink the
 > frame to less lines/columns than the minimum window dimensions we
 > allow.

There are two minimum window dimensions we "allow": The first are the
hard-coded 5/2 values in handle_window_change_signal and additionally
imposed in frame_windows_min_size.  IIUC neither of these takes care of
menu or tab bars nor of any window splitting.  The second is the
'frame-windows-min-size' code that takes care of the entire frame layout
in an equal fashion for GUI frames and TTY frames.  In either case we do
not shrink the dimensions of our windows but keep them as in the GUI
example above.

 > Also, I'd be happier if you could describe the sequence of
 > events that lead to frame and window resizing following a SIGWINCH.

IIUC we call change_frame_size, possibly delay it, and eventually call
adjust_frame_size just as we do for GUI frames.  There's no special
magic involved.

The problematic thing I see is that the entire cursor wrapping code
including the check in cmcheckmagic seem botched because when a window
is virtually drawn below the border of the terminal window (as in the
GUI example sketched above) we do not check whether the cursor ends up
below that border too.  In particular the form

   /* First the degenerate case */
     if (row == curY (tty) && col == curX (tty)) /* already there */
     return;

in cmgoto seems to indicate that we leave the cursor at its current
location.  Maybe we do that even earlier, for example, in

   if (curY (tty) == vpos
       && curX (tty) == hpos)
     return;

of tty_cursor_to.  And maybe neither of these is relevant because
redisplay simply does nothing if the size of the root window does not
change (I wouldn't know where and how redisplay checks that).  At the
very end the check in cmcheckmagic aborts us in any such case.

martin