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.
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. */
calln (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));
}
}
/* Remove window from the table of dead windows. */
Fremhash (make_fixnum (w->sequence_number),
window_dead_windows_table);
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. */
{
if (!EQ (w->contents, p->buffer))
{
wset_buffer (w, p->buffer);
window_discard_buffer_from_window (w->contents, window, false);
}
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 (list (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 ()));
window_discard_buffer_from_window (w->contents, window, false);
/* 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 (list (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;
}