Fix segfault in some cases when restoring a selected window

* src/xdisp.c (restore_selected_window): Fix the more grave
problems caused by a function deleting the previously selected
frame or window (bug#39977).
This commit is contained in:
martin rudalics 2020-10-01 02:00:06 +02:00 committed by Lars Ingebrigtsen
parent 5d6e65d57a
commit aea7788b92

View file

@ -12400,12 +12400,12 @@ unwind_format_mode_line (Lisp_Object vector)
mode_line_string_face_prop = AREF (vector, 5);
/* Select window before buffer, since it may change the buffer. */
if (!NILP (old_window))
if (WINDOW_LIVE_P (old_window))
{
/* If the operation that we are unwinding had selected a window
on a different frame, reset its frame-selected-window. For a
text terminal, reset its top-frame if necessary. */
if (!NILP (target_frame_window))
if (WINDOW_LIVE_P (target_frame_window))
{
Lisp_Object frame
= WINDOW_FRAME (XWINDOW (target_frame_window));
@ -12422,7 +12422,7 @@ unwind_format_mode_line (Lisp_Object vector)
/* Restore point of target_frame_window's buffer (Bug#32777).
But do this only after old_window has been reselected to
avoid that the window point of target_frame_window moves. */
if (!NILP (target_frame_window))
if (WINDOW_LIVE_P (target_frame_window))
{
Lisp_Object buffer = AREF (vector, 10);
@ -12850,23 +12850,68 @@ update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run)
Tab-bars
***********************************************************************/
#ifdef HAVE_WINDOW_SYSTEM
/* Select `frame' temporarily without running all the code in
do_switch_frame.
FIXME: Maybe do_switch_frame should be trimmed down similarly
when `norecord' is set. */
/* Restore WINDOW as the selected window and its frame as the selected
frame. If WINDOW is dead but the selected frame is live, make the
latter's selected window the selected window. If both, WINDOW and
the selected frame, are dead, assign selected frame and window from
some arbitrary live frame. Abort if no such frame can be found. */
static void
fast_set_selected_frame (Lisp_Object frame)
restore_selected_window (Lisp_Object window)
{
if (!EQ (selected_frame, frame))
if (WINDOW_LIVE_P (window))
/* If WINDOW is live, make it the selected window and its frame's
selected window and set the selected frame to its frame. */
{
selected_frame = frame;
selected_window = XFRAME (frame)->selected_window;
selected_window = window;
selected_frame = XWINDOW (window)->frame;
FRAME_SELECTED_WINDOW (XFRAME (selected_frame)) = window;
}
else if (FRAMEP (selected_frame) && FRAME_LIVE_P (XFRAME (selected_frame)))
/* If WINDOW is dead but the selected frame is still live, make the
latter's selected window the selected one. */
selected_window = FRAME_SELECTED_WINDOW (XFRAME (selected_frame));
else
/* If WINDOW and the selected frame are dead, choose some live,
non-child and non-tooltip frame as the new selected frame and
make its selected window the selected window. */
{
Lisp_Object tail;
Lisp_Object frame UNINIT;
FOR_EACH_FRAME (tail, frame)
{
struct frame *f = XFRAME (frame);
if (!FRAME_PARENT_FRAME (f) && !FRAME_TOOLTIP_P (f))
{
selected_frame = frame;
selected_window = FRAME_SELECTED_WINDOW (f);
return;
}
}
/* Abort if we cannot find a live frame. */
emacs_abort ();
}
}
#endif /* HAVE_WINDOW_SYSTEM */
/* Restore WINDOW, if live, as its frame's selected window. */
static void
restore_frame_selected_window (Lisp_Object window)
{
if (WINDOW_LIVE_P (window))
/* If WINDOW is live, make it its frame's selected window. If that
frame is the selected frame, make WINDOW the selected window as
well. */
{
Lisp_Object frame = XWINDOW (window)->frame;
FRAME_SELECTED_WINDOW (XFRAME (frame)) = window;
if (EQ (frame, selected_frame))
selected_window = window;
}
}
/* Update the tab-bar item list for frame F. This has to be done
before we start to fill in any display lines. Called from
@ -12939,9 +12984,10 @@ update_tab_bar (struct frame *f, bool save_match_data)
XFRAME (selected_frame)->selected_window));
#ifdef HAVE_WINDOW_SYSTEM
Lisp_Object frame;
record_unwind_protect (fast_set_selected_frame, selected_frame);
record_unwind_protect (restore_selected_window, selected_window);
XSETFRAME (frame, f);
fast_set_selected_frame (frame);
selected_frame = frame;
selected_window = FRAME_SELECTED_WINDOW (f);
#endif
/* Build desired tab-bar items from keymaps. */
@ -13873,9 +13919,10 @@ update_tool_bar (struct frame *f, bool save_match_data)
/* Since we only explicitly preserve selected_frame,
check that selected_window would be redundant. */
XFRAME (selected_frame)->selected_window));
record_unwind_protect (fast_set_selected_frame, selected_frame);
record_unwind_protect (restore_selected_window, selected_window);
XSETFRAME (frame, f);
fast_set_selected_frame (frame);
selected_frame = frame;
selected_window = FRAME_SELECTED_WINDOW (f);
/* Build desired tool-bar items from keymaps. */
new_tool_bar
@ -25217,11 +25264,14 @@ static int
display_mode_lines (struct window *w)
{
Lisp_Object old_selected_window = selected_window;
Lisp_Object old_selected_frame = selected_frame;
Lisp_Object new_frame = w->frame;
Lisp_Object old_frame_selected_window = XFRAME (new_frame)->selected_window;
ptrdiff_t count = SPECPDL_INDEX ();
int n = 0;
record_unwind_protect (restore_selected_window, selected_window);
record_unwind_protect
(restore_frame_selected_window, XFRAME (new_frame)->selected_window);
if (window_wants_mode_line (w))
{
Lisp_Object window;
@ -25287,9 +25337,8 @@ display_mode_lines (struct window *w)
++n;
}
XFRAME (new_frame)->selected_window = old_frame_selected_window;
selected_frame = old_selected_frame;
selected_window = old_selected_window;
unbind_to (count, Qnil);
if (n > 0)
w->must_be_updated_p = true;
return n;