Subject: bug#68235: 29.1.90; Switching tabs stops following process output in selected window

From: martin rudalics <[email-masked]>
Date: Fri, 8 Mar 2024 10:21:04 +0100

> We could leave it called on every set-window-configuration
 > even with an empty window list, as a general post-hook.

I've now reworked the patch for the following reasons:

- Changing the existing behavior of 'window-state-put' in order to make
   it behave like 'set-window-configuration' in certain regards is a bad
   idea IMO.  Someone, somewhere might be used to the current behavior.

- The entire idea of deleting windows when their buffers are dead might
   annoy people who prefer a fixed frame layout with a predefined number
   of windows.

The new patch attached renames 'window-kept-windows-functions' to
`window-restore-dead-buffer-windows'.  That variable is no more a hook
but may be

- t to never delete a dead buffer window,

- 'delete' to do what 'window-state-put' does now

- 'dedicated' to do what 'set-window-configuration' does now

- nil to keep the current behavior

- a function to do what 'window-kept-windows-functions' did.

I abandoned the hook idea because having more than one function work on
a list of kept windows means asking for troubles.  In addition, I now
pass a third argument to that function - 'configuration' if called by
'set-window-configuration' and 'state' if called by 'window-state-put'.

When looking into this I (re)discovered that 'set-window-configuration'
leaves dead buffer windows alone if they do have a buffer in the
configuration that function is about to replace.  This comment

       /* Kludge Alert!
	 Mark all windows now on frame as "deleted".
	 Restoring the new configuration "undeletes" any that are in it.

	 Save their current buffers in their height fields, since we may
	 need it later, if a buffer saved in the configuration is now
	 dead.  */

tells how this was done in the past and these two snippets

       /* Since combination limit makes sense for an internal windows
	 only, we use this slot to save the buffer for the sake of
	 possible resurrection in Fset_window_configuration.  */
       wset_combination_limit (w, w->contents);

and

	  /* If we squirreled away the buffer, restore it now.  */
	  if (BUFFERP (w->combination_limit))
	    wset_buffer (w, w->combination_limit);

explain how this is done now.  (Calling delete_all_child_windows and
running SAFE_NALLOCA in 'set-window-configuration' is a bad idea given
the fact that the body of 'save-window-excursion' practically never
changes the existing configuration, but I won't discuss that here.)

Suffice it to say that 'set-window-configuration' leaves a dead buffer
window alone if it shows a buffer in the configuration it is about to
replace.  'window-state-put' does not do that because it has no idea of
the identity of windows and making it map the current window tree into
the window tree it is about to restore does not look like an appealing
endeavor to me (besides the fact that it would change existing behavior
again).

Now what I did was to add two more entries for each window passed to the
'window-restore-dead-buffer-windows' function:

- One for the dedicated status of the window in the saved configuration
   or state.  If you restore the old buffer, you should also set that, I
   suppose.

- One that is t if the window was live in the configuration replaced by
   'set-window-configuration'.  It might help to decide whether the new
   buffer would be a better alternative than restoring the dead buffer.
   I don't think the tab bar code would need it.

Have a look.

 > BTW, I didn't test yet what happens when
 > window-kept-windows-functions is non-nil but does nothing.
 > Here is what happens:
 >
 >    Error muted by safe_call: (get-scratch-buffer-create)
 >    signaled (wrong-type-argument window-live-p #<window 7>)
 >
 > But maybe this is ok.

I now made it call 'window-restore-dead-buffer-windows' iff it is a
function (it may still have the wrong number of arguments though).  BTW
the "Error muted by safe_call" messages are helpful - once you get used
to them.

martin

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index fe3dc573df5..951c898a6ee 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -6264,15 +6264,10 @@ Window Configurations
 @code{minibuffer-selected-window}.  In this case, the function returns
 @code{nil}.  Otherwise, it returns @code{t}.
 
-If the buffer of a window of @var{configuration} has been killed since
-@var{configuration} was made, that window is, as a rule, removed from
-the restored configuration.  However, if that window is the last window
-remaining in the restored configuration, another live buffer is shown in
-it.  Also, if the variable @var{window-kept-windows-functions} is
-non-@code{nil}, any window whose buffer is now dead is not deleted.
-Rather, this function will show another live buffer in that window and
-include an entry for that window when calling any function in
-@var{window-kept-windows-functions} (@pxref{Window Hooks}).
+This function consults the variable
+@code{window-restore-dead-buffer-windows} (see below) when it tries to
+restore a window whose buffer was killed after @var{configuration} was
+recorded.
 
 Here is a way of using this function to get the same effect as
 @code{save-window-excursion}:
@@ -6361,14 +6356,9 @@ Window Configurations
 frame before putting @var{state} into it.  If @var{window} is @code{nil},
 it puts the window state into a new window.
 
-If the buffer of any window recorded in @var{state} has been killed
-since @var{state} was made, that window is, as a rule, not restored.
-However, if that window is the only window in @var{state}, another live
-buffer will be shown in it.  Also, if the variable
-@var{window-kept-windows-functions} is non-@code{nil}, any window whose
-buffer is now dead is restored.  This function will show another live
-buffer in it and include an entry for that window when calling a
-function in @var{window-kept-windows-functions} (@pxref{Window Hooks}).
+This function consults the variable
+@code{window-restore-dead-buffer-windows} (see below) when it tries to
+restore a window whose buffer was killed after @var{state} was recorded.
 
 If the optional argument @var{ignore} is non-@code{nil}, it means to ignore
 minimum window sizes and fixed-size restrictions.  If @var{ignore}
@@ -6376,6 +6366,74 @@ Window Configurations
 and/or two columns.
 @end defun
 
+By default, @code{set-window-configuration} and @code{window-state-put}
+may delete a window from the restored configuration when they find out
+that its buffer was killed since the corresponding configuration or
+state has been recorded.  The variable described next can be used to
+fine-tune that behavior.
+
+@cindex restoring windows whose buffers are dead
+@defvar window-restore-dead-buffer-windows
+This variable specifies how @code{set-window-configuration} or
+@code{window-state-put} shall handle a window whose buffer has been
+killed since the corresponding configuration or state was made.  Any
+such window may be live - in which case it shows some other buffer - or
+dead at the time one of these functions is called.  As a rule,
+@code{set-window-configuration} leaves the window alone if it is live
+while @code{window-state-put} deletes it.
+
+The following values can be used to override the default behavior for
+dead windows in the case of @code{set-window-configuration} and for dead
+and live windows in the case of @code{window-state-put}.
+
+@table @asis
+@item @code{t}
+This value means to restore the window and show some other buffer in it.
+
+@item @code{delete}
+This means to unconditionally try to delete the window.
+
+@item @code{dedicated}
+This means to try to delete the window if and only if it is dedicated to
+its buffer.
+
+@item @code{nil}
+This is the default and means that @code{set-window-configuration} will
+try to delete the window if and only if it is dedicated to its buffer
+and @code{window-state-put} will unconditionally try to delete it.
+
+@item a function
+This means to restore the window, show some other buffer in it and add
+an entry for that window to a list that will be later passed as argument
+to that function.
+@end table
+
+If a window cannot be deleted (typically, because it is the last window
+on its frame), @code{set-window-configuration} and
+@code{window-state-put} will show another buffer in it.
+
+If the value of this variable is a function, that function should take
+three arguments.  The first argument specifies the frame whose windows
+have been restored.  The third argument is either the constant
+@code{configuration} if the windows are restored by
+@code{set-window-configuration} or the constant @code{state} if the
+windows are restored by @code{window-state-put}.
+
+The second argument specifies a list of entries for @emph{any} window
+whose previous buffer has been encountered dead at the time
+@code{set-window-configuration} or @code{window-state-put} tried to
+restore it in that window (minibuffer windows are excluded).  This means
+that the function specified by this variable may also delete windows
+encountered live by @code{set-window-configuration}.
+
+Each entry is a list of six values - the window whose buffer was found
+dead, the dead buffer or its name, the positions of start and point of
+the buffer in that window, the dedicated status of the window as
+previously reported by @code{window-dedicated-p} and a flag that is
+@code{t} if the window has been encountered live by
+@code{set-window-configuration} and @code{nil} otherwise.
+@end defvar
+
 The functions @code{window-state-get} and @code{window-state-put} also
 allow exchanging the contents of two live windows.  The following
 function does precisely that:
@@ -6636,27 +6694,6 @@ Window Hooks
 buffer are (re)fontified because a window was scrolled or its size
 changed.  @xref{Other Font Lock Variables}.
 
-@cindex window kept windows functions
-@defvar window-kept-windows-functions
-   This variable holds a list of functions that Emacs will call after
-restoring a window configuration via @code{set-window-configuration} or
-state via @code{window-state-put} (@pxref{Window Configurations}).  When
-the value of this variable is non-@code{nil}, these functions will not
-delete any window whose buffer has been killed since the corresponding
-configuration or state was saved, but show some live buffer in it.
-
-The value should be a list of functions that take two arguments.  The
-first argument specifies the frame whose windows have been restored.
-The second argument specifies a list of entries for each window whose
-buffer has been found dead at the time @code{set-window-configuration}
-or @code{window-state-put} tried to restore it.  Each entry is a list of
-four values - the window whose buffer was found dead, the dead buffer,
-and the last known positions of start and point of the buffer in that
-window.  Any function run by this hook should check that the window is
-live since another function run by this hook may have deleted it in the
-meantime.
-@end defvar
-
 @cindex window change functions
    The remainder of this section covers six hooks that are called
 during redisplay provided a significant, non-scrolling change of a
diff --git a/lisp/window.el b/lisp/window.el
index 29336f573f8..90094d020e4 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -6286,7 +6286,8 @@ window--state-put-2
       (when state
 	(let* ((old-buffer-or-name (car state))
 	       (buffer (get-buffer old-buffer-or-name))
-	       (state (cdr state)))
+	       (state (cdr state))
+	       (dedicated (cdr (assq 'dedicated state))))
 	  (if (buffer-live-p buffer)
 	      (with-current-buffer buffer
 		(set-window-buffer window buffer)
@@ -6345,7 +6346,7 @@ window--state-put-2
 				window delta t ignore nil nil nil pixelwise))
 		      (window-resize window delta t ignore pixelwise))))
 		;; Set dedicated status.
-		(set-window-dedicated-p window (cdr (assq 'dedicated state)))
+		(set-window-dedicated-p window dedicated)
 		;; Install positions (maybe we should do this after all
 		;; windows have been created and sized).
 		(ignore-errors
@@ -6388,12 +6389,14 @@ window--state-put-2
                                                  (set-marker (make-marker) m2
                                                              buffer))))))
                                    prev-buffers))))
-	    ;; We don't want to raise an error in case the buffer does
-	    ;; not exist anymore, so we switch to a previous one and
-	    ;; save the window with the intention of deleting it later
-	    ;; if possible.
-	    (switch-to-prev-buffer window)
-	    (if window-kept-windows-functions
+	    (unless (window-minibuffer-p window)
+	      ;; We don't want to raise an error in case the buffer does
+	      ;; not exist anymore, so we switch to a previous one and
+	      ;; save the window with the intention of deleting it later
+	      ;; if possible.
+	      (switch-to-prev-buffer window)
+	      (cond
+	       ((functionp window-restore-dead-buffer-windows)
 		(let* ((start (cdr (assq 'start state)))
 		       ;; Handle both - marker positions from writable
 		       ;; states and markers from non-writable states.
@@ -6404,9 +6407,15 @@ window--state-put-2
 		       (point-pos (if (markerp point)
 				      (marker-last-position point)
 				    point)))
-		  (push (list window old-buffer-or-name start-pos point-pos)
-			window-state-put-kept-windows))
-	      (push window window-state-put-stale-windows))))))))
+		  (push (list window old-buffer-or-name
+			      start-pos point-pos dedicated nil)
+			window-state-put-kept-windows)))
+	       ((or (and dedicated
+			 (eq window-restore-dead-buffer-windows 'dedicated))
+		    (memq window-restore-dead-buffer-windows '(nil delete)))
+		;; Try to delete the window.
+		(push window window-state-put-stale-windows)))
+	      (set-window-dedicated-p window nil))))))))
 
 (defun window-state-put (state &optional window ignore)
   "Put window state STATE into WINDOW.
@@ -6421,16 +6430,9 @@ window-state-put
 windows can get as small as `window-safe-min-height' and
 `window-safe-min-width'.
 
-If the abnormal hook `window-kept-windows-functions' is non-nil,
-do not delete any windows saved by STATE whose buffers were
-deleted since STATE was saved.  Rather, show some live buffer in
-them and call the functions in `window-kept-windows-functions'
-with a list of two arguments: the frame where STATE was put and a
-list of entries for each such window.  Each entry contains four
-elements - the window, its old buffer and the last positions of
-`window-start' and `window-point' for the buffer in that window.
-Always check the window for liveness because another function run
-by this hook may have deleted it."
+If this function tries to restore a non-minibuffer window whose buffer
+was killed since STATE was made, it will consult the variable
+`window-restore-dead-buffer-windows' on how to proceed."
   (setq window-state-put-stale-windows nil)
   (setq window-state-put-kept-windows nil)
 
@@ -6544,10 +6546,9 @@ window-state-put
 	  (when (and (window-valid-p window)
                      (eq (window-deletable-p window) t))
 	    (delete-window window))))
-      (when window-kept-windows-functions
-	(run-hook-with-args
-	 'window-kept-windows-functions
-	 frame window-state-put-kept-windows)
+      (when (functionp window-restore-dead-buffer-windows)
+	(funcall window-restore-dead-buffer-windows
+	 frame window-state-put-kept-windows 'state)
 	(setq window-state-put-kept-windows nil))
       (window--check frame))))
 
diff --git a/src/buffer.c b/src/buffer.c
index 126f3eb055a..e8daa93c2d9 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -327,6 +327,11 @@ bset_name (struct buffer *b, Lisp_Object val)
   b->name_ = val;
 }
 static void
+bset_last_name (struct buffer *b, Lisp_Object val)
+{
+  b->last_name_ = val;
+}
+static void
 bset_overwrite_mode (struct buffer *b, Lisp_Object val)
 {
   b->overwrite_mode_ = val;
@@ -647,6 +652,7 @@ DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 2, 0,
   name = Fcopy_sequence (buffer_or_name);
   set_string_intervals (name, NULL);
   bset_name (b, name);
+  bset_last_name (b, name);
 
   b->inhibit_buffer_hooks = !NILP (inhibit_buffer_hooks);
   bset_undo_list (b, SREF (name, 0) != ' ' ? Qnil : Qt);
@@ -866,6 +872,7 @@ DEFUN ("make-indirect-buffer", Fmake_indirect_buffer, Smake_indirect_buffer,
   name = Fcopy_sequence (name);
   set_string_intervals (name, NULL);
   bset_name (b, name);
+  bset_last_name (b, name);
 
   /* An indirect buffer shares undo list of its base (Bug#18180).  */
   bset_undo_list (b, BVAR (b->base_buffer, undo_list));
@@ -1282,6 +1289,16 @@ DEFUN ("buffer-name", Fbuffer_name, Sbuffer_name, 0, 1, 0,
   return BVAR (decode_buffer (buffer), name);
 }
 
+DEFUN ("buffer-last-name", Fbuffer_last_name, Sbuffer_last_name, 0, 1, 0,
+       doc: /* Return last name of BUFFER, as a string.
+BUFFER defaults to the current buffer.
+
+This is the last name of BUFFER before it has been renamed or killed. */)
+  (Lisp_Object buffer)
+{
+  return BVAR (decode_buffer (buffer), last_name);
+}
+
 DEFUN ("buffer-file-name", Fbuffer_file_name, Sbuffer_file_name, 0, 1, 0,
        doc: /* Return name of file BUFFER is visiting, or nil if none.
 No argument or nil as argument means use the current buffer.  */)
@@ -1652,6 +1669,7 @@ DEFUN ("rename-buffer", Frename_buffer, Srename_buffer, 1, 2,
   (register Lisp_Object newname, Lisp_Object unique)
 {
   register Lisp_Object tem, buf;
+  Lisp_Object oldname = BVAR (current_buffer, name);
   Lisp_Object requestedname = newname;
 
   CHECK_STRING (newname);
@@ -1669,12 +1687,12 @@ DEFUN ("rename-buffer", Frename_buffer, Srename_buffer, 1, 2,
       if (NILP (unique) && XBUFFER (tem) == current_buffer)
 	return BVAR (current_buffer, name);
       if (!NILP (unique))
-	newname = Fgenerate_new_buffer_name (newname,
-	                                     BVAR (current_buffer, name));
+	newname = Fgenerate_new_buffer_name (newname, oldname);
       else
 	error ("Buffer name `%s' is in use", SDATA (newname));
     }
 
