Subject: bug#7464: 24.0.50; mouse highlighting vanishes upon unsplitting window

From: Eli Zaretskii <[email-masked]>
Date: Wed, 28 Mar 2012 20:56:14 +0200

> From: Stephen Berman <[email-masked]>
> Cc: [email-masked],  [email-masked]
> Date: Sun, 25 Mar 2012 14:57:18 +0200
> 
> >> why do I not see the bug in Emacs 23?  (And why does Chong Yidong
> >> not see it in Emacs 24?)
> >
> > I don't know.  I can describe in detail what I saw in the debugger and
> > where, and then you and Chong could step through the same places and
> > see what, if anything, is different for you there.
> 
> It would interest me to try, when you have the time.

Here's what I see in GDB when I step through Emacs 24 code
(delete-other-windows-internal):

  (gdb) break Fdelete_other_windows_internal
  Breakpoint 3 at 0x11e73c0: file window.c, line 2572.
  (gdb) r -q
  Starting program: D:\usr\emacs\src/./oo/i386/emacs.exe -q
  [New Thread 5492.0x458]
  [New Thread 5492.0x824]
  [New Thread 5492.0x17e4]

  Breakpoint 3, Fdelete_other_windows_internal (window=56121349, root=53254170)
      at window.c:2572
  2572      w = decode_any_window (window);
  (gdb) n
  2573      XSETWINDOW (window, w);
  (gdb) p w->current_matrix
  $1 = (struct glyph_matrix *) 0x348c980
  (gdb) p w->current_matrix->rows
  $2 = (struct glyph_row *) 0x40e4000
  (gdb) p (w->current_matrix->rows+1)->mouse_face_p
  $3 = 1

Here we see the values of the window's current_matrix glyph matrix,
and the pointer to the first glyph row of the matrix.  We also see
that the second row (== screen line) has the mouse_face_p flag set,
which tells redisplay that some of this row's glyphs are shown in
mouse face.

Stepping further:

  (gdb) n
  2574      f = XFRAME (w->frame);
  (gdb)
  2576      if (NILP (root))
  (gdb)
  2579          root = FRAME_ROOT_WINDOW (f);
  (gdb)
  2580          r = XWINDOW (root);
  (gdb)
  2596      if (EQ (window, root))
  (gdb)
  2602      else if (MINI_WINDOW_P (w)) /* && top > 0) */
  (gdb)
  2605      if (!NILP (w->buffer))
  (gdb)
  2607          startpos = marker_position (w->start);
  (gdb)
  2608          top = WINDOW_TOP_EDGE_LINE (w)
  (gdb)
  2611          if (!EQ (window, FRAME_SELECTED_WINDOW (f)))
  (gdb)
  2650      BLOCK_INPUT;
  (gdb)
  2651      free_window_matrices (r);
  (gdb) p (w->current_matrix->rows+1)->mouse_face_p
  $4 = 1
  (gdb) p w->current_matrix
  $5 = (struct glyph_matrix *) 0x348c980
  (gdb) p w->current_matrix->rows
  $6 = (struct glyph_row *) 0x40e4000

Still the same values of the pointers.  But when we step over the call
to free_window_matrices, we get this:

  (gdb) n
  2653      windows_or_buffers_changed++;
  (gdb) p w->current_matrix
  $7 = (struct glyph_matrix *) 0x0

The glyph matrix was freed and its pointer is now NULL.  And then the
call to adjust_glyphs allocates a new glyph matrix, with different
pointers:

  (gdb) until 2772
  Fdelete_other_windows_internal (window=56121349, root=53328389)
      at window.c:2772
  2772      adjust_glyphs (f);
  (gdb) p w->current_matrix
  $10 = (struct glyph_matrix *) 0x0
  (gdb) n
  2773      UNBLOCK_INPUT;
  (gdb) p w->current_matrix
  $11 = (struct glyph_matrix *) 0x348c880
  (gdb) p w->current_matrix->rows
  $12 = (struct glyph_row *) 0x413b000

However, the mouse_face_p flag is of course reset in this newly
allocated matrix:

  (gdb) p (w->current_matrix->rows+1)->mouse_face_p
  $13 = 0

A similar picture is seen in Emacs 23.3 (I didn't have 23.4 where I
tried this, sorry):

  (gdb) break Fdelete_other_windows
  Breakpoint 3 at 0x10d5476: file window.c, line 2501.
  (gdb) r -q
  Starting program: D:\usr\emacs-23.3\src/./oo/i386/emacs.exe -q
  [New Thread 3240.0x11f8]
  [New Thread 3240.0x628]
  [New Thread 3240.0xe40]

  Breakpoint 3, Fdelete_other_windows (window=45307906) at window.c:2501
  2501      if (NILP (window))
  (gdb) n
  2502        window = selected_window;
  (gdb)
  2505      w = XWINDOW (window);
  (gdb)
  2507      startpos = marker_position (w->start);
  (gdb) p w
  $1 = (struct window *) 0x3824800
  (gdb) p $1->current_matrix
  $2 = (struct glyph_matrix *) 0x2f18c00
  (gdb) p $1->current_matrix->rows
  $3 = (struct glyph_row *) 0x2d6a000
  (gdb) p ($1->current_matrix->rows+1)->mouse_face_p
  $4 = 1

