Mercurial > hg > xemacs-beta
diff src/window.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/window.c Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,5672 @@ +/* Window creation, deletion and examination for XEmacs. + Copyright (C) 1985-1987, 1992-1995 Free Software Foundation, Inc. + Copyright (C) 1994, 1995 Board of Trustees, University of Illinois. + Copyright (C) 1995, 1996 Ben Wing. + Copyright (C) 1996 Chuck Thompson. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: FSF 19.30. */ +/* Beginning to diverge significantly. */ + +/* This file has been Mule-ized. */ + +#include <config.h> +#include "lisp.h" + +#include "buffer.h" +#include "faces.h" +#include "frame.h" +#include "objects.h" +#include "glyphs.h" +#include "redisplay.h" +#include "window.h" +#include "commands.h" + +Lisp_Object Qwindowp, Qwindow_live_p; +Lisp_Object Qwindow_configurationp; +Lisp_Object Qscroll_up; +Lisp_Object Qscroll_down; +Lisp_Object Qtemp_buffer_show_hook; +Lisp_Object Qdisplay_buffer; + +#ifdef MEMORY_USAGE_STATS +Lisp_Object Qface_cache; +Lisp_Object Qglyph_cache; +Lisp_Object Qline_start_cache; +#ifdef HAVE_SCROLLBARS +Lisp_Object Qscrollbar_instances; +#endif +Lisp_Object Qother_redisplay; +#endif + +static int window_pixel_width_to_char_width (struct window *w, + int pixel_width, + int include_margins_p); +static int window_char_width_to_pixel_width (struct window *w, + int char_width, + int include_margins_p); +static int window_pixel_height_to_char_height (struct window *w, + int pixel_height, + int include_gutters_p); +static int window_char_height_to_pixel_height (struct window *w, + int char_height, + int include_gutters_p); +static void change_window_height (struct window *w, int delta, int widthflag); + + +#ifdef ENERGIZE +extern void energize_buffer_shown_hook (); +extern void energize_buffer_hidden_hook (); +extern void energize_window_selected_hook (); +extern void energize_window_deselected_hook (); +extern Lisp_Object desired_psheet_buffer (); +extern void make_psheets_desired (); +#endif + +/* Thickness of shadow border around 3d modelines. */ +Lisp_Object Vmodeline_shadow_thickness; + +/* Whether a modeline should be displayed. */ +Lisp_Object Vhas_modeline_p; + +/* Scroll if point lands on the bottom line and that line is partially + clipped. */ +int scroll_on_clipped_lines; + +/* The minibuffer window of the selected frame. + Note that you cannot test for minibufferness of an arbitrary window + by comparing against this; but you can test for minibufferness of + the selected window. */ +Lisp_Object minibuf_window; + +/* Non-nil means it is the window for C-M-v to scroll + when the minibuffer is selected. */ +Lisp_Object Vminibuf_scroll_window; + +/* Non-nil means this is the buffer whose window C-M-v should scroll. */ +Lisp_Object Vother_window_scroll_buffer; + +/* Non-nil means it's function to call to display temp buffers. */ +Lisp_Object Vtemp_buffer_show_function; + +Lisp_Object Vtemp_buffer_show_hook; + +/* If a window gets smaller than either of these, it is removed. */ +int window_min_height; +int window_min_width; + +/* Hook run at end of temp_output_buffer_show. */ +Lisp_Object Qtemp_buffer_show_hook; + +/* Number of lines of continuity in scrolling by screenfuls. */ +int next_screen_context_lines; + +/* List of freed window configurations with 1 - 10 windows. */ +Lisp_Object Vwindow_configuration_free_list[10]; + +#define SET_LAST_MODIFIED(w, cache_too) \ +do { \ + (w)->last_modified[CURRENT_DISP] = Qzero; \ + (w)->last_modified[DESIRED_DISP] = Qzero; \ + (w)->last_modified[CMOTION_DISP] = Qzero; \ + if (cache_too) \ + (w)->line_cache_last_updated = Qzero; \ +} while (0) + +#define SET_LAST_FACECHANGE(w) \ +do { \ + (w)->last_facechange[CURRENT_DISP] = Qzero; \ + (w)->last_facechange[DESIRED_DISP] = Qzero; \ + (w)->last_facechange[CMOTION_DISP] = Qzero; \ +} while (0) + + +static Lisp_Object mark_window (Lisp_Object, void (*) (Lisp_Object)); +static void print_window (Lisp_Object, Lisp_Object, int); +static void finalize_window (void *header, int for_disksave); +DEFINE_LRECORD_IMPLEMENTATION ("window", window, + mark_window, print_window, finalize_window, + 0, 0, struct window); + +#define MARK_DISP_VARIABLE(field) \ + ((markobj) (window->field[CURRENT_DISP])); \ + ((markobj) (window->field[DESIRED_DISP])); \ + ((markobj) (window->field[CMOTION_DISP])); + +static Lisp_Object +mark_window (Lisp_Object obj, void (*markobj) (Lisp_Object)) +{ + struct window *window = XWINDOW (obj); + ((markobj) (window->frame)); + ((markobj) (window->mini_p)); + ((markobj) (window->next)); + ((markobj) (window->prev)); + ((markobj) (window->hchild)); + ((markobj) (window->vchild)); + ((markobj) (window->parent)); + ((markobj) (window->buffer)); + MARK_DISP_VARIABLE (start); + MARK_DISP_VARIABLE (pointm); + ((markobj) (window->sb_point)); /* #### move to scrollbar.c? */ + ((markobj) (window->use_time)); + MARK_DISP_VARIABLE (last_modified); + MARK_DISP_VARIABLE (last_point); + MARK_DISP_VARIABLE (last_start); + MARK_DISP_VARIABLE (last_facechange); + ((markobj) (window->line_cache_last_updated)); + ((markobj) (window->redisplay_end_trigger)); + /* Cached specifiers: */ + ((markobj) (window->display_table)); + ((markobj) (window->has_modeline_p)); + ((markobj) (window->modeline_shadow_thickness)); +#ifdef HAVE_SCROLLBARS + ((markobj) (window->scrollbar_width)); + ((markobj) (window->scrollbar_height)); + ((markobj) (window->scrollbar_pointer)); +#endif + ((markobj) (window->left_margin_width)); + ((markobj) (window->right_margin_width)); +#ifdef HAVE_TOOLBARS + ((markobj) (window->toolbar[0])); + ((markobj) (window->toolbar[1])); + ((markobj) (window->toolbar[2])); + ((markobj) (window->toolbar[3])); + ((markobj) (window->toolbar_size[0])); + ((markobj) (window->toolbar_size[1])); + ((markobj) (window->toolbar_size[2])); + ((markobj) (window->toolbar_size[3])); + ((markobj) (window->toolbar_visible_p[0])); + ((markobj) (window->toolbar_visible_p[1])); + ((markobj) (window->toolbar_visible_p[2])); + ((markobj) (window->toolbar_visible_p[3])); + ((markobj) (window->toolbar_buttons_captioned_p)); + ((markobj) (window->default_toolbar)); + ((markobj) (window->default_toolbar_height)); + ((markobj) (window->default_toolbar_width)); + ((markobj) (window->default_toolbar_visible_p)); +#endif + ((markobj) (window->minimum_line_ascent)); + ((markobj) (window->minimum_line_descent)); + ((markobj) (window->use_left_overflow)); + ((markobj) (window->use_right_overflow)); +#ifdef HAVE_MENUBARS + ((markobj) (window->menubar_visible_p)); +#endif + ((markobj) (window->text_cursor_visible_p)); + /* End cached specifiers. */ + ((markobj) (window->dedicated)); + mark_face_cachels (window->face_cachels, markobj); + mark_glyph_cachels (window->glyph_cachels, markobj); + return (Qnil); +} + +static void +print_window (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + char buf[200]; + + if (print_readably) + error ("printing unreadable object #<window 0x%x>", + XWINDOW (obj)->header.uid); + + write_c_string ("#<window", printcharfun); + if (!NILP (XWINDOW (obj)->buffer)) + { + Lisp_Object name = XBUFFER (XWINDOW (obj)->buffer)->name; + write_c_string (" on ", printcharfun); + print_internal (name, printcharfun, 1); + } + sprintf (buf, " 0x%x>", XWINDOW (obj)->header.uid); + write_c_string (buf, printcharfun); +} + +static void +finalize_window (void *header, int for_disksave) +{ + struct window *w = (struct window *) header; + + if (w->line_start_cache) + { + Dynarr_free (w->line_start_cache); + w->line_start_cache = 0; + } + + if (w->face_cachels) + { + int i; + + for (i = 0; i < Dynarr_length (w->face_cachels); i++) + { + struct face_cachel *cachel = Dynarr_atp (w->face_cachels, i); + if (cachel->merged_faces) + { + Dynarr_free (cachel->merged_faces); + cachel->merged_faces = 0; + } + } + Dynarr_free (w->face_cachels); + w->face_cachels = 0; + } + + if (w->glyph_cachels) + { + Dynarr_free (w->glyph_cachels); + w->glyph_cachels = 0; + } +} + +#define INIT_DISP_VARIABLE(field, initialization) \ + p->field[CURRENT_DISP] = initialization; \ + p->field[DESIRED_DISP] = initialization; \ + p->field[CMOTION_DISP] = initialization; + +/* We have an implicit assertion that the first two elements (default + and modeline faces) are always present in the face_element_cache. + Normally redisplay ensures this. However, it is possible for a + window to get created and functions which reference these values + called before redisplay works with the window for the first time. + All callers of allocate_window should therefore call + reset_face_cachels on the created window. We can't do it + here because the window must have its frame pointer set or + reset_face_cachels will fail. */ +Lisp_Object +allocate_window (void) +{ + Lisp_Object val = Qnil; + struct window *p = alloc_lcrecord (sizeof (struct window), + lrecord_window); + + zero_lcrecord (p); + XSETWINDOW (val, p); + + p->dead = 0; + p->frame = Qnil; + p->mini_p = Qnil; + p->next = Qnil; + p->prev = Qnil; + p->hchild = Qnil; + p->vchild = Qnil; + p->parent = Qnil; + p->buffer = Qnil; + INIT_DISP_VARIABLE (start, Fmake_marker ()); + INIT_DISP_VARIABLE (pointm, Fmake_marker ()); + p->sb_point = Fmake_marker (); + p->use_time = Qzero; + INIT_DISP_VARIABLE (last_modified, Qzero); + INIT_DISP_VARIABLE (last_point, Fmake_marker ()); + INIT_DISP_VARIABLE (last_start, Fmake_marker ()); + INIT_DISP_VARIABLE (last_facechange, Qzero); + p->face_cachels = Dynarr_new (struct face_cachel); + p->glyph_cachels = Dynarr_new (struct glyph_cachel); + p->line_start_cache = Dynarr_new (struct line_start_cache); + p->line_cache_last_updated = Qzero; + INIT_DISP_VARIABLE (last_point_x, 0); + INIT_DISP_VARIABLE (last_point_y, 0); + INIT_DISP_VARIABLE (window_end_pos, 0); + p->redisplay_end_trigger = Qnil; + /* cached specifier values: will get set properly later */ + p->display_table = Qnil; + p->has_modeline_p = Qnil; + p->modeline_shadow_thickness = Qnil; +#ifdef HAVE_SCROLLBARS + p->scrollbar_width = Qnil; + p->scrollbar_height = Qnil; +#endif + p->left_margin_width = Qnil; + p->right_margin_width = Qnil; +#ifdef HAVE_TOOLBARS + p->toolbar_size[0] = Qnil; + p->toolbar_size[1] = Qnil; + p->toolbar_size[2] = Qnil; + p->toolbar_size[3] = Qnil; +#endif + p->minimum_line_ascent = Qnil; + p->minimum_line_descent = Qnil; + p->use_left_overflow = Qnil; + p->use_right_overflow = Qnil; +#ifdef HAVE_MENUBARS + p->menubar_visible_p = Qnil; +#endif +#ifdef HAVE_TOOLBARS + p->toolbar_buttons_captioned_p = Qnil; +#endif + p->text_cursor_visible_p = Qnil; + /* end cached specifier values. */ + + p->dedicated = Qnil; + p->windows_changed = 1; + p->shadow_thickness_changed = 1; + + return (val); +} +#undef INIT_DISP_VARIABLE + +/* + * The redisplay structures used to be stored with each window. While + * they are logically something associated with frames they can't be + * stored there with a redisplay which handles variable height lines. + * Lines in horizontally split windows might not line up. So they get + * stored with the windows. + * + * The problem with this is window configurations. When restoring a + * window configuration it now becomes problematic to do an + * incremental redisplay. The solution is to store the redisplay + * structures with the frame as they should be but laid out in the + * same manner as the window structure. Thus is born the window + * mirror. + * + * It also becomes a convenient place to stick scrollbar instances + * since they extrapolate out to having the same problem described for + * the display structures. + */ + +/* Create a new mindow mirror structure and associated redisplay + structs. */ +static struct window_mirror * +new_window_mirror (struct frame *f) +{ + struct window_mirror *t; + + t = (struct window_mirror *) + xmalloc (sizeof (struct window_mirror)); + memset (t, 0, sizeof (struct window_mirror)); + + t->frame = f; + + t->current_display_lines = Dynarr_new (struct display_line); + t->desired_display_lines = Dynarr_new (struct display_line); + t->buffer = NULL; + +#ifdef HAVE_SCROLLBARS + t->scrollbar_vertical_instance = NULL; + t->scrollbar_horizontal_instance = NULL; +#endif + + return t; +} + +/* Synchronize the mirror structure with a given window structure. + This is normally called from update_frame_window_mirror with a + starting window of f->root_window. */ +static struct window_mirror * +update_mirror_internal (Lisp_Object win, struct window_mirror *mir) +{ + if (NILP (win)) + { + if (mir) + { + free_window_mirror (mir); + mir = 0; + } + return mir; + } + else + if (!mir) + mir = new_window_mirror (XFRAME (XWINDOW (win)->frame)); + + mir->next = update_mirror_internal (XWINDOW (win)->next, mir->next); + mir->hchild = update_mirror_internal (XWINDOW (win)->hchild, mir->hchild); + mir->vchild = update_mirror_internal (XWINDOW (win)->vchild, mir->vchild); + + /* + * If the redisplay structs are not empty and the mirror has + * children, then this mirror structure was formerly being used for + * display but is no longer. Reset its current display structs so + * that redisplay doesn't accidentally think they are accurate if it + * is later used for display purposes once again. Also, mark the + * scrollbar instance as not active. + */ + if (mir->vchild || mir->hchild) + { + /* The redisplay structures are big. Leaving them around in + non-leaf windows can add up to a lot of wasted space. So + don't do it. */ + free_display_structs (mir); + mir->current_display_lines = Dynarr_new (struct display_line); + mir->desired_display_lines = Dynarr_new (struct display_line); + +#ifdef HAVE_SCROLLBARS + update_window_scrollbars (XWINDOW (win), mir, 0, 0); +#endif + mir->buffer = NULL; + } + + return mir; +} + +/* Given a window mirror, determine which real window it contains the + redisplay structures for. */ +static Lisp_Object +real_window_internal (Lisp_Object win, struct window_mirror *rmir, + struct window_mirror *mir) +{ + Lisp_Object retval; + + for (; !NILP (win) && rmir ; win = XWINDOW (win)->next, rmir = rmir->next) + { + if (mir == rmir) + return win; + if (!NILP (XWINDOW (win)->vchild)) + { + retval = real_window_internal (XWINDOW (win)->vchild, rmir->vchild, + mir); + if (!NILP (retval)) + return retval; + } + if (!NILP (XWINDOW (win)->hchild)) + { + retval = real_window_internal (XWINDOW (win)->hchild, rmir->hchild, + mir); + if (!NILP (retval)) + return retval; + } + } + + return Qnil; +} + +/* Given a real window, find the mirror structure which contains its + redisplay structures. */ +static struct window_mirror * +find_window_mirror_internal (Lisp_Object win, struct window_mirror *rmir, + struct window *w) +{ + struct window_mirror *retval; + + for (; !NILP (win); win = XWINDOW (win)->next, rmir = rmir->next) + { + if (w == XWINDOW (win)) + return rmir; + if (!NILP (XWINDOW (win)->vchild) && + (retval = find_window_mirror_internal (XWINDOW (win)->vchild, + rmir->vchild, w))) + return retval; + if (!NILP (XWINDOW (win)->hchild) && + (retval = find_window_mirror_internal (XWINDOW (win)->hchild, + rmir->hchild, w))) + return retval; + } + + return 0; +} + +/* Update the mirror structure for the given frame. */ +void +update_frame_window_mirror (struct frame *f) +{ + f->root_mirror = update_mirror_internal (f->root_window, f->root_mirror); + f->mirror_dirty = 0; +} + +/* Free a given mirror structure along with all of its children as + well as their associated display structures. */ +void +free_window_mirror (struct window_mirror *mir) +{ + struct window_mirror *p; + + p = mir; + while (mir) + { + if (mir->hchild) free_window_mirror (mir->hchild); + if (mir->vchild) free_window_mirror (mir->vchild); +#ifdef HAVE_SCROLLBARS + release_window_mirror_scrollbars (mir); +#endif + free_display_structs (mir); + mir = mir->next; + xfree (p); + p = mir; + } +} + +/* Given a mirror structure, return the window it mirrors. Calls + real_window_internal to do most of the work. */ +Lisp_Object +real_window (struct window_mirror *mir, int no_abort) +{ + Lisp_Object retval; + + retval = real_window_internal (mir->frame->root_window, + mir->frame->root_mirror, mir); + + if (NILP (retval) && !no_abort) + abort (); + + return retval; +} + +/* Given a real window, return its mirror structure. Calls + find_window_mirror_internal to do all of the work. */ +struct window_mirror * +find_window_mirror (struct window *w) +{ + struct frame *f = XFRAME (w->frame); + if (f->mirror_dirty) + update_frame_window_mirror (f); + return find_window_mirror_internal (f->root_window, f->root_mirror, w); +} + +/***************************************************************************** + find_window_by_pixel_pos + + Given a pixel position relative to a frame, find the window at that + position. + ****************************************************************************/ +struct window * +find_window_by_pixel_pos (unsigned int pix_x, unsigned int pix_y, + Lisp_Object win) +{ + struct window *w; + + if (NILP (win)) + return 0; + + for (; !NILP (win); win = XWINDOW (win)->next) + { + if (!NILP (XWINDOW (win)->vchild) && + (w = find_window_by_pixel_pos (pix_x, pix_y, XWINDOW (win)->vchild))) + return w; + if (!NILP (XWINDOW (win)->hchild) && + (w = find_window_by_pixel_pos (pix_x, pix_y, XWINDOW (win)->hchild))) + return w; + w = XWINDOW (win); + if (pix_x >= WINDOW_LEFT (w) + && pix_x <= WINDOW_RIGHT (w) + && pix_y >= WINDOW_TOP (w) + && pix_y <= WINDOW_BOTTOM (w)) + return w; + } + return 0; +} + +/* Return a pointer to the display structures for the given window. */ +display_line_dynarr * +window_display_lines (struct window *w, int which) +{ + struct window_mirror *t; + + if (XFRAME (w->frame)->mirror_dirty) + update_frame_window_mirror (XFRAME (w->frame)); + t = find_window_mirror (w); + if (!t) + abort (); + + if (which == CURRENT_DISP) + return t->current_display_lines; + else if (which == DESIRED_DISP) + return t->desired_display_lines; + else if (which == CMOTION_DISP) + /* The CMOTION_DISP display lines are global. */ + return cmotion_display_lines; + else + abort (); + + return 0; /* shut up compiler */ +} + +struct buffer * +window_display_buffer (struct window *w) +{ + struct window_mirror *t; + + if (XFRAME (w->frame)->mirror_dirty) + update_frame_window_mirror (XFRAME (w->frame)); + t = find_window_mirror (w); + if (!t) + abort (); + + return t->buffer; +} + +void +set_window_display_buffer (struct window *w, struct buffer *b) +{ + struct window_mirror *t; + + if (XFRAME (w->frame)->mirror_dirty) + update_frame_window_mirror (XFRAME (w->frame)); + t = find_window_mirror (w); + if (!t) + abort (); + + t->buffer = b; +} + + +/* Determining a windows position based solely on its pixel + positioning doesn't work. Instead, we do it the intelligent way, + by checking its positioning in the window hierarchy. */ +int +window_is_leftmost (struct window *w) +{ + Lisp_Object parent, current_ancestor, window; + + XSETWINDOW (window, w); + + parent = XWINDOW (window)->parent; + current_ancestor = window; + + while (!NILP (parent)) + { + if (!NILP (XWINDOW (parent)->hchild) && + !EQ (XWINDOW (parent)->hchild, current_ancestor)) + return 0; + + current_ancestor = parent; + parent = XWINDOW (parent)->parent; + } + + return 1; +} + +int +window_is_rightmost (struct window *w) +{ + Lisp_Object parent, current_ancestor, window; + + XSETWINDOW (window, w); + + parent = XWINDOW (window)->parent; + current_ancestor = window; + + while (!NILP (parent)) + { + if (!NILP (XWINDOW (parent)->hchild) + && !NILP (XWINDOW (current_ancestor)->next)) + return 0; + + current_ancestor = parent; + parent = XWINDOW (parent)->parent; + } + + return 1; +} + +static int +window_full_width_p (struct window *w) +{ + return window_is_leftmost (w) && window_is_rightmost (w); +} + +static int +window_is_highest (struct window *w) +{ + Lisp_Object parent, current_ancestor, window; + + XSETWINDOW (window, w); + + parent = XWINDOW (window)->parent; + current_ancestor = window; + + while (!NILP (parent)) + { + if (!NILP (XWINDOW (parent)->vchild) && + !EQ (XWINDOW (parent)->vchild, current_ancestor)) + return 0; + + current_ancestor = parent; + parent = XWINDOW (parent)->parent; + } + + /* This is really to catch the minibuffer but we make it generic in + case we ever change things around to let the minibuffer be on top. */ + if (NILP (XWINDOW (current_ancestor)->prev)) + return 1; + else + return 0; +} + +static int +window_is_lowest (struct window *w) +{ + Lisp_Object parent, current_ancestor, window; + + XSETWINDOW (window, w); + + parent = XWINDOW (window)->parent; + current_ancestor = window; + + while (!NILP (parent)) + { + if (!NILP (XWINDOW (parent)->vchild) + && !NILP (XWINDOW (current_ancestor)->next)) + return 0; + + current_ancestor = parent; + parent = XWINDOW (parent)->parent; + } + + return 1; +} + +#if 0 /* not currently used */ + +static int +window_full_height_p (struct window *w) +{ + return window_is_highest (w) && window_is_lowest (w); +} + +#endif + +int +window_truncation_on (struct window *w) +{ + /* Horizontally scrolled windows are truncated. */ + if (w->hscroll) + return 1; + + /* If truncate_partial_width_windows is true and the window is not + the full width of the frame it is truncated. */ + if (truncate_partial_width_windows + && !(window_is_leftmost (w) && window_is_rightmost (w))) + return 1; + + /* If the window's buffer's value of truncate_lines is non-nil, then + the window is truncated. */ + if (!NILP (XBUFFER (w->buffer)->truncate_lines)) + return 1; + + return 0; +} + +int +window_needs_vertical_divider (struct window *w) +{ +#ifdef HAVE_SCROLLBARS + struct frame *f = XFRAME (w->frame); + + if (!window_scrollbar_width (w)) + { + if (f->scrollbar_on_left) + return (!window_is_leftmost (w)); + else + return (!window_is_rightmost (w)); + } + else + return 0; +#else + return (!window_is_leftmost (w)); +#endif +} + +int +window_scrollbar_width (struct window *w) +{ +#ifdef HAVE_SCROLLBARS + if (!WINDOW_WIN_P (w) + || MINI_WINDOW_P (w) + || NILP (w->buffer)) + /* #### when does NILP (w->buffer) happen? */ + return 0; + + return XINT (w->scrollbar_width); +#else + return 0; +#endif +} + +/* Horizontal scrollbars are only active on windows with truncation + turned on. */ +int +window_scrollbar_height (struct window *w) +{ +#ifdef HAVE_SCROLLBARS + if (!WINDOW_WIN_P (w) + || MINI_WINDOW_P (w) + || NILP (w->buffer) + || !window_truncation_on (w)) + return 0; + + return XINT (w->scrollbar_height); +#else + return 0; +#endif +} + +int +window_modeline_height (struct window *w) +{ + struct frame *f = XFRAME (w->frame); + int modeline_height; + + if (MINI_WINDOW_P (w) || NILP (w->buffer)) + { + modeline_height = 0; + } + else if (!WINDOW_HAS_MODELINE_P (w)) + { + if (window_scrollbar_height (w)) + modeline_height = 0; + else + { + modeline_height = FRAMEMETH (f, divider_height, ()); + + if (!EQ (Qzero, w->modeline_shadow_thickness) && FRAME_WIN_P (f)) + modeline_height += (2 * MODELINE_SHADOW_THICKNESS (w)); + } + } + else + { + if (noninteractive) + modeline_height = 0; + else + { + display_line_dynarr *dla; + + /* We don't force a regeneration of the modeline here. + Instead it is now a precondition that any function calling + this should make sure that one of these structures is + up-to-date. In practice this only affects two internal + redisplay functions, regenerate_window and + regenerate_window_point_center. */ + /* We check DESIRED_DISP because if it is valid it is more + up-to-date than CURRENT_DISP. For calls to this outside + of redisplay it doesn't matter which structure we check + since there is a redisplay condition that these + structures be identical outside of redisplay. */ + dla = window_display_lines (w, DESIRED_DISP); + if (dla && Dynarr_length (dla) && Dynarr_atp (dla, 0)->modeline) + modeline_height = (Dynarr_atp (dla, 0)->ascent + + Dynarr_atp (dla, 0)->descent); + else + { + dla = window_display_lines (w, CURRENT_DISP); + if (dla && Dynarr_length (dla) && Dynarr_atp (dla, 0)->modeline) + modeline_height = (Dynarr_atp (dla, 0)->ascent + + Dynarr_atp (dla, 0)->descent); + else + /* This should be an abort except I'm not yet 100% + confident that it won't ever get hit (though I + haven't been able to trigger it). It is extremely + unlikely to cause any noticable problem and even if + it does it will be a minor display glitch. */ + /* #### Bullshit alert. It does get hit and it causes + noticeable glitches. real_current_modeline_height + is a kludge to fix this for 19.14. */ + modeline_height = real_current_modeline_height (w); + } + + if (!EQ (Qzero, w->modeline_shadow_thickness) && FRAME_WIN_P (f)) + modeline_height += (2 * MODELINE_SHADOW_THICKNESS (w)); + } + } + + return modeline_height; +} + +/***************************************************************************** + margin_width_internal + + For a given window, return the width in pixels of the specified margin. + ****************************************************************************/ +static int +margin_width_internal (struct window *w, int left_margin) +{ + struct buffer *b; + int window_cwidth = window_char_width (w, 1); + int margin_cwidth; + int font_width; + Lisp_Object window = Qnil; + + /* We might be getting called on a non-leaf. */ + if (NILP (w->buffer)) + return 0; + + /* The minibuffer never has margins. */ + if (MINI_WINDOW_P (w)) + return 0; + + XSETWINDOW (window, w); + b = XBUFFER (w->buffer); + margin_cwidth = (left_margin ? XINT (w->left_margin_width) : + XINT (w->right_margin_width)); + + default_face_height_and_width (window, 0, &font_width); + + /* The left margin takes precedence over the right margin so we + subtract its width from the space available for the right + margin. */ + if (!left_margin) + window_cwidth -= XINT (w->left_margin_width); + + /* The margin cannot be wider than the window is. We allow the + value to be bigger since it is possible for the user to enlarge + the window such that the left margin value would no longer be too + big, but we won't return a value that is larger. */ + if (margin_cwidth > window_cwidth) + margin_cwidth = window_cwidth; + + /* At the user level the margin is always specified in characters. + Internally however it is manipulated in terms of pixels. */ + return (margin_cwidth * font_width); +} + +int +window_left_margin_width (struct window *w) +{ + return margin_width_internal (w, 1); +} + +int +window_right_margin_width (struct window *w) +{ + return margin_width_internal (w, 0); +} + +static int +window_top_toolbar_height (struct window *w) +{ + /* #### implement this shit. */ + return 0; +} + +/* #### Currently used in scrollbar.c. Does it actually need to be? */ +int +window_bottom_toolbar_height (struct window *w) +{ + return 0; +} + +static int +window_left_toolbar_width (struct window *w) +{ + return 0; +} + +static int +window_right_toolbar_width (struct window *w) +{ + return 0; +} + +/***************************************************************************** + Window Gutters + + The gutters of a window are those areas in the boundary defined by + w->pixel_top, w->pixel_left, w->pixel_height and w->pixel_width which + do not contain text. Items which may be in the gutters include + scrollbars, toolbars and modelines. The margin areas are not + included. This is an exception made because redisplay special cases + the handling of those areas in many places in such a way that + including them in the gutter area would make life difficult. + + The size functions refer to height for the bottom and top gutters and + width for the left and right gutters. The starting position + functions refer to the Y coord for bottom and top gutters and the X + coord for left and right gutters. All starting positions are + relative to the frame, not the window. + ****************************************************************************/ + +int +window_top_gutter_height (struct window *w) +{ + int toolbar_height = window_top_toolbar_height (w); + + if (!NILP (w->hchild) || !NILP (w->vchild)) + return 0; + +#ifdef HAVE_SCROLLBARS + if (XFRAME (w->frame)->scrollbar_on_top) + return window_scrollbar_height (w) + toolbar_height; + else +#endif + return toolbar_height; +} + +int +window_bottom_gutter_height (struct window *w) +{ + int other_height; + + if (!NILP (w->hchild) || !NILP (w->vchild)) + return 0; + else + other_height = + window_modeline_height (w) + window_bottom_toolbar_height (w); + +#ifdef HAVE_SCROLLBARS + if (!XFRAME (w->frame)->scrollbar_on_top) + return window_scrollbar_height (w) + other_height; + else +#endif + return other_height; +} + +static int +window_left_right_gutter_width_internal (struct window *w, int modeline) +{ + struct frame *f = XFRAME (w->frame); + int scrollbar_width = window_scrollbar_width (w); + + if (!NILP (w->hchild) || !NILP (w->vchild)) + return 0; + + if (!modeline) + { + if (scrollbar_width) + return scrollbar_width; + else if (window_needs_vertical_divider (w)) + return FRAMEMETH (f, divider_width, ()); + else + return 0; + } + else + { + /* The shadows on the 3D modelines provide a visual break + between the modelines of horizontally split windows. 2D + modelines need some help, though. */ + if (!EQ (Qzero, w->modeline_shadow_thickness)) + return 0; + else if (window_needs_vertical_divider (w)) + return FRAMEMETH (f, divider_width, ()); + else + return 0; + } +} + +int +window_left_gutter_width (struct window *w, int modeline) +{ + if (!NILP (w->hchild) || !NILP (w->vchild)) + return 0; + +#ifdef HAVE_SCROLLBARS + if (XFRAME (w->frame)->scrollbar_on_left) + { +#endif + return (window_left_right_gutter_width_internal (w, modeline) + + window_left_toolbar_width (w)); +#ifdef HAVE_SCROLLBARS + } + else + return window_left_toolbar_width (w); +#endif +} + +int +window_right_gutter_width (struct window *w, int modeline) +{ + if (!NILP (w->hchild) || !NILP (w->vchild)) + return 0; + +#ifdef HAVE_SCROLLBARS + if (!XFRAME (w->frame)->scrollbar_on_left) + { + return (window_left_right_gutter_width_internal (w, modeline) + + window_right_toolbar_width (w)); + } + else +#endif + return window_right_toolbar_width (w); +} + + +DEFUN ("windowp", Fwindowp, Swindowp, 1, 1, 0 /* +Return t if OBJ is a window. +*/ ) + (obj) + Lisp_Object obj; +{ + return (WINDOWP (obj) ? Qt : Qnil); +} + +DEFUN ("window-live-p", Fwindow_live_p, Swindow_live_p, 1, 1, 0 /* +Return t if OBJ is a window which is currently visible. +*/ ) + (obj) + Lisp_Object obj; +{ + return (WINDOWP (obj) && WINDOW_LIVE_P (XWINDOW (obj)) ? Qt : Qnil); +} + +DEFUN ("selected-window", Fselected_window, Sselected_window, 0, 1, 0 /* +Return the window that the cursor now appears in and commands apply to. +If the optional argument CON-DEV-OR-FRAME is specified and is a frame, return +the selected window used by that frame. If CON-DEV-OR-FRAME is a device, +then the selected frame on that device will be used. If CON-DEV-OR-FRAME +is a console, the selected frame on that console's selected device will +be used. Otherwise, the selected frame is used. +*/ ) + (con_dev_or_frame) + Lisp_Object con_dev_or_frame; +{ + struct frame *f; + + if (NILP (con_dev_or_frame) && NILP (Fselected_device (Qnil))) + return Qnil; /* happens at startup */ + + f = decode_frame_or_selected (con_dev_or_frame); + return FRAME_SELECTED_WINDOW (f); +} + +DEFUN ("minibuffer-window", Fminibuffer_window, Sminibuffer_window, 0, 1, 0 /* +Return the window used now for minibuffers. +If the optional argument CON-DEV-OR-FRAME is specified and is a frame, return +the minibuffer window used by that frame. If CON-DEV-OR-FRAME is a device, +then the selected frame on that device will be used. If CON-DEV-OR-FRAME +is a console, the selected frame on that console's selected device will +be used. Otherwise, the selected frame is used. +*/ ) + (con_dev_or_frame) + Lisp_Object con_dev_or_frame; +{ + struct frame *f = decode_frame_or_selected (con_dev_or_frame); + + return FRAME_MINIBUF_WINDOW (f); +} + +DEFUN ("window-minibuffer-p", Fwindow_minibuffer_p, Swindow_minibuffer_p, 1, 1, 0 /* +Return non-nil if WINDOW is a minibuffer window. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return (MINI_WINDOW_P (w) ? Qt : Qnil); +} + +DEFUN ("window-first-hchild", Fwindow_first_hchild, Swindow_first_hchild, + 1, 1, 0 /* +Return the first horizontal child of WINDOW, or nil. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return w->hchild; +} + +DEFUN ("window-first-vchild", Fwindow_first_vchild, Swindow_first_vchild, + 1, 1, 0 /* +Return the first vertical child of WINDOW, or nil. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return w->vchild; +} + +DEFUN ("window-next-child", Fwindow_next_child, Swindow_next_child, + 1, 1, 0 /* +Return the next window on the same level as WINDOW, or nil. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return w->next; +} + +DEFUN ("window-previous-child", Fwindow_previous_child, Swindow_previous_child, + 1, 1, 0 /* +Return the previous window on the same level as WINDOW, or nil. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return w->prev; +} + +DEFUN ("window-parent", Fwindow_parent, Swindow_parent, + 1, 1, 0 /* +Return the parent of WINDOW, or nil. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return w->parent; +} + +DEFUN ("window-lowest-p", Fwindow_lowest_p, Swindow_lowest_p, 1, 1, 0 /* +Return non-nil if WINDOW is along the bottom of its frame. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return window_is_lowest (w) ? Qt : Qnil; +} + +DEFUN ("window-highest-p", Fwindow_highest_p, Swindow_highest_p, 1, 1, 0 /* +Return non-nil if WINDOW is along the top of its frame. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return window_is_highest (w) ? Qt : Qnil; +} + +DEFUN ("window-leftmost-p", Fwindow_leftmost_p, Swindow_leftmost_p, 1, 1, 0 /* +Return non-nil if WINDOW is along the left edge of its frame. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return window_is_leftmost (w) ? Qt : Qnil; +} + +DEFUN ("window-rightmost-p", Fwindow_rightmost_p, Swindow_rightmost_p, + 1, 1, 0 /* +Return non-nil if WINDOW is along the right edge of its frame. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return window_is_rightmost (w) ? Qt : Qnil; +} + +DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, + Spos_visible_in_window_p, 0, 2, 0 /* +Return t if position POS is currently on the frame in WINDOW. +Returns nil if that position is scrolled vertically out of view. +POS defaults to point in WINDOW's buffer; WINDOW, to the selected window. +*/ ) + (pos, window) + Lisp_Object pos, window; +{ + struct window *w; + Bufpos top; + Bufpos posint; + struct buffer *buf; + + w = decode_window (window); + top = marker_position (w->start[CURRENT_DISP]); + + buf = XBUFFER (w->buffer); + + if (NILP (pos)) + posint = BUF_PT (buf); + else + { + CHECK_INT_COERCE_MARKER (pos); + posint = XINT (pos); + } + + if (posint < top || posint > BUF_ZV (buf)) + return Qnil; + + /* w->start can be out of range. If it is, do something reasonable. */ + if (top < BUF_BEGV (buf) || top > BUF_ZV (buf)) + return Qnil; + + if (point_would_be_visible (w, top, posint)) + return Qt; + else + return Qnil; +} + + +struct window * +decode_window (Lisp_Object window) +{ + if (NILP (window)) + return XWINDOW (Fselected_window (Qnil)); + + CHECK_LIVE_WINDOW (window); + return XWINDOW (window); +} + +DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0 /* +Return the buffer that WINDOW is displaying. +*/ ) + (window) + Lisp_Object window; +{ + return decode_window (window)->buffer; +} + +DEFUN ("window-frame", Fwindow_frame, Swindow_frame, 0, 1, 0 /* +Return the frame that window WINDOW is on. +*/ ) + (window) + Lisp_Object window; +{ + return decode_window (window)->frame; +} + +DEFUN ("window-height", Fwindow_height, Swindow_height, 0, 1, 0 /* +Return the number of default lines in WINDOW. +This actually works by dividing the window's pixel height (including +the modeline and horizontal scrollbar, if any) by the height of the +default font; therefore, the number of displayed lines will probably +be different. +Use `window-height' to get consistent results in geometry calculations. +Use `window-displayed-height' to get the actual number of lines +currently displayed in a window. +*/ ) + (window) + Lisp_Object window; +{ + return make_int (window_char_height (decode_window (window), 1)); +} + +DEFUN ("window-displayed-height", Fwindow_displayed_height, + Swindow_displayed_height, 0, 1, 0 /* +Return the number of lines currently displayed in WINDOW. +This counts the actual number of lines displayed in WINDOW +(as opposed to `window-height'). The modeline and horizontal +scrollbar do not count as lines. If there is some blank space +between the end of the buffer and the end of the window, this +function pretends that there are lines of text in the default +font there. +*/ ) + (window) + Lisp_Object window; +{ + return make_int (window_displayed_height (decode_window (window))); +} + +DEFUN ("window-pixel-height", Fwindow_pixel_height, Swindow_pixel_height, + 0, 1, 0 /* +Return the height of WINDOW in pixels. Defaults to current window. +This includes the window's modeline and horizontal scrollbar (if any). +*/ ) + (window) + Lisp_Object window; +{ + return (make_int (decode_window (window)->pixel_height)); +} + +DEFUN ("window-width", Fwindow_width, Swindow_width, 0, 1, 0 /* +Return the number of display columns in WINDOW. +This is the width that is usable columns available for text in WINDOW. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + return (make_int (window_char_width (w, 0))); +} + +DEFUN ("window-pixel-width", Fwindow_pixel_width, Swindow_pixel_width, + 0, 1, 0 /* +Return the width of WINDOW in pixels. Defaults to current window. +*/ ) + (window) + Lisp_Object window; +{ + return (make_int (decode_window (window)->pixel_width)); +} + +DEFUN ("window-hscroll", Fwindow_hscroll, Swindow_hscroll, 0, 1, 0 /* +Return the number of columns by which WINDOW is scrolled from left margin. +*/ ) + (window) + Lisp_Object window; +{ + return (make_int (decode_window (window)->hscroll)); +} + +DEFUN ("set-window-hscroll", Fset_window_hscroll, Sset_window_hscroll, 2, 2, 0 /* +Set number of columns WINDOW is scrolled from left margin to NCOL. +NCOL should be zero or positive. +*/ ) + (window, ncol) + Lisp_Object window, ncol; +{ + struct window *w; + int ncols; + + CHECK_INT (ncol); + ncols = XINT (ncol); + if (ncols < 0) ncols = 0; + w = decode_window (window); + if (w->hscroll != ncols) + MARK_CLIP_CHANGED; /* FSF marks differently but we aren't FSF. */ + w->hscroll = ncols; + return ncol; +} + +#if 0 /* bogus RMS crock */ + +xxDEFUN ("window-redisplay-end-trigger", Fwindow_redisplay_end_trigger, + Swindow_redisplay_end_trigger, 0, 1, 0 /* +Return WINDOW's redisplay end trigger value. +See `set-window-redisplay-end-trigger' for more information. +*/ ) + (window) + Lisp_Object window; +{ + return decode_window (window)->redisplay_end_trigger; +} + +xxDEFUN ("set-window-redisplay-end-trigger", Fset_window_redisplay_end_trigger, + Sset_window_redisplay_end_trigger, 2, 2, 0 /* +Set WINDOW's redisplay end trigger value to VALUE. +VALUE should be a buffer position (typically a marker) or nil. +If it is a buffer position, then if redisplay in WINDOW reaches a position +beyond VALUE, the functions in `redisplay-end-trigger-functions' are called +with two arguments: WINDOW, and the end trigger value. +Afterwards the end-trigger value is reset to nil. +*/ ) + (window, value) + register Lisp_Object window, value; +{ + register struct window *w; + + w = decode_window (window); + w->redisplay_end_trigger = value; + return value; +} + +#endif /* 0 */ + +DEFUN ("window-pixel-edges", Fwindow_pixel_edges, Swindow_pixel_edges, + 0, 1, 0 /* +Return a list of the pixel edge coordinates of WINDOW. +\(LEFT TOP RIGHT BOTTOM), all relative to 0, 0 at top left corner of frame. +The frame toolbars and menubars are considered to be outside of this area. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + struct frame *f = XFRAME (w->frame); + int left, top; + + left = w->pixel_left - FRAME_LEFT_BORDER_END (f); + top = w->pixel_top - FRAME_TOP_BORDER_END (f); + + return list4 (make_int (left), + make_int (top), + make_int (left + w->pixel_width), + make_int (top + w->pixel_height)); +} + +DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0 /* +Return current value of point in WINDOW. +For a nonselected window, this is the value point would have +if that window were selected. + +Note that, when WINDOW is the selected window and its buffer +is also currently selected, the value returned is the same as (point). +It would be more strictly correct to return the `top-level' value +of point, outside of any save-excursion forms. +But that is hard to define. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + + /* The special check for current buffer is necessary for this + function to work as defined when called within an excursion. */ + if (w == XWINDOW (Fselected_window (XFRAME (w->frame)->device)) + && current_buffer == XBUFFER (w->buffer)) + return Fpoint (Qnil); + return Fmarker_position (w->pointm[CURRENT_DISP]); +} + +DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0 /* +Return position at which display currently starts in WINDOW. +This is updated by redisplay or by calling `set-window-start'. +*/ ) + (window) + Lisp_Object window; +{ + return Fmarker_position (decode_window (window)->start[CURRENT_DISP]); +} + +DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0 /* +Return position at which display currently ends in WINDOW. +This is updated by redisplay, when it runs to completion. +Simply changing the buffer text or setting `window-start' +does not update this value. +If GUARANTEE is non-nil, then the return value is guaranteed to be +the value of window-end at the end of the next full redisplay assuming +nothing else changes in the meantime. This function is potentially much +slower with this flag set. +*/ ) + (window, guarantee) + Lisp_Object window, guarantee; +{ + Lisp_Object value; + struct window *w = decode_window (window); + Lisp_Object buf; + + if (NILP (guarantee)) + { + buf = w->buffer; + CHECK_BUFFER (buf); + + XSETINT (value, BUF_Z (XBUFFER (buf)) - w->window_end_pos[CURRENT_DISP]); + + return value; + } + else + { + Bufpos startp = marker_position (w->start[CURRENT_DISP]); + return (make_int (end_of_last_line (w, startp))); + } +} + +DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0 /* +Make point value in WINDOW be at position POS in WINDOW's buffer. +*/ ) + (window, pos) + Lisp_Object window, pos; +{ + struct window *w = decode_window (window); + + CHECK_INT_COERCE_MARKER (pos); + if (w == XWINDOW (Fselected_window (Qnil))) + Fgoto_char (pos, Fcurrent_buffer ()); + else + set_marker_restricted (w->pointm[CURRENT_DISP], pos, w->buffer); + + MARK_POINT_CHANGED; + return pos; +} + +DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0 /* +Make display in WINDOW start at position POS in WINDOW's buffer. +Optional third arg NOFORCE non-nil inhibits next redisplay +from overriding motion of point in order to display at this exact start. +*/ ) + (window, pos, noforce) + Lisp_Object window, pos, noforce; +{ + struct window *w = decode_window (window); + + CHECK_INT_COERCE_MARKER (pos); + set_marker_restricted (w->start[CURRENT_DISP], pos, w->buffer); + /* this is not right, but much easier than doing what is right. */ + w->start_at_line_beg = 0; + if (NILP (noforce)) + w->force_start = 1; + w->redo_modeline = 1; + SET_LAST_MODIFIED (w, 0); + SET_LAST_FACECHANGE (w); + + MARK_WINDOWS_CHANGED (w); + + return pos; +} + +DEFUN ("window-dedicated-p", Fwindow_dedicated_p, Swindow_dedicated_p, + 1, 1, 0 /* +Return WINDOW's dedicated object, usually t or nil. +See also `set-window-dedicated-p'. +*/ ) + (window) + Lisp_Object window; +{ + return decode_window (window)->dedicated; +} + +DEFUN ("set-window-dedicated-p", Fset_window_dedicated_p, + Sset_window_dedicated_p, 2, 2, 0 /* +Control whether WINDOW is dedicated to the buffer it displays. +If it is dedicated, Emacs will not automatically change +which buffer appears in it. +The second argument is the new value for the dedication flag; +non-nil means yes. +*/ ) + (window, arg) + Lisp_Object window, arg; +{ + register struct window *w = decode_window (window); + + if (NILP (arg)) + w->dedicated = Qnil; + else + w->dedicated = Qt; + + return w->dedicated; +} + +/* FSFmacs has window-display-table here. We have display table as a + specifier. */ + + +/* Record info on buffer window w is displaying + when it is about to cease to display that buffer. */ +static void +unshow_buffer (struct window *w) +{ + Lisp_Object buf = w->buffer; + + if (XBUFFER (buf) != XMARKER (w->pointm[CURRENT_DISP])->buffer) + abort (); + +#ifdef ENERGIZE + energize_buffer_hidden_hook (w); +#endif + + /* FSF disables this check, so I'll do it too. I hope it won't + break things. --ben */ +#if 0 + if (w == XWINDOW (Fselected_window (Qnil)) + || ! EQ (buf, XWINDOW (Fselected_window (Qnil))->buffer)) + /* Do this except when the selected window's buffer + is being removed from some other window. */ +#endif + /* last_window_start records the start position that this buffer + had in the last window to be disconnected from it. + Now that this statement is unconditional, + it is possible for the buffer to be displayed in the + selected window, while last_window_start reflects another + window which was recently showing the same buffer. + Some people might say that might be a good thing. Let's see. */ + XBUFFER (buf)->last_window_start = + marker_position (w->start[CURRENT_DISP]); + + /* Point in the selected window's buffer + is actually stored in that buffer, and the window's pointm isn't used. + So don't clobber point in that buffer. */ + if (! EQ (buf, XWINDOW (Fselected_window (Qnil))->buffer)) + { + struct buffer *b= XBUFFER (buf); + BUF_SET_PT (b, bufpos_clip_to_bounds (BUF_BEGV (b), + marker_position (w->pointm[CURRENT_DISP]), + BUF_ZV (b))); + } +} + +/* Put REPLACEMENT into the window structure in place of OLD. */ +static void +replace_window (Lisp_Object old, Lisp_Object replacement) +{ + Lisp_Object tem; + struct window *o = XWINDOW (old), *p = XWINDOW (replacement); + + /* If OLD is its frame's root_window, then replacement is the new + root_window for that frame. */ + + if (EQ (old, FRAME_ROOT_WINDOW (XFRAME (o->frame)))) + FRAME_ROOT_WINDOW (XFRAME (o->frame)) = replacement; + + WINDOW_LEFT (p) = WINDOW_LEFT (o); + WINDOW_TOP (p) = WINDOW_TOP (o); + WINDOW_WIDTH (p) = WINDOW_WIDTH (o); + WINDOW_HEIGHT (p) = WINDOW_HEIGHT (o); + + p->next = tem = o->next; + if (!NILP (tem)) + XWINDOW (tem)->prev = replacement; + + p->prev = tem = o->prev; + if (!NILP (tem)) + XWINDOW (tem)->next = replacement; + + p->parent = tem = o->parent; + if (!NILP (tem)) + { + if (EQ (XWINDOW (tem)->vchild, old)) + XWINDOW (tem)->vchild = replacement; + if (EQ (XWINDOW (tem)->hchild, old)) + XWINDOW (tem)->hchild = replacement; + } + + /* #### Here, if replacement is a vertical combination + and so is its new parent, we should make replacement's + children be children of that parent instead. */ +} + +/* we're deleting W; set the structure of W to indicate this. */ + +static void +mark_window_as_deleted (struct window *w) +{ + /* In the loop + (while t (split-window) (delete-window)) + we end up with a tree of deleted windows which are all connected + through the `next' slot. This might not seem so bad, as they're + deleted, and will presumably be GCed - but if even *one* of those + windows is still being pointed to, by the user, or by a window + configuration, then *all* of those windows stick around. + + Since the window-configuration code doesn't need any of the + pointers to other windows (they are all recreated from the + window-config data), we set them all to nil so that we + are able to collect more actual garbage. + */ + w->next = Qnil; + w->prev = Qnil; + w->hchild = Qnil; + w->vchild = Qnil; + w->parent = Qnil; + + w->dead = 1; + + /* Free the extra data structures attached to windows immediately so + they don't sit around consuming excess space. They will be + reinitialized by the window-configuration code as necessary. */ + finalize_window ((void *) w, 0); +} + +DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 2, "" /* +Remove WINDOW from the display. Default is selected window. +If window is the only one on the frame, the frame is destroyed. +Normally, you cannot delete the last non-minibuffer-only frame (you must +use `save-buffers-kill-emacs' or `kill-emacs'). However, if optional +second argument FORCE is non-nil, you can delete the last frame. (This +will automatically call `save-buffers-kill-emacs'.) +*/ ) + (window, force) + Lisp_Object window, force; +{ + /* This function can GC if this is the only window in the frame */ + struct window *w; + Lisp_Object parent; + struct window *par; + Lisp_Object frame; + struct frame *f; + struct device *d; + + /* Note: this function is called by other C code on non-leaf + windows. */ + + /* Do the equivalent of decode_window() but don't error out on + deleted window; it's OK to delete an already-deleted window. */ + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_WINDOW (window); + w = XWINDOW (window); + + /* It's okay to delete an already-deleted window. */ + if (! WINDOW_LIVE_P (w)) + return Qnil; + + frame = WINDOW_FRAME (w); + f = XFRAME (frame); + d = XDEVICE (FRAME_DEVICE (f)); + + if (TOP_LEVEL_WINDOW_P (w)) + { + if (NILP (memq_no_quit (frame, DEVICE_FRAME_LIST (d)))) + /* this frame isn't fully initialized yet; don't blow up. */ + return Qnil; + + if (MINI_WINDOW_P (XWINDOW (window))) + error ("Attempt to delete the minibuffer window"); + + /* It has been suggested that it's a good thing for C-x 0 to have this + behavior, but not such a good idea for #'delete-window to have it. + Maybe C-x 0 should be bound to something else, or maybe frame + deletion should only happen when this is called interactively. + */ + delete_frame_internal (f, !NILP (force), 0, 0); + return Qnil; + } + + /* At this point, we know the window has a parent. */ + parent = w->parent; + par = XWINDOW (parent); + + MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); + + /* Are we trying to delete any frame's selected window? + Note that we could be dealing with a non-leaf window + where the selected window is one of our children. + So, we check by scanning all the ancestors of the + frame's selected window and comparing each one with + WINDOW. */ + { + Lisp_Object pwindow; + + pwindow = FRAME_SELECTED_WINDOW (f); + + while (!NILP (pwindow)) + { + if (EQ (window, pwindow)) + break; + pwindow = XWINDOW (pwindow)->parent; + } + + if (EQ (window, pwindow)) + { + /* OK, we found it. */ + Lisp_Object alternative; + alternative = Fnext_window (window, Qlambda, Qnil, Qnil); + + /* If we're about to delete the selected window on the + selected frame, then we should use Fselect_window to select + the new window. On the other hand, if we're about to + delete the selected window on any other frame, we shouldn't do + anything but set the frame's selected_window slot. */ + if (EQ (frame, Fselected_frame (Qnil))) + Fselect_window (alternative); + else + set_frame_selected_window (f, alternative); + } + } + + /* w->buffer is nil in a non-leaf window; in this case, + get rid of the markers we maintain that point into that buffer. */ + if (!NILP (w->buffer)) + { + unshow_buffer (w); + unchain_marker (w->pointm[CURRENT_DISP]); + unchain_marker (w->pointm[DESIRED_DISP]); + unchain_marker (w->pointm[CMOTION_DISP]); + unchain_marker (w->start[CURRENT_DISP]); + unchain_marker (w->start[DESIRED_DISP]); + unchain_marker (w->start[CMOTION_DISP]); + unchain_marker (w->sb_point); + /* This breaks set-window-configuration if windows in the saved + configuration get deleted and multiple frames are in use. */ + /* w->buffer = Qnil; */ + } + + /* close up the hole in the sibling list */ + if (!NILP (w->next)) + XWINDOW (w->next)->prev = w->prev; + if (!NILP (w->prev)) + XWINDOW (w->prev)->next = w->next; + if (EQ (window, par->hchild)) + par->hchild = w->next; + if (EQ (window, par->vchild)) + par->vchild = w->next; + + /* Find one of our siblings to give our space to. */ + { + Lisp_Object sib = w->prev; + if (NILP (sib)) + { + /* If w gives its space to its next sibling, that sibling needs + to have its top/left side pulled back to where w's is. + set_window_{height,width} will re-position the sibling's + children. */ + sib = w->next; + WINDOW_TOP (XWINDOW (sib)) = WINDOW_TOP (w); + WINDOW_LEFT (XWINDOW (sib)) = WINDOW_LEFT (w); + } + + /* Stretch that sibling. */ + if (!NILP (par->vchild)) + set_window_pixheight + (sib, (WINDOW_HEIGHT (XWINDOW (sib)) + WINDOW_HEIGHT (w)), 1); + if (!NILP (par->hchild)) + set_window_pixwidth + (sib, (WINDOW_WIDTH (XWINDOW (sib)) + WINDOW_WIDTH (w)), 1); + } + + /* If parent now has only one child, + put the child into the parent's place. */ + { + Lisp_Object parchild = par->hchild; + if (NILP (parchild)) + parchild = par->vchild; + if (NILP (XWINDOW (parchild)->next)) + { + replace_window (parent, parchild); + mark_window_as_deleted (XWINDOW (parent)); + } + } + + /* Since we may be deleting combination windows, we must make sure that + not only W but all its children have been marked as deleted. */ + if (!NILP (w->hchild)) + delete_all_subwindows (XWINDOW (w->hchild)); + else if (!NILP (w->vchild)) + delete_all_subwindows (XWINDOW (w->vchild)); + + mark_window_as_deleted (w); + + f->mirror_dirty = 1; + return Qnil; +} + + +DEFUN ("next-window", Fnext_window, Snext_window, 0, 4, 0 /* +Return next window after WINDOW in canonical ordering of windows. +If omitted, WINDOW defaults to the selected window. + +Optional second arg MINIBUF t means count the minibuffer window even +if not active. MINIBUF nil or omitted means count the minibuffer iff +it is active. MINIBUF neither t nor nil means not to count the +minibuffer even if it is active. + +Several frames may share a single minibuffer; if the minibuffer +counts, all windows on all frames that share that minibuffer count +too. Therefore, `next-window' can be used to iterate through the +set of windows even when the minibuffer is on another frame. If the +minibuffer does not count, only windows from WINDOW's frame count. + +Optional third arg ALL-FRAMES t means include windows on all frames. +ALL-FRAMES nil or omitted means cycle within the frames as specified +above. ALL-FRAMES = `visible' means include windows on all visible frames. +ALL-FRAMES = 0 means include windows on all visible and iconified frames. +If ALL-FRAMES is a frame, restrict search to windows on that frame. +Anything else means restrict to WINDOW's frame. + +Optional fourth argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on WINDOW's console. Otherwise, all windows are considered. + +If you use consistent values for MINIBUF, ALL-FRAMES, and CONSOLE, you +can use `next-window' to iterate through the entire cycle of acceptable +windows, eventually ending up back at the window you started with. +`previous-window' traverses the same cycle, in the reverse order. +*/ ) + (window, minibuf, all_frames, console) + Lisp_Object window, minibuf, all_frames, console; +{ + Lisp_Object tem; + Lisp_Object start_window; + + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_LIVE_WINDOW (window); + + start_window = window; + + /* minibuf == nil may or may not include minibuffers. + Decide if it does. */ + if (NILP (minibuf)) + minibuf = (minibuf_level ? minibuf_window : Qlambda); + else if (! EQ (minibuf, Qt)) + minibuf = Qlambda; + /* Now minibuf can be t => count all minibuffer windows, + lambda => count none of them, + or a specific minibuffer window (the active one) to count. */ + + /* all_frames == nil doesn't specify which frames to include. */ + if (NILP (all_frames)) + all_frames = (! EQ (minibuf, Qlambda) + ? (FRAME_MINIBUF_WINDOW + (XFRAME + (WINDOW_FRAME + (XWINDOW (window))))) + : Qnil); + else if (EQ (all_frames, Qvisible)) + ; + else if (ZEROP (all_frames)) + ; + else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window))) + /* If all_frames is a frame and window arg isn't on that frame, just + return the first window on the frame. */ + return frame_first_window (XFRAME (all_frames)); + else if (! EQ (all_frames, Qt)) + all_frames = Qnil; + /* Now all_frames is t meaning search all frames, + nil meaning search just current frame, + visible meaning search just visible frames, + 0 meaning search visible and iconified frames, + or a window, meaning search the frame that window belongs to. */ + + /* Do this loop at least once, to get the next window, and perhaps + again, if we hit the minibuffer and that is not acceptable. */ + do + { + /* Find a window that actually has a next one. This loop + climbs up the tree. */ + while (tem = XWINDOW (window)->next, NILP (tem)) + if (tem = XWINDOW (window)->parent, !NILP (tem)) + window = tem; + else /* window must be minibuffer window now */ + { + /* We've reached the end of this frame. + Which other frames are acceptable? */ + tem = WINDOW_FRAME (XWINDOW (window)); + + if (! NILP (all_frames)) + { + Lisp_Object tem1; + + tem1 = tem; + tem = next_frame (tem, all_frames, console); + /* In the case where the minibuffer is active, + and we include its frame as well as the selected one, + next_frame may get stuck in that frame. + If that happens, go back to the selected frame + so we can complete the cycle. */ + if (EQ (tem, tem1)) + XSETFRAME (tem, selected_frame ()); + } + + tem = FRAME_ROOT_WINDOW (XFRAME (tem)); + break; + } + + window = tem; + + /* If we're in a combination window, find its first child and + recurse on that. Otherwise, we've found the window we want. */ + while (1) + { + if (!NILP (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NILP (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else break; + } + } + /* "acceptable" is the correct spelling. */ + /* Which windows are acceptable? + Exit the loop and accept this window if + this isn't a minibuffer window, + or we're accepting all minibuffer windows, + or this is the active minibuffer and we are accepting that one, or + we've come all the way around and we're back at the original window. */ + while (MINI_WINDOW_P (XWINDOW (window)) + && ! EQ (minibuf, Qt) + && ! EQ (minibuf, window) + && ! EQ (window, start_window)); + + return window; +} + +DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 4, 0 /* +Return the window preceeding WINDOW in canonical ordering of windows. +If omitted, WINDOW defaults to the selected window. + +Optional second arg MINIBUF t means count the minibuffer window even +if not active. MINIBUF nil or omitted means count the minibuffer iff +it is active. MINIBUF neither t nor nil means not to count the +minibuffer even if it is active. + +Several frames may share a single minibuffer; if the minibuffer +counts, all windows on all frames that share that minibuffer count +too. Therefore, `previous-window' can be used to iterate through +the set of windows even when the minibuffer is on another frame. If +the minibuffer does not count, only windows from WINDOW's frame count + +If optional third arg ALL-FRAMES t means include windows on all frames. +ALL-FRAMES nil or omitted means cycle within the frames as specified +above. ALL-FRAMES = `visible' means include windows on all visible frames. +ALL-FRAMES = 0 means include windows on all visible and iconified frames. +If ALL-FRAMES is a frame, restrict search to windows on that frame. +Anything else means restrict to WINDOW's frame. + +Optional fourth argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on WINDOW's console. Otherwise, all windows are considered. + +If you use consistent values for MINIBUF, ALL-FRAMES, and CONSOLE, you +can use `previous-window' to iterate through the entire cycle of acceptable +windows, eventually ending up back at the window you started with. +`next-window' traverses the same cycle, in the reverse order. +*/ ) + (window, minibuf, all_frames, console) + Lisp_Object window, minibuf, all_frames, console; +{ + Lisp_Object tem; + Lisp_Object start_window; + + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_LIVE_WINDOW (window); + + start_window = window; + + /* minibuf == nil may or may not include minibuffers. + Decide if it does. */ + if (NILP (minibuf)) + minibuf = (minibuf_level ? minibuf_window : Qlambda); + else if (! EQ (minibuf, Qt)) + minibuf = Qlambda; + /* Now minibuf can be t => count all minibuffer windows, + lambda => count none of them, + or a specific minibuffer window (the active one) to count. */ + + /* all_frames == nil doesn't specify which frames to include. + Decide which frames it includes. */ + if (NILP (all_frames)) + all_frames = (! EQ (minibuf, Qlambda) + ? (FRAME_MINIBUF_WINDOW + (XFRAME + (WINDOW_FRAME + (XWINDOW (window))))) + : Qnil); + else if (EQ (all_frames, Qvisible)) + ; + else if (ZEROP (all_frames)) + ; + else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window))) + /* If all_frames is a frame and window arg isn't on that frame, just + return the first window on the frame. */ + return frame_first_window (XFRAME (all_frames)); + else if (! EQ (all_frames, Qt)) + all_frames = Qnil; + /* Now all_frames is t meaning search all frames, + nil meaning search just current frame, + visible meaning search just visible frames, + 0 meaning search visible and iconified frames, + or a window, meaning search the frame that window belongs to. */ + + /* Do this loop at least once, to get the next window, and perhaps + again, if we hit the minibuffer and that is not acceptable. */ + do + { + /* Find a window that actually has a next one. This loop + climbs up the tree. */ + while (tem = XWINDOW (window)->prev, NILP (tem)) + if (tem = XWINDOW (window)->parent, !NILP (tem)) + window = tem; + else /* window must be minibuffer window now */ + { + /* We have found the top window on the frame. + Which frames are acceptable? */ + tem = WINDOW_FRAME (XWINDOW (window)); + + if (! NILP (all_frames)) + /* It's actually important that we use prev_frame here, + rather than next_frame. All the windows acceptable + according to the given parameters should form a ring; + Fnext_window and Fprevious_window should go back and + forth around the ring. If we use next_frame here, + then Fnext_window and Fprevious_window take different + paths through the set of acceptable windows. + window_loop assumes that these `ring' requirement are + met. */ + { + Lisp_Object tem1; + + tem1 = tem; + tem = prev_frame (tem, all_frames, console); + /* In the case where the minibuffer is active, + and we include its frame as well as the selected one, + next_frame may get stuck in that frame. + If that happens, go back to the selected frame + so we can complete the cycle. */ + if (EQ (tem, tem1)) + XSETFRAME (tem, selected_frame ()); + } + + /* If this frame has a minibuffer, find that window first, + because it is conceptually the last window in that frame. */ + if (FRAME_HAS_MINIBUF_P (XFRAME (tem))) + tem = FRAME_MINIBUF_WINDOW (XFRAME (tem)); + else + tem = FRAME_ROOT_WINDOW (XFRAME (tem)); + + break; + } + + window = tem; + + /* If we're in a combination window, find its first child and + recurse on that. Otherwise, we've found the window we want. */ + while (1) + { + if (!NILP (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NILP (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else break; + while (tem = XWINDOW (window)->next, !NILP (tem)) + window = tem; + } + } + /* "acceptable" is the correct spelling. */ + /* Which windows are acceptable? + Exit the loop and accept this window if + this isn't a minibuffer window, + or we're accepting all minibuffer windows, + or this is the active minibuffer and we are accepting that one, or + we've come all the way around and we're back at the original window. */ + while (MINI_WINDOW_P (XWINDOW (window)) + && ! EQ (minibuf, Qt) + && ! EQ (minibuf, window) + && ! EQ (window, start_window)); + + return window; +} + +DEFUN ("next-vertical-window", Fnext_vertical_window, Snext_vertical_window, + 0, 1, 0 /* +Return the next window which is vertically after WINDOW. +*/ ) + (window) + Lisp_Object window; +{ + Lisp_Object root; + struct window *w = decode_window (window); + XSETWINDOW (window, w); + + if (MINI_WINDOW_P (XWINDOW (window))) + return Qnil; + + root = FRAME_ROOT_WINDOW (XFRAME (WINDOW_FRAME (XWINDOW (window)))); + + if (EQ (window, root)) + { + while (1) + if (!NILP (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NILP (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else + return window; + } + + do + { + if (!NILP (XWINDOW (window)->parent) && + !NILP (XWINDOW (XWINDOW (window)->parent)->vchild)) + { + if (!NILP (XWINDOW (window)->next)) + return XWINDOW (window)->next; + else + window = XWINDOW (window)->parent; + } + else + window = XWINDOW (window)->parent; + } + while (!EQ (window, root)); + + while (1) + if (!NILP (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NILP (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else + return window; +} + +DEFUN ("other-window", Fother_window, Sother_window, 1, 3, "p" /* +Select the N'th different window on this frame. +All windows on current frame are arranged in a cyclic order. +This command selects the window N steps away in that order. +A negative N moves in the opposite order. + +If optional argument FRAME is `visible', search all visible frames. +If FRAME is 0, search all visible and iconified frames. +If FRAME is t, search all frames. +If FRAME is nil, search only the selected frame. +If FRAME is a frame, search only that frame. + +Optional third argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on FRAME'S console, or on the selected console if FRAME is not a frame. +Otherwise, all windows are considered. +*/ ) + (n, frame, console) + Lisp_Object n, frame, console; +{ + int i; + Lisp_Object w; + + CHECK_INT (n); + w = Fselected_window (Qnil); + i = XINT (n); + + while (i > 0) + { + w = Fnext_window (w, Qnil, frame, console); + i--; + } + while (i < 0) + { + w = Fprevious_window (w, Qnil, frame, console); + i++; + } + Fselect_window (w); + return Qnil; +} + + +/* Look at all windows, performing an operation specified by TYPE + with argument OBJ. + + If FRAMES is Qt, look at all frames, if Qnil, look at just the selected + frame. If FRAMES is a frame, just look at windows on that frame. + If MINI is non-zero, perform the operation on minibuffer windows too. +*/ + +enum window_loop +{ + WINDOW_LOOP_UNUSED, + GET_BUFFER_WINDOW, /* Arg is buffer */ + GET_LRU_WINDOW, /* Arg is t for full-width windows only */ + DELETE_OTHER_WINDOWS, /* Arg is window not to delete */ + DELETE_BUFFER_WINDOWS, /* Arg is buffer */ + GET_LARGEST_WINDOW, + UNSHOW_BUFFER, /* Arg is buffer */ + GET_BUFFER_WINDOW_COUNT, /* Arg is buffer */ + GET_BUFFER_MRU_WINDOW /* Arg is buffer */ +}; + +static Lisp_Object +window_loop (enum window_loop type, + Lisp_Object obj, + int mini, + Lisp_Object frames, + int dedicated_too, + Lisp_Object console) +{ + /* This function can GC if type == DELETE_BUFFER_WINDOWS or UNSHOW_BUFFER */ + Lisp_Object w; + Lisp_Object best_window = Qnil; + Lisp_Object next_window; + Lisp_Object last_window; + struct frame *frame; + Lisp_Object frame_arg = Qt; + int count = 0; /* for GET_BUFFER_WINDOW_COUNT */ + /* #### I think the change of "precomputing" last_window and next_window + * #### catch the lossage this is meant(?) to punt on... + */ + int lose_lose = 0; + Lisp_Object devcons, concons; + + /* FRAME_ARG is Qlambda to stick to one frame, + Qvisible to consider all visible frames, + or Qt otherwise. */ + + /* If we're only looping through windows on a particular frame, + FRAME points to that frame. If we're looping through windows + on all frames, FRAME is 0. */ + + if (FRAMEP (frames)) + frame = XFRAME (frames); + else if (NILP (frames)) + frame = selected_frame (); + else + frame = 0; + if (frame) + frame_arg = Qlambda; + else if (ZEROP (frames)) + frame_arg = frames; + else if (EQ (frames, Qvisible)) + frame_arg = frames; + + DEVICE_LOOP_NO_BREAK (devcons, concons) + { + Lisp_Object device = XCAR (devcons); + Lisp_Object the_frame = Qnil; + + if (frame) + XSETFRAME (the_frame, frame); + else + the_frame = DEVICE_SELECTED_FRAME (XDEVICE (device)); + + if (NILP (the_frame)) + continue; + + if (!device_matches_console_spec (the_frame, device, console)) + continue; + + /* Pick a window to start with. */ + if (WINDOWP (obj)) + w = obj; + else + w = FRAME_SELECTED_WINDOW (XFRAME (the_frame)); + + /* Figure out the last window we're going to mess with. Since + Fnext_window, given the same options, is guaranteed to go in a + ring, we can just use Fprevious_window to find the last one. + + We can't just wait until we hit the first window again, + because it might be deleted. */ + + last_window = Fprevious_window (w, mini ? Qt : Qnil, frame_arg, Qt); + + best_window = Qnil; + for (;;) + { + struct window *p = XWINDOW (w); + struct frame *w_frame = XFRAME (WINDOW_FRAME (p)); + + /* Pick the next window now, since some operations will delete + the current window. */ + next_window = Fnext_window (w, mini ? Qt : Qnil, frame_arg, Qt); + + /* #### Still needed ?? */ + /* Given the outstanding quality of the rest of this code, + I feel no shame about putting this piece of shit in. */ + if (++lose_lose >= 500) + return Qnil; + + /* Note that we do not pay attention here to whether + the frame is visible, since Fnext_window skips non-visible frames + if that is desired, under the control of frame_arg. */ + if (! MINI_WINDOW_P (p) + || (mini && minibuf_level > 0)) + switch (type) + { + case GET_BUFFER_WINDOW: + { + if (XBUFFER (p->buffer) == XBUFFER (obj)) + return w; + break; + } + + case GET_BUFFER_WINDOW_COUNT: + { + if (XBUFFER (p->buffer) == XBUFFER (obj)) + count++; + break; + } + + case GET_LRU_WINDOW: + { + /* t as arg means consider only full-width windows */ + if (!NILP (obj) + && !window_full_width_p (p)) + break; + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (p) + || (dedicated_too ? 0 : !NILP (p->dedicated))) + break; + if (NILP (best_window) + || (XINT (XWINDOW (best_window)->use_time) + > XINT (p->use_time))) + best_window = w; + break; + } + + case GET_BUFFER_MRU_WINDOW: + { + /* #### what about the first check in GET_LRU_WINDOW? */ + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (p) + || (dedicated_too ? 0 : !NILP (p->dedicated))) + break; + + if (XBUFFER (p->buffer) == XBUFFER (obj)) + { + if (NILP (best_window) + || (XINT (XWINDOW (best_window)->use_time) + < XINT (p->use_time))) + best_window = w; + } + break; + } + + case DELETE_OTHER_WINDOWS: + { + /* Don't delete the last window on a frame; this can + happen when the minibuffer is selected, and would + cause the frame to be deleted. */ + if (p != XWINDOW (obj) && !TOP_LEVEL_WINDOW_P (XWINDOW (w))) + Fdelete_window (w, Qnil); + break; + } + + case DELETE_BUFFER_WINDOWS: + { + if (EQ (p->buffer, obj)) + { + struct frame *f = XFRAME (WINDOW_FRAME (p)); + + /* If this window is dedicated, and in a frame + of its own, kill the frame. */ + if (EQ (w, FRAME_ROOT_WINDOW (f)) + && !NILP (p->dedicated) + && other_visible_frames (f)) + { + /* Skip the other windows on this frame. + There might be one, the minibuffer! */ + if (! EQ (w, last_window)) + while (f == XFRAME (WINDOW_FRAME + (XWINDOW (next_window)))) + { + /* As we go, check for the end of the + loop. We mustn't start going + around a second time. */ + if (EQ (next_window, last_window)) + { + last_window = w; + break; + } + next_window = Fnext_window (next_window, + mini ? Qt : Qnil, + frame_arg, Qt); + } + /* Now we can safely delete the frame. */ + Fdelete_frame (WINDOW_FRAME (p), Qnil); + } + else + /* If we're deleting the buffer displayed in + the only window on the frame, find a new + buffer to display there. */ + if (NILP (p->parent)) + { + Lisp_Object new_buffer; + new_buffer = Fother_buffer (obj, Qnil, Qnil); + if (NILP (new_buffer)) + new_buffer = Fget_buffer_create (QSscratch); + Fset_window_buffer (w, new_buffer); + if (EQ (w, Fselected_window (Qnil))) + Fset_buffer (p->buffer); + } + else + Fdelete_window (w, Qnil); + } + break; + } + + case GET_LARGEST_WINDOW: + { + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (p) + || (dedicated_too ? 0 : !NILP (p->dedicated))) + break; + { + /* write the check as follows to avoid tripping + error_check_window() --ben */ + struct window *b = NILP (best_window) ? 0 : + XWINDOW (best_window); + if (NILP (best_window) + || ((WINDOW_HEIGHT (p) * WINDOW_WIDTH (p)) + > (WINDOW_HEIGHT (b) * WINDOW_WIDTH (b)))) + best_window = w; + } + break; + } + + case UNSHOW_BUFFER: + { + if (EQ (p->buffer, obj)) + { + /* Find another buffer to show in this window. */ + Lisp_Object another_buffer = + Fother_buffer (obj, Qnil, Qnil); + if (NILP (another_buffer)) + another_buffer + = Fget_buffer_create (QSscratch); + /* If this window is dedicated, and in a frame + of its own, kill the frame. */ + if (EQ (w, FRAME_ROOT_WINDOW (w_frame)) + && !NILP (p->dedicated) + && other_visible_frames (w_frame)) + { + /* Skip the other windows on this frame. + There might be one, the minibuffer! */ + if (! EQ (w, last_window)) + while (w_frame == XFRAME (WINDOW_FRAME + (XWINDOW (next_window)))) + { + /* As we go, check for the end of the + loop. We mustn't start going + around a second time. */ + if (EQ (next_window, last_window)) + { + last_window = w; + break; + } + next_window = Fnext_window (next_window, + mini ? Qt : Qnil, + frame_arg, Qt); + } + /* Now we can safely delete the frame. */ + delete_frame_internal (XFRAME (WINDOW_FRAME (p)), + 0, 0, 0); + } + else + { + /* Otherwise show a different buffer in the + window. */ + p->dedicated = Qnil; + Fset_window_buffer (w, another_buffer); + if (EQ (w, Fselected_window (Qnil))) + Fset_buffer (p->buffer); + } + } + break; + } + + default: + abort (); + } + + if (EQ (w, last_window)) + break; + + w = next_window; + } + } + + if (type == GET_BUFFER_WINDOW_COUNT) + return (make_int (count)); + else + return (best_window); +} + +#if 0 /* not currently used */ + +int +buffer_window_count (struct buffer *b, struct frame *f) +{ + Lisp_Object buffer, frame; + + XSETFRAME (frame, f); + XSETBUFFER (buffer, b); + + return XINT (window_loop (GET_BUFFER_WINDOW_COUNT, buffer, 0, frame, 1, + Qnil)); +} + +int +buffer_window_mru (struct window *w) +{ + Lisp_Object window = + window_loop (GET_BUFFER_MRU_WINDOW, w->buffer, 0, w->frame, 1, Qnil); + + if (NILP (window)) + return 0; + else if (XWINDOW (window) == w) + return 1; + else + return 0; +} + +#endif + + +DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 2, 0 /* +Return the window least recently selected or used for display. +If optional argument FRAME is `visible', search all visible frames. +If FRAME is 0, search all visible and iconified frames. +If FRAME is t, search all frames. +If FRAME is nil, search only the selected frame. +If FRAME is a frame, search only that frame. + +Optional second argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on FRAME'S console, or on the selected console if FRAME is not a frame. +Otherwise, all windows are considered. +*/ ) + (frame, console) + Lisp_Object frame, console; +{ + Lisp_Object w; + /* First try for a non-dedicated window that is full-width */ + w = window_loop (GET_LRU_WINDOW, Qt, 0, frame, 0, console); + if (!NILP (w) && !EQ (w, Fselected_window (Qnil))) + return w; + + /* Then try for any non-dedicated window */ + w = window_loop (GET_LRU_WINDOW, Qnil, 0, frame, 0, console); + if (!NILP (w) && !EQ (w, Fselected_window (Qnil))) + return w; + +#if 0 + /* FSFmacs never returns a dedicated window here. If we do, + it makes `display-buffer' not work right. #### All of this + shit is so disgusting and awful that it needs to be rethought + from scratch. */ + /* then try for a dedicated window that is full-width */ + w = window_loop (GET_LRU_WINDOW, Qt, 0, frame, 1, console); + if (!NILP (w) && !EQ (w, Fselected_window (Qnil))) + return w; + + /* If none of them, then all windows, dedicated or not. */ + w = window_loop (GET_LRU_WINDOW, Qnil, 0, frame, 1, console); + + /* At this point we damn well better have found something. */ + if (NILP (w)) abort (); +#endif + + return (w); +} + +DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 2, 0 /* +Return the window largest in area. +If optional argument FRAME is `visible', search all visible frames. +If FRAME is 0, search all visible and iconified frames. +If FRAME is t, search all frames. +If FRAME is nil, search only the selected frame. +If FRAME is a frame, search only that frame. + +Optional second argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on FRAME'S console, or on the selected console if FRAME is not a frame. +Otherwise, all windows are considered. +*/ ) + (frame, console) + Lisp_Object frame, console; +{ + /* Don't search dedicated windows because FSFmacs doesn't. + This stuff is all black magic so don't try to apply common + sense to it. */ + return window_loop (GET_LARGEST_WINDOW, Qnil, 0, frame, 0, console); +} + +DEFUN ("get-buffer-window", Fget_buffer_window, Sget_buffer_window, 1, 3, 0 /* +Return a window currently displaying BUFFER, or nil if none. +If optional argument FRAME is `visible', search all visible frames. +If optional argument FRAME is 0, search all visible and iconified frames. +If FRAME is t, search all frames. +If FRAME is nil, search only the selected frame. +If FRAME is a frame, search only that frame. + +Optional third argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on FRAME'S console, or on the selected console if FRAME is not a frame. +Otherwise, all windows are considered. +*/ ) + (buffer, frame, console) + Lisp_Object buffer, frame, console; +{ + buffer = Fget_buffer (buffer); + if (BUFFERP (buffer)) + /* Search dedicated windows too. (Doesn't matter here anyway.) */ + return window_loop (GET_BUFFER_WINDOW, buffer, 1, frame, 1, console); + else + return Qnil; +} + +/* These functions used to be `buffer-left-margin-pixel-width', etc. + but there is no sensible way to implement those functions, since + you can't in general derive a window from a buffer. */ + +DEFUN ("window-left-margin-pixel-width", Fwindow_left_margin_pixel_width, + Swindow_left_margin_pixel_width, 0, 1, 0 /* +Return the width in pixels of the left outside margin of window WINDOW. +If WINDOW is nil, the selected window is assumed. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + + return (make_int (window_left_margin_width (w))); +} + +DEFUN ("window-right-margin-pixel-width", Fwindow_right_margin_pixel_width, + Swindow_right_margin_pixel_width, 0, 1, 0 /* +Return the width in pixels of the right outside margin of window WINDOW. +If WINDOW is nil, the selected window is assumed. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + + return (make_int (window_right_margin_width (w))); +} + +DEFUN ("delete-other-windows", Fdelete_other_windows, Sdelete_other_windows, + 0, 1, "" /* +Make WINDOW (or the selected window) fill its frame. +Only the frame WINDOW is on is affected. +This function tries to reduce display jumps +by keeping the text previously visible in WINDOW +in the same place on the frame. Doing this depends on +the value of (window-start WINDOW), so if calling this function +in a program gives strange scrolling, make sure the window-start +value is reasonable when this function is called. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w = decode_window (window); + struct buffer *b = XBUFFER (w->buffer); + Bufpos start_pos; + int old_top = WINDOW_TOP (w); + + XSETWINDOW (window, w); + + if (MINI_WINDOW_P (w) && old_top > 0) + error ("Can't expand minibuffer to full frame"); + + /* Ignore dedicated windows. */ + window_loop (DELETE_OTHER_WINDOWS, window, 0, w->frame, 0, Qnil); + + start_pos = marker_position (w->start[CURRENT_DISP]); + + /* Try to minimize scrolling, by setting the window start to the + point which will cause the text at the old window start to be at + the same place on the frame. But don't try to do this if the + window start is outside the visible portion (as might happen when + the display is not current, due to typeahead). */ + if (start_pos >= BUF_BEGV (b) && start_pos <= BUF_ZV (b) + && !MINI_WINDOW_P (w)) + { + Bufpos new_start = start_with_line_at_pixpos (w, start_pos, old_top); + + if (new_start >= BUF_BEGV (b) && new_start <= BUF_ZV (b)) + { + Fset_marker (w->start[CURRENT_DISP], make_int (new_start), + w->buffer); + w->start_at_line_beg = beginning_of_line_p (b, new_start); + } + /* We need to do this, so that the window-scroll-functions + get called. */ + w->force_start = 1; + } + + return Qnil; +} + +DEFUN ("delete-windows-on", Fdelete_windows_on, Sdelete_windows_on, + 1, 3, "bDelete windows on (buffer): " /* +Delete all windows showing BUFFER. +Optional second argument FRAME controls which frames are affected. +If nil or omitted, delete all windows showing BUFFER in any frame. +If t, delete only windows showing BUFFER in the selected frame. +If `visible', delete all windows showing BUFFER in any visible frame. +If a frame, delete only windows showing BUFFER in that frame. + +Optional third argument CONSOLE controls which consoles or devices the +returned window may be on. If CONSOLE is a console, return windows only +on that console. If CONSOLE is a device, return windows only on that +device. If CONSOLE is a console type, return windows only on consoles +of that type. If CONSOLE is 'window-system, return any windows on any +window-system consoles. If CONSOLE is nil or omitted, return windows only +on FRAME'S console, or on the selected console if FRAME is not a frame. +Otherwise, all windows are considered. +*/ ) + (buffer, frame, console) + Lisp_Object buffer, frame, console; +{ + /* This function can GC */ + /* FRAME uses t and nil to mean the opposite of what window_loop + expects. */ + if (!FRAMEP (frame)) + frame = NILP (frame) ? Qt : Qnil; + + if (!NILP (buffer)) + { + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer); + /* Ignore dedicated windows. */ + window_loop (DELETE_BUFFER_WINDOWS, buffer, 0, frame, 0, console); + } + return Qnil; +} + +DEFUN ("replace-buffer-in-windows", Freplace_buffer_in_windows, + Sreplace_buffer_in_windows, + 1, 1, "bReplace buffer in windows: " /* +Replace BUFFER with some other buffer in all windows showing it. +*/ ) + (buffer) + Lisp_Object buffer; +{ + /* This function can GC */ + if (!NILP (buffer)) + { + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer); + /* Ignore dedicated windows. */ + window_loop (UNSHOW_BUFFER, buffer, 0, Qt, 0, Qnil); + } + return Qnil; +} + +/* The smallest acceptable dimensions for a window. Anything smaller + might crash Emacs. */ +#define MIN_SAFE_WINDOW_WIDTH (2) +#define MIN_SAFE_WINDOW_HEIGHT (2) + +/* Make sure that window_min_height and window_min_width are + not too small; if they are, set them to safe minima. */ + +static void +check_min_window_sizes (void) +{ + /* Smaller values might permit a crash. */ + if (window_min_width < MIN_SAFE_WINDOW_WIDTH) + window_min_width = MIN_SAFE_WINDOW_WIDTH; + if (window_min_height < MIN_SAFE_WINDOW_HEIGHT) + window_min_height = MIN_SAFE_WINDOW_HEIGHT; +} + +/* If *ROWS or *COLS are too small a size for FRAME, set them to the + minimum allowable size. */ +void +check_frame_size (struct frame *frame, int *rows, int *cols) +{ + /* For height, we have to see whether the frame has a minibuffer, and + whether it wants a modeline. */ + int min_height = + (FRAME_MINIBUF_ONLY_P (frame) ? MIN_SAFE_WINDOW_HEIGHT - 1 + : (! FRAME_HAS_MINIBUF_P (frame)) ? MIN_SAFE_WINDOW_HEIGHT + : 2 * MIN_SAFE_WINDOW_HEIGHT - 1); + + if (*rows < min_height) + *rows = min_height; + if (*cols < MIN_SAFE_WINDOW_WIDTH) + *cols = MIN_SAFE_WINDOW_WIDTH; +} + +/* Normally the window is deleted if it gets too small. + nodelete nonzero means do not do this. + (The caller should check later and do so if appropriate) */ +static void +set_window_pixsize (Lisp_Object window, int new_pixsize, int nodelete, + int set_height) +{ + struct window *w = XWINDOW (window); + struct frame *f = XFRAME (w->frame); + struct window *c; + int old_pixsize = (set_height ? WINDOW_HEIGHT (w) : WINDOW_WIDTH (w)); + Lisp_Object child, minor_kid, major_kid; + int minsize; + int line_size; + int defheight, defwidth; + + /* #### This is very likely incorrect and instead the char_to_pixel_ + functions should be called. */ + default_face_height_and_width (window, &defheight, &defwidth); + line_size = (set_height ? defheight : defwidth); + + minsize = (set_height ? window_min_height : window_min_width); + minsize *= line_size; + + check_min_window_sizes (); + + if (!nodelete + && !TOP_LEVEL_WINDOW_P (w) + && new_pixsize < minsize) + { + Fdelete_window (window, Qnil); + return; + } + + SET_LAST_MODIFIED (w, 0); + SET_LAST_FACECHANGE (w); + MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); /* multiple windows affected */ + if (set_height) + { + WINDOW_HEIGHT (w) = new_pixsize; + major_kid = w->vchild; + minor_kid = w->hchild; + } + else + { + WINDOW_WIDTH (w) = new_pixsize; + major_kid = w->hchild; + minor_kid = w->vchild; + } + + if (!NILP (minor_kid)) + { + for (child = minor_kid; !NILP (child); child = XWINDOW (child)->next) + { + if (set_height) + WINDOW_TOP (XWINDOW (child)) = WINDOW_TOP (w); + else + WINDOW_LEFT (XWINDOW (child)) = WINDOW_LEFT (w); + + set_window_pixsize (child, new_pixsize, nodelete, set_height); + } + } + else if (!NILP (major_kid)) + { + int last_pos, last_old_pos, pos, old_pos, first; + int pixel_adj_left = new_pixsize - old_pixsize; + int div_val = old_pixsize << 1; + + /* Sometimes we may get called with our old size. In that case + we don't need to do anything. */ + if (!pixel_adj_left) + return; + + last_pos = first = (set_height ? WINDOW_TOP (w) : WINDOW_LEFT (w)); + last_old_pos = 0; + + for (child = major_kid; !NILP (child); child = c->next) + { + c = XWINDOW (child); + + if (set_height) + { + old_pos = last_old_pos + WINDOW_HEIGHT (c); + WINDOW_TOP (c) = last_pos; + } + else + { + old_pos = last_old_pos + WINDOW_WIDTH (c); + WINDOW_LEFT (c) = last_pos; + } + + pos = (((old_pos * new_pixsize) << 1) + old_pixsize) / div_val; + /* All but the the last window should have a height which is + a multiple of the default line height. */ + if (!NILP (c->next)) + pos = (pos / line_size) * line_size; + + /* Avoid confusion: don't delete child if it becomes too small */ + set_window_pixsize (child, pos + first - last_pos, 1, set_height); + + last_pos = pos + first; + last_old_pos = old_pos; + } + + /* Now delete any children that became too small. */ + if (!nodelete) + for (child = major_kid; !NILP (child); child = XWINDOW (child)->next) + { + if (set_height) + set_window_pixheight (child, WINDOW_HEIGHT (XWINDOW (child)), 0); + else + set_window_pixwidth (child, WINDOW_WIDTH (XWINDOW (child)), 0); + } + } +} + +/* Set the height of WINDOW and all its inferiors. */ +void +set_window_pixheight (Lisp_Object window, int new_pixheight, int nodelete) +{ + set_window_pixsize (window, new_pixheight, nodelete, 1); +} + +/* Recursively set width of WINDOW and its inferiors. */ +void +set_window_pixwidth (Lisp_Object window, int new_pixwidth, int nodelete) +{ + set_window_pixsize (window, new_pixwidth, nodelete, 0); +} + + +static int window_select_count; + +DEFUN ("set-window-buffer", Fset_window_buffer, Sset_window_buffer, 2, 2, 0 /* +Make WINDOW display BUFFER as its contents. +BUFFER can be a buffer or buffer name. +*/ ) + (window, buffer) + Lisp_Object window, buffer; +{ + Lisp_Object tem; + struct window *w = decode_window (window); + + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer); + + if (!BUFFER_LIVE_P (XBUFFER (buffer))) + error ("Attempt to display deleted buffer"); + + tem = w->buffer; + if (NILP (tem)) + error ("Window is deleted"); + + /* While this seems like a logical thing to do, it causes problems + because of saved window configurations. It is possible for a + buffer to get restored into a window in which it is already being + displayed, but start and point are actually at completely + different locations. So we let this function complete fully and + it will then make sure redisplay correctly updates things. + + #### This is a kludge. The correct approach is not to do this + but to fix set-window-configuration. */ +#if 0 + else if (EQ (tem, buffer)) + return (Qnil); +#endif + else if (! EQ (tem, Qt)) /* w->buffer is t when the window + is first being set up. */ + { + if (!NILP (w->dedicated) && !EQ (tem, buffer)) + error ("Window is dedicated to buffer %s", + string_data (XSTRING (XBUFFER (tem)->name))); + + unshow_buffer (w); + } + + w->buffer = buffer; + w->window_end_pos[CURRENT_DISP] = 0; + w->hscroll = 0;; + Fset_marker (w->pointm[CURRENT_DISP], + make_int (BUF_PT (XBUFFER (buffer))), + buffer); + set_marker_restricted (w->start[CURRENT_DISP], + make_int (XBUFFER (buffer)->last_window_start), + buffer); + Fset_marker (w->sb_point, w->start[CURRENT_DISP], buffer); + w->start_at_line_beg = 0; + w->force_start = 0; /* Lucid fix */ + SET_LAST_MODIFIED (w, 1); + SET_LAST_FACECHANGE (w); + MARK_WINDOWS_CHANGED (w); + recompute_all_cached_specifiers_in_window (w); + if (EQ (window, Fselected_window (Qnil))) + { + Fset_buffer (buffer); +#ifdef ENERGIZE + energize_buffer_shown_hook (w); +#endif + } + return Qnil; +} + +DEFUN ("select-window", Fselect_window, Sselect_window, 1, 1, 0 /* +Select WINDOW. Most editing will apply to WINDOW's buffer. +The main editor command loop selects the buffer of the selected window +before each command. +*/ ) + (window) + Lisp_Object window; +{ + struct window *w; + Lisp_Object old_selected_window = Fselected_window (Qnil); + + CHECK_LIVE_WINDOW (window); + w = XWINDOW (window); + + /* we have already caught dead-window errors */ + if (!NILP (w->hchild) || !NILP (w->vchild)) + error ("Trying to select non-leaf window"); + + w->use_time = make_int (++window_select_count); + if (EQ (window, old_selected_window)) + return window; + + /* deselect the old window, if it exists (it might not exist if + the selected device has no frames, which occurs at startup) */ + if (!NILP (old_selected_window)) + { + struct window *ow = XWINDOW (old_selected_window); + +#ifdef ENERGIZE + if (! MINI_WINDOW_P (w)) + energize_window_deselected_hook (ow); +#endif + + Fset_marker (ow->pointm[CURRENT_DISP], + make_int (BUF_PT (XBUFFER (ow->buffer))), + ow->buffer); + + MARK_WINDOWS_CHANGED (ow); + } + + /* now select the window's frame */ + set_frame_selected_window (XFRAME (WINDOW_FRAME (w)), window); + + select_frame_1 (WINDOW_FRAME (w)); + + /* also select the window's buffer */ + Frecord_buffer (w->buffer); + Fset_buffer (w->buffer); + + /* Go to the point recorded in the window. + This is important when the buffer is in more + than one window. It also matters when + redisplay_window has altered point after scrolling, + because it makes the change only in the window. */ + { + Bufpos new_point = marker_position (w->pointm[CURRENT_DISP]); + if (new_point < BUF_BEGV (current_buffer)) + new_point = BUF_BEGV (current_buffer); + else if (new_point > BUF_ZV (current_buffer)) + new_point = BUF_ZV (current_buffer); + + BUF_SET_PT (current_buffer, new_point); + } + + MARK_WINDOWS_CHANGED (w); + +#ifdef ENERGIZE + if (! MINI_WINDOW_P (w)) + energize_window_selected_hook (w); +#endif + + return window; +} + +Lisp_Object +display_buffer (Lisp_Object buffer, Lisp_Object not_this_window_p, + Lisp_Object override_frame) +{ + return call3 (Qdisplay_buffer, buffer, not_this_window_p, override_frame); +} + +void +temp_output_buffer_show (Lisp_Object buf, Lisp_Object same_frame) +{ + /* This function can GC */ + Lisp_Object window; + struct window *w; + struct buffer *b = XBUFFER (buf); + + BUF_SAVE_MODIFF (XBUFFER (buf)) = BUF_MODIFF (b); + widen_buffer (b, 0); + BUF_SET_PT (b, BUF_BEG (b)); + + if (!EQ (Vtemp_buffer_show_function, Qnil)) + call1 (Vtemp_buffer_show_function, buf); + else + { + window = display_buffer (buf, Qnil, same_frame); + + if (!EQ (XWINDOW (window)->frame, Fselected_frame (Qnil))) + Fmake_frame_visible (WINDOW_FRAME (XWINDOW (window))); + + Vminibuf_scroll_window = window; + w = XWINDOW (window); + w->hscroll = 0; + set_marker_restricted (w->start[CURRENT_DISP], make_int (1), buf); + set_marker_restricted (w->pointm[CURRENT_DISP], make_int (1), buf); + set_marker_restricted (w->sb_point, make_int (1), buf); + + /* Run temp-buffer-show-hook, with the chosen window selected. */ + if (!preparing_for_armageddon) + { + Lisp_Object tem; + tem = Fboundp (Qtemp_buffer_show_hook); + if (!NILP (tem)) + { + tem = Fsymbol_value (Qtemp_buffer_show_hook); + if (!NILP (tem)) + { + int count = specpdl_depth (); + + /* Select the window that was chosen, for running + the hook. */ + record_unwind_protect (save_window_excursion_unwind, + Fcurrent_window_configuration (Qnil)); + + Fselect_window (window); + run_hook (Qtemp_buffer_show_hook); + unbind_to (count, Qnil); + } + } + } + } +} + +static void +make_dummy_parent (Lisp_Object window) +{ + Lisp_Object new = Qnil; + struct window *o = XWINDOW (window); + struct window *p = alloc_lcrecord (sizeof (struct window), + lrecord_window); + + XSETWINDOW (new, p); + copy_lcrecord (p, o); + + /* Don't copy the pointers to the line start cache or the face + instances. */ + p->line_start_cache = Dynarr_new (struct line_start_cache); + p->face_cachels = Dynarr_new (struct face_cachel); + p->glyph_cachels = Dynarr_new (struct glyph_cachel); + + /* Put new into window structure in place of window */ + replace_window (window, new); + + o->next = Qnil; + o->prev = Qnil; + o->vchild = Qnil; + o->hchild = Qnil; + o->parent = new; + + p->start[CURRENT_DISP] = Qnil; + p->start[DESIRED_DISP] = Qnil; + p->start[CMOTION_DISP] = Qnil; + p->pointm[CURRENT_DISP] = Qnil; + p->pointm[DESIRED_DISP] = Qnil; + p->pointm[CMOTION_DISP] = Qnil; + p->sb_point = Qnil; + p->buffer = Qnil; +} + +DEFUN ("split-window", Fsplit_window, Ssplit_window, 0, 3, "" /* +Split WINDOW, putting SIZE lines in the first of the pair. +WINDOW defaults to selected one and SIZE to half its size. +If optional third arg HOR-FLAG is non-nil, split side by side +and put SIZE columns in the first of the pair. +*/ ) + (window, chsize, horflag) + Lisp_Object window, chsize, horflag; +{ + Lisp_Object new; + struct window *o, *p; + struct frame *f; + int size; + int psize; + + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_WINDOW (window); + + o = XWINDOW (window); + f = XFRAME (WINDOW_FRAME (o)); + + if (NILP (chsize)) + { + if (!NILP (horflag)) + /* In the new scheme, we are symmetric with respect to separators + so there is no need to do weird things here. */ + { + psize = WINDOW_WIDTH (o) >> 1; + size = window_pixel_width_to_char_width (o, psize, 0); + } + else + { + psize = WINDOW_HEIGHT (o) >> 1; + size = window_pixel_height_to_char_height (o, psize, 1); + } + } + else + { + CHECK_INT (chsize); + size = XINT (chsize); + if (!NILP (horflag)) + psize = window_char_width_to_pixel_width (o, size, 0); + else + psize = window_char_height_to_pixel_height (o, size, 1); + } + + if (MINI_WINDOW_P (o)) + error ("Attempt to split minibuffer window"); + else if (FRAME_NO_SPLIT_P (XFRAME (WINDOW_FRAME (o)))) + error ("Attempt to split unsplittable frame"); + + check_min_window_sizes (); + + if (NILP (horflag)) + { + if (size < window_min_height) + error ("Window height %d too small (after splitting)", size); + if (size + window_min_height > window_char_height (o, 1)) + error ("Window height %d too small (after splitting)", + window_char_height (o, 1) - size); + if (NILP (o->parent) + || NILP (XWINDOW (o->parent)->vchild)) + { + make_dummy_parent (window); + reset_face_cachels (XWINDOW (window)); + new = o->parent; + XWINDOW (new)->vchild = window; + XFRAME (o->frame)->mirror_dirty = 1; + } + } + else + { + if (size < window_min_width) + error ("Window width %d too small (after splitting)", size); + if (size + window_min_width > window_char_width (o, 0)) + error ("Window width %d too small (after splitting)", + window_char_width (o, 0) - size); + if (NILP (o->parent) + || NILP (XWINDOW (o->parent)->hchild)) + { + make_dummy_parent (window); + reset_face_cachels (XWINDOW (window)); + new = o->parent; + XWINDOW (new)->hchild = window; + XFRAME (o->frame)->mirror_dirty = 1; + } + } + + /* Now we know that window's parent is a vertical combination + if we are dividing vertically, or a horizontal combination + if we are making side-by-side windows */ + + MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); + new = allocate_window (); + p = XWINDOW (new); + + p->frame = o->frame; + p->next = o->next; + if (!NILP (p->next)) + XWINDOW (p->next)->prev = new; + p->prev = window; + o->next = new; + p->parent = o->parent; + p->buffer = Qt; + + reset_face_cachels (p); + reset_glyph_cachels (p); + + + /* Apportion the available frame space among the two new windows */ + + if (!NILP (horflag)) + { + WINDOW_HEIGHT (p) = WINDOW_HEIGHT (o); + WINDOW_TOP (p) = WINDOW_TOP (o); + WINDOW_WIDTH (p) = WINDOW_WIDTH (o) - psize; + WINDOW_WIDTH (o) = psize; + WINDOW_LEFT (p) = WINDOW_LEFT (o) + psize; + } + else + { + WINDOW_LEFT (p) = WINDOW_LEFT (o); + WINDOW_WIDTH (p) = WINDOW_WIDTH (o); + WINDOW_HEIGHT (p) = WINDOW_HEIGHT (o) - psize; + WINDOW_HEIGHT (o) = psize; + WINDOW_TOP (p) = WINDOW_TOP (o) + psize; + } + + XFRAME (p->frame)->mirror_dirty = 1; + /* do this last (after the window is completely initialized and + the mirror-dirty flag is set) so that specifier recomputation + caused as a result of this will work properly and not abort. */ + Fset_window_buffer (new, o->buffer); + return new; +} + + +DEFUN ("enlarge-window", Fenlarge_window, Senlarge_window, 1, 3, "_p" /* +Make the selected window ARG lines bigger. +From program, optional second arg non-nil means grow sideways ARG columns, +and optional third ARG specifies the window to change instead of the +selected window. +*/ ) + (n, side, window) + Lisp_Object n, side, window; +{ + struct window *w = decode_window (window); + CHECK_INT (n); + change_window_height (w, XINT (n), !NILP (side)); + return Qnil; +} + +DEFUN ("shrink-window", Fshrink_window, Sshrink_window, 1, 3, "_p" /* +Make the selected window ARG lines smaller. +From program, optional second arg non-nil means shrink sideways ARG columns, +and optional third ARG specifies the window to change instead of the +selected window. +*/ ) + (n, side, window) + Lisp_Object n, side, window; +{ + struct window *w = decode_window (window); + CHECK_INT (n); + change_window_height (w, -XINT (n), !NILP (side)); + return Qnil; +} + +static int +window_pixel_height (Lisp_Object window) +{ + struct window *w = XWINDOW (window); + return WINDOW_HEIGHT (w); +} + +static int +window_pixel_height_to_char_height (struct window *w, int pixel_height, + int include_gutters_p) +{ + int avail_height; + int defheight, defwidth; + int char_height; + Lisp_Object window; + + XSETWINDOW (window, w); + + avail_height = (pixel_height - + (include_gutters_p ? 0 : + window_top_gutter_height (w) + + window_bottom_gutter_height (w))); + + default_face_height_and_width (window, &defheight, &defwidth); + + char_height = avail_height / defheight; + + /* It's the calling function's responsibility to check these values + and make sure they're not out of range. + + #### We need to go through the calling functions and actually + do this. */ + if (char_height < 0) + return 0; + else + return char_height; +} + +static int +window_char_height_to_pixel_height (struct window *w, int char_height, + int include_gutters_p) +{ + int avail_height; + int defheight, defwidth; + int pixel_height; + + Lisp_Object window; + + XSETWINDOW (window, w); + + default_face_height_and_width (window, &defheight, &defwidth); + + avail_height = char_height * defheight; + pixel_height = (avail_height + + (include_gutters_p ? 0 : + window_top_gutter_height (w) + + window_bottom_gutter_height (w))); + + /* It's the calling function's responsibility to check these values + and make sure they're not out of range. + + #### We need to go through the calling functions and actually + do this. */ + if (pixel_height < 0) + return 0; + else + return pixel_height; +} + +/* Return number of default lines of text can fit in the window W. + If INCLUDE_GUTTERS_P is 1, include "gutter" space (modeline plus + horizontal scrollbar) in the space that is used for the calculation. + */ +int +window_char_height (struct window *w, int include_gutters_p) +{ + return window_pixel_height_to_char_height (w, WINDOW_HEIGHT (w), + include_gutters_p); +} + +/* + * Return number of lines currently displayed in window w. If + * end-of-buffer is displayed then the area below end-of-buffer is assume + * to be blank lines of default height. + * Does not include the modeline. + */ +int +window_displayed_height (struct window *w) +{ + struct buffer *b = XBUFFER (w->buffer); + display_line_dynarr *dla = window_display_lines (w, CURRENT_DISP); + int num_lines; + Charcount end_pos = (BUF_Z (b) - w->window_end_pos[CURRENT_DISP] > BUF_ZV (b) + ? -1 + : w->window_end_pos[CURRENT_DISP]); + + if (!Dynarr_length (dla)) + return window_char_height (w, 0); + + num_lines = Dynarr_length (dla); + + /* #### Document and assert somewhere that w->window_end_pos == -1 + indicates that end-of-buffer is being displayed. */ + if (end_pos == -1) + { + struct display_line *dl = Dynarr_atp (dla, 0); + int ypos1 = dl->ypos + dl->descent; + int ypos2 = WINDOW_TEXT_BOTTOM (w); + Lisp_Object window; + int defheight, defwidth; + + XSETWINDOW (window, w); + + if (dl->modeline) + { + num_lines--; + + if (Dynarr_length (dla) == 1) + ypos1 = WINDOW_TEXT_TOP (w); + else + { + dl = Dynarr_atp (dla, Dynarr_length (dla) - 1); + /* If this line is clipped then we know that there is no + blank room between eob and the modeline. If we are + scrolling on clipped lines just know off the clipped + line and return .*/ + if (scroll_on_clipped_lines && dl->clip) + return (num_lines - 1); + ypos1 = dl->ypos + dl->descent - dl->clip; + } + } + + default_face_height_and_width (window, &defheight, &defwidth); + /* #### This probably needs to know about the clipping area once a + final definition is decided on. */ + num_lines += ((ypos2 - ypos1) / defheight); + } + else + { + if (num_lines > 1 && Dynarr_atp (dla, 0)->modeline) + num_lines--; + + if (scroll_on_clipped_lines + && Dynarr_atp (dla, Dynarr_length (dla) - 1)->clip) + num_lines--; + } + + return num_lines; +} + +static int +window_pixel_width (Lisp_Object window) +{ + struct window *w = XWINDOW (window); + return WINDOW_WIDTH (w); +} + +static int +window_pixel_width_to_char_width (struct window *w, int pixel_width, + int include_margins_p) +{ + int avail_width; + int char_width; + int defheight, defwidth; + Lisp_Object window; + + XSETWINDOW (window, w); + + avail_width = (pixel_width - + window_left_gutter_width (w, 0) - + window_right_gutter_width (w, 0) - + (include_margins_p ? 0 : window_left_margin_width (w)) - + (include_margins_p ? 0 : window_right_margin_width (w))); + + default_face_height_and_width (window, &defheight, &defwidth); + + char_width = (avail_width / defwidth); + + /* It's the calling function's responsibility to check these values + and make sure they're not out of range. + + #### We need to go through the calling functions and actually + do this. */ + if (char_width < 0) + return 0; + else + return char_width; +} + +static int +window_char_width_to_pixel_width (struct window *w, int char_width, + int include_margins_p) +{ + int avail_width; + int pixel_width; + int defheight, defwidth; + Lisp_Object window; + + XSETWINDOW (window, w); + + default_face_height_and_width (window, &defheight, &defwidth); + + avail_width = char_width * defwidth; + pixel_width = (avail_width + + window_left_gutter_width (w, 0) + + window_right_gutter_width (w, 0) + + (include_margins_p ? 0 : window_left_margin_width (w)) + + (include_margins_p ? 0 : window_right_margin_width (w))); + + /* It's the calling function's responsibility to check these values + and make sure they're not out of range. + + #### We need to go through the calling functions and actually + do this. */ + if (pixel_width < 0) + return 0; + else + return pixel_width; +} + +/* This returns the usable space which doesn't include space needed by + scrollbars or divider lines. */ +int +window_char_width (struct window *w, int include_margins_p) +{ + return window_pixel_width_to_char_width (w, WINDOW_WIDTH (w), + include_margins_p); +} + +#define MINSIZE(w) \ + (widthflag \ + ? window_min_width * defwidth \ + : (defheight * (MINI_WINDOW_P (XWINDOW (w)) ? 1 : window_min_height))) + +#define CURBEG(w) \ + *(widthflag ? (int *) &WINDOW_LEFT (w) : (int *) &WINDOW_TOP (w)) + +#define CURSIZE(w) \ + *(widthflag ? (int *) &WINDOW_WIDTH (w) : (int *) &WINDOW_HEIGHT (w)) + +#define CURCHARSIZE(w) \ + (widthflag ? window_char_width (w, 0) : window_char_height (w, 1)) + +#define MINCHARSIZE(window) \ + (widthflag ? window_min_width : MINI_WINDOW_P (XWINDOW (window)) \ + ? 1 : window_min_height) + +/* Unlike set_window_pixheight, this function + also changes the heights of the siblings so as to + keep everything consistent. */ + +static void +change_window_height (struct window *win, int delta, int widthflag) +{ + Lisp_Object parent; + Lisp_Object window = Qnil; + struct window *w; + struct frame *f; + int *sizep; + int (*sizefun) (Lisp_Object) = (widthflag + ? window_pixel_width + : window_pixel_height); + void (*setsizefun) (Lisp_Object, int, int) = (widthflag + ? set_window_pixwidth + : set_window_pixheight); + int dim; + int defheight, defwidth; + + if (delta == 0) + return; + + check_min_window_sizes (); + + XSETWINDOW (window, win); + f = XFRAME (win->frame); + if (EQ (window, FRAME_ROOT_WINDOW (f))) + error ("Won't change only window"); + + /* #### This is very likely incorrect and instead the char_to_pixel_ + functions should be called. */ + default_face_height_and_width (window, &defheight, &defwidth); + + while (1) + { + w = XWINDOW (window); + parent = w->parent; + if (NILP (parent)) + { + if (widthflag) + error ("No other window to side of this one"); + break; + } + if (widthflag + ? !NILP (XWINDOW (parent)->hchild) + : !NILP (XWINDOW (parent)->vchild)) + break; + window = parent; + } + + sizep = &CURSIZE (w); + dim = CURCHARSIZE (w); + + if ((dim + delta) < MINCHARSIZE (window)) + { + if (MINI_WINDOW_P (XWINDOW (window))) + return; + else if (!NILP (parent)) + { + Fdelete_window (window, Qnil); + return; + } + } + + delta *= (widthflag ? defwidth : defheight); + + { + int maxdelta; + + maxdelta = ((!NILP (parent)) + ? (*sizefun) (parent) - *sizep + : ((!NILP (w->next)) + ? (*sizefun) (w->next) - MINSIZE (w->next) + : ((!NILP (w->prev)) + ? (*sizefun) (w->prev) - MINSIZE (w->prev) + /* This is a frame with only one window, + a minibuffer-only or a minibufferless frame. */ + : (delta = 0)))); + + if (delta > maxdelta) + /* This case traps trying to make the minibuffer + the full frame, or make the only window aside from the + minibuffer the full frame. */ + delta = maxdelta; + + if (delta == 0) + return; + +#if 0 /* FSFmacs */ + /* #### Chuck: is this correct? */ + if (*sizep + delta < MINSIZE (window)) + { + Fdelete_window (window); + return; + } +#endif + } + + if (!NILP (w->next) && + (*sizefun) (w->next) - delta >= (int) MINSIZE (w->next)) + { + CURBEG (XWINDOW (w->next)) += delta; + (*setsizefun) (w->next, (*sizefun) (w->next) - delta, 0); + (*setsizefun) (window, *sizep + delta, 0); + } + else if (!NILP (w->prev) && + (*sizefun) (w->prev) - delta >= (int) MINSIZE (w->prev)) + { + (*setsizefun) (w->prev, (*sizefun) (w->prev) - delta, 0); + CURBEG (w) -= delta; + (*setsizefun) (window, *sizep + delta, 0); + } + else + { + int delta1; + int opht = (*sizefun) (parent); + + /* If trying to grow this window to or beyond size of the parent, + make delta1 so big that, on shrinking back down, + all the siblings end up with less than one line and are deleted. */ + if (opht <= *sizep + delta) + delta1 = opht * opht * 2; + /* Otherwise, make delta1 just right so that if we add delta1 + lines to this window and to the parent, and then shrink + the parent back to its original size, the new proportional + size of this window will increase by delta. */ + else + delta1 = (delta * opht * 100) / ((opht - *sizep - delta) * 100); + + /* Add delta1 lines or columns to this window, and to the parent, + keeping things consistent while not affecting siblings. */ + CURSIZE (XWINDOW (parent)) = opht + delta1; + (*setsizefun) (window, *sizep + delta1, 0); + + /* Squeeze out delta1 lines or columns from our parent, + shriking this window and siblings proportionately. + This brings parent back to correct size. + Delta1 was calculated so this makes this window the desired size, + taking it all out of the siblings. */ + (*setsizefun) (parent, opht, 0); + } + + SET_LAST_MODIFIED (w, 0); + SET_LAST_FACECHANGE (w); + MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); +} +#undef MINSIZE +#undef CURBEG +#undef CURSIZE +#undef CURCHARSIZE +#undef MINCHARSIZE + + + +/* Scroll contents of window WINDOW up N lines. */ +void +window_scroll (Lisp_Object window, Lisp_Object n, int direction, + Error_behavior errb) +{ + struct window *w = XWINDOW (window); + struct buffer *b = XBUFFER (w->buffer); + int selected = EQ (window, Fselected_window (Qnil)); + int value = 0; + Lisp_Object point, tem; + + if (selected) + point = make_int (BUF_PT (b)); + else + { + Bufpos pos = marker_position (w->pointm[CURRENT_DISP]); + + if (pos < BUF_BEGV (b)) + pos = BUF_BEGV (b); + else if (pos > BUF_ZV (b)) + pos = BUF_ZV (b); + + point = make_int (pos); + } + + /* Always set force_start so that redisplay_window will run + thw window-scroll-functions. */ + w->force_start = 1; + + /* #### When the fuck does this happen? I'm so glad that history has + completely documented the behavior of the scrolling functions under + all circumstances. */ + tem = Fpos_visible_in_window_p (point, window); + if (NILP (tem)) + { + Fvertical_motion (make_int (-window_char_height (w, 0) / 2), window); + Fset_marker (w->start[CURRENT_DISP], point, w->buffer); + w->start_at_line_beg = beginning_of_line_p (b, XINT (point)); + MARK_WINDOWS_CHANGED (w); + } + + if (!NILP (n)) + { + if (EQ (n, Qminus)) + direction *= -1; + else + { + n = Fprefix_numeric_value (n); + value = XINT (n) * direction; + + if (!value) + return; /* someone just made a pointless call */ + } + } + + /* If the user didn't specify how far to scroll then we have to figure it + out by ourselves. */ + if (NILP (n) || EQ (n, Qminus)) + { + /* Going forwards is easy. If that is what we are doing then just + set value and the section which handles the user specifying a + positive value will work. */ + if (direction == 1) + { + value = window_displayed_height (w) - next_screen_context_lines; + value = (value < 1 ? 1 : value); + } + + /* Going backwards is hard. We can't use the same loop used if the + user specified a negative value because we care about + next_screen_context_lines. In a variable height world you don't + know how many lines above you can actually be displayed and still + have the context lines appear. So we leave value set to 0 and add + a separate section to deal with this. */ + + } + + if (direction == 1 && !value) + { + return; + } + else if (value > 0) + { + int vtarget; + Bufpos startp, old_start; + + old_start = marker_position (w->start[CURRENT_DISP]); + startp = vmotion (w, old_start, value, &vtarget); + + if (vtarget < value && + (w->window_end_pos[CURRENT_DISP] == -1 + || (BUF_Z (b) - w->window_end_pos[CURRENT_DISP] > BUF_ZV (b)))) + { + maybe_signal_error (Qend_of_buffer, Qnil, Qwindow, errb); + return; + } + else + { + set_marker_restricted (w->start[CURRENT_DISP], make_int (startp), + w->buffer); + w->force_start = 1; + w->start_at_line_beg = beginning_of_line_p (b, startp); + MARK_WINDOWS_CHANGED (w); + + if (!point_would_be_visible (w, startp, XINT (point))) + { + if (selected) + BUF_SET_PT (b, startp); + else + set_marker_restricted (w->pointm[CURRENT_DISP], + make_int (startp), + w->buffer); + } + } + } + else if (value < 0) + { + int vtarget; + Bufpos startp, old_start; + + old_start = marker_position (w->start[CURRENT_DISP]); + startp = vmotion (w, old_start, value, &vtarget); + + if (vtarget > value + && marker_position (w->start[CURRENT_DISP]) == BUF_BEGV (b)) + { + maybe_signal_error (Qbeginning_of_buffer, Qnil, Qwindow, errb); + return; + } + else + { + set_marker_restricted (w->start[CURRENT_DISP], make_int (startp), + w->buffer); + w->force_start = 1; + w->start_at_line_beg = beginning_of_line_p (b, startp); + MARK_WINDOWS_CHANGED (w); + + if (!point_would_be_visible (w, startp, XINT (point))) + { + Bufpos new_point; + + if (MINI_WINDOW_P (w)) + new_point = startp; + else + new_point = start_of_last_line (w, startp); + + if (selected) + BUF_SET_PT (b, new_point); + else + set_marker_restricted (w->pointm[CURRENT_DISP], + make_int (new_point), + w->buffer); + } + } + } + else /* value == 0 && direction == -1 */ + { + if (marker_position (w->start[CURRENT_DISP]) == BUF_BEGV (b)) + { + maybe_signal_error (Qbeginning_of_buffer, Qnil, Qwindow, errb); + return; + } + else + { + int vtarget; + int movement = next_screen_context_lines - 1; + Bufpos old_startp = marker_position (w->start[CURRENT_DISP]); + Bufpos bottom = vmotion (w, old_startp, movement, &vtarget); + Bufpos startp = + start_with_point_on_display_line (w, bottom, + -1 - (movement - vtarget)); + + if (startp >= old_startp) + startp = vmotion (w, old_startp, -1, NULL); + + set_marker_restricted (w->start[CURRENT_DISP], make_int (startp), + w->buffer); + w->force_start = 1; + w->start_at_line_beg = beginning_of_line_p (b, startp); + MARK_WINDOWS_CHANGED (w); + + if (!point_would_be_visible (w, startp, XINT (point))) + { + Bufpos new_point = start_of_last_line (w, startp); + + if (selected) + BUF_SET_PT (b, new_point); + else + set_marker_restricted (w->pointm[CURRENT_DISP], + make_int (new_point), + w->buffer); + } + } + } + +} + +DEFUN ("scroll-up", Fscroll_up, Sscroll_up, 0, 1, "_P" /* +Scroll text of current window upward ARG lines; or near full screen if no ARG. +A near full screen is `next-screen-context-lines' less than a full screen. +Negative ARG means scroll downward. +When calling from a program, supply a number as argument or nil. +*/ ) + (n) + Lisp_Object n; +{ + window_scroll (Fselected_window (Qnil), n, 1, ERROR_ME); + return Qnil; +} + +DEFUN ("scroll-down", Fscroll_down, Sscroll_down, 0, 1, "_P" /* +Scroll text of current window downward ARG lines; or near full screen if no ARG. +A near full screen is `next-screen-context-lines' less than a full screen. +Negative ARG means scroll upward. +When calling from a program, supply a number as argument or nil. +*/ ) + (n) + Lisp_Object n; +{ + window_scroll (Fselected_window (Qnil), n, -1, ERROR_ME); + return Qnil; +} + +DEFUN ("other-window-for-scrolling", Fother_window_for_scrolling, Sother_window_for_scrolling, 0, 0, 0 /* +Return the other window for \"other window scroll\" commands. +If in the minibuffer, `minibuffer-scroll-window' if non-nil +specifies the window. +If `other-window-scroll-buffer' is non-nil, a window +showing that buffer is used. +*/ ) + () +{ + Lisp_Object window; + Lisp_Object selected_window = Fselected_window (Qnil); + + if (MINI_WINDOW_P (XWINDOW (selected_window)) + && !NILP (Vminibuf_scroll_window)) + window = Vminibuf_scroll_window; + /* If buffer is specified, scroll that buffer. */ + else if (!NILP (Vother_window_scroll_buffer)) + { + window = Fget_buffer_window (Vother_window_scroll_buffer, Qnil, Qnil); + if (NILP (window)) + window = display_buffer (Vother_window_scroll_buffer, Qt, Qnil); + } + else + { + /* Nothing specified; look for a neighboring window on the same + frame. */ + window = Fnext_window (selected_window, Qnil, Qnil, Qnil); + + if (EQ (window, selected_window)) + /* That didn't get us anywhere; look for a window on another + visible frame. */ + do + window = Fnext_window (window, Qnil, Qt, Qnil); + while (! FRAME_VISIBLE_P (XFRAME (WINDOW_FRAME (XWINDOW (window)))) + && ! EQ (window, selected_window)); + } + + CHECK_LIVE_WINDOW (window); + + if (EQ (window, selected_window)) + error ("There is no other window"); + + return window; + } + +DEFUN ("scroll-other-window", Fscroll_other_window, Sscroll_other_window, 0, 1, "_P" /* +Scroll next window upward ARG lines; or near full frame if no ARG. +The next window is the one below the current one; or the one at the top +if the current one is at the bottom. Negative ARG means scroll downward. +When calling from a program, supply a number as argument or nil. + +If in the minibuffer, `minibuf-scroll-window' if non-nil +specifies the window to scroll. +If `other-window-scroll-buffer' is non-nil, scroll the window +showing that buffer, popping the buffer up if necessary. +*/ ) + (n) + Lisp_Object n; +{ + window_scroll (Fother_window_for_scrolling (), n, 1, ERROR_ME); + return Qnil; +} + +DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 0, 1, "_P" /* +Scroll selected window display ARG columns left. +Default for ARG is window width minus 2. +*/ ) + (arg) + Lisp_Object arg; +{ + Lisp_Object window = Fselected_window (Qnil); + struct window *w = XWINDOW (window); + + if (NILP (arg)) + arg = make_int (window_char_width (w, 0) - 2); + else + arg = Fprefix_numeric_value (arg); + + return + Fset_window_hscroll (window, make_int (w->hscroll + XINT (arg))); +} + +DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 1, "_P" /* +Scroll selected window display ARG columns right. +Default for ARG is window width minus 2. +*/ ) + (arg) + Lisp_Object arg; +{ + Lisp_Object window = Fselected_window (Qnil); + struct window *w = XWINDOW (window); + + if (NILP (arg)) + arg = make_int (window_char_width (w, 0) - 2); + else + arg = Fprefix_numeric_value (arg); + + return + Fset_window_hscroll (window, make_int (w->hscroll - XINT (arg))); +} + +DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "_P" /* +Center point in WINDOW and redisplay frame. With ARG, put point on line ARG. +The desired position of point is always relative to the window. +Just C-u as prefix means put point in the center of the window. +No arg (i.e., it is nil) erases the entire frame and then +redraws with point in the center of the window. +If WINDOW is nil, the selected window is used. +*/ ) + (n, window) + Lisp_Object n, window; +{ + struct window *w; + struct buffer *b; + Bufpos startp; + Bufpos opoint; + + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_WINDOW (window); + w = XWINDOW (window); + b = XBUFFER (w->buffer); + + opoint = BUF_PT (b); + + if (NILP (n) || CONSP (n)) + { + struct frame *f = XFRAME (w->frame); + + if (NILP (n)) + { + MARK_FRAME_CHANGED (f); + SET_FRAME_CLEAR (f); + } + + startp = start_with_line_at_pixpos (w, opoint, window_half_pixpos (w)); + } + else + { + n = Fprefix_numeric_value (n); + CHECK_INT (n); + + startp = start_with_point_on_display_line (w, opoint, XINT (n)); + } + + Fset_marker (w->start[CURRENT_DISP], make_int (startp), w->buffer); + + w->start_at_line_beg = beginning_of_line_p (b, startp); + w->force_start = 1; + MARK_WINDOWS_CHANGED (w); + return Qnil; +} + +DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, + 1, 2, "_P" /* +Position point relative to WINDOW. +With no argument, position text at center of window. +An argument specifies window line; zero means top of window, +negative means relative to bottom of window. +If WINDOW is nil, the selected window is used. +*/ ) + (arg, window) + Lisp_Object arg, window; +{ + struct window *w; + struct buffer *b; + int height; + Bufpos start, new_point; + int selected; + + if (NILP (window)) + window = Fselected_window (Qnil); + else + CHECK_WINDOW (window); + w = XWINDOW (window); + b = XBUFFER (w->buffer); + + height = window_displayed_height (w); + selected = EQ (window, Fselected_window (w->frame)); + + if (NILP (arg)) + { + int retval; + + if (XINT (w->last_modified[CURRENT_DISP]) >= BUF_MODIFF (b) + && XINT (w->last_facechange[CURRENT_DISP]) >= BUF_FACECHANGE (b)) + { + new_point = point_at_center (w, CURRENT_DISP, 0, 0); + + if (selected) + BUF_SET_PT (b, new_point); + else + Fset_window_point (window, make_int (new_point)); + + retval = line_at_center (w, CURRENT_DISP, 0, 0); + } + else + { + start = marker_position (w->start[CURRENT_DISP]); + if (start < BUF_BEGV (b)) + start = BUF_BEGV (b); + else if (start > BUF_ZV (b)) + start = BUF_ZV (b); + + if (selected) + new_point = BUF_PT (b); + else + new_point = marker_position (w->pointm[CURRENT_DISP]); + + new_point = point_at_center (w, CMOTION_DISP, start, BUF_PT (b)); + + if (selected) + BUF_SET_PT (b, new_point); + else + Fset_window_point (window, make_int (new_point)); + + retval = line_at_center (w, CMOTION_DISP, start, BUF_PT (b)); + } + + return (make_int (retval)); + } + else + { + /* #### Is this going to work right when at eob? */ + arg = Fprefix_numeric_value (arg); + if (XINT (arg) < 0) + XSETINT (arg, XINT (arg) + height); + } + + start = marker_position (w->start[CURRENT_DISP]); + if (start < BUF_BEGV (b) || start > BUF_ZV (b)) + { + if (selected) + new_point = BUF_PT (b); + else + new_point = marker_position (w->pointm[CURRENT_DISP]); + + new_point = vmotion (XWINDOW (window), new_point, -height / 2, 0); + + if (selected) + BUF_SET_PT (b, new_point); + else + Fset_window_point (window, make_int (new_point)); + + Fset_marker (w->start[CURRENT_DISP], make_int (new_point), + w->buffer); + w->start_at_line_beg = beginning_of_line_p (b, new_point); + w->force_start = 1; + } + else + { + if (selected) + BUF_SET_PT (b, start); + else + Fset_window_point (window, make_int (start)); + } + + if (selected) + return Fvertical_motion (arg, window); + else + { + int vpos; + new_point = vmotion (XWINDOW (window), + marker_position (w->pointm[CURRENT_DISP]), + XINT (arg), &vpos); + Fset_window_point (window, make_int (new_point)); + return make_int (vpos); + } +} + + +static int +map_windows_1 (Lisp_Object window, + int (*mapfun) (struct window *w, void *closure), + void *closure) +{ + int retval; + + for (; !NILP (window); window = XWINDOW (window)->next) + { + struct window *w = XWINDOW (window); + if (!NILP (w->vchild)) + retval = map_windows_1 (w->vchild, mapfun, closure); + else if (!NILP (w->hchild)) + retval = map_windows_1 (w->hchild, mapfun, closure); + else + retval = (mapfun) (w, closure); + if (retval) + return retval; + } + + return 0; +} + +/* Map MAPFUN over the windows in F. CLOSURE is passed to each + invocation of MAPFUN. If any invocation of MAPFUN returns non-zero, + the mapping is halted and the value returned is the return value + of map_windows(). Otherwise, map_windows() maps over all windows + in F and returns 0. */ + +int +map_windows (struct frame *f, int (*mapfun) (struct window *w, void *closure), + void *closure) +{ + return map_windows_1 (FRAME_ROOT_WINDOW (f), mapfun, closure); +} + + +static void +modeline_shadow_thickness_changed (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + w->shadow_thickness_changed = 1; + MARK_WINDOWS_CHANGED (w); +} + +/* also used in scrollbar.c */ +void +some_window_value_changed (Lisp_Object specifier, struct window *w, + Lisp_Object oldval) +{ + MARK_WINDOWS_CHANGED (w); +} + +#ifdef MEMORY_USAGE_STATS + +struct window_stats +{ + int face; + int glyph; +#ifdef HAVE_SCROLLBARS + int scrollbar; +#endif + int line_start; + int other_redisplay; + int other; +}; + +static void +compute_window_mirror_usage (struct window_mirror *mir, + struct window_stats *stats, + struct overhead_stats *ovstats) +{ + if (!mir) + return; + stats->other += malloced_storage_size (mir, sizeof (struct window_mirror), + ovstats); +#ifdef HAVE_SCROLLBARS + { + struct device *d = XDEVICE (FRAME_DEVICE (mir->frame)); + + stats->scrollbar += + compute_scrollbar_instance_usage (d, mir->scrollbar_vertical_instance, + ovstats); + stats->scrollbar += + compute_scrollbar_instance_usage (d, mir->scrollbar_horizontal_instance, + ovstats); + } +#endif + stats->other_redisplay += + compute_display_line_dynarr_usage (mir->current_display_lines, ovstats); + stats->other_redisplay += + compute_display_line_dynarr_usage (mir->desired_display_lines, ovstats); +} + +static void +compute_window_usage (struct window *w, struct window_stats *stats, + struct overhead_stats *ovstats) +{ + memset (stats, 0, sizeof (*stats)); + stats->other += malloced_storage_size (w, sizeof (struct window), ovstats); + stats->face += compute_face_cachel_usage (w->face_cachels, ovstats); + stats->glyph += compute_glyph_cachel_usage (w->glyph_cachels, ovstats); + stats->line_start += + compute_line_start_cache_dynarr_usage (w->line_start_cache, ovstats); + compute_window_mirror_usage (find_window_mirror (w), stats, ovstats); +} + +DEFUN ("window-memory-usage", Fwindow_memory_usage, Swindow_memory_usage, + 1, 1, 0 /* +Return stats about the memory usage of window WINDOW. +The values returned are in the form an alist of usage types and byte +counts. The byte counts attempt to encompass all the memory used +by the window (separate from the memory logically associated with a +buffer or frame), including internal structures and any malloc() +overhead associated with them. In practice, the byte counts are +underestimated because certain memory usage is very hard to determine +(e.g. the amount of memory used inside the Xt library or inside the +X server) and because there is other stuff that might logically +be associated with a window, buffer, or frame (e.g. window configurations, +glyphs) but should not obviously be included in the usage counts. + +Multiple slices of the total memory usage may be returned, separated +by a nil. Each slice represents a particular view of the memory, a +particular way of partitioning it into groups. Within a slice, there +is no overlap between the groups of memory, and each slice collectively +represents all the memory concerned. +*/ ) + (window) + Lisp_Object window; +{ + struct window_stats stats; + struct overhead_stats ovstats; + + CHECK_WINDOW (window); /* dead windows should be allowed, no? */ + memset (&ovstats, 0, sizeof (ovstats)); + compute_window_usage (XWINDOW (window), &stats, &ovstats); + + return nconc2 ( +#ifdef HAVE_SCROLLBARS + list6 +#else + list5 +#endif + (Fcons (Qface_cache, make_int (stats.face)), + Fcons (Qglyph_cache, make_int (stats.glyph)), +#ifdef HAVE_SCROLLBARS + Fcons (Qscrollbar_instances, make_int (stats.scrollbar)), +#endif + Fcons (Qline_start_cache, make_int (stats.line_start)), + Fcons (Qother_redisplay, make_int (stats.other_redisplay)), + Fcons (Qother, make_int (stats.other))), + list4 (Qnil, + Fcons (Qactually_requested, + make_int (ovstats.was_requested)), + Fcons (Qmalloc_overhead, + make_int (ovstats.malloc_overhead)), + Fcons (Qdynarr_overhead, + make_int (ovstats.dynarr_overhead)))); +} + +#endif /* MEMORY_USAGE_STATS */ + + +/************************************************************************/ +/* Window configurations */ +/************************************************************************/ + +/* #### This window configuration stuff has had serious bugs lurking in it + for years; it would be a -huge- win if this was reimplemented in lisp. + */ + +/* If you add anything to this structure make sure saved_window_equal + knows about it. */ +struct saved_window +{ + Lisp_Object window; /* window */ + Lisp_Object buffer; /* buffer */ + Lisp_Object start; /* copied marker */ + Lisp_Object pointm; /* copied marker */ + Lisp_Object sb_point; /* copied marker */ + Lisp_Object mark; /* copied marker */ + int pixel_left; + int pixel_top; + int pixel_width; + int pixel_height; + int hscroll; + int parent_index; /* index into saved_windows */ + int prev_index; /* index into saved_windows */ + Lisp_Object dedicated; + char start_at_line_beg; /* boolean */ +}; + +/* If you add anything to this structure make sure window_config_equal + knows about it. */ +struct window_config +{ + struct lcrecord_header header; + int frame_width; + int frame_height; +#if 0 /* FSFmacs */ + Lisp_Object selected_frame; +#endif + Lisp_Object current_window; + Lisp_Object current_buffer; + Lisp_Object minibuf_scroll_window; + Lisp_Object root_window; + /* Record the values of window-min-width and window-min-height + so that window sizes remain consistent with them. */ + int min_width, min_height; +#ifdef ENERGIZE + /* The buffer whose p_sheets are visible */ + Lisp_Object p_sheet_buffer; +#endif + int saved_windows_count; + /* Zero-sized arrays aren't ANSI C */ + struct saved_window saved_windows[1]; +}; +#define SAVED_WINDOW_N(conf, n) (&((conf)->saved_windows[(n)])) + +static Lisp_Object mark_window_config (Lisp_Object, void (*) (Lisp_Object)); +static void print_window_config (Lisp_Object, Lisp_Object, int); +static unsigned int sizeof_window_config (CONST void *); + +#define XWINDOW_CONFIGURATION(x) XRECORD (x, window_configuration, struct window_config) +#define XSETWINDOW_CONFIGURATION(x, p) XSETRECORD (x, p, window_configuration) +#define WINDOW_CONFIGURATIONP(x) RECORDP (x, window_configuration) +#define GC_WINDOW_CONFIGURATIONP(x) GC_RECORDP (x, window_configuration) +#define CHECK_WINDOW_CONFIGURATION(x) CHECK_RECORD (x, window_configuration) + +DEFINE_LRECORD_SEQUENCE_IMPLEMENTATION ("window-configuration", + window_configuration, + mark_window_config, + print_window_config, + 0, 0, 0, sizeof_window_config, + struct window_config); + +static Lisp_Object +mark_window_config (Lisp_Object obj, void (*markobj) (Lisp_Object)) +{ + struct window_config *config = XWINDOW_CONFIGURATION (obj); + int i; + ((markobj) (config->current_window)); + ((markobj) (config->current_buffer)); + ((markobj) (config->minibuf_scroll_window)); + ((markobj) (config->root_window)); +#ifdef ENERGIZE + ((markobj) (config->p_sheet_buffer)); +#endif + for (i = 0; i < config->saved_windows_count; i++) + { + struct saved_window *s = SAVED_WINDOW_N (config, i); + ((markobj) (s->window)); + ((markobj) (s->buffer)); + ((markobj) (s->start)); + ((markobj) (s->pointm)); + ((markobj) (s->sb_point)); + ((markobj) (s->mark)); + ((markobj) (s->dedicated)); + } + return (Qnil); +} + +static int +sizeof_window_config_for_n_windows (int n) +{ + return (sizeof (struct window_config) + + /* n - 1 because zero-sized arrays aren't ANSI C */ + (n - 1) *sizeof (struct saved_window)); +} + +static unsigned int +sizeof_window_config (CONST void *h) +{ + CONST struct window_config *c = h; + return sizeof_window_config_for_n_windows (c->saved_windows_count); +} + +static void +print_window_config (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + struct window_config *config = XWINDOW_CONFIGURATION (obj); + char buf[200]; + if (print_readably) + error ("printing unreadable object #<window-configuration 0x%x>", + config->header.uid); + write_c_string ("#<window-configuration ", printcharfun); + sprintf (buf, "0x%x>", config->header.uid); + write_c_string (buf, printcharfun); +} + +/* Returns a boolean indicating whether the two saved windows are + identical. */ +static int +saved_window_equal (struct saved_window *win1, struct saved_window *win2) +{ + if (!EQ (win1->window, win2->window)) + return 0; + else if (!EQ (win1->buffer, win2->buffer)) + return 0; + else if (NILP (Fequal (win1->start, win2->start))) + return 0; + else if (NILP (Fequal (win1->pointm, win2->pointm))) + return 0; + else if (NILP (Fequal (win1->sb_point, win2->sb_point))) + return 0; + else if (NILP (Fequal (win1->mark, win2->mark))) + return 0; + else if (win1->pixel_left != win2->pixel_left) + return 0; + else if (win1->pixel_top != win2->pixel_top) + return 0; + else if (win1->pixel_width != win2->pixel_width) + return 0; + else if (win1->pixel_height != win2->pixel_height) + return 0; + else if (win1->hscroll != win2->hscroll) + return 0; + else if (win1->parent_index != win2->parent_index) + return 0; + else if (win1->prev_index != win2->prev_index) + return 0; + else if (!EQ (win1->dedicated, win2->dedicated)) + return 0; + else if (win1->start_at_line_beg != win2->start_at_line_beg) + return 0; + + return 1; +} + +/* Returns a boolean indicating whether the two given configurations + are identical. */ +static int +window_config_equal (Lisp_Object conf1, Lisp_Object conf2) +{ + struct window_config *fig1, *fig2; + int i; + + /* First check if they are truly the same. */ + if (EQ (conf1, conf2)) + return 1; + + fig1 = XWINDOW_CONFIGURATION (conf1); + fig2 = XWINDOW_CONFIGURATION (conf2); + + if (fig1->saved_windows_count != fig2->saved_windows_count) + return 0; + else if (!EQ (fig1->current_window, fig2->current_window)) + return 0; + else if (!EQ (fig1->current_buffer, fig2->current_buffer)) + return 0; + else if (!EQ (fig1->root_window, fig2->root_window)) + return 0; + else if (!EQ (fig1->minibuf_scroll_window, fig2->minibuf_scroll_window)) + return 0; + else if (fig1->frame_width != fig2->frame_width) + return 0; + else if (fig1->frame_height != fig2->frame_height) + return 0; +#ifdef ENERGIZE + else if (!EQ (fig1->p_sheet_buffer, fig2->p_sheet_buffer)) + return 0; +#endif + + for (i = 0; i < fig1->saved_windows_count; i++) + { + if (!saved_window_equal (SAVED_WINDOW_N (fig1, i), + SAVED_WINDOW_N (fig2, i))) + return 0; + } + + return 1; +} + +DEFUN ("window-configuration-p", Fwindow_configuration_p, + Swindow_configuration_p, 1, 1, 0 /* +T if OBJECT is a window-configuration object. +*/ ) + (obj) + Lisp_Object obj; +{ + return (WINDOW_CONFIGURATIONP (obj) ? Qt : Qnil); +} + +/* + * There are getting to be a lot of functions which traverse the + * window structure doing various things. It may be worth writing a + * generic map-windows function. + * #### I just did. Feel free to rewrite. --ben + */ +void +mark_windows_in_use (Lisp_Object window, int mark) +{ + for (; !NILP (window) ; window = XWINDOW (window)->next) + { + XWINDOW (window)->config_mark = mark; + + if (!NILP (XWINDOW (window)->vchild)) + mark_windows_in_use (XWINDOW (window)->vchild, mark); + else if (!NILP (XWINDOW (window)->hchild)) + mark_windows_in_use (XWINDOW (window)->hchild, mark); + } +} + +/* Lisp_Object return value so it can be used in record_unwind_protect() */ +static Lisp_Object +free_window_configuration (Lisp_Object window_config) +{ + int i; + struct window_config *config = XWINDOW_CONFIGURATION (window_config); + + /* Free all the markers. It's not completely necessary that + we do this (window configs sitting in a free list aren't + marked normally so the markers wouldn't be marked anyway) + but it's more efficient. */ + for (i = 0; i < config->saved_windows_count; i++) + { + struct saved_window *p = SAVED_WINDOW_N (config, i); + + if (!NILP (p->pointm)) + { + free_marker (XMARKER (p->pointm)); + p->pointm = Qnil; + } + if (!NILP (p->start)) + { + free_marker (XMARKER (p->start)); + p->start = Qnil; + } + if (!NILP (p->sb_point)) + { + free_marker (XMARKER (p->sb_point)); + p->sb_point = Qnil; + } + if (!NILP (p->mark)) + { + free_marker (XMARKER (p->mark)); + p->mark = Qnil; + } + } + + if (config->saved_windows_count <= countof (Vwindow_configuration_free_list)) + free_managed_lcrecord (Vwindow_configuration_free_list + [config->saved_windows_count - 1], + window_config); + + return Qnil; +} + +DEFUN ("set-window-configuration", + Fset_window_configuration, Sset_window_configuration, + 1, 1, 0 /* +Set the configuration of windows and buffers as specified by CONFIGURATION. +CONFIGURATION must be a value previously returned +by `current-window-configuration' (which see). +*/ ) + (configuration) + Lisp_Object configuration; +{ + struct window *w; + struct window_config *config; + struct saved_window *p; + Lisp_Object new_current_buffer; + int k; + Lisp_Object frame; + struct frame *f; + struct gcpro gcpro1; + Lisp_Object old_window_config; + int previous_frame_height; + int previous_frame_width; + int specpdl_count = specpdl_depth (); + + GCPRO1 (configuration); + + CHECK_WINDOW_CONFIGURATION (configuration); + config = XWINDOW_CONFIGURATION (configuration); + + frame = XWINDOW (SAVED_WINDOW_N (config, 0)->window)->frame; + f = XFRAME (frame); + + /* Do not signal an error here if the frame was deleted. There are + reasonable cases where we could get here with a deleted frame and + just want to do close to nothing instead. */ + + if (FRAME_LIVE_P (f)) + { + /* restore the frame characteristics */ +#ifdef ENERGIZE + if (FRAME_X_P (f)) + { + Lisp_Object new_desired = config->p_sheet_buffer; + + if (BUFFERP (new_desired) && + !BUFFER_LIVE_P (XBUFFER (new_desired))) + new_desired = Qnil; /* the desired buffer was killed */ + + /* need to restore the desired buffer */ + if (!EQ (new_desired, desired_psheet_buffer (f))) + make_psheets_desired (f, new_desired); + } +#endif + + new_current_buffer = config->current_buffer; + if (!BUFFER_LIVE_P (XBUFFER (new_current_buffer))) + new_current_buffer = Qnil; + + /* + * Assumed precondition: w->config_mark = 0 for all w + * This procedure should ensure this is true by the time it exits + * to ensure the precondition for future calls. + * + * We use w->config_mark to know whether we're modifying a + * window that is currently visible on the frame (#### we + * should just be able to check whether the window is dead + * or not, but this way is safer?). As we process each + * window, we set its config_mark to 0. At the end, we + * go through all the windows that used to be on the frame, + * set each one's config_mark to 0 (to maintain the + * assumed precondition) and delete each one that's no + * longer in use. + * + * #### Using a window-configuration to keep track of + * the current windows is wasteful. All we need is the + * list of windows, so we could just use a dynarr. + */ + old_window_config = Fcurrent_window_configuration (frame); + + /* If the new configuration is already equal to the old, then stop + right here. This saves the work below and it also saves + triggering a full redisplay of this window. This is a huge win + when using the mouse since the mode motion code uses + save-window-excursion extensively but will rarely cause the + configuration to actually change. */ + if (window_config_equal (configuration, old_window_config)) + { + free_window_configuration (old_window_config); + UNGCPRO; + return Qnil; + } + + /* We can't quit or even check for quit because that may cause + investigation of the frame state, which may crash if the frame is + in an inconsistent state. */ + begin_dont_check_for_quit (); + record_unwind_protect (free_window_configuration, old_window_config); + + mark_windows_in_use (f->root_window, 1); + + previous_frame_width = FRAME_WIDTH (f); + previous_frame_height = FRAME_HEIGHT (f); + /* If the frame has been resized since this window configuration was + made, we change the frame to the size specified in the + configuration, restore the configuration, and then resize it + back. We keep track of the prevailing height in these variables. */ + if (config->frame_height != FRAME_HEIGHT (f) + || config->frame_width != FRAME_WIDTH (f)) + change_frame_size (f, config->frame_height, config->frame_width, 0); + + /* Temporarily avoid any problems with windows that are smaller + than they are supposed to be. */ + window_min_height = 1; + window_min_width = 1; + + /* OK, now restore all the windows in the window config. + This may involve "undeleting" windows, since the + windows in the window config may be deleted. + */ + for (k = 0; k < config->saved_windows_count; k++) + { + p = SAVED_WINDOW_N (config, k); + w = XWINDOW (p->window); + w->next = Qnil; + + /* The window might be dead. In this case, its redisplay + structures were freed, so we need to reallocate them. */ + if (!w->face_cachels) + { + w->face_cachels = Dynarr_new (struct face_cachel); + reset_face_cachels (w); + } + if (!w->glyph_cachels) + w->glyph_cachels = Dynarr_new (struct glyph_cachel); + if (!w->line_start_cache) + w->line_start_cache = Dynarr_new (struct line_start_cache); + w->dead = 0; + + if (p->parent_index >= 0) + w->parent = SAVED_WINDOW_N (config, p->parent_index)->window; + else + w->parent = Qnil; + + if (p->prev_index >= 0) + { + w->prev = SAVED_WINDOW_N (config, p->prev_index)->window; + + /* This is true for a minibuffer-only frame. */ + if (!NILP (w->mini_p) && EQ (w->prev, p->window)) + w->next = Qnil; + else + XWINDOW (w->prev)->next = p->window; + } + else + { + w->prev = Qnil; + if (!NILP (w->parent)) + { + if (WINDOW_WIDTH (p) == WINDOW_WIDTH (XWINDOW (w->parent))) + { + XWINDOW (w->parent)->vchild = p->window; + XWINDOW (w->parent)->hchild = Qnil; + } + else + { + XWINDOW (w->parent)->hchild = p->window; + XWINDOW (w->parent)->vchild = Qnil; + } + } + } + if (!w->config_mark) + { + /* #### This should be equivalent to the window previously + having been dead. If we're brave, we'll put in an + assertion to this effect. */ + MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); + } + else /* if (!EQ (w->buffer, p->buffer)) */ + { + /* With the new redisplay we let it know that a change has + been made and it will take care of the rest. If we don't + tell it something has possibly changed it could lead to + incorrect display. */ + MARK_WINDOWS_CHANGED (w); + } + + WINDOW_LEFT (w) = WINDOW_LEFT (p); + WINDOW_TOP (w) = WINDOW_TOP (p); + WINDOW_WIDTH (w) = WINDOW_WIDTH (p); + WINDOW_HEIGHT (w) = WINDOW_HEIGHT (p); + w->hscroll = p->hscroll; + /* #### Here we need to restore the saved specs for + has-modeline-p, scrollbar-width, scrollbar-height, + modeline-shadow-thickness, left-margin-width, + right-margin-width, and current-display-table. */ + w->dedicated = p->dedicated; + w->line_cache_last_updated = Qzero; + SET_LAST_MODIFIED (w, 1); + SET_LAST_FACECHANGE (w); + w->config_mark = 0; + + /* Reinstall the saved buffer and pointers into it. */ + if (NILP (p->buffer)) + w->buffer = p->buffer; + else + { + if (BUFFER_LIVE_P (XBUFFER (p->buffer))) + /* If saved buffer is alive, install it. */ + { + w->buffer = p->buffer; + w->start_at_line_beg = p->start_at_line_beg; + set_marker_restricted (w->start[CURRENT_DISP], + Fmarker_position (p->start), + w->buffer); + set_marker_restricted (w->pointm[CURRENT_DISP], + Fmarker_position (p->pointm), + w->buffer); + set_marker_restricted (w->sb_point, + Fmarker_position (p->sb_point), + w->buffer); + Fset_marker (XBUFFER (w->buffer)->mark, + Fmarker_position (p->mark), w->buffer); + + /* As documented in Fcurrent_window_configuration, don't + save 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[CURRENT_DISP], Fcurrent_buffer ()); + } + else if (NILP (w->buffer) || + !BUFFER_LIVE_P (XBUFFER (w->buffer))) + /* Else if window's old buffer is dead too, get a live one. */ + { + /* #### The following line makes me nervous... */ + /* w->buffer = Fcdr (Fcar (XFRAME (w->frame)->buffer_alist));*/ + w->buffer = Fget_buffer_create (QSscratch); + /* w->buffer = Fother_buffer (Qnil, w->frame, Qnil); */ + /* This will set the markers to beginning of visible + range. */ + set_marker_restricted (w->start[CURRENT_DISP], Qzero, w->buffer); + set_marker_restricted (w->pointm[CURRENT_DISP], Qzero, + w->buffer); + set_marker_restricted (w->sb_point, Qzero, w->buffer); + w->start_at_line_beg = 1; + } + else + /* Keeping window's old buffer; make sure the markers + are real. */ + { + /* Set window markers at start of visible range. */ + if (XMARKER (w->start[CURRENT_DISP])->buffer == 0) + set_marker_restricted (w->start[CURRENT_DISP], Qzero, + w->buffer); + if (XMARKER (w->sb_point)->buffer == 0) + set_marker_restricted (w->sb_point, Qzero, w->buffer); + if (XMARKER (w->pointm[CURRENT_DISP])->buffer == 0) + set_marker_restricted (w->pointm[CURRENT_DISP], + make_int + (BUF_PT (XBUFFER (w->buffer))), + w->buffer); + w->start_at_line_beg = 1; + } + } + } + + FRAME_ROOT_WINDOW (f) = config->root_window; + /* Note that FSFmacs unilaterally calls Fselect_window() here, and + then calls do_switch_frame() below to select the frame that was + recorded in the window config as being selected. + + Instead, we don't ever change the selected frame, and either + call Fselect_window() below if the window config's frame is + currently selected, or just set the selected window of the + window config's frame. */ + + /* Set the frame height to the value it had before this function. */ + if (previous_frame_height != FRAME_HEIGHT (f) + || previous_frame_width != FRAME_WIDTH (f)) + change_frame_size (f, previous_frame_height, previous_frame_width, 0); + + /* If restoring in the current frame make the window current, + otherwise just update the frame selected_window slot to be + the restored current_window. */ + if (f == selected_frame ()) + { + /* When using `pop-window-configuration', often the minibuffer + ends up as the selected window even though it's not active ... + I really don't know the cause of this, but it should never + happen. This kludge should fix it. + + #### Find out why this is really going wrong. */ + if (!minibuf_level && + MINI_WINDOW_P (XWINDOW (config->current_window))) + Fselect_window (Fnext_window (config->current_window, + Qnil, Qnil, Qnil)); + else + Fselect_window (config->current_window); + if (!NILP (new_current_buffer)) + Fset_buffer (new_current_buffer); + else + Fset_buffer (XWINDOW (Fselected_window (Qnil))->buffer); +#ifdef ENERGIZE + energize_buffer_shown_hook (XWINDOW (Fselected_window (Qnil))); +#endif + } + else + set_frame_selected_window (f, config->current_window); + } + + /* Restore the minimum heights recorded in the configuration. */ + window_min_height = config->min_height; + window_min_width = config->min_width; + +#if 0 /* FSFmacs */ + /* see above comment */ + /* Fselect_window will have made f the selected frame, so we + reselect the proper frame here. Fhandle_switch_frame will change the + selected window too, but that doesn't make the call to + Fselect_window above totally superfluous; it still sets f's + selected window. */ + if (FRAME_LIVE_P (XFRAME (config->selected_frame))) + do_switch_frame (config->selected_frame, Qnil, 0); +#endif + + Vminibuf_scroll_window = config->minibuf_scroll_window; + + if (FRAME_LIVE_P (f)) + { + /* Do this before calling recompute_all_cached_specifiers_in_window() + so that things like redisplay_redraw_cursor() won't abort due + to no window mirror present. */ + f->mirror_dirty = 1; + + config = XWINDOW_CONFIGURATION (old_window_config); + for (k = 0; k < config->saved_windows_count; k++) + { + p = SAVED_WINDOW_N (config, k); + w = XWINDOW (p->window); + /* Remember, we set w->config_mark on all currently visible + windows, and reset it on all newly visible windows. + Any windows still marked need to be deleted. */ + if (w->config_mark) + { + mark_window_as_deleted (w); + w->config_mark = 0; + } + else + { + /* We just potentially changed the window's buffer and + potentially turned a dead window into a live one, + so we need to recompute the cached specifier values. */ + recompute_all_cached_specifiers_in_window (w); + } + } + } + + /* Now restore things, when everything else if OK. */ + + unbind_to (specpdl_count, Qnil); + + UNGCPRO; + + return (Qnil); +} + +/* Mark all subwindows of a window as deleted. The argument + W is actually the subwindow tree of the window in question. */ + +void +delete_all_subwindows (struct window *w) +{ + if (!NILP (w->next)) + delete_all_subwindows (XWINDOW (w->next)); + if (!NILP (w->vchild)) + delete_all_subwindows (XWINDOW (w->vchild)); + if (!NILP (w->hchild)) + delete_all_subwindows (XWINDOW (w->hchild)); + + mark_window_as_deleted (w); +} + + +static int +count_windows (struct window *window) +{ + int count = 1; + if (!NILP (window->next)) + count += count_windows (XWINDOW (window->next)); + if (!NILP (window->vchild)) + count += count_windows (XWINDOW (window->vchild)); + if (!NILP (window->hchild)) + count += count_windows (XWINDOW (window->hchild)); + return count; +} + +static int +saved_window_index (Lisp_Object window, struct window_config *config, int lim) +{ + int j; + for (j = 0; j < lim; j++) + { + if (EQ (SAVED_WINDOW_N (config, j)->window, window)) + { + return (j); + } + } + abort (); + return 0; /* suppress compiler warning */ +} + +static int +save_window_save (Lisp_Object window, struct window_config *config, int i) +{ + struct saved_window *p; + struct window *w; + + for (; !NILP (window); window = w->next) + { + p = SAVED_WINDOW_N (config, i); + w = XWINDOW (window); + + i++; + p->window = window; + p->buffer = w->buffer; + WINDOW_LEFT (p) = WINDOW_LEFT (w); + WINDOW_TOP (p) = WINDOW_TOP (w); + WINDOW_WIDTH (p) = WINDOW_WIDTH (w); + WINDOW_HEIGHT (p) = WINDOW_HEIGHT (w); + p->hscroll = w->hscroll; + /* #### Here we need to save the specs for + has-modeline-p, scrollbar-width, scrollbar-height, + modeline-shadow-thickness, left-margin-width, + right-margin-width, current-display-table, etc. */ + if (!NILP (w->buffer)) + { + /* Save w's value of point in the window configuration. + If w is the selected window, then get the value of point + from the buffer; pointm is garbage in the selected window. */ + if (EQ (window, Fselected_window (Qnil))) + { + p->pointm = noseeum_make_marker (); + Fset_marker (p->pointm, + make_int (BUF_PT (XBUFFER (w->buffer))), + w->buffer); + } + else + p->pointm = noseeum_copy_marker (w->pointm[CURRENT_DISP], Qnil); + + p->start = noseeum_copy_marker (w->start[CURRENT_DISP], Qnil); + p->sb_point = noseeum_copy_marker (w->sb_point, Qnil); + p->start_at_line_beg = w->start_at_line_beg; + + p->mark = noseeum_copy_marker (XBUFFER (w->buffer)->mark, Qnil); + } + else + { + p->pointm = Qnil; + p->start = Qnil; + p->sb_point = Qnil; + p->mark = Qnil; + p->start_at_line_beg = 0; + } + + if (NILP (w->parent)) + p->parent_index = -1; + else + p->parent_index = saved_window_index (w->parent, config, i); + if (NILP (w->prev)) + p->prev_index = -1; + else + p->prev_index = saved_window_index (w->prev, config, i); + p->dedicated = w->dedicated; + if (!NILP (w->vchild)) + i = save_window_save (w->vchild, config, i); + if (!NILP (w->hchild)) + i = save_window_save (w->hchild, config, i); + } + + return i; +} + +#if 0 /* FSFmacs */ +/* Added to doc string: + +This also records the currently selected frame, and FRAME's focus +redirection (see `redirect-frame-focus'). + +*/ +#endif + +DEFUN ("current-window-configuration", + Fcurrent_window_configuration, Scurrent_window_configuration, 0, 1, 0 /* +Return an object representing the current window configuration of FRAME. +If FRAME is nil or omitted, use the selected frame. +This describes the number of windows, their sizes and current buffers, +and for each displayed buffer, where display starts, and the positions of +point and mark. An exception is made for point in the current buffer: +its value is -not- saved. +*/ ) + (frame) + Lisp_Object frame; +{ + Lisp_Object result = Qnil; + struct frame *f; + int n_windows; + + struct window_config *config; + + f = decode_frame (frame); + + n_windows = count_windows (XWINDOW (FRAME_ROOT_WINDOW (f))); + if (n_windows <= countof (Vwindow_configuration_free_list)) + config = + XWINDOW_CONFIGURATION (allocate_managed_lcrecord + (Vwindow_configuration_free_list + [n_windows - 1])); + else + /* More than ten windows; just allocate directly */ + config = alloc_lcrecord (sizeof_window_config_for_n_windows (n_windows), + lrecord_window_configuration); + XSETWINDOW_CONFIGURATION (result, config); + + config->frame_width = FRAME_WIDTH (f); + config->frame_height = FRAME_HEIGHT (f); + config->current_window = FRAME_SELECTED_WINDOW (f); + XSETBUFFER (config->current_buffer, current_buffer); + config->minibuf_scroll_window = Vminibuf_scroll_window; + config->root_window = FRAME_ROOT_WINDOW (f); + config->min_height = window_min_height; + config->min_width = window_min_width; + config->saved_windows_count = n_windows; +#ifdef ENERGIZE + { + config->p_sheet_buffer = desired_psheet_buffer (f); + if (ZEROP (config->p_sheet_buffer)) /* #### necessaryp? */ + config->p_sheet_buffer = Qnil; + } +#endif + save_window_save (FRAME_ROOT_WINDOW (f), config, 0); + return (result); +} + +Lisp_Object +save_window_excursion_unwind (Lisp_Object window_config) +{ + Lisp_Object val = Fset_window_configuration (window_config); + free_window_configuration (window_config); + return val; +} + +DEFUN ("save-window-excursion", Fsave_window_excursion, Ssave_window_excursion, + 0, UNEVALLED, 0 /* +Execute body, preserving window sizes and contents. +Restores which buffer appears in which window, where display starts, +as well as the current buffer. +Does not restore the value of point in current buffer. +*/ ) + (args) + Lisp_Object args; +{ + /* This function can GC */ + Lisp_Object val; + int speccount = specpdl_depth (); + + record_unwind_protect (save_window_excursion_unwind, + Fcurrent_window_configuration (Qnil)); + val = Fprogn (args); + return unbind_to (speccount, val); +} + + +#ifdef DEBUG_XEMACS +/* This is short and simple in elisp, but... it was written to debug + problems purely on the C side. That is where we need to call it so + here it is. */ +static void +debug_print_window (Lisp_Object window, int level) +{ + Lisp_Object child; + int i = level; + + child = Fwindow_first_vchild (window); + if (NILP (child)) + child = Fwindow_first_hchild (window); + + while (i > 0) + { + fprintf (stderr, "\t"); + i--; + } + + fprintf (stderr, "#<window"); + if (!NILP (XWINDOW (window)->buffer) && BUFFERP (XWINDOW (window)->buffer)) + { + struct buffer *b = XBUFFER (XWINDOW (window)->buffer); + fprintf (stderr, " on %s", string_data (XSTRING (b->name))); + } + fprintf (stderr, " 0x%x>", XWINDOW (window)->header.uid); + + while (!NILP (child)) + { + debug_print_window (child, level + 1); + child = Fwindow_next_child (child); + } +} + +void debug_print_windows (struct frame *f); +void +debug_print_windows (struct frame *f) +{ + debug_print_window (f->root_window, 0); + fprintf (stderr, "\n"); +} +#endif /* DEBUG_XEMACS */ + + +/************************************************************************/ +/* initialization */ +/************************************************************************/ + +void +syms_of_window (void) +{ + defsymbol (&Qwindowp, "windowp"); + defsymbol (&Qwindow_live_p, "window-live-p"); + defsymbol (&Qwindow_configurationp, "window-configuration-p"); + defsymbol (&Qscroll_up, "scroll-up"); + defsymbol (&Qscroll_down, "scroll-down"); + defsymbol (&Qtemp_buffer_show_hook, "temp-buffer-show-hook"); + defsymbol (&Qdisplay_buffer, "display-buffer"); + +#ifdef MEMORY_USAGE_STATS + defsymbol (&Qface_cache, "face-cache"); + defsymbol (&Qglyph_cache, "glyph-cache"); + defsymbol (&Qline_start_cache, "line-start-cache"); +#ifdef HAVE_SCROLLBARS + defsymbol (&Qscrollbar_instances, "scrollbar-instances"); +#endif + defsymbol (&Qother_redisplay, "other-redisplay"); + /* Qother in general.c */ +#endif + + defsubr (&Sselected_window); + defsubr (&Sminibuffer_window); + defsubr (&Swindow_minibuffer_p); + defsubr (&Swindowp); + defsubr (&Swindow_live_p); + defsubr (&Swindow_first_hchild); + defsubr (&Swindow_first_vchild); + defsubr (&Swindow_next_child); + defsubr (&Swindow_previous_child); + defsubr (&Swindow_parent); + defsubr (&Swindow_lowest_p); + defsubr (&Swindow_highest_p); + defsubr (&Swindow_leftmost_p); + defsubr (&Swindow_rightmost_p); + defsubr (&Spos_visible_in_window_p); + defsubr (&Swindow_buffer); + defsubr (&Swindow_frame); + defsubr (&Swindow_height); + defsubr (&Swindow_displayed_height); + defsubr (&Swindow_width); + defsubr (&Swindow_pixel_height); + defsubr (&Swindow_pixel_width); + defsubr (&Swindow_hscroll); +#if 0 /* bogus RMS crock */ + defsubr (&Swindow_redisplay_end_trigger); + defsubr (&Sset_window_redisplay_end_trigger); +#endif + defsubr (&Sset_window_hscroll); + defsubr (&Swindow_pixel_edges); + defsubr (&Swindow_point); + defsubr (&Swindow_start); + defsubr (&Swindow_end); + defsubr (&Sset_window_point); + defsubr (&Sset_window_start); + defsubr (&Swindow_dedicated_p); + defsubr (&Sset_window_dedicated_p); + defsubr (&Snext_window); + defsubr (&Sprevious_window); + defsubr (&Snext_vertical_window); + defsubr (&Sother_window); + defsubr (&Sget_lru_window); + defsubr (&Sget_largest_window); + defsubr (&Sget_buffer_window); + defsubr (&Swindow_left_margin_pixel_width); + defsubr (&Swindow_right_margin_pixel_width); + defsubr (&Sdelete_other_windows); + defsubr (&Sdelete_windows_on); + defsubr (&Sreplace_buffer_in_windows); + defsubr (&Sdelete_window); + defsubr (&Sset_window_buffer); + defsubr (&Sselect_window); + defsubr (&Ssplit_window); + defsubr (&Senlarge_window); + defsubr (&Sshrink_window); + defsubr (&Sscroll_up); + defsubr (&Sscroll_down); + defsubr (&Sscroll_left); + defsubr (&Sscroll_right); + defsubr (&Sother_window_for_scrolling); + defsubr (&Sscroll_other_window); + defsubr (&Srecenter); + defsubr (&Smove_to_window_line); +#ifdef MEMORY_USAGE_STATS + defsubr (&Swindow_memory_usage); +#endif + defsubr (&Swindow_configuration_p); + defsubr (&Sset_window_configuration); + defsubr (&Scurrent_window_configuration); + defsubr (&Ssave_window_excursion); +} + +void +vars_of_window (void) +{ + /* Make sure all windows get marked */ + minibuf_window = Qnil; + staticpro (&minibuf_window); + + DEFVAR_BOOL ("scroll-on-clipped-lines", &scroll_on_clipped_lines /* +*Non-nil means to scroll if point lands on a line which is clipped. +*/ ); + scroll_on_clipped_lines = 1; + + DEFVAR_LISP ("temp-buffer-show-hook", &Vtemp_buffer_show_hook /* +See `temp-buffer-show-function'. +*/ ); + Vtemp_buffer_show_hook = Qnil; + + DEFVAR_LISP ("temp-buffer-show-function", &Vtemp_buffer_show_function /* +Non-nil means call as function to display a help buffer. +The function is called with one argument, the buffer to be displayed. +Used by `with-output-to-temp-buffer'. +If this function is used, then it must do the entire job of showing +the buffer; `temp-buffer-show-hook' is not run unless this function runs it. +*/ ); + Vtemp_buffer_show_function = Qnil; + + DEFVAR_LISP ("minibuffer-scroll-window", &Vminibuf_scroll_window /* +Non-nil means it is the window that \\<minibuffer-local-map>\\[scroll-other-window] in minibuffer should scroll. +*/ ); + Vminibuf_scroll_window = Qnil; + + DEFVAR_LISP ("other-window-scroll-buffer", &Vother_window_scroll_buffer /* +If non-nil, this is a buffer and \\[scroll-other-window] should scroll its window. +*/ ); + Vother_window_scroll_buffer = Qnil; + + DEFVAR_INT ("next-screen-context-lines", &next_screen_context_lines /* +*Number of lines of continuity when scrolling by screenfuls. +*/ ); + next_screen_context_lines = 2; + + DEFVAR_INT ("window-min-height", &window_min_height /* +*Delete any window less than this tall (including its modeline). +*/ ); + window_min_height = 4; + + DEFVAR_INT ("window-min-width", &window_min_width /* +*Delete any window less than this wide. +*/ ); + window_min_width = 10; + + { + int i; + + for (i = 0; i < countof (Vwindow_configuration_free_list); i++) + { + Vwindow_configuration_free_list[i] = + make_lcrecord_list (sizeof_window_config_for_n_windows (i + 1), + lrecord_window_configuration); + staticpro (&Vwindow_configuration_free_list[i]); + } + } +} + +void +specifier_vars_of_window (void) +{ + DEFVAR_SPECIFIER ("modeline-shadow-thickness", &Vmodeline_shadow_thickness /* +*How thick to draw 3D shadows around modelines. +If this is set to 0, modelines will be the traditional 2D. Sizes above +10 will be accepted but the maximum thickness that will be drawn is 10. +This is a specifier; use `set-specifier' to change it. +*/ ); + Vmodeline_shadow_thickness = Fmake_specifier (Qinteger); + /* The initial value for modeline-shadow-thickness is 2, but if the + user removes all specifications we provide a fallback value of 0, + which is probably what was expected. */ + set_specifier_fallback (Vmodeline_shadow_thickness, + list1 (Fcons (Qnil, Qzero))); + Fadd_spec_to_specifier (Vmodeline_shadow_thickness, make_int (2), + Qnil, Qnil, Qnil); + set_specifier_caching (Vmodeline_shadow_thickness, + slot_offset (struct window, + modeline_shadow_thickness), + modeline_shadow_thickness_changed, + 0, 0); + + DEFVAR_SPECIFIER ("has-modeline-p", &Vhas_modeline_p /* +*Whether the modeline should be displayed. +This is a specifier; use `set-specifier' to change it. +*/ ); + Vhas_modeline_p = Fmake_specifier (Qboolean); + set_specifier_fallback (Vhas_modeline_p, + list1 (Fcons (Qnil, Qt))); + set_specifier_caching (Vhas_modeline_p, + slot_offset (struct window, + has_modeline_p), + /* #### It's strange that we need a special + flag to indicate that the shadow-thickness + has changed, but not one to indicate that + the modeline has been turned off or on. */ + some_window_value_changed, + 0, 0); +}