diff --git a/src/window.c b/src/window.c index a87b4834aa..7f616bc5ac 100644 --- a/src/window.c +++ b/src/window.c @@ -607,6 +607,121 @@ select_window_1 (Lisp_Object window, bool inhibit_point_swap) set_point_from_marker (XWINDOW (window)->pointm); } + +/** Temporarily select a window with minimum overhead. */ + +static Lisp_Object Vwith_window_selected_vector; + +static Lisp_Object +with_window_selected_unwind_data (Lisp_Object window) +{ + Lisp_Object vector, buffer; + struct frame *f = WINDOW_XFRAME (XWINDOW (window)); + + vector = Vwith_window_selected_vector; + Vwith_window_selected_vector = Qnil; + + if (NILP (vector)) + vector = make_nil_vector (4); + + /* Current buffer. To be restored if still alive. */ + XSETBUFFER (buffer, current_buffer); + ASET (vector, 0, buffer); + /* Selected window. To be restored if still alive. */ + ASET (vector, 1, selected_window); + /* Selected window of WINDOW's frame. To be restored if still + alive. */ + ASET (vector, 2, f->selected_window); + /* For a TTY frame save its top-frame. To be restored if still + alive. */ + if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + ASET (vector, 3, FRAME_TTY (f)->top_frame); + else + ASET (vector, 3, Qnil); + + return vector; +} + + +static void +unwind_with_window_selected (Lisp_Object vector) +{ + Lisp_Object buffer = AREF (vector, 0); + Lisp_Object window = AREF (vector, 1); + Lisp_Object frame_selected_window = AREF (vector, 2); + Lisp_Object top_frame = AREF (vector, 3); + + /* Restore things iff the window is still alive. */ + if (WINDOW_LIVE_P (window)) + { + struct window *w = XWINDOW (window); + Lisp_Object frame = WINDOW_FRAME (w); + struct frame *f = XFRAME (frame); + + /* Restore the frame's selected window if it's still alive. */ + if (WINDOW_LIVE_P (frame_selected_window)) + WINDOW_XFRAME (XWINDOW (frame_selected_window))->selected_window + = frame_selected_window; + + /* Make w->contents current before calling select_window_1 because + the latter sets PT from w->pointm. */ + set_buffer_internal_1 (XBUFFER (w->contents)); + select_window_1 (window, false); + + if (FRAMEP (top_frame) && FRAME_LIVE_P (XFRAME (top_frame))) + selected_frame = top_frame; + else + selected_frame = frame; + + f->selected_window = window; + } + + /* Restore current buffer if it's still alive. */ + if (BUFFER_LIVE_P (XBUFFER (buffer))) + set_buffer_internal_1 (XBUFFER (buffer)); + + Vwith_window_selected_vector = vector; +} + + +/** Canonical form to run something with WINDOW temporarily selected. + Note: This does not pop the unwind-protect stack and subsequently + call unwind_with_window_selected. Callers have to provide the + corresponding unbind_to form themselves. **/ +void +with_window_selected (Lisp_Object window) +{ + if (!EQ (window, selected_window) + || current_buffer != XBUFFER (XWINDOW (selected_window)->contents)) + { + struct window *w = XWINDOW (window); + Lisp_Object frame = WINDOW_FRAME (w); + struct frame *f = XFRAME (frame); + struct window *ow = XWINDOW (selected_window); + + record_unwind_protect (unwind_with_window_selected, + with_window_selected_unwind_data (window)); + + set_buffer_internal_1 (XBUFFER (w->contents)); + + if (BUFFERP (ow->contents)) + set_marker_both (ow->pointm, ow->contents, + BUF_PT (XBUFFER (ow->contents)), + BUF_PT_BYTE (XBUFFER (ow->contents))); + + selected_window = window; + + if (!NILP (XWINDOW (window)->pointm)) + set_point_from_marker (XWINDOW (window)->pointm); + + selected_frame = frame; + f->selected_window = window; + if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) + FRAME_TTY (f)->top_frame = frame; + } +} + + DEFUN ("select-window", Fselect_window, Sselect_window, 1, 2, 0, doc: /* Select WINDOW which must be a live window. Also make WINDOW's frame the selected frame and WINDOW that frame's @@ -8151,6 +8266,9 @@ init_window_once (void) minibuf_selected_window = Qnil; staticpro (&minibuf_selected_window); + Vwith_window_selected_vector = Qnil; + staticpro (&Vwith_window_selected_vector); + pdumper_do_now_and_after_late_load (init_window_once_for_pdumper); } diff --git a/src/window.h b/src/window.h index 7f7de58846..67f4bbd9e0 100644 --- a/src/window.h +++ b/src/window.h @@ -1192,6 +1192,7 @@ #define CHECK_LIVE_WINDOW(WINDOW) \ extern void temp_output_buffer_show (Lisp_Object); extern void replace_buffer_in_windows (Lisp_Object); extern void replace_buffer_in_windows_safely (Lisp_Object); +extern void with_window_selected (Lisp_Object); /* This looks like a setter, but it is a bit special. */ extern void wset_buffer (struct window *, Lisp_Object); extern bool window_outdated (struct window *); diff --git a/src/xdisp.c b/src/xdisp.c index 5ff54b2884..840437f76a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12967,20 +12967,17 @@ #define MODE_LINE_NOPROP_LEN(start) \ static Lisp_Object Vmode_line_unwind_vector; static Lisp_Object -format_mode_line_unwind_data (struct frame *target_frame, - struct buffer *obuf, - Lisp_Object owin, - bool save_proptrans) +format_mode_line_unwind_data (bool save_proptrans) { - Lisp_Object vector, tmp; + Lisp_Object vector; /* Reduce consing by keeping one vector in - Vwith_echo_area_save_vector. */ + Vmode_line_unwind_vector. */ vector = Vmode_line_unwind_vector; Vmode_line_unwind_vector = Qnil; if (NILP (vector)) - vector = make_nil_vector (12); + vector = make_nil_vector (6); ASET (vector, 0, make_fixnum (mode_line_target)); ASET (vector, 1, make_fixnum (MODE_LINE_NOPROP_LEN (0))); @@ -12989,44 +12986,12 @@ format_mode_line_unwind_data (struct frame *target_frame, ASET (vector, 4, mode_line_string_face); ASET (vector, 5, mode_line_string_face_prop); - if (obuf) - XSETBUFFER (tmp, obuf); - else - tmp = Qnil; - ASET (vector, 6, tmp); - ASET (vector, 7, owin); - if (target_frame) - { - Lisp_Object buffer = XWINDOW (target_frame->selected_window)->contents; - struct buffer *b = XBUFFER (buffer); - struct buffer *cb = current_buffer; - - /* Similarly to `with-selected-window', if the operation selects - a window on another frame, we must restore that frame's - selected window, and (for a tty) the top-frame. */ - ASET (vector, 8, target_frame->selected_window); - if (FRAME_TERMCAP_P (target_frame)) - ASET (vector, 9, FRAME_TTY (target_frame)->top_frame); - - /* If we select a window on another frame, make sure that that - selection does not leave its buffer's point modified when - unwinding (Bug#32777). */ - ASET (vector, 10, buffer); - current_buffer = b; - ASET (vector, 11, build_marker (current_buffer, PT, PT_BYTE)); - current_buffer = cb; - } - return vector; } static void unwind_format_mode_line (Lisp_Object vector) { - Lisp_Object old_window = AREF (vector, 7); - Lisp_Object target_frame_window = AREF (vector, 8); - Lisp_Object old_top_frame = AREF (vector, 9); - mode_line_target = XFIXNUM (AREF (vector, 0)); mode_line_noprop_ptr = mode_line_noprop_buf + XFIXNUM (AREF (vector, 1)); mode_line_string_list = AREF (vector, 2); @@ -13035,55 +13000,9 @@ unwind_format_mode_line (Lisp_Object vector) mode_line_string_face = AREF (vector, 4); mode_line_string_face_prop = AREF (vector, 5); - /* Select window before buffer, since it may change the buffer. */ - 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 (WINDOW_LIVE_P (target_frame_window)) - { - Lisp_Object frame - = WINDOW_FRAME (XWINDOW (target_frame_window)); - - if (!EQ (frame, WINDOW_FRAME (XWINDOW (old_window)))) - Fselect_window (target_frame_window, Qt); - - if (!NILP (old_top_frame) && !EQ (old_top_frame, frame)) - Fselect_frame (old_top_frame, Qt); - } - - Fselect_window (old_window, Qt); - - /* 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 (WINDOW_LIVE_P (target_frame_window)) - { - Lisp_Object buffer = AREF (vector, 10); - - if (BUFFER_LIVE_P (XBUFFER (buffer))) - { - struct buffer *cb = current_buffer; - - current_buffer = XBUFFER (buffer); - set_point_from_marker (AREF (vector, 11)); - ASET (vector, 11, Qnil); - current_buffer = cb; - } - } - } - - if (!NILP (AREF (vector, 6))) - { - set_buffer_internal_1 (XBUFFER (AREF (vector, 6))); - ASET (vector, 6, Qnil); - } - Vmode_line_unwind_vector = vector; } - /* Store a single character C for the frame title in mode_line_noprop_buf. Re-allocate mode_line_noprop_buf if necessary. */ @@ -13195,15 +13114,11 @@ gui_consider_frame_title (Lisp_Object frame) Bug#34317. */ specbind (Qinhibit_redisplay, Qt); - /* Switch to the buffer of selected window of the frame. Set up - mode_line_target so that display_mode_element will output into - mode_line_noprop_buf; then display the title. */ + with_window_selected (f->selected_window); + record_unwind_protect (unwind_format_mode_line, - format_mode_line_unwind_data - (NULL, current_buffer, Qnil, false)); + format_mode_line_unwind_data (false)); - set_buffer_internal_1 - (XBUFFER (XWINDOW (f->selected_window)->contents)); fmt = FRAME_ICONIFIED_P (f) ? Vicon_title_format : Vframe_title_format; mode_line_target = MODE_LINE_TITLE; @@ -13497,69 +13412,6 @@ update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run) Tab-bars ***********************************************************************/ -/* 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 -restore_selected_window (Lisp_Object window) -{ - 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_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 (); - } -} - -/* 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 prepare_menu_bars. If SAVE_MATCH_DATA, we must save @@ -13582,11 +13434,8 @@ update_tab_bar (struct frame *f, bool save_match_data) if (do_update) { - Lisp_Object window; - struct window *w; - - window = FRAME_SELECTED_WINDOW (f); - w = XWINDOW (window); + Lisp_Object window = FRAME_SELECTED_WINDOW (f); + struct window *w = XWINDOW (window); /* If the user has switched buffers or windows, we need to recompute to reflect the new bindings. But we'll @@ -13600,16 +13449,10 @@ update_tab_bar (struct frame *f, bool save_match_data) || update_mode_lines || window_buffer_changed (w)) { - struct buffer *prev = current_buffer; specpdl_ref count = SPECPDL_INDEX (); Lisp_Object new_tab_bar; int new_n_tab_bar; - /* Set current_buffer to the buffer of the selected - window of the frame, so that we get the right local - keymaps. */ - set_buffer_internal_1 (XBUFFER (w->contents)); - /* Save match data, if we must. */ if (save_match_data) record_unwind_save_match_data (); @@ -13621,20 +13464,8 @@ update_tab_bar (struct frame *f, bool save_match_data) specbind (Qoverriding_local_map, Qnil); } - /* We must temporarily set the selected frame to this frame - before calling tab_bar_items, because the calculation of - the tab-bar keymap uses the selected frame (see - `tab-bar-make-keymap' in tab-bar.el). */ - eassert (EQ (selected_window, - /* Since we only explicitly preserve selected_frame, - check that selected_window would be redundant. */ - XFRAME (selected_frame)->selected_window)); #ifdef HAVE_WINDOW_SYSTEM - Lisp_Object frame; - record_unwind_protect (restore_selected_window, selected_window); - XSETFRAME (frame, f); - selected_frame = frame; - selected_window = FRAME_SELECTED_WINDOW (f); + with_window_selected (window); #endif /* Build desired tab-bar items from keymaps. */ @@ -13657,7 +13488,6 @@ update_tab_bar (struct frame *f, bool save_match_data) } unbind_to (count, Qnil); - set_buffer_internal_1 (prev); } } } @@ -14495,11 +14325,8 @@ update_tool_bar (struct frame *f, bool save_match_data) if (do_update) { - Lisp_Object window; - struct window *w; - - window = FRAME_SELECTED_WINDOW (f); - w = XWINDOW (window); + Lisp_Object window = FRAME_SELECTED_WINDOW (f); + struct window *w = XWINDOW (window); /* If the user has switched buffers or windows, we need to recompute to reflect the new bindings. But we'll @@ -14513,16 +14340,10 @@ update_tool_bar (struct frame *f, bool save_match_data) || update_mode_lines || window_buffer_changed (w)) { - struct buffer *prev = current_buffer; specpdl_ref count = SPECPDL_INDEX (); - Lisp_Object frame, new_tool_bar; + Lisp_Object new_tool_bar; int new_n_tool_bar; - /* Set current_buffer to the buffer of the selected - window of the frame, so that we get the right local - keymaps. */ - set_buffer_internal_1 (XBUFFER (w->contents)); - /* Save match data, if we must. */ if (save_match_data) record_unwind_save_match_data (); @@ -14534,18 +14355,7 @@ update_tool_bar (struct frame *f, bool save_match_data) specbind (Qoverriding_local_map, Qnil); } - /* We must temporarily set the selected frame to this frame - before calling tool_bar_items, because the calculation of - the tool-bar keymap uses the selected frame (see - `tool-bar-make-keymap' in tool-bar.el). */ - eassert (EQ (selected_window, - /* Since we only explicitly preserve selected_frame, - check that selected_window would be redundant. */ - XFRAME (selected_frame)->selected_window)); - record_unwind_protect (restore_selected_window, selected_window); - XSETFRAME (frame, f); - selected_frame = frame; - selected_window = FRAME_SELECTED_WINDOW (f); + with_window_selected (window); /* Build desired tool-bar items from keymaps. */ new_tool_bar @@ -14567,7 +14377,6 @@ update_tool_bar (struct frame *f, bool save_match_data) } unbind_to (count, Qnil); - set_buffer_internal_1 (prev); } } } @@ -26075,15 +25884,8 @@ redisplay_mode_lines (Lisp_Object window, bool force) static int display_mode_lines (struct window *w) { - Lisp_Object old_selected_window = selected_window; - Lisp_Object new_frame = w->frame; - specpdl_ref 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; @@ -26101,12 +25903,6 @@ display_mode_lines (struct window *w) wset_mode_line_help_echo (w, Qnil); } - selected_frame = new_frame; - /* FIXME: If we were to allow the mode-line's computation changing the buffer - or window's point, then we'd need select_window_1 here as well. */ - XSETWINDOW (selected_window, w); - XFRAME (new_frame)->selected_window = selected_window; - /* These will be set while the mode line specs are processed. */ line_number_displayed = false; w->column_number_displayed = -1; @@ -26115,7 +25911,7 @@ display_mode_lines (struct window *w) { Lisp_Object window_mode_line_format = window_parameter (w, Qmode_line_format); - struct window *sel_w = XWINDOW (old_selected_window); + struct window *sel_w = XWINDOW (selected_window); /* Select mode line face based on the real selected window. */ display_mode_line (w, @@ -26150,8 +25946,6 @@ display_mode_lines (struct window *w) ++n; } - unbind_to (count, Qnil); - if (n > 0) w->must_be_updated_p = true; return n; @@ -26167,6 +25961,7 @@ display_mode_lines (struct window *w) static int display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) { + Lisp_Object window; struct it it; struct face *face; specpdl_ref count = SPECPDL_INDEX (); @@ -26191,9 +25986,11 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) made up of many separate strings. */ it.paragraph_embedding = L2R; + XSETWINDOW (window, w); + with_window_selected (window); + record_unwind_protect (unwind_format_mode_line, - format_mode_line_unwind_data (NULL, NULL, - Qnil, false)); + format_mode_line_unwind_data (false)); /* Temporarily make frame's keyboard the current kboard so that kboard-local variables in the mode_line_format will get the right @@ -26931,7 +26728,6 @@ DEFUN ("format-mode-line", Fformat_mode_line, Sformat_mode_line, struct it it; int len; struct window *w; - struct buffer *old_buffer = NULL; int face_id; bool no_props = FIXNUMP (face); specpdl_ref count = SPECPDL_INDEX (); @@ -26964,17 +26760,15 @@ DEFUN ("format-mode-line", Fformat_mode_line, Sformat_mode_line, : EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID : DEFAULT_FACE_ID; - old_buffer = current_buffer; + with_window_selected (window); /* Save things including mode_line_proptrans_alist, and set that to nil so that we don't alter the outer value. */ record_unwind_protect (unwind_format_mode_line, - format_mode_line_unwind_data - (XFRAME (WINDOW_FRAME (w)), - old_buffer, selected_window, true)); + format_mode_line_unwind_data (true)); mode_line_proptrans_alist = Qnil; - Fselect_window (window, Qt); + /* This should be covered by 'unwind_with_window_selected'. */ set_buffer_internal_1 (XBUFFER (buffer)); init_iterator (&it, w, -1, -1, NULL, face_id);