Again, the mouse_face_p flag is set on the second glyph row.

Now let's step into window_loop:

  (gdb) n
  2508      top = WINDOW_TOP_EDGE_LINE (w) - FRAME_TOP_MARGIN (XFRAME (WINDOW_FRAM
  E (w)));
  (gdb)
  2510      if (MINI_WINDOW_P (w) && top > 0)
  (gdb)
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) s
  window_loop (type=DELETE_OTHER_WINDOWS, obj=58869765, mini=0, frames=58867717)
      at window.c:2191
  2191      if (FRAMEP (frames))
  (gdb) n
  2192        f = XFRAME (frames);
  (gdb)
  2198      if (f)
  (gdb)
  2199        frame_arg = Qlambda;
  (gdb)
  2212      if (WINDOWP (obj))
  (gdb)
  2213        window = obj;
  (gdb)
  2219      windows = window_list_1 (window, mini ? Qt : Qnil, frame_arg);
  (gdb)
  2221      best_window = Qnil;
  (gdb)
  2223      for (; CONSP (windows); windows = XCDR (windows))
  (gdb)
  2227          window = XCAR (windows);
  (gdb)
  2228          w = XWINDOW (window);
  (gdb)
  2233          if (!MINI_WINDOW_P (w)
  (gdb)
  2237            switch (type)
  (gdb)
  2274                if (!EQ (window, obj))
  (gdb)
  2223      for (; CONSP (windows); windows = XCDR (windows))
  (gdb)
  2227          window = XCAR (windows);
  (gdb)
  2228          w = XWINDOW (window);
  (gdb)
  2233          if (!MINI_WINDOW_P (w)
  (gdb)
  2237            switch (type)
  (gdb)
  2274                if (!EQ (window, obj))
  (gdb)
  2275                  Fdelete_window (window);

And then into Fdelete_window:

  (gdb) s
  Fdelete_window (window=58900997) at window.c:1544
  1544      if (NILP (window))
  (gdb) n
  1547        CHECK_LIVE_WINDOW (window);
  (gdb) p $1
  $5 = (struct window *) 0x3824800
  (gdb) up 2
  #2  0x010d555d in Fdelete_other_windows (window=58869765) at window.c:2513
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) p $1->current_matrix
  $6 = (struct glyph_matrix *) 0x2f18c00
  (gdb) p $1->current_matrix->rows
  $7 = (struct glyph_row *) 0x2d6a000
  (gdb) down 2
  #0  Fdelete_window (window=58900997) at window.c:1547
  1547        CHECK_LIVE_WINDOW (window);
  (gdb) n
  1549      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
  (gdb)
  1550      delete_window (window);

And into delete_window:

  (gdb) s
  delete_window (window=58900997) at window.c:1569
  1569      CHECK_WINDOW (window);
  (gdb) n
  1570      p = XWINDOW (window);
  (gdb)
  1573      if (NILP (p->buffer)
  (gdb)
  1578      parent = p->parent;
  (gdb)
  1579      if (NILP (parent))
  (gdb)
  1581      par = XWINDOW (parent);
  (gdb) until 1654
  delete_window (window=58900997) at window.c:1654
  1654      free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)));

You can see that it is going to free the matrices of the frame's root
window.  This will, of course, free all the glyph matrices of all the
child windows on that frame.  Observe:

  (gdb) up 3
  #3  0x010d555d in Fdelete_other_windows (window=58869765) at window.c:2513
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) p w->current_matrix
  $8 = (struct glyph_matrix *) 0x2f18c00
  (gdb) p w->current_matrix->rows
  $9 = (struct glyph_row *) 0x2d6a000

Before the call to free_window_matrices, the pointers are still
intact.  Now...

  (gdb) down 3
  #0  delete_window (window=58900997) at window.c:1654
  1654      free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)));
  (gdb) n
  1656      tem = p->next;
  (gdb) up 3
  #3  0x010d555d in Fdelete_other_windows (window=58869765) at window.c:2513
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) p w->current_matrix
  $10 = (struct glyph_matrix *) 0x0

The matrix is a NULL pointer.  And later:

  (gdb) until 1758
  delete_window (window=58908165) at window.c:1758
  1758      adjust_glyphs (f);
  (gdb) up 3
  #3  0x010d555d in Fdelete_other_windows (window=58869765) at window.c:2513
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) p w->current_matrix
  $13 = (struct glyph_matrix *) 0x0
  (gdb) down 3
  #0  delete_window (window=58908165) at window.c:1758
  1758      adjust_glyphs (f);
  (gdb) n
  1759      UNBLOCK_INPUT;
  (gdb) up 3
  #3  0x010d555d in Fdelete_other_windows (window=58869765) at window.c:2513
  2513      window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_FRAME (w));
  (gdb) p w->current_matrix
  $14 = (struct glyph_matrix *) 0x3872000
  (gdb) p w->current_matrix->rows
  $15 = (struct glyph_row *) 0x39ee000

As in Emacs 24, the call to adjust_glyphs reallocates a new glyph
matrix for the window that is left on the frame, and the mouse_face_p
flag is zero in that matrix.

So, while the code was indeed rewritten, the problem that caused the
bug was not affected: freeing the glyph matrix of the window without
signaling to the display engine that it should recreate the mouse
highlight on the next redisplay cycle.