+  bset_last_name (current_buffer, oldname);
   bset_name (current_buffer, newname);
 
   /* Catch redisplay's attention.  Unless we do this, the mode lines for
@@ -2087,6 +2105,7 @@ DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ",
      This gets rid of them for certain.  */
   reset_buffer_local_variables (b, 1);
 
+  bset_last_name (b, BVAR (b, name));
   bset_name (b, Qnil);
 
   block_input ();
@@ -4658,6 +4677,7 @@ init_buffer_once (void)
   /* These used to be stuck at 0 by default, but now that the all-zero value
      means Qnil, we have to initialize them explicitly.  */
   bset_name (&buffer_local_flags, make_fixnum (0));
+  bset_last_name (&buffer_local_flags, make_fixnum (0));
   bset_mark (&buffer_local_flags, make_fixnum (0));
   bset_local_var_alist (&buffer_local_flags, make_fixnum (0));
   bset_keymap (&buffer_local_flags, make_fixnum (0));
@@ -6018,6 +6038,7 @@ Functions (implicitly) running this hook are `get-buffer-create',
   defsubr (&Smake_indirect_buffer);
   defsubr (&Sgenerate_new_buffer_name);
   defsubr (&Sbuffer_name);
+  defsubr (&Sbuffer_last_name);
   defsubr (&Sbuffer_file_name);
   defsubr (&Sbuffer_base_buffer);
   defsubr (&Sbuffer_local_value);
diff --git a/src/buffer.h b/src/buffer.h
index 87ba2802b39..bbe1aeff668 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -309,6 +309,9 @@ #define BVAR(buf, field) ((buf)->field ## _)
   /* The name of this buffer.  */
   Lisp_Object name_;
 
+  /* The last name of this buffer before it was renamed or killed.  */
+  Lisp_Object last_name_;
+
   /* The name of the file visited in this buffer, or nil.  */
   Lisp_Object filename_;
 
diff --git a/src/window.c b/src/window.c
index ea761fad8bc..031019de7fe 100644
--- a/src/window.c
+++ b/src/window.c
@@ -7109,23 +7109,9 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
 the mini-window of the frame doesn't get set to the corresponding element
 of CONFIGURATION.
 
-Normally, this function will try to delete any dead window in
-CONFIGURATION whose buffer has been deleted since CONFIGURATION was
-made.  However, if the abnormal hook `window-kept-windows-functions' is
-non-nil, it will preserve such a window in the restored layout and show
-another buffer in it.
-
-After restoring the frame layout, this function runs the abnormal hook
-`window-kept-windows-functions' with two arguments - the frame whose
-layout it has restored and a list of entries for each window whose
-buffer has been found dead when it tried to restore CONFIGURATION: Each
-entry is a list of four elements <window, buffer, start, point> where
-`window' denotes the window whose buffer was found dead, `buffer'
-denotes the dead buffer, and `start' and `point' denote the last known
-positions of `window-start' and `window-point' of the buffer in that
-window.  Any function run by this hook should check such a window for
-liveness because another function run by this hook may have deleted it
-in the meantime."
+This function consults the variable `window-restore-dead-buffer-windows'
+when restoring a window whose buffer was killed after CONFIGURATION was
+recorded.
 
 If CONFIGURATION was made from a frame that is now deleted,
 only frame-independent values can be restored.  In this case,
@@ -7378,10 +7364,12 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
 		   BUF_PT (XBUFFER (w->contents)),
 		   BUF_PT_BYTE (XBUFFER (w->contents)));
 	      w->start_at_line_beg = true;
-	      if (!NILP (Vwindow_kept_windows_functions))
-		kept_windows = Fcons (list4 (window, p->buffer,
+	      if (FUNCTIONP (Vwindow_restore_dead_buffer_windows)
+		  && !MINI_WINDOW_P (w))
+		kept_windows = Fcons (listn (6, window, p->buffer,
 					     Fmarker_last_position (p->start),
-					     Fmarker_last_position (p->pointm)),
+					     Fmarker_last_position (p->pointm),
+					     p->dedicated, Qt),
 				      kept_windows);
 	    }
 	  else if (!NILP (w->start))
@@ -7398,16 +7386,25 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
 	      set_marker_restricted_both (w->pointm, w->contents, 0, 0);
 	      set_marker_restricted_both (w->old_pointm, w->contents, 0, 0);
 	      w->start_at_line_beg = true;
-	      if (!NILP (w->dedicated))
-		/* Record this window as dead.  */
-		dead_windows = Fcons (window, dead_windows);
-	      /* Make sure window is no more dedicated.  */
-	      wset_dedicated (w, Qnil);
-	      if (!NILP (Vwindow_kept_windows_functions))
-		kept_windows = Fcons (list4 (window, p->buffer,
-					     Fmarker_last_position (p->start),
-					     Fmarker_last_position (p->pointm)),
-				      kept_windows);
+	      if (!MINI_WINDOW_P (w))
+		{
+		  if (FUNCTIONP (Vwindow_restore_dead_buffer_windows))
+		    kept_windows
+		      = Fcons (listn (6, window, p->buffer,
+				      Fmarker_last_position (p->start),
+				      Fmarker_last_position (p->pointm),
+				      p->dedicated, Qnil),
+			       kept_windows);
+		  else if (EQ (Vwindow_restore_dead_buffer_windows, Qdelete)
+			   || (!NILP (p->dedicated)
+			       && (NILP (Vwindow_restore_dead_buffer_windows)
+				   || EQ (Vwindow_restore_dead_buffer_windows,
+					  Qdedicated))))
+		    /* Try to delete this window later.  */
+		    dead_windows = Fcons (window, dead_windows);
+		  /* Make sure window is no more dedicated.  */
+		  wset_dedicated (w, Qnil);
+		}
 	    }
 	}
 
@@ -7459,13 +7456,12 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
       unblock_input ();
 
       /* Scan dead buffer windows.  */
-      if (!NILP (Vwindow_kept_windows_functions))
-	for (; CONSP (dead_windows); dead_windows = XCDR (dead_windows))
-	  {
-	    window = XCAR (dead_windows);
-	    if (WINDOW_LIVE_P (window) && !EQ (window, FRAME_ROOT_WINDOW (f)))
-	      delete_deletable_window (window);
-	  }
+      for (; CONSP (dead_windows); dead_windows = XCDR (dead_windows))
+	{
+	  window = XCAR (dead_windows);
+	  if (WINDOW_LIVE_P (window) && !EQ (window, FRAME_ROOT_WINDOW (f)))
+	    delete_deletable_window (window);
+	}
 
       /* Record the selected window's buffer here.  The window should
 	 already be the selected one from the call above.  */
@@ -7513,9 +7509,9 @@ DEFUN ("set-window-configuration", Fset_window_configuration,
 
   SAFE_FREE ();
 
-  if (!NILP (Vrun_hooks) && !NILP (Vwindow_kept_windows_functions))
-    run_hook_with_args_2 (Qwindow_kept_windows_functions, frame,
-			  kept_windows);
+  if (!NILP (Vrun_hooks) && FUNCTIONP (Vwindow_restore_dead_buffer_windows))
+    safe_calln (Vwindow_restore_dead_buffer_windows,
+		frame, kept_windows, Qconfiguration);
 
   return FRAME_LIVE_P (f) ? Qt : Qnil;
 }
@@ -8514,8 +8510,9 @@ syms_of_window (void)
   DEFSYM (Qheader_line_format, "header-line-format");
   DEFSYM (Qtab_line_format, "tab-line-format");
   DEFSYM (Qno_other_window, "no-other-window");
-  DEFSYM (Qwindow_kept_windows_functions,
-	  "window-kept-windows-functions");
+  DEFSYM (Qconfiguration, "configuration");
+  DEFSYM (Qdelete, "delete");
+  DEFSYM (Qdedicated, "dedicated");
 
   DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function,
 	       doc: /* Non-nil means call as function to display a help buffer.
@@ -8673,27 +8670,59 @@ syms_of_window (void)
 call is performed with the frame temporarily selected.  */);
   Vwindow_configuration_change_hook = Qnil;
 
-  DEFVAR_LISP ("window-kept-windows-functions",
-	       Vwindow_kept_windows_functions,
-	       doc: /* Functions run after restoring a window configuration or state.
-These functions are called by `set-window-configuration' and
-`window-state-put'.  When the value of this variable is non-nil, these
-functions restore any window whose buffer has been deleted since the
-corresponding configuration or state was saved.  Rather than deleting
-such a window, `set-window-configuration' and `window-state-put' show
-some live buffer in it.
-
-The value should be a list of functions that take two arguments.  The
-first argument specifies the frame whose configuration has been
-restored.  The second argument, if non-nil, specifies a list of entries
-for each window whose buffer has been found dead at the time
-'set-window-configuration' or `window-state-put' tried to restore it in
-that window.  Each entry is a list of four values - the window whose
-buffer was found dead, the dead buffer, and the positions of start and
-point of the buffer in that window.  Note that the window may be already
-dead since another function on this list may have deleted it in the
-meantime.  */);
-  Vwindow_kept_windows_functions = Qnil;
+  DEFVAR_LISP ("window-restore-dead-buffer-windows",
+	       Vwindow_restore_dead_buffer_windows,
+	       doc: /* Control restoration of dead buffer windows.
+This variable specifies how the functions `set-window-configuration' and
+`window-state-put' shall handle a window whose buffer has been killed
+since the corresponding configuration or state was made.  Any such
+window may be live - in which case it shows some other buffer - or dead
+at the time one of these functions is called.
+
+As a rule, `set-window-configuration' leaves the window alone if it is
+live while `window-state-put' deletes it.  The following values can be
+used to override the default behavior for dead windows in the case of
+`set-window-configuration' and for dead and live windows in the case of
+`window-state-put'.
+
+- t means to restore the window and show some other buffer in it.
+
+- `delete' means to try to delete the window.
+
+- `dedicated' means to try to delete the window if and only if it is
+  dedicated to its buffer.
+
+- nil, the default, means that `set-window-configuration' will try to
+  delete the window if and only if it is dedicated to its buffer and
+  `window-state-put' will unconditionally try to delete it.
+
+- a function means to restore the window, show some other buffer in it
+  and add an entry for that window to a list that will be later passed
+  as argument to that function.
+
+If a window cannot be deleted (typically, because it is the last window
+on its frame), show another buffer in it.
+
+If the value is a function, it should take three arguments.  The first
+argument specifies the frame whose windows have been restored.  The
+third argument is the constant `configuration' if the windows are
+restored by `set-window-configuration' and the constant `state' if the
+windows are restored by `window-state-put'.
+
+The second argument specifies a list of entries for @emph{any} window
+whose previous buffer has been encountered dead at the time
+`set-window-configuration' or `window-state-put' tried to restore it in
+that window (minibuffer windows are excluded).  This means that the
+function specified by this variable may also delete windows encountered
+live by `set-window-configuration'.
+
+Each entry is a list of six values - the window whose buffer was found
+dead, the dead buffer or its name, the positions of start and point of
+the buffer in that window, the dedicated status of the window as
+reported by `window-dedicated-p' and a boolean - t if the window was
+live when `set-window-configuration' tried to restore it and nil
+otherwise.  */);
+  Vwindow_restore_dead_buffer_windows = Qnil;
 
   DEFVAR_LISP ("recenter-redisplay", Vrecenter_redisplay,
 	       doc: /* Non-nil means `recenter' redraws entire frame.