From 2edcc6d4223a2837e594ff83c13832533b5e3ef8 Mon Sep 17 00:00:00 2001 From: Pip Cet Date: Fri, 10 Apr 2026 11:35:22 +0000 Subject: [PATCH] Ensure selected_frame isn't the deleted frame (bug#80789) The previous code would sometimes exit the loop with frame1 set to the last considered frame, even if that wasn't a suitable frame to switch to. * src/frame.c (delete_frame): Reset 'frame1' in the loops if we don't match the break condition, so we don't think we've found a suitable replacement frame if we haven't. Error if, for some reason, that fails. --- src/frame.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/frame.c b/src/frame.c index d0d02eaeb1f..2c0a27cf47c 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2733,8 +2733,6 @@ delete_frame (Lisp_Object frame, Lisp_Object force) error ("Attempt to delete the only frame"); } - /* At this point, we are committed to deleting the frame. - There is no more chance for errors to prevent it. */ sf = SELECTED_FRAME (); /* Don't let the frame remain selected. */ if (f == sf) @@ -2754,7 +2752,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) frames on the same terminal as FRAME, excluding FRAME which we are about to delete. */ frame1 = safe_calln (Qget_mru_frame, Qvisible, Qnil, frame); - if (!NILP (frame1)) + if (FRAMEP (frame1)) { struct frame *f1 = XFRAME (frame1); @@ -2764,6 +2762,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force) || EQ (frame1, frame)) frame1 = Qnil; } + else + frame1 = Qnil; } if (NILP (frame1)) @@ -2783,6 +2783,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) && FRAME_TERMINAL (f) == FRAME_TERMINAL (f1) && FRAME_VISIBLE_P (f1)) break; + frame1 = Qnil; } /* If there is none, find *some* other frame. */ @@ -2805,6 +2806,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) } break; } + frame1 = Qnil; } } #ifdef NS_IMPL_COCOA @@ -2821,8 +2823,18 @@ delete_frame (Lisp_Object frame, Lisp_Object force) #endif } - do_switch_frame (frame1, 0, 1, Qnil); + if (FRAMEP (frame1) && XFRAME (frame1) != f) + do_switch_frame (frame1, 0, 1, Qnil); + else if (EQ (force, Qnoelisp)) + { + /* This is the last frame, and it's being forcibly + deleted. There's no way to recover from this. */ + Fkill_emacs (make_fixnum (70), Qnil); + } + else + emacs_abort (); sf = SELECTED_FRAME (); + eassert (sf != f); } } else @@ -2830,6 +2842,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force) frame. */ move_minibuffers_onto_frame (f, selected_frame, true); + /* At this point, we are committed to deleting the frame. + There is no more chance for errors to prevent it. */ /* Don't let echo_area_window to remain on a deleted frame. */ if (EQ (f->minibuffer_window, echo_area_window)) echo_area_window = sf->minibuffer_window;