Function: set-window-configuration

set-window-configuration is a function defined in window.c.

Signature

(set-window-configuration CONFIGURATION &optional DONT-SET-FRAME DONT-SET-MINIWINDOW)

Documentation

Set the configuration of windows and buffers as specified by CONFIGURATION.

CONFIGURATION must be a value previously returned by current-window-configuration (which see).

Normally, this function selects the frame of the CONFIGURATION, but if DONT-SET-FRAME is non-nil, it leaves selected the frame which was current at the start of the function. If DONT-SET-MINIWINDOW is non-nil, the mini-window of the frame doesn't get set to the corresponding element of CONFIGURATION.

This function consults the variable window-restore-killed-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, the return value is nil. Otherwise the value is t.

View in manual

Probably introduced at or before Emacs version 18.

Source Code

// Defined in /usr/src/emacs/src/window.c
// Skipping highlighting due to helpful-max-highlight.
{
  register struct save_window_data *data;
  struct Lisp_Vector *saved_windows;
  Lisp_Object new_current_buffer;
  Lisp_Object frame;
  Lisp_Object kept_windows = Qnil;
  Lisp_Object old_frame = selected_frame;
  struct frame *f;
  ptrdiff_t old_point = -1;
  USE_SAFE_ALLOCA;

  CHECK_WINDOW_CONFIGURATION (configuration);

  data = (struct save_window_data *) XVECTOR (configuration);
  saved_windows = XVECTOR (data->saved_windows);

  new_current_buffer = data->f_current_buffer;
  if (!BUFFER_LIVE_P (XBUFFER (new_current_buffer)))
    new_current_buffer = Qnil;
  else
    {
      if (XBUFFER (new_current_buffer) == current_buffer)
	/* The code further down "preserves point" by saving here PT in
	   old_point and then setting it later back into PT.  When the
	   current-selected-window and the final-selected-window both show
	   the current buffer, this suffers from the problem that the
	   current PT is the window-point of the current-selected-window,
	   while the final PT is the point of the final-selected-window, so
	   this copy from one PT to the other would end up moving the
	   window-point of the final-selected-window to the window-point of
	   the current-selected-window.  So we have to be careful which
	   point of the current-buffer we copy into old_point.  */
	if (EQ (XWINDOW (data->current_window)->contents, new_current_buffer)
	    && WINDOWP (selected_window)
	    && EQ (XWINDOW (selected_window)->contents, new_current_buffer)
	    && !EQ (selected_window, data->current_window))
	  old_point = marker_position (XWINDOW (data->current_window)->pointm);
	else
	  old_point = PT;
      else
	/* BUF_PT (XBUFFER (new_current_buffer)) gives us the position of
	   point in new_current_buffer as of the last time this buffer was
	   used.  This can be non-deterministic since it can be changed by
	   things like jit-lock by mere temporary selection of some random
	   window that happens to show this buffer.
	   So if possible we want this arbitrary choice of "which point" to
	   be the one from the to-be-selected-window so as to prevent this
	   window's cursor from being copied from another window.  */
	if (EQ (XWINDOW (data->current_window)->contents, new_current_buffer)
	    /* If current_window = selected_window, its point is in BUF_PT.  */
	    && !EQ (selected_window, data->current_window))
	  old_point = marker_position (XWINDOW (data->current_window)->pointm);
	else
	  old_point = BUF_PT (XBUFFER (new_current_buffer));
    }

  frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
  f = XFRAME (frame);

  /* If f is a dead frame, don't bother rebuilding its window tree.
     However, there is other stuff we should still try to do below.  */
  if (FRAME_LIVE_P (f))
    {
      Lisp_Object window;
      Lisp_Object dead_windows = Qnil;
      Lisp_Object tem, par, pers;
      struct window *w;
      struct saved_window *p;
      struct window *root_window;
      struct window **leaf_windows;
      ptrdiff_t i, k, n_leaf_windows;

      /* Don't do this within the main loop below: This may call Lisp
	 code and is thus potentially unsafe while input is blocked.  */
      for (k = 0; k < saved_windows->header.size; k++)
	{
	  p = SAVED_WINDOW_N (saved_windows, k);
	  window = p->window;
	  w = XWINDOW (window);

	  if (BUFFERP (w->contents)
	      && !EQ (w->contents, p->buffer)
	      && BUFFER_LIVE_P (XBUFFER (p->buffer))
	      && (NILP (Fminibufferp (p->buffer, Qnil))))
	    /* If a window we restore gets another buffer, record the
	       window's old buffer.  */
	    call1 (Qrecord_window_buffer, window);
	}

      /* Disallow set_window_size_hook, temporarily.  */
      f->can_set_window_size = false;
      /* The mouse highlighting code could get screwed up
	 if it runs during this.  */
      block_input ();

      /* "Swap out" point from the selected window's buffer
	 into the window itself.  (Normally the pointm of the selected
	 window holds garbage.)  We do this now, before
	 restoring the window contents, and prevent it from
	 being done later on when we select a new window.  */
      if (! NILP (XWINDOW (selected_window)->contents))
	{
	  w = XWINDOW (selected_window);
	  set_marker_both (w->pointm,
			   w->contents,
			   BUF_PT (XBUFFER (w->contents)),
			   BUF_PT_BYTE (XBUFFER (w->contents)));
	}

      fset_redisplay (f);

      /* Problem: Freeing all matrices and later allocating them again
	 is a serious redisplay flickering problem.  What we would
	 really like to do is to free only those matrices not reused
	 below.  */
      root_window = XWINDOW (FRAME_ROOT_WINDOW (f));
      ptrdiff_t nwindows = count_windows (root_window);
      SAFE_NALLOCA (leaf_windows, 1, nwindows);
      n_leaf_windows = get_leaf_windows (root_window, leaf_windows, 0);

      /* 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.  */
      delete_all_child_windows (FRAME_ROOT_WINDOW (f));

      for (k = 0; k < saved_windows->header.size; k++)
	{
	  p = SAVED_WINDOW_N (saved_windows, k);
	  window = p->window;
	  w = XWINDOW (window);
	  wset_next (w, Qnil);

	  if (!NILP (p->parent))
	    wset_parent
	      (w, SAVED_WINDOW_N (saved_windows, XFIXNAT (p->parent))->window);
	  else
	    wset_parent (w, Qnil);

	  if (!NILP (p->prev))
	    {
	      wset_prev
		(w, SAVED_WINDOW_N (saved_windows, XFIXNAT (p->prev))->window);
	      wset_next (XWINDOW (w->prev), p->window);
	    }
	  else
	    {
	      wset_prev (w, Qnil);
	      if (!NILP (w->parent))
		wset_combination (XWINDOW (w->parent),
				  (XFIXNUM (p->total_cols)
				   != XWINDOW (w->parent)->total_cols),
				  p->window);
	    }

	  /* If we squirreled away the buffer, restore it now.  */
	  if (BUFFERP (w->combination_limit))
	    wset_buffer (w, w->combination_limit);
	  w->pixel_left = XFIXNAT (p->pixel_left);
	  w->pixel_top = XFIXNAT (p->pixel_top);
	  w->pixel_width = XFIXNAT (p->pixel_width);
	  w->pixel_height = XFIXNAT (p->pixel_height);
	  w->left_col = XFIXNAT (p->left_col);
	  w->top_line = XFIXNAT (p->top_line);
	  w->total_cols = XFIXNAT (p->total_cols);
	  w->total_lines = XFIXNAT (p->total_lines);
	  wset_normal_cols (w, p->normal_cols);
	  wset_normal_lines (w, p->normal_lines);
	  w->hscroll = XFIXNAT (p->hscroll);
	  w->suspend_auto_hscroll = !NILP (p->suspend_auto_hscroll);
	  w->min_hscroll = XFIXNAT (p->min_hscroll);
	  w->hscroll_whole = XFIXNAT (p->hscroll_whole);
	  w->vscroll = -XFIXNAT (p->vscroll);
	  wset_display_table (w, p->display_table);
	  w->left_margin_cols = XFIXNUM (p->left_margin_cols);
	  w->right_margin_cols = XFIXNUM (p->right_margin_cols);
	  w->left_fringe_width = XFIXNUM (p->left_fringe_width);
	  w->right_fringe_width = XFIXNUM (p->right_fringe_width);
	  w->fringes_outside_margins = !NILP (p->fringes_outside_margins);
	  w->fringes_persistent = !NILP (p->fringes_persistent);
	  w->scroll_bar_width = XFIXNUM (p->scroll_bar_width);
	  w->scroll_bar_height = XFIXNUM (p->scroll_bar_height);
	  w->scroll_bars_persistent = !NILP (p->scroll_bars_persistent);
	  wset_vertical_scroll_bar_type (w, p->vertical_scroll_bar_type);
	  wset_horizontal_scroll_bar_type (w, p->horizontal_scroll_bar_type);
	  wset_dedicated (w, p->dedicated);
	  wset_combination_limit (w, p->combination_limit);
	  /* Restore any window parameters that have been saved.
	     Parameters that have not been saved are left alone.  */
	  for (tem = p->window_parameters; CONSP (tem); tem = XCDR (tem))
	    {
	      pers = XCAR (tem);
	      if (CONSP (pers))
		{
		  if (NILP (XCDR (pers)))
		    {
		      par = Fassq (XCAR (pers), w->window_parameters);
		      if (CONSP (par) && !NILP (XCDR (par)))
			/* Reset a parameter to nil if and only if it
			   has a non-nil association.  Don't make new
			   associations.  */
			Fsetcdr (par, Qnil);
		    }
		  else
		    /* Always restore a non-nil value.  */
		    Fset_window_parameter (window, XCAR (pers), XCDR (pers));
		}
	    }

	  if ((NILP (dont_set_miniwindow) || !MINI_WINDOW_P (w))
	      && BUFFERP (p->buffer) && BUFFER_LIVE_P (XBUFFER (p->buffer)))
	    /* If saved buffer is alive, install it, unless it's a
	       minibuffer we explicitly prohibit.  */
	    {
	      wset_buffer (w, p->buffer);
	      w->start_at_line_beg = !NILP (p->start_at_line_beg);
	      set_marker_restricted (w->start, p->start, w->contents);
	      set_marker_restricted (w->pointm, p->pointm, w->contents);
	      set_marker_restricted (w->old_pointm, p->old_pointm, w->contents);
	      /* As documented in Fcurrent_window_configuration, don't
		 restore the location of point in the buffer which was
		 current when the window configuration was recorded.  */
	      if (!EQ (p->buffer, new_current_buffer)
		  && XBUFFER (p->buffer) == current_buffer)
		Fgoto_char (w->pointm);
	    }
	  else if (BUFFERP (w->contents) && BUFFER_LIVE_P (XBUFFER (w->contents)))
	    /* Keep window's old buffer; make sure the markers are real.  */
	    {
	      /* Set window markers at start of visible range.  */
	      if (XMARKER (w->start)->buffer == 0)
		set_marker_restricted_both (w->start, w->contents, 0, 0);
	      if (XMARKER (w->pointm)->buffer == 0)
		set_marker_restricted_both
		  (w->pointm, w->contents,
		   BUF_PT (XBUFFER (w->contents)),
		   BUF_PT_BYTE (XBUFFER (w->contents)));
	      if (XMARKER (w->old_pointm)->buffer == 0)
		set_marker_restricted_both
		  (w->old_pointm, w->contents,
		   BUF_PT (XBUFFER (w->contents)),
		   BUF_PT_BYTE (XBUFFER (w->contents)));
	      w->start_at_line_beg = true;
	      if (FUNCTIONP (window_restore_killed_buffer_windows)
		  && !MINI_WINDOW_P (w))
		kept_windows = Fcons (listn (6, window, p->buffer,
					     Fmarker_last_position (p->start),
					     Fmarker_last_position (p->pointm),
					     p->dedicated, Qt),
				      kept_windows);
	    }
	  else if (!NILP (w->start))
	    /* Leaf window has no live buffer, get one.  */
	    {
	      /* Get the buffer via other_buffer_safely in order to
		 avoid showing an unimportant buffer and, if necessary, to
		 recreate *scratch* in the course (part of Juanma's bs-show
		 scenario from March 2011).  */
	      wset_buffer (w, other_buffer_safely (Fcurrent_buffer ()));
	      /* This will set the markers to beginning of visible
		 range.  */
	      set_marker_restricted_both (w->start, w->contents, 0, 0);
	      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 (!MINI_WINDOW_P (w))
		{
		  if (FUNCTIONP (window_restore_killed_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 (window_restore_killed_buffer_windows, Qdelete)
			   || (!NILP (p->dedicated)
			       && (NILP (window_restore_killed_buffer_windows)
				   || EQ (window_restore_killed_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);
		}
	    }
	}

      fset_root_window (f, data->root_window);
      /* Arrange *not* to restore point in the buffer that was
	 current when the window configuration was saved.  */
      if (EQ (XWINDOW (data->current_window)->contents, new_current_buffer))
	set_marker_restricted (XWINDOW (data->current_window)->pointm,
			       make_fixnum (old_point),
			       XWINDOW (data->current_window)->contents);

      /* In the following call to select_window, prevent "swapping out
	 point" in the old selected window using the buffer that has
	 been restored into it.  We already swapped out that point
	 from that window's old buffer.

	 Do not record the buffer here.  We do that in a separate call
	 to select_window below.  See also Bug#16207.  */
      select_window (data->current_window, Qt, true);
      BVAR (XBUFFER (XWINDOW (selected_window)->contents),
	    last_selected_window)
	= selected_window;

      /* We may have deleted windows above.  Then again, maybe we
	 haven't: the functions we call to maybe delete windows can
	 decide a window cannot be deleted.  Force recalculation of
	 Vwindow_list next time it is needed, to make sure stale
	 windows with no buffers don't escape into the wild, which
	 will cause crashes elsewhere.  */
      Vwindow_list = Qnil;

      if (NILP (data->focus_frame)
	  || (FRAMEP (data->focus_frame)
	      && FRAME_LIVE_P (XFRAME (data->focus_frame))))
	Fredirect_frame_focus (frame, data->focus_frame);

      /* Now, free glyph matrices in windows that were not reused.  */
      for (i = 0; i < n_leaf_windows; i++)
	if (NILP (leaf_windows[i]->contents))
	  free_window_matrices (leaf_windows[i]);

      /* Allow set_window_size_hook again and resize frame's windows
	 if necessary.  But change frame size only to preserve window
	 minimum sizes.  */
      f->can_set_window_size = true;
      adjust_frame_size (f, -1, -1, 4, false, Qset_window_configuration);

      adjust_frame_glyphs (f);
      unblock_input ();

      /* Scan dead buffer windows.  */
      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.  */
      if (WINDOW_LIVE_P (data->current_window))
	select_window (data->current_window, Qnil, false);

      /* select_window will have made f the selected frame, so we
	 reselect the proper frame here.  do_switch_frame will change
	 the selected window too, but that doesn't make the call to
	 select_window above totally superfluous; it still sets f's
	 selected window.  */
      if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
	do_switch_frame (NILP (dont_set_frame)
                         ? data->selected_frame
                         : old_frame
                         , 0, 0, Qnil);
    }

  FRAME_WINDOW_CHANGE (f) = true;

  if (!NILP (new_current_buffer))
    {
      Fset_buffer (new_current_buffer);
      /* If the new current buffer doesn't appear in the selected
	 window, go to its old point (Bug#12208).

	 The original fix used data->current_window below which caused
	 false positives (compare Bug#31695) when data->current_window
	 is not on data->selected_frame.  This happens, for example,
	 when read_minibuf restores the configuration of a stand-alone
	 minibuffer frame: After switching to the previously selected
	 "normal" frame, point of that frame's selected window jumped
	 unexpectedly because new_current_buffer is usually *not*
	 shown in data->current_window - the minibuffer frame's
	 selected window.  Using selected_window instead fixes this
	 because do_switch_frame has set up selected_window already to
	 the "normal" frame's selected window and that window *does*
	 show new_current_buffer.  */
      if (!EQ (XWINDOW (selected_window)->contents, new_current_buffer))
	Fgoto_char (make_fixnum (old_point));
    }

  Vminibuf_scroll_window = data->minibuf_scroll_window;
  minibuf_selected_window = data->minibuf_selected_window;

  SAFE_FREE ();

  if (!NILP (Vrun_hooks) && FUNCTIONP (window_restore_killed_buffer_windows))
    safe_calln (window_restore_killed_buffer_windows,
		frame, kept_windows, Qconfiguration);

  return FRAME_LIVE_P (f) ? Qt : Qnil;
}