# HG changeset patch # User Jim Blandy # Date 674150404 0 # Node ID 8df170a4c9d116426b0f596cc7c5df542f18c920 # Parent 7db4ff4204a5585ebeda1e2d9336e2e807cf49bd Initial revision diff -r 7db4ff4204a5 -r 8df170a4c9d1 src/window.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/window.c Mon May 13 16:00:04 1991 +0000 @@ -0,0 +1,2720 @@ +/* Window creation, deletion and examination for GNU Emacs. + Does not include redisplay. + Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 1, or (at your option) +any later version. + +GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#include "lisp.h" +#include "buffer.h" +#include "screen.h" +#include "window.h" +#include "commands.h" +#include "indent.h" +#include "termchar.h" +#include "disptab.h" + +Lisp_Object Qwindowp; + +Lisp_Object Fnext_window (), Fdelete_window (), Fselect_window (); +Lisp_Object Fset_window_buffer (), Fsplit_window (), Frecenter (); + +static void delete_all_subwindows (); +static struct window *decode_window(); + +/* This is the window in which the terminal's cursor should + be left when nothing is being done with it. This must + always be a leaf window, and its buffer is selected by + the top level editing loop at the end of each command. + + This value is always the same as + SCREEN_SELECTED_WINDOW (selected_screen). */ + +Lisp_Object selected_window; + +#ifndef MULTI_SCREEN + +/* The root window for the screen. + This is accessed via SCREEN_ROOT_WINDOW (selected_screen). */ +Lisp_Object root_window; + +#endif + +/* The minibuffer window of the selected screen. + 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; + +/* Window that the mouse is over (nil if no mouse support). */ +Lisp_Object Vmouse_window; + +/* Last mouse click data structure (nil if no mouse support). */ +Lisp_Object Vmouse_event; + +/* Non-nil means it's function to call to display temp buffers. */ +Lisp_Object Vtemp_buffer_show_function; + +/* If a window gets smaller than either of these, it is removed. */ +int window_min_height; +int window_min_width; + +/* Nonzero implies Fdisplay_buffer should create windows. */ +int pop_up_windows; + +/* Nonzero implies make new X screens for Fdisplay_buffer. */ +int auto_new_screen; + +/* Non-nil means use this function instead of default */ +Lisp_Object Vauto_new_screen_function; + +/* Function to call to handle Fdisplay_buffer. */ +Lisp_Object Vdisplay_buffer_function; + +/* Fdisplay_buffer always splits the largest window + if that window is more than this high. */ +int split_height_threshold; + +/* Number of lines of continuity in scrolling by screenfuls. */ +int next_screen_context_lines; + +/* Incremented for each window created. */ +static int sequence_number; + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +DEFUN ("windowp", Fwindowp, Swindowp, 1, 1, 0, + "Returns t if OBJ is a window.") + (obj) + Lisp_Object obj; +{ + return XTYPE (obj) == Lisp_Window ? Qt : Qnil; +} + +Lisp_Object +make_window () +{ + register Lisp_Object val; + register struct window *p; + + /* Add sizeof (Lisp_Object) here because sizeof (struct Lisp_Vector) + includes the first element. */ + val = Fmake_vector ( + make_number ((sizeof (struct window) - sizeof (struct Lisp_Vector) + + sizeof (Lisp_Object)) + / sizeof (Lisp_Object)), + Qnil); + XSETTYPE (val, Lisp_Window); + p = XWINDOW (val); + XFASTINT (p->sequence_number) = ++sequence_number; + XFASTINT (p->left) = XFASTINT (p->top) + = XFASTINT (p->height) = XFASTINT (p->width) + = XFASTINT (p->hscroll) = 0; + XFASTINT (p->last_point_x) = XFASTINT (p->last_point_y) = 0; + p->start = Fmake_marker (); + p->pointm = Fmake_marker (); + XFASTINT (p->use_time) = 0; + p->screen = Qnil; + p->display_table = Qnil; + p->dedicated = Qnil; + return val; +} + +DEFUN ("selected-window", Fselected_window, Sselected_window, 0, 0, 0, + "Return the window that the cursor now appears in and commands apply to.") + () +{ + return selected_window; +} + +DEFUN ("minibuffer-window", Fminibuffer_window, Sminibuffer_window, 0, 0, 0, + "Return the window used now for minibuffers.") + () +{ +#ifdef MULTI_SCREEN + if (minibuf_level == 0 + && !EQ (minibuf_window, selected_screen->minibuffer_window) + && !EQ (Qnil, selected_screen->minibuffer_window)) + { + Fset_window_buffer (selected_screen->minibuffer_window, + XWINDOW (minibuf_window)->buffer); + minibuf_window = selected_screen->minibuffer_window; + } + + if (SCREENP (Vglobal_minibuffer_screen)) + minibuf_window = XSCREEN (Vglobal_minibuffer_screen)->minibuffer_window; + else + minibuf_window = selected_screen->minibuffer_window; + +#endif /* MULTI_SCREEN */ + return minibuf_window; +} + +DEFUN ("window-minibuffer-p", Fwindow_minibuffer_p, Swindow_minibuffer_p, 1, 1, 0, + "Returns 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 ("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 screen in WINDOW.\n\ +Returns nil if that position is scrolled vertically out of view.\n\ +POS defaults to point; WINDOW, to the selected window.") + (pos, window) + Lisp_Object pos, window; +{ + register struct window *w; + register int top; + register int height; + register int posint; + register struct buffer *buf; + struct position posval; + + if (NULL (pos)) + posint = point; + else + { + CHECK_NUMBER_COERCE_MARKER (pos, 0); + posint = XINT (pos); + } + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 1); + w = XWINDOW (window); + top = marker_position (w->start); + + if (posint < top) + return Qnil; + + height = XFASTINT (w->height) - ! MINI_WINDOW_P (w); + + buf = XBUFFER (w->buffer); + if (XFASTINT (w->last_modified) >= BUF_MODIFF (buf)) + { + /* If screen is up to date, + use the info recorded about how much text fit on it. */ + if (posint < BUF_Z (buf) - XFASTINT (w->window_end_pos) + || (XFASTINT (w->window_end_vpos) < height)) + return Qt; + return Qnil; + } + else + { + if (posint > BUF_Z (buf)) + return Qnil; + + /* If that info is not correct, calculate afresh */ + posval = *compute_motion (top, 0, 0, posint, height, 0, + XFASTINT (w->width) - 1 + - (XFASTINT (w->width) + XFASTINT (w->left) + != XSCREEN (w->screen)->width), + XINT (w->hscroll), 0); + + return posval.vpos < height ? Qt : Qnil; + } +} + +static struct window * +decode_window (window) + register Lisp_Object window; +{ + if (NULL (window)) + return XWINDOW (selected_window); + + CHECK_WINDOW (window, 0); + 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-height", Fwindow_height, Swindow_height, 0, 1, 0, + "Return the number of lines in WINDOW (including its mode line).") + (window) + Lisp_Object window; +{ + return decode_window (window)->height; +} + +DEFUN ("window-width", Fwindow_width, Swindow_width, 0, 1, 0, + "Return the number of columns in WINDOW.") + (window) + Lisp_Object window; +{ + register struct window *w = decode_window (window); + register int width = w->width; + + /* If this window does not end at the right margin, + must deduct one column for the border */ + if ((width + w->left) == SCREEN_WIDTH (XSCREEN (WINDOW_SCREEN (w)))) + return width; + return width - 1; +} + +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 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.\n\ +NCOL should be zero or positive.") + (window, ncol) + register Lisp_Object window, ncol; +{ + register struct window *w; + + CHECK_NUMBER (ncol, 1); + if (XINT (ncol) < 0) XFASTINT (ncol) = 0; + if (XFASTINT (ncol) >= (1 << (SHORTBITS - 1))) + args_out_of_range (ncol, Qnil); + w = decode_window (window); + if (w->hscroll != ncol) + clip_changed = 1; /* Prevent redisplay shortcuts */ + w->hscroll = ncol; + return ncol; +} + +DEFUN ("window-edges", Fwindow_edges, Swindow_edges, 0, 1, 0, + "Return a list of the edge coordinates of WINDOW.\n\ +\(LEFT TOP RIGHT BOTTOM), all relative to 0, 0 at top left corner of screen.\n\ +RIGHT is one more than the rightmost column used by WINDOW,\n\ +and BOTTOM is one more than the bottommost row used by WINDOW\n\ + and its mode-line.") + (window) + Lisp_Object window; +{ + register struct window *w = decode_window (window); + + return Fcons (w->left, Fcons (w->top, + Fcons (make_number (XFASTINT (w->left) + XFASTINT (w->width)), + Fcons (make_number (XFASTINT (w->top) + + XFASTINT (w->height)), + Qnil)))); +} + +/* Find the window containing column x, row y, and return it as a + Lisp_Object. If x, y is on the window's modeline, set *modeline_p + to 1; otherwise set it to 0. If there is no window under x, y + return nil and leave *modeline_p unmodified. */ +Lisp_Object +window_from_coordinates (screen, x, y, modeline_p) + SCREEN_PTR screen; + int x, y; + int *modeline_p; +{ + register Lisp_Object tem, first; + + first = SCREEN_SELECTED_WINDOW (screen); + tem = next_screen_window (screen, first, Qt); + + while (1) + { + int found = coordinates_in_window (XWINDOW (tem), &x, &y); + + if (found) + { + + *modeline_p = (found == -1); + return tem; + } + + if (EQ (tem, first)) + return Qnil; + + tem = next_screen_window (screen, tem, Qt); + } +} + +DEFUN ("locate-window-from-coordinates", + Flocate_window_from_coordinates, Slocate_window_from_coordinates, + 2, 2, 0, + "Return window on SCREEN containing position COORDINATES.\n\ +COORDINATES is a list (SCREEN-X SCREEN-Y) of coordinates\n\ +which are relative to 0,0 at the top left corner of the screen.") + (screen, coordinates) + Lisp_Object screen, coordinates; +{ + int part; + + CHECK_SCREEN (screen, 0); + CHECK_CONS (coordinates, 1); + + return window_from_coordinates (XSCREEN (screen), + XINT (Fcar (coordinates)), + XINT (Fcar (Fcdr (coordinates))), + &part); +} + +DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0, + "Return current value of point in WINDOW.\n\ +For a nonselected window, this is the value point would have\n\ +if that window were selected.\n\ +\n\ +Note that, when WINDOW is the selected window and its buffer\n\ +is also currently selected, the value returned is the same as (point).\n\ +It would be more strictly correct to return the `top-level' value\n\ +of point, outside of any save-excursion forms.\n\ +But that is hard to define.") + (window) + Lisp_Object window; +{ + register struct window *w = decode_window (window); + + if (w == XWINDOW (selected_window) + && current_buffer == XBUFFER (w->buffer)) + return Fpoint (); + return Fmarker_position (w->pointm); +} + +DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, + "Return position at which display currently starts in WINDOW.") + (window) + Lisp_Object window; +{ + return Fmarker_position (decode_window (window)->start); +} + +DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 1, 0, + "Return position at which display currently ends in WINDOW.") + (window) + Lisp_Object window; +{ + Lisp_Object value; + struct window *w = decode_window (window); + + XSET (value, Lisp_Int, + BUF_Z (current_buffer) - XFASTINT (w->window_end_pos)); + + return value; +} + +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; +{ + register struct window *w = decode_window (window); + + CHECK_NUMBER_COERCE_MARKER (pos, 1); + if (w == XWINDOW (selected_window)) + Fgoto_char (pos); + else + set_marker_restricted (w->pointm, pos, w->buffer); + + 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.\n\ +Optional third arg NOFORCE non-nil inhibits next redisplay\n\ +from overriding motion of point in order to display at this exact start.") + (window, pos, noforce) + Lisp_Object window, pos, noforce; +{ + register struct window *w = decode_window (window); + + CHECK_NUMBER_COERCE_MARKER (pos, 1); + set_marker_restricted (w->start, pos, w->buffer); + /* this is not right, but much easier than doing what is right. */ + w->start_at_line_beg = Qnil; + if (NULL (noforce)) + w->force_start = Qt; + w->update_mode_line = Qt; + XFASTINT (w->last_modified) = 0; + return pos; +} + +DEFUN ("window-dedicated-p", Fwindow_dedicated_p, Swindow_dedicated_p, + 1, 1, 0, + "Return WINDOW's dedicated object, usually t or nil.\n\ +See also `set-window-buffer-dedicated'.") + (window) + Lisp_Object window; +{ + return decode_window (window)->dedicated; +} + +DEFUN ("set-window-buffer-dedicated", Fset_window_buffer_dedicated, + Sset_window_buffer_dedicated, 2, 2, 0, + "Make WINDOW display BUFFER and be dedicated to that buffer.\n\ +Then Emacs will not automatically change which buffer appears in WINDOW.\n\ +If BUFFER is nil, make WINDOW not be dedicated (but don't change which\n\ +buffer appears in it currently).") + (window, arg) + Lisp_Object window, arg; +{ + register struct window *w = decode_window (window); + + if (NULL (arg)) + w->dedicated = Qnil; + else + { + Fset_window_buffer (window, Fget_buffer_create (arg)); + w->dedicated = Qt; + } + + return w->dedicated; +} + +DEFUN ("window-display-table", Fwindow_display_table, Swindow_display_table, + 0, 1, 0, + "Return the display-table that WINDOW is using.") + (window) + Lisp_Object window; +{ + return decode_window (window)->display_table; +} + +/* Get the display table for use currently on window W. + This is either W's display table or W's buffer's display table. + Ignore the specified tables if they are not valid; + if no valid table is specified, return 0. */ + +struct Lisp_Vector * +window_display_table (w) + struct window *w; +{ + Lisp_Object tem; + tem = w->display_table; + if (XTYPE (tem) == Lisp_Vector && XVECTOR (tem)->size == DISP_TABLE_SIZE) + return XVECTOR (tem); + tem = XBUFFER (w->buffer)->display_table; + if (XTYPE (tem) == Lisp_Vector && XVECTOR (tem)->size == DISP_TABLE_SIZE) + return XVECTOR (tem); + tem = Vstandard_display_table; + if (XTYPE (tem) == Lisp_Vector && XVECTOR (tem)->size == DISP_TABLE_SIZE) + return XVECTOR (tem); + return 0; +} + +DEFUN ("set-window-display-table", + Fset_window_display_table, Sset_window_display_table, 2, 2, 0, + "Set WINDOW's display-table to TABLE.") + (window, table) + register Lisp_Object window, table; +{ + register struct window *w; + register Lisp_Object z; /* Return value. */ + + w = decode_window (window); + w->display_table = table; + return table; +} + +/* Record info on buffer window w is displaying + when it is about to cease to display that buffer. */ +static +unshow_buffer (w) + register struct window *w; +{ + Lisp_Object buf = w->buffer; + + if (XBUFFER (buf) != XMARKER (w->pointm)->buffer) + abort (); + + if (w == XWINDOW (selected_window) + || ! EQ (buf, XWINDOW (selected_window)->buffer)) + /* Do this except when the selected window's buffer + is being removed from some other window. */ + XBUFFER (buf)->last_window_start = marker_position (w->start); + + /* 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 (selected_window)->buffer)) + BUF_PT (XBUFFER (buf)) + = clip_to_bounds (BUF_BEGV (XBUFFER (buf)), + marker_position (w->pointm), + BUF_ZV (XBUFFER (buf))); +} + +/* Put replacement into the window structure in place of old. */ +static +replace_window (old, replacement) + Lisp_Object old, replacement; +{ + register Lisp_Object tem; + register struct window *o = XWINDOW (old), *p = XWINDOW (replacement); + + /* If OLD is its screen's root_window, then replacement is the new + root_window for that screen. */ + + if (old == XSCREEN (o->screen)->root_window) + XSCREEN (o->screen)->root_window = replacement; + + p->left = o->left; + p->top = o->top; + p->width = o->width; + p->height = o->height; + + p->next = tem = o->next; + if (!NULL (tem)) + XWINDOW (tem)->prev = replacement; + + p->prev = tem = o->prev; + if (!NULL (tem)) + XWINDOW (tem)->next = replacement; + + p->parent = tem = o->parent; + if (!NULL (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. ***/ +} + +DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "", + "Remove WINDOW from the display. Default is selected window.") + (window) + register Lisp_Object window; +{ + register Lisp_Object tem, parent, sib; + register struct window *p; + register struct window *par; + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 0); + + p = XWINDOW (window); + parent = p->parent; + if (NULL (parent)) + error ("Attempt to delete minibuffer or sole ordinary window"); + par = XWINDOW (parent); + + windows_or_buffers_changed++; + + if (EQ (window, selected_window)) + Fselect_window (Fnext_window (window, Qnil, Qnil)); + + tem = p->buffer; + /* tem is null for dummy parent windows + (which have inferiors but not any contents themselves) */ + if (!NULL (tem)) + { + unshow_buffer (p); + unchain_marker (p->pointm); + unchain_marker (p->start); + p->buffer = Qnil; + } + + tem = p->next; + if (!NULL (tem)) + XWINDOW (tem)->prev = p->prev; + + tem = p->prev; + if (!NULL (tem)) + XWINDOW (tem)->next = p->next; + + if (EQ (window, par->hchild)) + par->hchild = p->next; + if (EQ (window, par->vchild)) + par->vchild = p->next; + + /* Find one of our siblings to give our space to. */ + sib = p->prev; + if (NULL (sib)) + { + /* If p gives its space to its next sibling, that sibling needs + to have its top/left side pulled back to where p's is. + set_window_{height,width} will re-position the sibling's + children. */ + sib = p->next; + XFASTINT (XWINDOW (sib)->top) = p->top; + XFASTINT (XWINDOW (sib)->left) = p->left; + } + + /* Stretch that sibling. */ + if (!NULL (par->vchild)) + set_window_height (sib, + XFASTINT (XWINDOW (sib)->height) + XFASTINT (p->height), + 1); + if (!NULL (par->hchild)) + set_window_width (sib, + XFASTINT (XWINDOW (sib)->width) + XFASTINT (p->width), + 1); + + /* If parent now has only one child, + put the child into the parent's place. */ + + tem = par->hchild; + if (NULL (tem)) + tem = par->vchild; + if (NULL (XWINDOW (tem)->next)) + replace_window (parent, tem); + return Qnil; +} + +#ifdef MULTI_SCREEN +Lisp_Object +next_screen_window (screen, window, mini) + SCREEN_PTR screen; + Lisp_Object window, mini; +{ + Lisp_Object tem; + + if (NULL (window)) + window = SCREEN_SELECTED_WINDOW (screen); + + /* 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, NULL (tem)) + if (tem = XWINDOW (window)->parent, !NULL (tem)) + window = tem; + else + /* Since window's next and parent are nil, we have found + the minibuffer window of this screen. */ + { + tem = SCREEN_ROOT_WINDOW (screen); + 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 (!NULL (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NULL (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else break; + } + } + /* Exit the loop if + this isn't a minibuffer window, or + we're accepting all minibuffer windows, even when inactive, or + we're accepting active minibuffer windows and this one is. */ + while (MINI_WINDOW_P (XWINDOW (window)) + && !EQ (mini, Qt) + && (!NULL (mini) || !minibuf_level)); + + return window; +} +#endif + +extern Lisp_Object next_screen (), prev_screen (); + +DEFUN ("next-window", Fnext_window, Snext_window, 0, 3, 0, + "Return next window after WINDOW in canonical ordering of windows.\n\ +Optional second arg MINIBUF t means count the minibuffer window\n\ +even if not active. If MINIBUF is neither t nor nil it means\n\ +not to count the minibuffer even if it is active.\n\ +Optional third arg ALL-SCREENS t means include all windows in all screens;\n\ +otherwise cycle within the selected screen, with the exception that if a\n\ +global minibuffer screen is in use and MINIBUF is t, all screens are used.") + (window, mini, all_screens) + register Lisp_Object window, mini, all_screens; +{ + register Lisp_Object tem; + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 0); + +#ifdef MULTI_SCREEN + if (EQ (mini, Qt) + || (! NULL (mini) && minibuf_level)) + { + if (SCREENP (Vglobal_minibuffer_screen)) + all_screens = Qt; + } +#endif + + /* 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, NULL (tem)) + if (tem = XWINDOW (window)->parent, !NULL (tem)) + window = tem; + else + /* Since window's next and parent are nil, it must be + the minibuffer window of this screen. If all_screens, + jump to the next screen. */ + { + tem = WINDOW_SCREEN (XWINDOW (window)); +#ifdef MULTI_SCREEN + if (! NULL (all_screens)) + tem = next_screen (tem, NULL (mini) ? 0 : 1); +#endif + tem = SCREEN_ROOT_WINDOW (XSCREEN (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 (!NULL (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NULL (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else break; + } + } + /* Exit the loop if + this isn't a minibuffer window, or + we're accepting all minibuffer windows, even when inactive, or + we're accepting active minibuffer windows and this one is, or + this is a screen whose only window is a minibuffer window. */ + while (MINI_WINDOW_P (XWINDOW (window)) + && !EQ (mini, Qt) + && (!NULL (mini) || !minibuf_level) + && !EQ (SCREEN_ROOT_WINDOW (XSCREEN (XWINDOW (window)->screen)), + SCREEN_MINIBUF_WINDOW (XSCREEN (XWINDOW (window)->screen)))); + + return window; +} + +DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 3, 0, + "Return previous window before WINDOW in canonical ordering of windows.\n\ +Optional second arg MINIBUF t means count the minibuffer window\n\ +even if not active. If MINIBUF is neither t nor nil it means\n\ +not to count the minibuffer even if it is active.\n\ +Optional third arg ALL-SCREENS t means include all windows in all screens;\n\ +otherwise cycle within the selected screen, with the exception that if a\n\ +global minibuffer screen is in use and MINIBUF is t, all screens are used.") + (window, mini, all_screens) + register Lisp_Object window, mini, all_screens; +{ + register Lisp_Object tem; + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 0); + +#ifdef MULTI_SCREEN + if (EQ (mini, Qt) + || (! NULL (mini) && minibuf_level)) + { + if (SCREENP (Vglobal_minibuffer_screen)) + all_screens = Qt; + } +#endif + + /* Do this loop at least once, to get the previous window, and perhaps + again, if we hit the minibuffer and that is not acceptable. */ + do + { + /* Find a window that actually has a previous one. This loop + climbs up the tree. */ + while (tem = XWINDOW (window)->prev, NULL (tem)) + if (tem = XWINDOW (window)->parent, !NULL (tem)) + window = tem; + else + /* Since window's prev and parent are nil, we have found + the root window of this screen. If all_screens, jump + to the previous screen. */ + { + tem = WINDOW_SCREEN (XWINDOW (window)); +#ifdef MULTI_SCREEN + if (! NULL (all_screens)) + tem = prev_screen (tem, NULL (mini) ? 0 : 1); +#endif + tem = SCREEN_ROOT_WINDOW (XSCREEN (tem)); + break; + } + + window = tem; + /* If we're in a combination window, find its last child and + recurse on that. Otherwise, we've found the window we want. */ + while (1) + { + if (!NULL (XWINDOW (window)->hchild)) + window = XWINDOW (window)->hchild; + else if (!NULL (XWINDOW (window)->vchild)) + window = XWINDOW (window)->vchild; + else break; + while (tem = XWINDOW (window)->next, !NULL (tem)) + window = tem; + } + } + /* Exit the loop if + this isn't a minibuffer window, or + we're accepting all minibuffer windows, even when inactive, or + we're accepting active minibuffer windows and this one is, or + this is a screen whose only window is a minibuffer window. */ + while (MINI_WINDOW_P (XWINDOW (window)) + && !EQ (mini, Qt) + && (!NULL (mini) || !minibuf_level) + && !EQ (SCREEN_ROOT_WINDOW (XSCREEN (XWINDOW (window)->screen)), + SCREEN_MINIBUF_WINDOW (XSCREEN (XWINDOW (window)->screen)))); + + return window; +} + +DEFUN ("other-window", Fother_window, Sother_window, 1, 1, "p", + "Select the ARG'th different window on this screen.\n\ +All windows on current screen are arranged in a cyclic order.\n\ +This command selects the window ARG steps away in that order.\n\ +A negative ARG moves in the opposite order. If the optional second\n\ +argument ALL_SCREENS is non-nil, cycle through all screens.") + (n, all_screens) + register Lisp_Object n, all_screens; +{ + register int i; + register Lisp_Object w; + + CHECK_NUMBER (n, 0); + w = selected_window; + i = XINT (n); + + while (i > 0) + { + w = Fnext_window (w, Qnil, all_screens); + i--; + } + while (i < 0) + { + w = Fprevious_window (w, Qnil, all_screens); + i++; + } + Fselect_window (w); + return Qnil; +} + +/* Look at all windows, performing an operation specified by TYPE + with argument OBJ. + If SCREENS is Qt, look at all screens, if Qnil, look at just the selected + screen. If SCREENS is a screen, just look at windows on that screen. + 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 */ +}; + +static Lisp_Object +window_loop (type, obj, mini, screens) + enum window_loop type; + register Lisp_Object obj, screens; + int mini; +{ + register Lisp_Object w; + register Lisp_Object best_window; + register Lisp_Object next_window; + register Lisp_Object first_window; + SCREEN_PTR screen; + + /* If we're only looping through windows on a particular screen, + screen points to that screen. If we're looping through windows + on all screens, screen is 0. */ + if (SCREENP (screens)) + screen = XSCREEN (screens); + else if (NULL (screens)) + screen = selected_screen; + else + screen = 0; + + /* Pick a window to start with. */ + if (XTYPE (obj) == Lisp_Window) + first_window = obj; + else if (screen) + first_window = SCREEN_SELECTED_WINDOW (screen); + else + first_window = SCREEN_SELECTED_WINDOW (selected_screen); + + w = first_window; + best_window = Qnil; + do + { + /* Pick the next window now, since some operations will delete + the current window. */ +#ifdef MULTI_SCREEN + if (screen) + next_window = next_screen_window (screen, w, mini ? Qt : Qnil); + else +#endif /* MULTI_SCREEN */ + /* We know screen is 0, so we're looping through all screens. + Or we know this isn't a MULTI_SCREEN Emacs, so who cares? */ + next_window = Fnext_window (w, mini ? Qt : Qnil, Qt); + + if (!MINI_WINDOW_P (XWINDOW (w)) + || (mini && minibuf_level > 0)) + switch (type) + { + case GET_BUFFER_WINDOW: +#if 0 + /* Ignore invisible and iconified screens. */ + if (! SCREEN_VISIBLE_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w)))) + || SCREEN_ICONIFIED_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w))))) + break; +#endif + if (XBUFFER (XWINDOW (w)->buffer) == XBUFFER (obj)) + return w; + break; + + case GET_LRU_WINDOW: + /* t as arg means consider only full-width windows */ + if (!NULL (obj) && XFASTINT (XWINDOW (w)->width) != screen->width) + break; +#if 0 + /* Ignore invisible and iconified screens. */ + if (! SCREEN_VISIBLE_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w)))) + || SCREEN_ICONIFIED_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w))))) + break; +#endif + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (XWINDOW (w)) + || !NULL (XWINDOW (w)->dedicated)) + break; + if (NULL (best_window) + || (XFASTINT (XWINDOW (best_window)->use_time) + > XFASTINT (XWINDOW (w)->use_time))) + best_window = w; + break; + + case DELETE_OTHER_WINDOWS: + if (XWINDOW (w) != XWINDOW (obj)) + Fdelete_window (w); + break; + + case DELETE_BUFFER_WINDOWS: + if (EQ (XWINDOW (w)->buffer, obj)) + { + /* If we're deleting the buffer displayed in the only window + on the screen, find a new buffer to display there. */ + if (NULL (XWINDOW (w)->parent)) + { + Lisp_Object new_buffer = Fother_buffer (obj); + if (NULL (new_buffer)) + new_buffer + = Fget_buffer_create (build_string ("*scratch*")); + Fset_window_buffer (w, new_buffer); + Fset_buffer (XWINDOW (w)->buffer); + } + else + Fdelete_window (w); + } + break; + + case GET_LARGEST_WINDOW: +#if 0 + /* Ignore invisible and iconified screens. */ + if (! SCREEN_VISIBLE_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w)))) + || SCREEN_ICONIFIED_P (XSCREEN (WINDOW_SCREEN (XWINDOW (w))))) + break; +#endif + /* Ignore dedicated windows and minibuffers. */ + if (MINI_WINDOW_P (XWINDOW (w)) + || !NULL (XWINDOW (w)->dedicated)) + break; + { + struct window *best_window_ptr = XWINDOW (best_window); + struct window *w_ptr = XWINDOW (w); + if (NULL (best_window) || + (XFASTINT (w_ptr->height) * XFASTINT (w_ptr->width)) + > (XFASTINT (best_window_ptr->height) + * XFASTINT (best_window_ptr->width))) + best_window = w; + } + break; + + case UNSHOW_BUFFER: + if (EQ (XWINDOW (w)->buffer, obj)) + { + /* Find another buffer to show in this window. */ + Lisp_Object another_buffer = Fother_buffer (obj); + if (NULL (another_buffer)) + another_buffer + = Fget_buffer_create (build_string ("*scratch*")); + Fset_window_buffer (w, another_buffer); + if (EQ (w, selected_window)) + Fset_buffer (XWINDOW (w)->buffer); + } + break; + } + w = next_window; + } + while (! EQ (w, first_window)); + + return best_window; +} + +DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 1, 0, + "Return the window least recently selected or used for display.\n\ +If optional argument SCREENS is t, search all screens. If SCREEN is a\n\ +screen, search only that screen.\n") + (screens) + Lisp_Object screens; +{ + register Lisp_Object w; + /* First try for a window that is full-width */ + w = window_loop (GET_LRU_WINDOW, Qt, 0, screens); + if (!NULL (w) && !EQ (w, selected_window)) + return w; + /* If none of them, try the rest */ + return window_loop (GET_LRU_WINDOW, Qnil, 0, screens); +} + +DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 1, 0, + "Return the largest window in area.\n\ +If optional argument SCREENS is t, search all screens. If SCREEN is a\n\ +screen, search only that screen.\n") + (screen) + Lisp_Object screen; +{ + return window_loop (GET_LARGEST_WINDOW, Qnil, 0, + screen); +} + +DEFUN ("get-buffer-window", Fget_buffer_window, Sget_buffer_window, 1, 2, 0, + "Return a window currently displaying BUFFER, or nil if none.\n\ +If optional argument SCREENS is t, search all screens. If SCREEN is a\n\ +screen, search only that screen.\n") + (buffer, screen) + Lisp_Object buffer, screen; +{ + buffer = Fget_buffer (buffer); + if (XTYPE (buffer) == Lisp_Buffer) + return window_loop (GET_BUFFER_WINDOW, buffer, 1, screen); + else + return Qnil; +} + +DEFUN ("delete-other-windows", Fdelete_other_windows, Sdelete_other_windows, + 0, 1, "", + "Make WINDOW (or the selected window) fill its screen.\n\ +Only the screen WINDOW is on is affected.") + (window) + Lisp_Object window; +{ + struct window *w; + int opoint = point; + struct buffer *obuf = current_buffer; + int top; + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 0); + + w = XWINDOW (window); + top = XFASTINT (w->top); + + window_loop (DELETE_OTHER_WINDOWS, window, 0, WINDOW_SCREEN(w)); + + Fset_buffer (w->buffer); + SET_PT (marker_position (w->start)); + Frecenter (make_number (top)); + + set_buffer_internal (obuf); + SET_PT (opoint); + return Qnil; +} + +DEFUN ("delete-windows-on", Fdelete_windows_on, Sdelete_windows_on, + 1, 1, "bDelete windows on (buffer): ", + "Delete all windows showing BUFFER.") + (buffer) + Lisp_Object buffer; +{ + if (!NULL (buffer)) + { + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer, 0); + window_loop (DELETE_BUFFER_WINDOWS, buffer, 0, Qt); + } + 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; +{ + if (!NULL (buffer)) + { + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer, 0); + window_loop (UNSHOW_BUFFER, buffer, 0, Qt); + } + return Qnil; +} + +/* Set the height of WINDOW and all its inferiors. */ +/* 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) */ + +set_window_height (window, height, nodelete) + Lisp_Object window; + int height; + int nodelete; +{ + register struct window *w = XWINDOW (window); + register struct window *c; + int oheight = XFASTINT (w->height); + int top, pos, lastbot, opos, lastobot; + Lisp_Object child; + + if (!nodelete + && ! NULL (w->parent) + && height < window_min_height) + { + Fdelete_window (window); + return; + } + + XFASTINT (w->last_modified) = 0; + windows_or_buffers_changed++; + XFASTINT (w->height) = height; + if (!NULL (w->hchild)) + { + for (child = w->hchild; !NULL (child); child = XWINDOW (child)->next) + { + XWINDOW (child)->top = w->top; + set_window_height (child, height, nodelete); + } + } + else if (!NULL (w->vchild)) + { + lastbot = top = XFASTINT (w->top); + lastobot = 0; + for (child = w->vchild; !NULL (child); child = c->next) + { + c = XWINDOW (child); + + opos = lastobot + XFASTINT (c->height); + + XFASTINT (c->top) = lastbot; + + pos = (((opos * height) << 1) + oheight) / (oheight << 1); + + /* Avoid confusion: inhibit deletion of child if becomes too small */ + set_window_height (child, pos + top - lastbot, 1); + + /* Now advance child to next window, + and set lastbot if child was not just deleted. */ + lastbot = pos + top; + lastobot = opos; + } + /* Now delete any children that became too small. */ + if (!nodelete) + for (child = w->vchild; !NULL (child); child = XWINDOW (child)->next) + { + set_window_height (child, XINT (XWINDOW (child)->height), 0); + } + } +} + +/* Recursively set width of WINDOW and its inferiors. */ + +set_window_width (window, width, nodelete) + Lisp_Object window; + int width; + int nodelete; +{ + register struct window *w = XWINDOW (window); + register struct window *c; + int owidth = XFASTINT (w->width); + int left, pos, lastright, opos, lastoright; + Lisp_Object child; + + if (!nodelete && width < window_min_width) + { + Fdelete_window (window); + return; + } + + XFASTINT (w->last_modified) = 0; + windows_or_buffers_changed++; + XFASTINT (w->width) = width; + if (!NULL (w->vchild)) + { + for (child = w->vchild; !NULL (child); child = XWINDOW (child)->next) + { + XWINDOW (child)->left = w->left; + set_window_width (child, width, nodelete); + } + } + else if (!NULL (w->hchild)) + { + lastright = left = XFASTINT (w->left); + lastoright = 0; + for (child = w->hchild; !NULL (child); child = c->next) + { + c = XWINDOW (child); + + opos = lastoright + XFASTINT (c->width); + + XFASTINT (c->left) = lastright; + + pos = (((opos * width) << 1) + owidth) / (owidth << 1); + + /* Inhibit deletion for becoming too small */ + set_window_width (child, pos + left - lastright, 1); + + /* Now advance child to next window, + and set lastright if child was not just deleted. */ + lastright = pos + left, lastoright = opos; + } + /* Delete children that became too small */ + if (!nodelete) + for (child = w->hchild; !NULL (child); child = XWINDOW (child)->next) + { + set_window_width (child, XINT (XWINDOW (child)->width), 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.\n\ +BUFFER can be a buffer or buffer name.") + (window, buffer) + register Lisp_Object window, buffer; +{ + register Lisp_Object tem; + register struct window *w = decode_window (window); + + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer, 1); + + if (NULL (XBUFFER (buffer)->name)) + error ("Attempt to display deleted buffer"); + + tem = w->buffer; + if (NULL (tem)) + error ("Window is deleted"); + else if (! EQ (tem, Qt)) /* w->buffer is t when the window + is first being set up. */ + { + if (!NULL (w->dedicated) && !EQ (tem, buffer)) + error ("Window is dedicated to %s\n", tem); + + unshow_buffer (w); + } + + w->buffer = buffer; + Fset_marker (w->pointm, + make_number (BUF_PT (XBUFFER (buffer))), + buffer); + set_marker_restricted (w->start, + make_number (XBUFFER (buffer)->last_window_start), + buffer); + w->start_at_line_beg = Qnil; + XFASTINT (w->last_modified) = 0; + windows_or_buffers_changed++; + if (EQ (window, selected_window)) + Fset_buffer (buffer); + + return Qnil; +} + +DEFUN ("select-window", Fselect_window, Sselect_window, 1, 1, 0, + "Select WINDOW. Most editing will apply to WINDOW's buffer.\n\ +The main editor command loop selects the buffer of the selected window\n\ +before each command.") + (window) + register Lisp_Object window; +{ + register struct window *w; + register struct window *ow = XWINDOW (selected_window); + + CHECK_WINDOW (window, 0); + + w = XWINDOW (window); + + if (NULL (w->buffer)) + error ("Trying to select deleted window or non-leaf window"); + + XFASTINT (w->use_time) = ++window_select_count; + if (EQ (window, selected_window)) + return window; + + Fset_marker (ow->pointm, make_number (BUF_PT (XBUFFER (ow->buffer))), + ow->buffer); + + selected_window = window; +#ifdef MULTI_SCREEN + if (XSCREEN (WINDOW_SCREEN (w)) != selected_screen) + { + XSCREEN (WINDOW_SCREEN (w))->selected_window = window; + Fselect_screen (WINDOW_SCREEN (w), Qnil); + } + else + selected_screen->selected_window = window; +#endif + + record_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. */ + { + register int new_point = marker_position (w->pointm); + if (new_point < BEGV) + SET_PT (BEGV); + if (new_point > ZV) + SET_PT (ZV); + else + SET_PT (new_point); + } + + windows_or_buffers_changed++; + return window; +} + +DEFUN ("display-buffer", Fdisplay_buffer, Sdisplay_buffer, 1, 2, 0, + "Make BUFFER appear in some window but don't select it.\n\ +BUFFER can be a buffer or a buffer name.\n\ +If BUFFER is shown already in some window, just use that one,\n\ +unless the window is the selected window and the optional second\n\ +argument NOT_THIS_WINDOW is non-nil.\n\ +Returns the window displaying BUFFER.") + (buffer, not_this_window) + register Lisp_Object buffer, not_this_window; +{ + register Lisp_Object window; + + buffer = Fget_buffer (buffer); + CHECK_BUFFER (buffer, 0); + + if (!NULL (Vdisplay_buffer_function)) + return call2 (Vdisplay_buffer_function, buffer, not_this_window); + + if (NULL (not_this_window) + && XBUFFER (XWINDOW (selected_window)->buffer) == XBUFFER (buffer)) + return selected_window; + + window = Fget_buffer_window (buffer, Qnil); + if (!NULL (window) + && (NULL (not_this_window) || !EQ (window, selected_window))) + return window; + +#ifdef MULTI_SCREEN + if (auto_new_screen) + { + window + = Fscreen_selected_window (NULL (Vauto_new_screen_function) + ? Fx_create_screen (Qnil) + : call0 (Vauto_new_screen_function)); + Fset_window_buffer (window, buffer); +#if 0 + Fselect_screen (XWINDOW (window)->screen, Qnil); +#endif + return window; + } +#endif /* MULTI_SCREEN */ + + if (pop_up_windows) + { +#ifdef MULTI_SCREEN + /* When minibuffer screen is used, this is the previous screen. + Declared in minibuffer.c */ + extern struct screen *active_screen; + Lisp_Object screens; + + if (active_screen) + XSET (screens, Lisp_Screen, active_screen); + else + screens = Qnil; + +#endif + /* Don't try to create a window if would get an error */ + if (split_height_threshold < window_min_height << 1) + split_height_threshold = window_min_height << 1; + + window = Fget_largest_window (screens); + + if (!NULL (window) + && window_height (window) >= split_height_threshold + && + (XFASTINT (XWINDOW (window)->width) + == SCREEN_WIDTH (XSCREEN (WINDOW_SCREEN (XWINDOW (window)))))) + window = Fsplit_window (window, Qnil, Qnil); + else + { + window = Fget_lru_window (screens); + if ((EQ (window, selected_window) + || EQ (XWINDOW (window)->parent, Qnil)) + && window_height (window) >= window_min_height << 1) + window = Fsplit_window (window, Qnil, Qnil); + } + } + else + window = Fget_lru_window (Qnil); + + Fset_window_buffer (window, buffer); + return window; +} + +void +temp_output_buffer_show (buf) + register Lisp_Object buf; +{ + register struct buffer *old = current_buffer; + register Lisp_Object window; + register struct window *w; + + Fset_buffer (buf); + XBUFFER (buf)->save_modified = MODIFF; + BEGV = BEG; + ZV = Z; + SET_PT (BEG); + clip_changed = 1; + set_buffer_internal (old); + + if (!EQ (Vtemp_buffer_show_function, Qnil)) + call1 (Vtemp_buffer_show_function, buf); + else + { + window = Fdisplay_buffer (buf, Qnil); + +#ifdef MULTI_SCREEN + if (XSCREEN (XWINDOW (window)->screen) != selected_screen) + Fmake_screen_visible (XWINDOW (window)->screen); +#endif /* MULTI_SCREEN */ + Vminibuf_scroll_window = window; + w = XWINDOW (window); + XFASTINT (w->hscroll) = 0; + set_marker_restricted (w->start, make_number (1), buf); + set_marker_restricted (w->pointm, make_number (1), buf); + } +} + +static +make_dummy_parent (window) + Lisp_Object window; +{ + register Lisp_Object old, new; + register struct window *o, *p; + + old = window; + XSETTYPE (old, Lisp_Vector); + new = Fcopy_sequence (old); + XSETTYPE (new, Lisp_Window); + + o = XWINDOW (old); + p = XWINDOW (new); + XFASTINT (p->sequence_number) = ++sequence_number; + + /* 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 = Qnil; + p->pointm = Qnil; + p->buffer = Qnil; +} + +DEFUN ("split-window", Fsplit_window, Ssplit_window, 0, 3, "", + "Split WINDOW, putting SIZE lines in the first of the pair.\n\ +WINDOW defaults to selected one and SIZE to half its size.\n\ +If optional third arg HOR-FLAG is non-nil, split side by side\n\ +and put SIZE columns in the first of the pair.") + (window, chsize, horflag) + Lisp_Object window, chsize, horflag; +{ + register Lisp_Object new; + register struct window *o, *p; + register int size; + + if (NULL (window)) + window = selected_window; + else + CHECK_WINDOW (window, 0); + + o = XWINDOW (window); + + if (NULL (chsize)) + { + if (!NULL (horflag)) + /* Round odd size up, since this is for the left-hand window, + and it will lose a column for the separators. */ + size = ((XFASTINT (o->width) + 1) & -2) >> 1; + else + size = XFASTINT (o->height) >> 1; + } + else + { + CHECK_NUMBER (chsize, 1); + size = XINT (chsize); + } + + if (MINI_WINDOW_P (o)) + error ("Attempt to split minibuffer window"); + else if (SCREEN_NO_SPLIT_P (XSCREEN (WINDOW_SCREEN (o)))) + error ("Attempt to split unsplittable screen"); + + /* Smaller values might permit a crash. */ + if (window_min_width < 2) + window_min_width = 2; + if (window_min_height < 2) + window_min_height = 2; + + if (NULL (horflag)) + { + if (size < window_min_height + || size + window_min_height > XFASTINT (o->height)) + args_out_of_range_3 (window, chsize, horflag); + if (NULL (o->parent) + || NULL (XWINDOW (o->parent)->vchild)) + { + make_dummy_parent (window); + new = o->parent; + XWINDOW (new)->vchild = window; + } + } + else + { + if (size < window_min_width + || size + window_min_width > XFASTINT (o->width)) + args_out_of_range_3 (window, chsize, horflag); + if (NULL (o->parent) + || NULL (XWINDOW (o->parent)->hchild)) + { + make_dummy_parent (window); + new = o->parent; + XWINDOW (new)->hchild = window; + } + } + + /* 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 */ + + windows_or_buffers_changed++; + new = make_window (); + p = XWINDOW (new); + + p->screen = o->screen; + p->next = o->next; + if (!NULL (p->next)) + XWINDOW (p->next)->prev = new; + p->prev = window; + o->next = new; + p->parent = o->parent; + p->buffer = Qt; + + Fset_window_buffer (new, o->buffer); + + /* Apportion the available screen space among the two new windows */ + + if (!NULL (horflag)) + { + p->height = o->height; + p->top = o->top; + XFASTINT (p->width) = XFASTINT (o->width) - size; + XFASTINT (o->width) = size; + XFASTINT (p->left) = XFASTINT (o->left) + size; + } + else + { + p->left = o->left; + p->width = o->width; + XFASTINT (p->height) = XFASTINT (o->height) - size; + XFASTINT (o->height) = size; + XFASTINT (p->top) = XFASTINT (o->top) + size; + } + + return new; +} + +DEFUN ("enlarge-window", Fenlarge_window, Senlarge_window, 1, 2, "p", + "Make current window ARG lines bigger.\n\ +From program, optional second arg non-nil means grow sideways ARG columns.") + (n, side) + register Lisp_Object n, side; +{ + CHECK_NUMBER (n, 0); + change_window_height (XINT (n), !NULL (side)); + return Qnil; +} + +DEFUN ("shrink-window", Fshrink_window, Sshrink_window, 1, 2, "p", + "Make current window ARG lines smaller.\n\ +From program, optional second arg non-nil means shrink sideways ARG columns.") + (n, side) + register Lisp_Object n, side; +{ + CHECK_NUMBER (n, 0); + change_window_height (-XINT (n), !NULL (side)); + return Qnil; +} + +int +window_height (window) + Lisp_Object window; +{ + register struct window *p = XWINDOW (window); + return XFASTINT (p->height); +} + +int +window_width (window) + Lisp_Object window; +{ + register struct window *p = XWINDOW (window); + return XFASTINT (p->width); +} + +#define MINSIZE(w) \ + (widthflag ? window_min_width : window_min_height) + +#define CURBEG(w) \ + *(widthflag ? (int *) &(w)->left : (int *) &(w)->top) + +#define CURSIZE(w) \ + *(widthflag ? (int *) &(w)->width : (int *) &(w)->height) + +/* Unlike set_window_height, this function + also changes the heights of the siblings so as to + keep everything consistent. */ + +change_window_height (delta, widthflag) + register int delta; + int widthflag; +{ + register Lisp_Object parent; + Lisp_Object window; + register struct window *p; + int *sizep; + int (*sizefun) () = widthflag ? window_width : window_height; + register int (*setsizefun) () = (widthflag + ? set_window_width + : set_window_height); + + /* Smaller values might permit a crash. */ + if (window_min_width < 2) + window_min_width = 2; + if (window_min_height < 2) + window_min_height = 2; + + window = selected_window; + while (1) + { + p = XWINDOW (window); + parent = p->parent; + if (NULL (parent)) + { + if (widthflag) + error ("No other window to side of this one"); + break; + } + if (widthflag ? !NULL (XWINDOW (parent)->hchild) + : !NULL (XWINDOW (parent)->vchild)) + break; + window = parent; + } + + sizep = &CURSIZE (p); + + if (*sizep + delta < MINSIZE (p) + && !NULL (XWINDOW (window)->parent)) + { + Fdelete_window (window); + return; + } + + { + register int maxdelta; + register Lisp_Object tem; + + maxdelta = (!NULL (parent) ? (*sizefun) (parent) - *sizep + : (tem = (!NULL (p->next) ? p->next : p->prev), + (*sizefun) (tem) - MINSIZE (tem))); + + if (delta > maxdelta) + /* This case traps trying to make the minibuffer + the full screen, or make the only window aside from the + minibuffer the full screen. */ + delta = maxdelta; + } + + if (!NULL (p->next) && + (*sizefun) (p->next) - delta >= MINSIZE (p->next)) + { + (*setsizefun) (p->next, (*sizefun) (p->next) - delta, 0); + (*setsizefun) (window, *sizep + delta, 0); + CURBEG (XWINDOW (p->next)) += delta; + /* This does not change size of p->next, + but it propagates the new top edge to its children */ + (*setsizefun) (p->next, (*sizefun) (p->next), 0); + } + else if (!NULL (p->prev) && + (*sizefun) (p->prev) - delta >= MINSIZE (p->prev)) + { + (*setsizefun) (p->prev, (*sizefun) (p->prev) - delta, 0); + CURBEG (p) -= delta; + (*setsizefun) (window, *sizep + delta, 0); + } + else + { + register int delta1; + register 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); + } + + XFASTINT (p->last_modified) = 0; +} +#undef MINSIZE +#undef CURBEG +#undef CURSIZE + + +/* Return number of lines of text (not counting mode line) in W. */ + +int +window_internal_height (w) + struct window *w; +{ + int ht = XFASTINT (w->height); + + if (MINI_WINDOW_P (w)) + return ht; + + if (!NULL (w->parent) || !NULL (w->vchild) || !NULL (w->hchild) + || !NULL (w->next) || !NULL (w->prev) + || SCREEN_WANTS_MODELINE_P (XSCREEN (WINDOW_SCREEN (w)))) + return ht - 1; + + return ht; +} + +/* Scroll contents of window WINDOW up N lines. */ + +void +window_scroll (window, n) + Lisp_Object window; + int n; +{ + register struct window *w = XWINDOW (window); + register int opoint = point; + register int pos; + register int ht = window_internal_height (w); + register Lisp_Object tem; + int lose; + Lisp_Object bolp, nmoved; + + XFASTINT (tem) = point; + tem = Fpos_visible_in_window_p (tem, window); + + if (NULL (tem)) + { + Fvertical_motion (make_number (- ht / 2)); + XFASTINT (tem) = point; + Fset_marker (w->start, tem, w->buffer); + w->force_start = Qt; + } + + SET_PT (marker_position (w->start)); + lose = n < 0 && point == BEGV; + Fvertical_motion (make_number (n)); + pos = point; + bolp = Fbolp (); + SET_PT (opoint); + + if (lose) + Fsignal (Qbeginning_of_buffer, Qnil); + + if (pos < ZV) +#if 0 + /* Allow scrolling to an empty screen (end of buffer) + if that is exactly how far we wanted to go. */ + || XINT (nmoved) == n) +#endif + { + set_marker_restricted (w->start, make_number (pos), w->buffer); + w->start_at_line_beg = bolp; + w->update_mode_line = Qt; + XFASTINT (w->last_modified) = 0; + if (pos > opoint) + SET_PT (pos); + if (n < 0) + { + SET_PT (pos); + tem = Fvertical_motion (make_number (ht)); + if (point > opoint || XFASTINT (tem) < ht) + SET_PT (opoint); + else + Fvertical_motion (make_number (-1)); + } + } + else + Fsignal (Qend_of_buffer, Qnil); +} + +/* This is the guts of Fscroll_up and Fscroll_down. */ + +static void +scroll_command (n, direction) + register Lisp_Object n; + int direction; +{ + register int defalt; + int count = specpdl_ptr - specpdl; + + /* If selected window's buffer isn't current, make it current for the moment. + But don't screw up if window_scroll gets an error. */ + if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer) + { + record_unwind_protect (save_excursion_restore, save_excursion_save ()); + Fset_buffer (XWINDOW (selected_window)->buffer); + } + + defalt = (window_internal_height (XWINDOW (selected_window)) + - next_screen_context_lines); + defalt = direction * (defalt < 1 ? 1 : defalt); + + if (NULL (n)) + window_scroll (selected_window, defalt); + else if (EQ (n, Qminus)) + window_scroll (selected_window, - defalt); + else + { + n = Fprefix_numeric_value (n); + window_scroll (selected_window, XINT (n) * direction); + } + + unbind_to (count, Qnil); +} + +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.\n\ +A near full screen is `next-screen-context-lines' less than a full screen.\n\ +When calling from a program, supply a number as argument or nil.") + (n) + Lisp_Object n; +{ + scroll_command (n, 1); + 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.\n\ +A near full screen is `next-screen-context-lines' less than a full screen.\n\ +When calling from a program, supply a number as argument or nil.") + (n) + Lisp_Object n; +{ + scroll_command (n, -1); + return Qnil; +} + +DEFUN ("scroll-other-window", Fscroll_other_window, Sscroll_other_window, 0, 1, "P", + "Scroll text of next window upward ARG lines; or near full screen if no ARG.\n\ +The next window is the one below the current one; or the one at the top\n\ +if the current one is at the bottom.\n\ +When calling from a program, supply a number as argument or nil.\n\ +\n\ +If in the minibuffer, `minibuf-scroll-window' if non-nil\n\ +specifies the window to scroll.\n\ +If `other-window-scroll-buffer' is non-nil, scroll the window\n\ +showing that buffer, popping the buffer up if necessary.") + (n) + register Lisp_Object n; +{ + register Lisp_Object window; + register int ht; + register struct window *w; + register int count = specpdl_ptr - specpdl; + + if (MINI_WINDOW_P (XWINDOW (selected_window)) + && !NULL (Vminibuf_scroll_window)) + window = Vminibuf_scroll_window; + /* If buffer is specified, scroll that buffer. */ + else if (!NULL (Vother_window_scroll_buffer)) + { + window = Fget_buffer_window (Vother_window_scroll_buffer, Qnil); + if (NULL (window)) + window = Fdisplay_buffer (Vother_window_scroll_buffer, Qt); + } + else + /* Nothing specified; pick a neighboring window. */ + window = Fnext_window (selected_window, Qnil, Qt); + CHECK_WINDOW (window, 0); + + if (EQ (window, selected_window)) + error ("There is no other window"); + + w = XWINDOW (window); + ht = window_internal_height (w); + + /* Don't screw up if window_scroll gets an error. */ + record_unwind_protect (save_excursion_restore, save_excursion_save ()); + + Fset_buffer (w->buffer); + SET_PT (marker_position (w->pointm)); + + if (NULL (n)) + window_scroll (window, ht - next_screen_context_lines); + else if (EQ (n, Qminus)) + window_scroll (window, next_screen_context_lines - ht); + else + { + if (XTYPE (n) == Lisp_Cons) + n = Fcar (n); + CHECK_NUMBER (n, 0); + window_scroll (window, XINT (n)); + } + + Fset_marker (w->pointm, make_number (point), Qnil); + unbind_to (count); + + return Qnil; +} + +DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 1, 1, "P", + "Scroll selected window display ARG columns left.\n\ +Default for ARG is window width minus 2.") + (arg) + register Lisp_Object arg; +{ + + if (NULL (arg)) + XFASTINT (arg) = XFASTINT (XWINDOW (selected_window)->width) - 2; + else + arg = Fprefix_numeric_value (arg); + + return + Fset_window_hscroll (selected_window, + make_number (XINT (XWINDOW (selected_window)->hscroll) + + XINT (arg))); +} + +DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 1, 1, "P", + "Scroll selected window display ARG columns right.\n\ +Default for ARG is window width minus 2.") + (arg) + register Lisp_Object arg; +{ + if (NULL (arg)) + XFASTINT (arg) = XFASTINT (XWINDOW (selected_window)->width) - 2; + else + arg = Fprefix_numeric_value (arg); + + return + Fset_window_hscroll (selected_window, + make_number (XINT (XWINDOW (selected_window)->hscroll) + - XINT (arg))); +} + +DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", + "Center point in window and redisplay screen. With ARG, put point on line ARG.\n\ +The desired position of point is always relative to the current window.\n\ +Just C-u as prefix means put point in the center of the screen.\n\ +No arg (i.e., it is nil) erases the entire screen and then\n\ +redraws with point in the center.") + (n) + register Lisp_Object n; +{ + register struct window *w = XWINDOW (selected_window); + register int ht = window_internal_height (w); + register int opoint = point; + + if (NULL (n)) + { + extern int screen_garbaged; + + SET_SCREEN_GARBAGED (XSCREEN (WINDOW_SCREEN (w))); + XFASTINT (n) = ht / 2; + } + else if (XTYPE (n) == Lisp_Cons) /* Just C-u. */ + { + XFASTINT (n) = ht / 2; + } + else + { + n = Fprefix_numeric_value (n); + CHECK_NUMBER (n, 0); + } + + if (XINT (n) < 0) + XSETINT (n, XINT (n) + ht); + + XSETINT (n, - XINT (n)); + + Fvertical_motion (n); + Fset_marker (w->start, make_number (point), w->buffer); + w->start_at_line_beg = Fbolp (); + + SET_PT (opoint); + w->force_start = Qt; + + return Qnil; +} + +DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, + 1, 1, "P", + "Position point relative to window.\n\ +With no argument, position text at center of window.\n\ +An argument specifies screen line; zero means top of window,\n\ +negative means relative to bottom of window.") + (arg) + register Lisp_Object arg; +{ + register struct window *w = XWINDOW (selected_window); + register int height = window_internal_height (w); + register int start; + + if (NULL (arg)) + XFASTINT (arg) = height / 2; + else + { + arg = Fprefix_numeric_value (arg); + if (XINT (arg) < 0) + XSETINT (arg, XINT (arg) + height); + } + + start = marker_position (w->start); + if (start < BEGV || start > ZV) + { + Fvertical_motion (make_number (- height / 2)); + Fset_marker (w->start, make_number (point), w->buffer); + w->start_at_line_beg = Fbolp (); + w->force_start = Qt; + } + else + SET_PT (start); + + return Fvertical_motion (arg); +} + +struct save_window_data + { + int size_from_Lisp_Vector_struct; + struct Lisp_Vector *next_from_Lisp_Vector_struct; + Lisp_Object screen_width, screen_height; + Lisp_Object current_window; + Lisp_Object current_buffer; + Lisp_Object minibuf_scroll_window; + Lisp_Object root_window; + /* A vector, interpreted as a struct saved_window */ + Lisp_Object saved_windows; + }; +#define SAVE_WINDOW_DATA_SIZE 7 /* Arg to Fmake_vector */ + +/* This is saved as a Lisp_Vector */ +struct saved_window + { + /* these first two must agree with struct Lisp_Vector in lisp.h */ + int size_from_Lisp_Vector_struct; + struct Lisp_Vector *next_from_Lisp_Vector_struct; + + Lisp_Object window; + Lisp_Object buffer, start, pointm, mark; + Lisp_Object left, top, width, height, hscroll; + Lisp_Object parent, prev; + Lisp_Object start_at_line_beg; + Lisp_Object display_table; + }; +#define SAVED_WINDOW_VECTOR_SIZE 14 /* Arg to Fmake_vector */ + +#define SAVED_WINDOW_N(swv,n) \ + ((struct saved_window *) (XVECTOR ((swv)->contents[(n)]))) + +DEFUN ("window-configuration-p", Fwindow_configuration_p, Swindow_configuration_p, 1, 1, 0, + "T if OBJECT is a window-configration object.") + (obj) + Lisp_Object obj; +{ + if (XTYPE (obj) == Lisp_Window_Configuration) + return Qt; + 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.\n\ +CONFIGURATION must be a value previously returned\n\ +by `current-window-configuration' (which see).") + (arg) + Lisp_Object arg; +{ + register struct window *w; + register struct save_window_data *data; + struct Lisp_Vector *saved_windows; + register struct saved_window *p; + register Lisp_Object tem; + Lisp_Object new_current_buffer; + int k; + SCREEN_PTR s, screen_to_select; + + while (XTYPE (arg) != Lisp_Window_Configuration) + { + arg = wrong_type_argument (intern ("window-configuration-p"), arg); + } + + data = (struct save_window_data *) XVECTOR (arg); + saved_windows = XVECTOR (data->saved_windows); + + s = XSCREEN (XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->screen); + + if (XFASTINT (data->screen_height) != SCREEN_HEIGHT (s) + || XFASTINT (data->screen_width) != SCREEN_WIDTH (s)) + { + /* Presumably something clever could be done. + However, it doesn't seem worth the effort */ + error ("Screen size %dx%d in saved window configuration mismatches screen.", + XFASTINT (data->screen_height), + XFASTINT (data->screen_width)); + } + + windows_or_buffers_changed++; + new_current_buffer = data->current_buffer; + if (NULL (XBUFFER (new_current_buffer)->name)) + new_current_buffer = Qnil; + + /* Mark all windows now on screen as "deleted". + Restoring the new configuration "undeletes" any that are in it. */ + + delete_all_subwindows (XWINDOW (s->root_window)); +#if 0 + /* This loses when the minibuf screen is not s. */ + delete_all_subwindows (XWINDOW (XWINDOW (minibuf_window)->prev)); +#endif + + for (k = 0; k < saved_windows->size; k++) + { + p = SAVED_WINDOW_N (saved_windows, k); + w = XWINDOW (p->window); + w->next = Qnil; + + if (!NULL (p->parent)) + w->parent = SAVED_WINDOW_N (saved_windows, XFASTINT (p->parent))->window; + else + w->parent = Qnil; + + if (!NULL (p->prev)) + { + w->prev = SAVED_WINDOW_N (saved_windows, XFASTINT (p->prev))->window; +#ifdef MULTI_SCREEN + /* This is true for a minibuffer-only screen. */ + if (w->mini_p && EQ (w->prev, p->window)) + w->next = Qnil; + else +#endif /* MULTI_SCREEN */ + XWINDOW (w->prev)->next = p->window; + } + else + { + w->prev = Qnil; + if (!NULL (w->parent)) + { + if (EQ (p->width, XWINDOW (w->parent)->width)) + { + XWINDOW (w->parent)->vchild = p->window; + XWINDOW (w->parent)->hchild = Qnil; + } + else + { + XWINDOW (w->parent)->hchild = p->window; + XWINDOW (w->parent)->vchild = Qnil; + } + } + } + w->left = p->left; + w->top = p->top; + w->width = p->width; + w->height = p->height; + w->hscroll = p->hscroll; + w->display_table = p->display_table; + XFASTINT (w->last_modified) = 0; + + /* Reinstall the saved buffer and pointers into it. */ + if (NULL (p->buffer)) + w->buffer = p->buffer; + else + { + if (!NULL (XBUFFER (p->buffer)->name)) + /* 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, Fmarker_position (p->start), w->buffer); + set_marker_restricted (w->pointm, Fmarker_position (p->pointm), w->buffer); + Fset_marker (XBUFFER (w->buffer)->mark, + Fmarker_position (p->mark), w->buffer); + + if (!EQ (p->buffer, new_current_buffer) && + XBUFFER (p->buffer) == current_buffer) + Fgoto_char (w->pointm); + } + else if (NULL (XBUFFER (w->buffer)->name)) + /* Else if window's old buffer is dead too, get a live one. */ + { + w->buffer = Fcdr (Fcar (Vbuffer_alist)); + /* This will set the markers to beginning of visible range. */ + set_marker_restricted (w->start, make_number (0), w->buffer); + set_marker_restricted (w->pointm, make_number (0), w->buffer); + w->start_at_line_beg = Qt; + } + else + /* Keeping window's old buffer; make sure the markers are real. */ + /* Else if window's old buffer is dead too, get a live one. */ + { + /* Set window markers at start of visible range. */ + if (XMARKER (w->start)->buffer == 0) + set_marker_restricted (w->start, make_number (0), w->buffer); + if (XMARKER (w->pointm)->buffer == 0) + set_marker_restricted (w->pointm, + make_number (BUF_PT (XBUFFER (w->buffer))), + w->buffer); + w->start_at_line_beg = Qt; + } + } + } + + SCREEN_ROOT_WINDOW (s) = data->root_window; + +#ifdef MULTI_SCREEN + if (s != selected_screen && ! SCREEN_IS_TERMCAP (s)) + Fselect_screen (WINDOW_SCREEN (XWINDOW (data->root_window)), Qnil); +#endif + + if (s == selected_screen) + { + Fselect_window (data->current_window); + if (!NULL (new_current_buffer)) + Fset_buffer (new_current_buffer); + else + Fset_buffer (XWINDOW (selected_window)->buffer); + } + + Vminibuf_scroll_window = data->minibuf_scroll_window; + return (Qnil); +} + +/* Mark all windows now on screen as deleted + by setting their buffers to nil. */ + +static void +delete_all_subwindows (w) + register struct window *w; +{ + register int count = 1; + w->buffer = Qnil; + if (!NULL (w->next)) + delete_all_subwindows (XWINDOW (w->next)); + if (!NULL (w->vchild)) + delete_all_subwindows (XWINDOW (w->vchild)); + if (!NULL (w->hchild)) + delete_all_subwindows (XWINDOW (w->hchild)); +} + +static int +count_windows (window) + register struct window *window; +{ + register int count = 1; + if (!NULL (window->next)) + count += count_windows (XWINDOW (window->next)); + if (!NULL (window->vchild)) + count += count_windows (XWINDOW (window->vchild)); + if (!NULL (window->hchild)) + count += count_windows (XWINDOW (window->hchild)); + return count; +} + +static int +save_window_save (window, vector, i) + Lisp_Object window; + struct Lisp_Vector *vector; + int i; +{ + register struct saved_window *p; + register struct window *w; + register Lisp_Object tem; + + for (;!NULL (window); window = w->next) + { + p = SAVED_WINDOW_N (vector, i); + w = XWINDOW (window); + + XFASTINT (w->temslot) = i++; + p->window = window; + p->buffer = w->buffer; + p->left = w->left; + p->top = w->top; + p->width = w->width; + p->height = w->height; + p->hscroll = w->hscroll; + p->display_table = w->display_table; + if (!NULL (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, selected_window)) + { + p->pointm = Fmake_marker (); + Fset_marker (p->pointm, BUF_PT (XBUFFER (w->buffer)), + w->buffer); + } + else + p->pointm = Fcopy_marker (w->pointm); + + p->start = Fcopy_marker (w->start); + p->start_at_line_beg = w->start_at_line_beg; + + tem = XBUFFER (w->buffer)->mark; + p->mark = Fcopy_marker (tem); + } + else + { + p->pointm = Qnil; + p->start = Qnil; + p->mark = Qnil; + p->start_at_line_beg = Qnil; + } + + if (NULL (w->parent)) + p->parent = Qnil; + else + p->parent = XWINDOW (w->parent)->temslot; + + if (NULL (w->prev)) + p->prev = Qnil; + else + p->prev = XWINDOW (w->prev)->temslot; + + if (!NULL (w->vchild)) + i = save_window_save (w->vchild, vector, i); + if (!NULL (w->hchild)) + i = save_window_save (w->hchild, vector, i); + } + + return i; +} + +DEFUN ("current-window-configuration", + Fcurrent_window_configuration, Scurrent_window_configuration, 0, 0, 0, + "Return an object representing Emacs' current window configuration.\n\ +This describes the number of windows, their sizes and current buffers,\n\ +and for each displayed buffer, where display starts, and the positions of\n\ +point and mark. An exception is made for point in the current buffer:\n\ +its value is -not- saved.") + () +{ + register Lisp_Object tem; + register int n_windows; + register struct save_window_data *data; + register int i; + + n_windows = count_windows (XWINDOW (SCREEN_ROOT_WINDOW (selected_screen))); + data = (struct save_window_data *) + XVECTOR (Fmake_vector (make_number (SAVE_WINDOW_DATA_SIZE), + Qnil)); + XFASTINT (data->screen_width) = SCREEN_WIDTH (selected_screen); + XFASTINT (data->screen_height) = SCREEN_HEIGHT (selected_screen); + data->current_window = selected_window; + XSET (data->current_buffer, Lisp_Buffer, current_buffer); + data->minibuf_scroll_window = Vminibuf_scroll_window; + data->root_window = SCREEN_ROOT_WINDOW (selected_screen); + tem = Fmake_vector (make_number (n_windows), Qnil); + data->saved_windows = tem; + for (i = 0; i < n_windows; i++) + XVECTOR (tem)->contents[i] + = Fmake_vector (make_number (SAVED_WINDOW_VECTOR_SIZE), Qnil); + save_window_save (SCREEN_ROOT_WINDOW (selected_screen), + XVECTOR (tem), 0); + XSET (tem, Lisp_Window_Configuration, data); + return (tem); +} + +DEFUN ("save-window-excursion", Fsave_window_excursion, Ssave_window_excursion, + 0, UNEVALLED, 0, + "Execute body, preserving window sizes and contents.\n\ +Restores which buffer appears in which window, where display starts,\n\ +as well as the current buffer.\n\ +Does not restore the value of point in current buffer.") + (args) + Lisp_Object args; +{ + register Lisp_Object val; + register int count = specpdl_ptr - specpdl; + + record_unwind_protect (Fset_window_configuration, + Fcurrent_window_configuration ()); + val = Fprogn (args); + return unbind_to (count, val); +} + +init_window_once () +{ +#ifdef MULTI_SCREEN + selected_screen = make_terminal_screen (); + minibuf_window = selected_screen->minibuffer_window; + selected_window = selected_screen->selected_window; +#else /* not MULTI_SCREEN */ + extern Lisp_Object get_minibuffer (); + + root_window = make_window (0); + minibuf_window = make_window (0); + + XWINDOW (root_window)->next = minibuf_window; + XWINDOW (minibuf_window)->prev = root_window; + + /* These values 9 and 10 are arbitrary, + just so that there is "something there." + Correct values are put in in init_xdisp */ + + XFASTINT (XWINDOW (root_window)->width) = 10; + XFASTINT (XWINDOW (minibuf_window)->width) = 10; + + XFASTINT (XWINDOW (root_window)->height) = 9; + XFASTINT (XWINDOW (minibuf_window)->top) = 9; + XFASTINT (XWINDOW (minibuf_window)->height) = 1; + + /* Prevent error in Fset_window_buffer. */ + XWINDOW (root_window)->buffer = Qt; + XWINDOW (minibuf_window)->buffer = Qt; + + /* Now set them up for real. */ + Fset_window_buffer (root_window, Fcurrent_buffer ()); + Fset_window_buffer (minibuf_window, get_minibuffer (0)); + + selected_window = root_window; +#endif /* not MULTI_SCREEN */ +} + +syms_of_window () +{ + Qwindowp = intern ("windowp"); + staticpro (&Qwindowp); + + /* Make sure all windows get marked */ + staticpro (&minibuf_window); + + DEFVAR_LISP ("temp-buffer-show-function", &Vtemp_buffer_show_function, + "Non-nil means call as function to display a help buffer.\n\ +Used by `with-output-to-temp-buffer'."); + Vtemp_buffer_show_function = Qnil; + + DEFVAR_LISP ("display-buffer-function", &Vdisplay_buffer_function, + "If non-nil, function to call to handle `display-buffer'.\n\ +It will receive two args, the buffer and a flag which if non-nil means\n\ + that the currently selected window is not acceptable.\n\ +Commands such as `switch-to-buffer-other-window' and `find-file-other-window'\n\ +work using this function."); + Vdisplay_buffer_function = Qnil; + + DEFVAR_LISP ("mouse-window", &Vmouse_window, + "Window that the last mouse click occurred on."); + Vmouse_window = Qnil; + + DEFVAR_LISP ("mouse-event", &Vmouse_event, + "The last mouse-event object. A list of four elements:\n\ + ((X-POS Y-POS) WINDOW SCREEN-PART KEYSEQ).\n\ +KEYSEQ is a string, the key sequence to be looked up in the mouse maps.\n\ +WINDOW is the window that the click applies do.\n\ +If SCREEN-PART is non-nil, the event was on a scrollbar;\n\ +then Y-POS is really the total length of the scrollbar, while X-POS is\n\ +the relative position of the scrollbar's value within that total length.\n\ +SCREEN-PART is one of the following symbols:\n\ + `vertical-scrollbar', `vertical-slider',\n\ + `vertical-thumbup', `vertical-thumbdown',\n\ + `horizontal-scrollbar', `horizontal-slider',\n\ + `horizontal-thumbleft', `horizontal-thumbright'"); + Vmouse_event = Qnil; + + DEFVAR_LISP ("minibuffer-scroll-window", &Vminibuf_scroll_window, + "Non-nil means it is the window that C-M-v 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; + +#ifdef MULTI_SCREEN + DEFVAR_BOOL ("auto-new-screen", &auto_new_screen, + "*Non-nil means `display-buffer' should make a separate X-window."); + auto_new_screen = 0; + + DEFVAR_LISP ("auto-new-screen-function", &Vauto_new_screen_function, + "*If non-nil, function to call to handle automatic new screen creation.\n\ +It is called with no arguments and should return a newly created screen.\n\ +nil means call `x-create-screen' with a nil argument.\n\ +\n\ +A typical value might be `(lambda () (x-create-screen auto-screen-parms))'\n\ +where `auto-screen-parms' would hold the default screen parameters."); + Vauto_new_screen_function = Qnil; +#endif + + DEFVAR_BOOL ("pop-up-windows", &pop_up_windows, + "*Non-nil means display-buffer should make new windows."); + pop_up_windows = 1; + + 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 ("split-height-threshold", &split_height_threshold, + "*display-buffer would prefer to split the largest window if this large.\n\ +If there is only one window, it is split regardless of this value."); + split_height_threshold = 500; + + DEFVAR_INT ("window-min-height", &window_min_height, + "*Delete any window less than this tall (including its mode line)."); + window_min_height = 4; + + DEFVAR_INT ("window-min-width", &window_min_width, + "*Delete any window less than this wide."); + window_min_width = 10; + + defsubr (&Sselected_window); + defsubr (&Sminibuffer_window); + defsubr (&Swindow_minibuffer_p); + defsubr (&Swindowp); + defsubr (&Spos_visible_in_window_p); + defsubr (&Swindow_buffer); + defsubr (&Swindow_height); + defsubr (&Swindow_width); + defsubr (&Swindow_hscroll); + defsubr (&Sset_window_hscroll); + defsubr (&Swindow_edges); + defsubr (&Slocate_window_from_coordinates); + defsubr (&Swindow_point); + defsubr (&Swindow_start); + defsubr (&Swindow_end); + defsubr (&Sset_window_point); + defsubr (&Sset_window_start); + defsubr (&Swindow_dedicated_p); + defsubr (&Sset_window_buffer_dedicated); + defsubr (&Swindow_display_table); + defsubr (&Sset_window_display_table); + defsubr (&Snext_window); + defsubr (&Sprevious_window); + defsubr (&Sother_window); + defsubr (&Sget_lru_window); + defsubr (&Sget_largest_window); + defsubr (&Sget_buffer_window); + defsubr (&Sdelete_other_windows); + defsubr (&Sdelete_windows_on); + defsubr (&Sreplace_buffer_in_windows); + defsubr (&Sdelete_window); + defsubr (&Sset_window_buffer); + defsubr (&Sselect_window); + defsubr (&Sdisplay_buffer); + defsubr (&Ssplit_window); + defsubr (&Senlarge_window); + defsubr (&Sshrink_window); + defsubr (&Sscroll_up); + defsubr (&Sscroll_down); + defsubr (&Sscroll_left); + defsubr (&Sscroll_right); + defsubr (&Sscroll_other_window); + defsubr (&Srecenter); + defsubr (&Smove_to_window_line); + defsubr (&Swindow_configuration_p); + defsubr (&Sset_window_configuration); + defsubr (&Scurrent_window_configuration); + defsubr (&Ssave_window_excursion); +} + +keys_of_window () +{ + initial_define_key (control_x_map, '1', "delete-other-windows"); + initial_define_key (control_x_map, '2', "split-window"); + initial_define_key (control_x_map, '0', "delete-window"); + initial_define_key (control_x_map, 'o', "other-window"); + initial_define_key (control_x_map, '^', "enlarge-window"); + initial_define_key (control_x_map, '<', "scroll-left"); + initial_define_key (control_x_map, '>', "scroll-right"); + + initial_define_key (global_map, Ctl ('V'), "scroll-up"); + initial_define_key (meta_map, Ctl ('V'), "scroll-other-window"); + initial_define_key (meta_map, 'v', "scroll-down"); + + initial_define_key (global_map, Ctl('L'), "recenter"); + initial_define_key (meta_map, 'r', "move-to-window-line"); +}