Subject: bug#71343: 30.0.50; TTY frame doesn't automatically redisplay itself after having closed another frame

From: Daniel Clemente <[email-masked]>
Date: Fri, 21 Jun 2024 10:47:07 +0000

> Emacs doesn't really talk to the terminal emulator, unless the
> emulator supports known protocols of doing so.  You will see in
> lisp/term/xterm.el that Emacs supports that for xterm that is new
> enough.  Don't know what happens with urxvt, but did you try using
> xterm and saw the same problems?

I tried with xterm and I don't see the same problem. There's more
flickering and slowness but the frame I'm resizing gets redrawn.
I'm using:
xterm -e "emasclient" "-c" &

I just installed other terminals: mlterm, cool-retro-term, Eterm. They
show the problem, just like urxvt. The frame being resized (just after
closing another frame) isn't redrawn.

To simplify testing I'm using
for i in `seq 2`; do mlterm -e "emacsclient" "-c" "-e" '(dired "~")' &
sleep 1; done
and closing the second one and resizing the first one..

I also saw 2 other SIGSEGV with xterm and others, that show possibly
corrupted memory. I reported them as separate bugs but I don't know
whether they're related to this one.

>
> > > > - What if, when resizing the frame (something which Emacs detects),
> > > > Emacs knows/detects that a frame was closed, and decides not to delay
> > > > the redisplay?
> > >
> > > Redisplay of which frame?  Emacs only redisplays a frame if some
> > > change in buffer text justifies that.
> >
> > I propose it should repaint/redisplay the frame whose size changed.
>
> When a frame is resized, it is redisplayed, yes.  But that requires
> Emacs to know that it is resized.
>
> > Don't we know which frame is the one being resized? It should be f in
> > adjust_frame_size. It realizes that e.g. it's being resized from 72 to
> > 61 columns.
>
> I need a backtrace showing the call to adjust_frame_size, of a frame
> that is not redrawn.  I don't think I saw such a backtrace.

In this one, I made a frame less wide (it was around 70 columns, I
made it 60 columns) in the conditions when redraw doesn't happen.

Breakpoint 2, adjust_frame_size (f=0x6210000ede00, new_text_width=60,
    new_text_height=49, inhibit=5, pretend=false, parameter=XIL(0x40e0))
    at frame.c:646
646      int unit_width = FRAME_COLUMN_WIDTH (f);
(gdb) bt
#0  adjust_frame_size (f=0x6210000ede00, new_text_width=60,
    new_text_height=49, inhibit=5, pretend=false, parameter=XIL(0x40e0))
    at frame.c:646
#1  0x00005555564e04c3 in change_frame_size_1 (f=0x6210000ede00,
    new_width=60, new_height=50, pretend=false, delay=false, safe=false)
    at dispnew.c:6054
#2  0x00005555564e0526 in change_frame_size (f=0x6210000ede00,
    new_width=60, new_height=50, pretend=false, delay=false, safe=false)
    at dispnew.c:6087
#3  0x00005555564df618 in do_pending_window_change (safe=false)
    at dispnew.c:6014
#4  0x0000555556e479ee in wait_reading_process_output (time_limit=0,
    nsecs=0, read_kbd=-1, do_display=true, wait_for_cell=XIL(0),
    wait_proc=0x0, just_wait_proc=0) at process.c:5784
#5  0x00005555569b57da in kbd_buffer_get_event (kbp=0x7fffffffca50,
    used_mouse_menu=0x7fffffffd4f0, end_time=0x0) at keyboard.c:4079
#6  0x00005555569a6c45 in read_event_from_main_queue (end_time=0x0,
    local_getcjmp=0x7fffffffd110, used_mouse_menu=0x7fffffffd4f0)
    at keyboard.c:2330
#7  0x00005555569a75b1 in read_decoded_event_from_main_queue (
    end_time=0x0, local_getcjmp=0x7fffffffd110, prev_event=XIL(0),
    used_mouse_menu=0x7fffffffd4f0) at keyboard.c:2394
#8  0x00005555569adaa6 in read_char (commandflag=1,
    map=XIL(0x7fffec72ab93), prev_event=XIL(0),
    used_mouse_menu=0x7fffffffd4f0, end_time=0x0) at keyboard.c:3015
#9  0x00005555569e9ca2 in read_key_sequence (keybuf=0x7fffffffd7e0,
    prompt=XIL(0), dont_downcase_last=false,
    can_return_switch_frame=true, fix_current_buffer=true,
    prevent_redisplay=false, disable_text_conversion_p=false)
    at keyboard.c:10728
--Type <RET> for more, q to quit, c to continue without paging--
#10 0x000055555699b122 in command_loop_1 () at keyboard.c:1429
#11 0x0000555556cbb678 in internal_condition_case (
    bfun=0x55555699a22d <command_loop_1>, handlers=XIL(0x90),
    hfun=0x555556998204 <cmd_error>) at eval.c:1613
#12 0x0000555556999797 in command_loop_2 (handlers=XIL(0x90))
    at keyboard.c:1168
#13 0x0000555556cb84d8 in internal_catch (tag=XIL(0xfb40),
    func=0x555556999767 <command_loop_2>, arg=XIL(0x90)) at eval.c:1292
#14 0x000055555699969a in command_loop () at keyboard.c:1146
#15 0x0000555556996e7a in recursive_edit_1 () at keyboard.c:754
#16 0x0000555556997531 in Frecursive_edit () at keyboard.c:837
#17 0x0000555556989057 in main (argc=3, argv=0x7fffffffdee8)
    at emacs.c:2629
(gdb) bt full
#0  adjust_frame_size (f=0x6210000ede00, new_text_width=60,
    new_text_height=49, inhibit=5, pretend=false, parameter=XIL(0x40e0))
    at frame.c:646
        unit_width = 1458155539
        unit_height = 21845
        old_native_width = 1492352256
        old_native_height = 21845
        new_native_width = 0
        new_native_height = 0
        min_inner_width = -1
        min_inner_height = -1
        r = 0x7ffff4f2b8e4 <mcount+52>
        old_inner_width = -1
        old_inner_height = 32767
        new_inner_width = 0
        new_inner_height = 1718964966
        old_text_cols = 247
        old_text_lines = 0
        new_text_cols = 0
        new_text_lines = 0
        old_text_width = 0
        old_text_height = 0
        inhibit_horizontal = false
        inhibit_vertical = false
        frame = make_fixnum(23456254819227)
#1  0x00005555564e04c3 in change_frame_size_1 (f=0x6210000ede00,
    new_width=60, new_height=50, pretend=false, delay=false, safe=false)
    at dispnew.c:6054
No locals.
#2  0x00005555564e0526 in change_frame_size (f=0x6210000ede00,
--Type <RET> for more, q to quit, c to continue without paging--
    new_width=60, new_height=50, pretend=false, delay=false, safe=false)
    at dispnew.c:6087
        tail = <optimized out>
        frame = <optimized out>
#3  0x00005555564df618 in do_pending_window_change (safe=false)
    at dispnew.c:6014
        f = 0x6210000ede00
        tail = XIL(0x7fffec71a083)
        frame = XIL(0x6210000ede05)
#4  0x0000555556e479ee in wait_reading_process_output (time_limit=0,
[…]

> > (gdb) p *SELECTED_FRAME()
> > $102 = {
> >   header = {
> >     size = 4611686018595348501
> >   },
> [...]
> >   output_data = {
> >     tty = 0x0,
> >     x = 0x0,
> >     w32 = 0x0,
> >     ns = 0x0,
> >     pgtk = 0x0,
> >     haiku = 0x0,
> >     android = 0x0
> >   },
>
> This zero value exactly means this frame is "initial" it has not yet
> been set up as a terminal frame. That setup happens inside
> make-terminal-frame, here:
>
>   f = make_terminal_frame (t);
>
> and make_terminal_frame does
>
>   f->output_method = output_termcap;
>
> Somehow this frame escaped this code, so all kinds of weird things can
> happen with it.

It seems that under normal Emacs operation, the emacs daemon creates
an initial frame which has no output_data->tty.
In addition there are non-initial frames, created by
make_terminal_frame; one for each frame opened by the user.

Could it be that adjust_frame_size is wrongly running on the initial
frame, instead of on the terminal frame?
Mmm… apparently no, it's not that; I just verified it with the
commands below. There's the initial frame (F1) and the visible one
(F5), and f matches F5.

(gdb) list
641     */
642    void
643    adjust_frame_size (struct frame *f, int new_text_width, int
new_text_height,
644               int inhibit, bool pretend, Lisp_Object parameter)
645    {
646      int unit_width = FRAME_COLUMN_WIDTH (f);
647      int unit_height = FRAME_LINE_HEIGHT (f);
648      int old_native_width = FRAME_PIXEL_WIDTH (f);
649      int old_native_height = FRAME_PIXEL_HEIGHT (f);
650      int new_native_width, new_native_height;
(gdb) p f
$34 = (struct frame *) 0x6210000ede00
(gdb) p f->name
$35 = XIL(0x619000159144)
(gdb) xpr
Lisp_String
$36 = (struct Lisp_String *) 0x619000159140
"F5"
(gdb) p Vframe_list
$37 = XIL(0x7fffec71a083)
(gdb) xcons
$38 = (struct Lisp_Cons *) 0x7fffec71a080
{
  u = {
    s = {
      car = XIL(0x6210000ede05),
      u = {
        cdr = XIL(0x7ffff18f00f3),
        chain = 0x7ffff18f00f3
      }
    },
    gcaligned = 0x5
  }
}
(gdb) nextcons
$39 = XIL(0x7ffff18f00f3)
$40 = (struct Lisp_Cons *) 0x7ffff18f00f0
{
  u = {
    s = {
      car = XIL(0x621000003f05),
      u = {
        cdr = XIL(0),
        chain = 0x0
      }
    },
    gcaligned = 0x5
  }
}
(gdb) p 0x6210000ede05
$41 = 107820859973125
(gdb) xpr
Lisp_Vectorlike
PVEC_FRAME
$42 = (struct frame *) 0x6210000ede00
"F5"
No symbol "PVEC_TS_QUERY" in current context.
(gdb) p 0x621000003f05
$43 = 107820859014917
(gdb) xpr
Lisp_Vectorlike
PVEC_FRAME
$44 = (struct frame *) 0x621000003f00
"F1"
No symbol "PVEC_TS_QUERY" in current context.
(gdb)

> > By the way, C-x # isn't enough to close any Emacs frame. If I have
> > opened it by running   urxvtcd -e 'emacsclient' '-nw', then later C-x
> > # doesn't close it, it just says „No server buffers remain to edit“.
> > But C-x C-c does close it.
>
> Use "C-x 5 0" to delete the current frame.

Ok.
The problem reported in this bug report also happens if I use C-x 5 0