]> code.delx.au - gnu-emacs/blobdiff - src/window.c
Put doc strings in comments.
[gnu-emacs] / src / window.c
index 24c316e439511095e2bebde01fd3aa0e3ce2e67d..4c629113d9d4a74dd51ef36d532568e46513c115 100644 (file)
@@ -1,6 +1,6 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
-   Copyright (C) 1985,86,87,93,94,95,96,97,1998,2000
+   Copyright (C) 1985,86,87,93,94,95,96,97,1998,2000, 2001
    Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA.  */
 #include "lisp.h"
 #include "buffer.h"
 #include "keyboard.h"
+#include "keymap.h"
 #include "frame.h"
 #include "window.h"
 #include "commands.h"
@@ -51,11 +52,25 @@ Boston, MA 02111-1307, USA.  */
 #define max(a, b) ((a) < (b) ? (b) : (a))
 #endif
 
+/* Values returned from coordinates_in_window.  */
+
+enum window_part
+{
+  ON_NOTHING,
+  ON_TEXT,
+  ON_MODE_LINE,
+  ON_VERTICAL_BORDER,
+  ON_HEADER_LINE,
+  ON_LEFT_FRINGE,
+  ON_RIGHT_FRINGE
+};
+
 
 Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configuration_p;
-Lisp_Object Qwindow_size_fixed, Qleft_bitmap_area, Qright_bitmap_area;
+Lisp_Object Qwindow_size_fixed, Qleft_fringe, Qright_fringe;
 extern Lisp_Object Qheight, Qwidth;
 
+static int displayed_window_lines P_ ((struct window *));
 static struct window *decode_window P_ ((Lisp_Object));
 static Lisp_Object select_window_1 P_ ((Lisp_Object, int));
 static int count_windows P_ ((struct window *));
@@ -82,6 +97,10 @@ static int foreach_window_1 P_ ((struct window *,
                                 void *));
 static Lisp_Object window_list_1 P_ ((Lisp_Object, Lisp_Object, Lisp_Object));
 
+/* The value of `window-size-fixed'.  */
+
+int window_size_fixed;
+
 /* 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
@@ -203,8 +222,6 @@ Lisp_Object Vscroll_preserve_screen_position;
 static int inhibit_frame_unsplittable;
 #endif /* 0 */
 
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
 extern int scroll_margin;
 
 extern Lisp_Object Qwindow_scroll_functions, Vwindow_scroll_functions;
@@ -222,7 +239,7 @@ DEFUN ("window-live-p", Fwindow_live_p, Swindow_live_p, 1, 1, 0,
      (object)
      Lisp_Object object;
 {
-  return (WINDOWP (object) && ! NILP (XWINDOW (object)->buffer) ? Qt : Qnil);
+  return WINDOW_LIVE_P (object) ? Qt : Qnil;
 }
 
 Lisp_Object
@@ -230,20 +247,15 @@ make_window ()
 {
   Lisp_Object val;
   register struct window *p;
-  register struct Lisp_Vector *vec;
-  int i;
 
-  vec = allocate_vectorlike ((EMACS_INT) VECSIZE (struct window));
-  for (i = 0; i < VECSIZE (struct window); i++)
-    vec->contents[i] = Qnil;
-  vec->size = VECSIZE (struct window);
-  p = (struct window *) vec;
+  p = allocate_window ();
   XSETFASTINT (p->sequence_number, ++sequence_number);
   XSETFASTINT (p->left, 0);
   XSETFASTINT (p->top, 0);
   XSETFASTINT (p->height, 0);
   XSETFASTINT (p->width, 0);
   XSETFASTINT (p->hscroll, 0);
+  XSETFASTINT (p->min_hscroll, 0);
   p->orig_top = p->orig_height = Qnil;
   p->start = Fmake_marker ();
   p->pointm = Fmake_marker ();
@@ -417,17 +429,19 @@ 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;
+     Lisp_Object window, ncol;
 {
-  register struct window *w;
+  struct window *w = decode_window (window);
+  int hscroll;
 
   CHECK_NUMBER (ncol, 1);
-  if (XINT (ncol) < 0) XSETFASTINT (ncol, 0);
-  w = decode_window (window);
-  if (XINT (w->hscroll) != XINT (ncol))
-    /* Prevent redisplay shortcuts */
+  hscroll = max (0, XINT (ncol));
+  
+  /* Prevent redisplay shortcuts when changing the hscroll.  */
+  if (XINT (w->hscroll) != hscroll)
     XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
-  w->hscroll = ncol;
+  
+  w->hscroll = make_number (hscroll);
   return ncol;
 }
 
@@ -492,23 +506,23 @@ and BOTTOM is one more than the bottommost row used by WINDOW\n\
 
    X and Y are frame relative pixel coordinates.  */
 
-static int
+static enum window_part
 coordinates_in_window (w, x, y)
      register struct window *w;
      register int *x, *y;
 {
   /* Let's make this a global enum later, instead of using numbers
      everywhere.  */
-  enum {ON_NOTHING, ON_TEXT, ON_MODE_LINE, ON_VERTICAL_BORDER,
-       ON_HEADER_LINE, ON_LEFT_FRINGE, ON_RIGHT_FRINGE};
-
   struct frame *f = XFRAME (WINDOW_FRAME (w));
   int left_x, right_x, top_y, bottom_y;
   int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
-  int part;
+  enum window_part part;
   int ux = CANON_X_UNIT (f), uy = CANON_Y_UNIT (f);
   int x0 = XFASTINT (w->left) * ux;
   int x1 = x0 + XFASTINT (w->width) * ux;
+  /* The width of the area where the vertical line can be dragged.
+     (Between mode lines for instance.  */
+  int grabbable_width = ux;
 
   if (*x < x0 || *x >= x1)
     return ON_NOTHING;
@@ -550,10 +564,10 @@ coordinates_in_window (w, x, y)
       
       if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
        {
-         if (abs (*x - x0) < ux / 2)
+         if (abs (*x - x0) < grabbable_width)
            part = ON_VERTICAL_BORDER;
        }
-      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < grabbable_width)
        part = ON_VERTICAL_BORDER;
     }
   else if (WINDOW_WANTS_HEADER_LINE_P (w)
@@ -564,10 +578,10 @@ coordinates_in_window (w, x, y)
       
       if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
        {
-         if (abs (*x - x0) < ux / 2)
+         if (abs (*x - x0) < grabbable_width)
            part = ON_VERTICAL_BORDER;
        }
-      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < grabbable_width)
        part = ON_VERTICAL_BORDER;
     }
   /* Outside anything interesting?  */
@@ -576,7 +590,9 @@ coordinates_in_window (w, x, y)
           || *x < (left_x
                    - flags_area_width
                    - FRAME_LEFT_SCROLL_BAR_WIDTH (f) * ux)
-          || *x > right_x + flags_area_width)
+          || *x > (right_x
+                   + flags_area_width
+                   + FRAME_RIGHT_SCROLL_BAR_WIDTH (f) * ux))
     {
       part = ON_NOTHING;
     }
@@ -585,7 +601,7 @@ coordinates_in_window (w, x, y)
       if (!w->pseudo_window_p
          && !FRAME_HAS_VERTICAL_SCROLL_BARS (f)
          && !WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - right_x - flags_area_width) < ux / 2))
+         && (abs (*x - right_x - flags_area_width) < grabbable_width))
        {
          part = ON_VERTICAL_BORDER;
        }
@@ -647,15 +663,15 @@ DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p,
   "Return non-nil if COORDINATES are in WINDOW.\n\
 COORDINATES is a cons of the form (X . Y), X and Y being distances\n\
 measured in characters from the upper-left corner of the frame.\n\
-(0 .  0) denotes the character in the upper left corner of the\n\
+\(0 .  0) denotes the character in the upper left corner of the\n\
 frame.\n\
 If COORDINATES are in the text portion of WINDOW,\n\
    the coordinates relative to the window are returned.\n\
 If they are in the mode line of WINDOW, `mode-line' is returned.\n\
 If they are in the top mode line of WINDOW, `header-line' is returned.\n\
-If they are in the bitmap-area to the left of the window,\n\
-   `left-bitmap-area' is returned, if they are in the area on the right of\n\
-   the window, `right-bitmap-area' is returned.\n\
+If they are in the fringe to the left of the window,\n\
+   `left-fringe' is returned, if they are in the area on the right of\n\
+   the window, `right-fringe' is returned.\n\
 If they are on the border between WINDOW and its right sibling,\n\
    `vertical-line' is returned.")
   (coordinates, window)
@@ -679,30 +695,29 @@ If they are on the border between WINDOW and its right sibling,\n\
 
   switch (coordinates_in_window (w, &x, &y))
     {
-    case 0:                    /* NOT in window at all. */
+    case ON_NOTHING:
       return Qnil;
 
-    case 1:                    /* In text part of window. */
-      /* X and Y are now window relative pixel coordinates.
-        Convert them to canonical char units before returning
-        them.  */
+    case ON_TEXT:
+      /* X and Y are now window relative pixel coordinates.  Convert
+        them to canonical char units before returning them.  */
       return Fcons (CANON_X_FROM_PIXEL_X (f, x), 
                    CANON_Y_FROM_PIXEL_Y (f, y));
 
-    case 2:                    /* In mode line of window. */
+    case ON_MODE_LINE:
       return Qmode_line;
 
-    case 3:                    /* On right border of window.  */
+    case ON_VERTICAL_BORDER:
       return Qvertical_line;
 
-    case 4:
+    case ON_HEADER_LINE:
       return Qheader_line;
 
-    case 5:
-      return Qleft_bitmap_area;
+    case ON_LEFT_FRINGE:
+      return Qleft_fringe;
       
-    case 6:
-      return Qright_bitmap_area;
+    case ON_RIGHT_FRINGE:
+      return Qright_fringe;
 
     default:
       abort ();
@@ -731,16 +746,18 @@ check_window_containing (w, user_data)
      void *user_data;
 {
   struct check_window_data *cw = (struct check_window_data *) user_data;
-  int found;
+  enum window_part found;
+  int continue_p = 1;
 
   found = coordinates_in_window (w, cw->x, cw->y);
-  if (found)
+  if (found != ON_NOTHING)
     {
       *cw->part = found - 1;
       XSETWINDOW (*cw->window, w);
+      continue_p = 0;
     }
   
-  return !found;
+  return continue_p;
 }
 
 
@@ -780,7 +797,8 @@ window_from_coordinates (f, x, y, part, tool_bar_p)
       && tool_bar_p
       && WINDOWP (f->tool_bar_window)
       && XINT (XWINDOW (f->tool_bar_window)->height) > 0
-      && coordinates_in_window (XWINDOW (f->tool_bar_window), &x, &y))
+      && (coordinates_in_window (XWINDOW (f->tool_bar_window), &x, &y)
+         != ON_NOTHING))
     {
       *part = 0;
       window = f->tool_bar_window;
@@ -861,7 +879,7 @@ DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0,
 This is updated by redisplay, when it runs to completion.\n\
 Simply changing the buffer text or setting `window-start'\n\
 does not update this value.\n\
-If UP-TO-DATE is non-nil, compute the up-to-date position\n\
+If UPDATE is non-nil, compute the up-to-date position\n\
 if it isn't already recorded.")
   (window, update)
      Lisp_Object window, update;
@@ -888,6 +906,7 @@ if it isn't already recorded.")
     {
       struct text_pos startp;
       struct it it;
+      struct buffer *old_buffer = NULL, *b = XBUFFER (buf);
 
       /* In case W->start is out of the range, use something
          reasonable.  This situation occured when loading a file with
@@ -903,9 +922,20 @@ if it isn't already recorded.")
 
       /* Cannot use Fvertical_motion because that function doesn't
         cope with variable-height lines.  */
+      if (b != current_buffer)
+       {
+         old_buffer = current_buffer;
+         set_buffer_internal (b);
+       }
+      
       start_display (&it, w, startp);
       move_it_vertically (&it, window_box_height (w));
+      if (it.current_y < it.last_visible_y)
+       move_it_past_eol (&it);
       value = make_number (IT_CHARPOS (it));
+      
+      if (old_buffer)
+       set_buffer_internal (old_buffer);
     }
   else
     XSETINT (value, BUF_Z (XBUFFER (buf)) - XFASTINT (w->window_end_pos));
@@ -1161,7 +1191,7 @@ delete_window (window)
   register Lisp_Object tem, parent, sib;
   register struct window *p;
   register struct window *par;
-  FRAME_PTR frame;
+  struct frame *f;
 
   /* Because this function is called by other C code on non-leaf
      windows, the CHECK_LIVE_WINDOW macro would choke inappropriately,
@@ -1185,19 +1215,17 @@ delete_window (window)
 
   windows_or_buffers_changed++;
   Vwindow_list = Qnil;
-  frame = XFRAME (WINDOW_FRAME (p));
-  FRAME_WINDOW_SIZES_CHANGED (frame) = 1;
-  ensure_frame_matrix (frame);
+  f = XFRAME (WINDOW_FRAME (p));
+  FRAME_WINDOW_SIZES_CHANGED (f) = 1;
 
   /* Are we trying to delete any frame's selected window?  */
   {
-    Lisp_Object frame, pwindow;
+    Lisp_Object pwindow;
 
     /* See if the frame's selected window is either WINDOW
        or any subwindow of it, by finding all that window's parents
        and comparing each one with WINDOW.  */
-    frame = WINDOW_FRAME (XWINDOW (window));
-    pwindow = FRAME_SELECTED_WINDOW (XFRAME (frame));
+    pwindow = FRAME_SELECTED_WINDOW (f);
 
     while (!NILP (pwindow))
       {
@@ -1219,7 +1247,7 @@ delete_window (window)
        if (EQ (window, selected_window))
          Fselect_window (alternative);
        else
-         FRAME_SELECTED_WINDOW (XFRAME (frame)) = alternative;
+         FRAME_SELECTED_WINDOW (f) = alternative;
       }
   }
 
@@ -1238,7 +1266,7 @@ delete_window (window)
      events and other events that access glyph matrices are not
      processed while we are changing them.  */
   BLOCK_INPUT;
-  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (frame)));
+  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)));
 
   tem = p->next;
   if (!NILP (tem))
@@ -1295,7 +1323,7 @@ delete_window (window)
   p->buffer = p->hchild = p->vchild = Qnil;
 
   /* Adjust glyph matrices. */
-  adjust_glyphs (frame);
+  adjust_glyphs (f);
   UNBLOCK_INPUT;
 }
 
@@ -1632,7 +1660,7 @@ argument ALL_FRAMES is non-nil, cycle through all frames.")
     window = Fnext_window (window, Qnil, all_frames);
   for (; i < 0; ++i)
     window = Fprevious_window (window, Qnil, all_frames);
-  
+
   Fselect_window (window);
   return Qnil;
 }
@@ -1649,7 +1677,6 @@ MINIBUF neither nil nor t means never include the minibuffer window.")
   (frame, minibuf, window)
      Lisp_Object frame, minibuf, window;
 {
-
   if (NILP (window))
     window = selected_window;
   if (NILP (frame))
@@ -1689,8 +1716,7 @@ window_list_1 (window, minibuf, all_frames)
                 Qnil, look at just the selected frame;
                Qvisible, look at visible frames;
                a frame, just look at windows on that frame.
-   If MINI is non-zero, perform the operation on minibuffer windows too.
-*/
+   If MINI is non-zero, perform the operation on minibuffer windows too.  */
 
 enum window_loop
 {
@@ -1745,13 +1771,6 @@ window_loop (type, obj, mini, frames)
   else
     window = FRAME_SELECTED_WINDOW (SELECTED_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.  */
-
   windows = window_list_1 (window, mini ? Qt : Qnil, frame_arg);
   GCPRO1 (windows);
   best_window = Qnil;
@@ -1780,8 +1799,12 @@ window_loop (type, obj, mini, frames)
                    ? EQ (window, minibuf_window)
                    : 1))
              {
-               UNGCPRO;
-               return window;
+               if (NILP (best_window))
+                 best_window = window;
+               else if (EQ (window, selected_window))
+                 /* For compatibility with 20.x, prefer to return
+                    selected-window.  */
+                 best_window = window;
              }
            break;
 
@@ -1831,8 +1854,6 @@ window_loop (type, obj, mini, frames)
                       display there.  */
                    Lisp_Object buffer;
                    buffer = Fother_buffer (obj, Qnil, w->frame);
-                   if (NILP (buffer))
-                     buffer = Fget_buffer_create (build_string ("*scratch*"));
                    Fset_window_buffer (window, buffer);
                    if (EQ (window, selected_window))
                      Fset_buffer (w->buffer);
@@ -1868,8 +1889,6 @@ window_loop (type, obj, mini, frames)
                
                /* Find another buffer to show in this window.  */
                buffer = Fother_buffer (obj, Qnil, w->frame);
-               if (NILP (buffer))
-                 buffer = Fget_buffer_create (build_string ("*scratch*"));
                
                /* If this window is dedicated, and in a frame of its own,
                   kill the frame.  */
@@ -1987,13 +2006,12 @@ value is reasonable when this function is called.")
 {
   struct window *w;
   int startpos;
-  int top;
+  int top, new_top;
 
   if (NILP (window))
     window = selected_window;
   else
     CHECK_LIVE_WINDOW (window, 0);
-
   w = XWINDOW (window);
 
   startpos = marker_position (w->start);
@@ -2009,7 +2027,9 @@ value is reasonable when this function is called.")
      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 (startpos >= BUF_BEGV (XBUFFER (w->buffer))
+  new_top = XFASTINT (w->top) - FRAME_TOP_MARGIN (XFRAME (WINDOW_FRAME (w)));
+  if (new_top != top
+      && startpos >= BUF_BEGV (XBUFFER (w->buffer))
       && startpos <= BUF_ZV (XBUFFER (w->buffer)))
     {
       struct position pos;
@@ -2021,6 +2041,7 @@ value is reasonable when this function is called.")
       pos = *vmotion (startpos, -top, w);
 
       set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
+      w->window_end_valid = Qnil;
       w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
                               || FETCH_BYTE (pos.bytepos - 1) == '\n') ? Qt
                              : Qnil);
@@ -2403,14 +2424,12 @@ size_window (window, size, width_p, nodelete_p)
       min_size = window_min_height;
     }
   
-  if (old_size < window_min_width)
+  if (old_size < min_size)
     w->too_small_ok = Qt;
 
   /* Maybe delete WINDOW if it's too small.  */
   if (!nodelete_p && !NILP (w->parent))
     {
-      int min_size;
-
       if (!MINI_WINDOW_P (w) && !NILP (w->too_small_ok))
        min_size = width_p ? MIN_SAFE_WINDOW_WIDTH : MIN_SAFE_WINDOW_HEIGHT;
       else
@@ -2440,6 +2459,7 @@ size_window (window, size, width_p, nodelete_p)
       sideward = &w->hchild;
       forward = &w->vchild;
       w->height = make_number (size);
+      w->orig_height = Qnil;
     }
 
   if (!NILP (*sideward))
@@ -2607,7 +2627,8 @@ set_window_buffer (window, buffer, run_hooks_p)
   XSETFASTINT (w->window_end_vpos, 0);
   bzero (&w->last_cursor, sizeof w->last_cursor);
   w->window_end_valid = Qnil;
-  XSETFASTINT (w->hscroll, 0);
+  w->hscroll = w->min_hscroll = make_number (0);
+  w->vscroll = 0;
   set_marker_both (w->pointm, buffer, BUF_PT (b), BUF_PT_BYTE (b));
   set_marker_restricted (w->start,
                         make_number (b->last_window_start),
@@ -2708,9 +2729,7 @@ select_window_1 (window, recordflag)
   CHECK_LIVE_WINDOW (window, 0);
 
   w = XWINDOW (window);
-
-  if (NILP (w->buffer))
-    error ("Trying to select deleted window or non-leaf window");
+  w->frozen_window_start_p = 0;
 
   XSETFASTINT (w->use_time, ++window_select_count);
   if (EQ (window, selected_window))
@@ -3087,6 +3106,7 @@ temp_output_buffer_show (buf)
       Vminibuf_scroll_window = window;
       w = XWINDOW (window);
       XSETFASTINT (w->hscroll, 0);
+      XSETFASTINT (w->min_hscroll, 0);
       set_marker_restricted_both (w->start, buf, 1, 1);
       set_marker_restricted_both (w->pointm, buf, 1, 1);
 
@@ -3124,15 +3144,13 @@ make_dummy_parent (window)
 {
   Lisp_Object new;
   register struct window *o, *p;
-  register struct Lisp_Vector *vec;
   int i;
 
   o = XWINDOW (window);
-  vec = allocate_vectorlike ((EMACS_INT)VECSIZE (struct window));
+  p = allocate_window ();
   for (i = 0; i < VECSIZE (struct window); ++i)
-    vec->contents[i] = ((struct Lisp_Vector *)o)->contents[i];
-  vec->size = VECSIZE (struct window);
-  p = (struct window *)vec;
+    ((struct Lisp_Vector *) p)->contents[i]
+      = ((struct Lisp_Vector *)o)->contents[i];
   XSETWINDOW (new, p);
 
   XSETFASTINT (p->sequence_number, ++sequence_number);
@@ -3196,7 +3214,6 @@ SIZE includes that window's scroll bar, or the divider column to its right.")
     error ("Attempt to split fixed-size window");
 
   check_min_window_sizes ();
-  ensure_frame_matrix (fo);
 
   if (NILP (horflag))
     {
@@ -3277,7 +3294,8 @@ SIZE includes that window's scroll bar, or the divider column to its right.")
 \f
 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.")
+From program, optional second arg non-nil means grow sideways ARG columns.\n\
+Interactively, if an argument is not given, make the window one line bigger.")
   (arg, side)
      register Lisp_Object arg, side;
 {
@@ -3292,7 +3310,8 @@ From program, optional second arg non-nil means grow sideways ARG columns.")
 
 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.")
+From program, optional second arg non-nil means shrink sideways arg columns.\n\
+Interactively, if an argument is not given, make the window one line smaller.")
   (arg, side)
      register Lisp_Object arg, side;
 {
@@ -3797,6 +3816,8 @@ shrink_mini_window (w)
     }
   else if (XFASTINT (w->height) > 1)
     {
+      /* Distribute the additional lines of the mini-window
+        among the other windows.  */
       Lisp_Object window;
       XSETWINDOW (window, w);
       enlarge_window (window, 1 - XFASTINT (w->height), 0);
@@ -3828,7 +3849,7 @@ mark_window_cursors_off (w)
 }
 
 
-/* Return number of lines of text (not counting mode line) in W.  */
+/* Return number of lines of text (not counting mode lines) in W.  */
 
 int
 window_internal_height (w)
@@ -3836,13 +3857,19 @@ window_internal_height (w)
 {
   int ht = XFASTINT (w->height);
 
-  if (MINI_WINDOW_P (w))
-    return ht;
-
-  if (!NILP (w->parent) || !NILP (w->vchild) || !NILP (w->hchild)
-      || !NILP (w->next) || !NILP (w->prev)
-      || FRAME_WANTS_MODELINE_P (XFRAME (WINDOW_FRAME (w))))
-    return ht - 1;
+  if (!MINI_WINDOW_P (w))
+    {
+      if (!NILP (w->parent)
+         || !NILP (w->vchild)
+         || !NILP (w->hchild)
+         || !NILP (w->next)
+         || !NILP (w->prev)
+         || WINDOW_WANTS_MODELINE_P (w))
+       --ht;
+
+      if (WINDOW_WANTS_HEADER_LINE_P (w))
+       --ht;
+    }
 
   return ht;
 }
@@ -3881,7 +3908,7 @@ window_internal_width (w)
  ***********************************************************************/
 
 /* Scroll contents of window WINDOW up.  If WHOLE is non-zero, scroll
-   one screen-full, which is defined as the height of the window minus
+   N screen-fulls, which is defined as the height of the window minus
    next_screen_context_lines.  If WHOLE is zero, scroll up N lines
    instead.  Negative values of N mean scroll down.  NOERROR non-zero
    means don't signal an error if we try to move over BEGV or ZV,
@@ -3926,8 +3953,10 @@ window_scroll_pixel_based (window, n, whole, noerror)
   SET_TEXT_POS_FROM_MARKER (start, w->start);
   
   /* If PT is not visible in WINDOW, move back one half of
-     the screen.  */
-  tem = Fpos_visible_in_window_p (make_number (PT), window, Qnil);
+     the screen.  Allow PT to be partially visible, otherwise
+     something like (scroll-down 1) with PT in the line before
+     the partially visible one would recenter. */
+  tem = Fpos_visible_in_window_p (make_number (PT), window, Qt);
   if (NILP (tem))
     {
       /* Move backward half the height of the window.  Performance note:
@@ -3935,7 +3964,7 @@ window_scroll_pixel_based (window, n, whole, noerror)
         results for variable height lines.  */
       init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
       it.current_y = it.last_visible_y;
-      move_it_vertically (&it, -it.last_visible_y / 2);
+      move_it_vertically (&it, - window_box_height (w) / 2);
       
       /* The function move_iterator_vertically may move over more than
         the specified y-distance.  If it->w is small, e.g. a
@@ -3968,10 +3997,9 @@ window_scroll_pixel_based (window, n, whole, noerror)
   start_display (&it, w, start);
   if (whole)
     {
-      int screen_full = (it.last_visible_y
+      int screen_full = (window_box_height (w)
                         - next_screen_context_lines * CANON_Y_UNIT (it.f));
-      int direction = n < 0 ? -1 : 1;
-      int dy = direction * screen_full;
+      int dy = n * screen_full;
 
       /* Note that move_it_vertically always moves the iterator to the
          start of a line.  So, if the last line doesn't have a newline,
@@ -3993,10 +4021,13 @@ window_scroll_pixel_based (window, n, whole, noerror)
        {
          if (it.current_y + it.max_ascent + it.max_descent
              > it.last_visible_y)
-           /* The last line was only partially visible, make it fully
-              visible.  */
-           w->vscroll = (it.last_visible_y
-                         - it.current_y + it.max_ascent + it.max_descent);
+           {
+             /* The last line was only partially visible, make it fully
+                visible.  */
+             w->vscroll = (it.last_visible_y
+                           - it.current_y + it.max_ascent + it.max_descent);
+             adjust_glyphs (it.f);
+           }
          else if (noerror)
            return;
          else
@@ -4112,6 +4143,11 @@ window_scroll_line_based (window, n, whole, noerror)
   struct position posit;
   int original_vpos;
 
+  /* If scrolling screen-fulls, compute the number of lines to
+     scroll from the window's height.  */
+  if (whole)
+    n *= max (1, ht - next_screen_context_lines);
+
   startpos = marker_position (w->start);
 
   posit = *compute_motion (startpos, 0, 0, 0,
@@ -4243,8 +4279,7 @@ scroll_command (n, direction)
      Lisp_Object n;
      int direction;
 {
-  register int defalt;
-  int count = specpdl_ptr - specpdl;
+  int count = BINDING_STACK_SIZE ();
 
   xassert (abs (direction) == 1);
 
@@ -4259,14 +4294,10 @@ scroll_command (n, direction)
       ++windows_or_buffers_changed;
     }
 
-  defalt = (window_internal_height (XWINDOW (selected_window))
-           - next_screen_context_lines);
-  defalt = direction * (defalt < 1 ? 1 : defalt);
-
   if (NILP (n))
-    window_scroll (selected_window, defalt, 1, 0);
+    window_scroll (selected_window, direction, 1, 0);
   else if (EQ (n, Qminus))
-    window_scroll (selected_window, - defalt, 1, 0);
+    window_scroll (selected_window, -direction, 1, 0);
   else
     {
       n = Fprefix_numeric_value (n);
@@ -4358,18 +4389,14 @@ 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.")
   (arg)
-     register Lisp_Object arg;
+     Lisp_Object arg;
 {
-  register Lisp_Object window;
-  register int defalt;
-  register struct window *w;
-  register int count = specpdl_ptr - specpdl;
+  Lisp_Object window;
+  struct window *w;
+  int count = BINDING_STACK_SIZE ();
 
   window = Fother_window_for_scrolling ();
-
   w = XWINDOW (window);
-  defalt = window_internal_height (w) - next_screen_context_lines;
-  if (defalt < 1) defalt = 1;
 
   /* Don't screw up if window_scroll gets an error.  */
   record_unwind_protect (save_excursion_restore, save_excursion_save ());
@@ -4379,9 +4406,9 @@ showing that buffer, popping the buffer up if necessary.")
   SET_PT (marker_position (w->pointm));
 
   if (NILP (arg))
-    window_scroll (window, defalt, 1, 1);
+    window_scroll (window, 1, 1, 1);
   else if (EQ (arg, Qminus))
-    window_scroll (window, -defalt, 1, 1);
+    window_scroll (window, -1, 1, 1);
   else
     {
       if (CONSP (arg))
@@ -4402,16 +4429,22 @@ Default for ARG is window width minus 2.")
   (arg)
      register Lisp_Object arg;
 {
-
+  Lisp_Object result;
+  int hscroll;
+  struct window *w = XWINDOW (selected_window);
+  
   if (NILP (arg))
-    XSETFASTINT (arg, window_internal_width (XWINDOW (selected_window)) - 2);
+    XSETFASTINT (arg, window_internal_width (w) - 2);
   else
     arg = Fprefix_numeric_value (arg);
 
-  return
-    Fset_window_hscroll (selected_window,
-                        make_number (XINT (XWINDOW (selected_window)->hscroll)
-                                     + XINT (arg)));
+  hscroll = XINT (w->hscroll) + XINT (arg);
+  result = Fset_window_hscroll (selected_window, make_number (hscroll));
+
+  if (interactive_p (0))
+    w->min_hscroll = w->hscroll;
+
+  return result;
 }
 
 DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 1, "P",
@@ -4420,72 +4453,24 @@ Default for ARG is window width minus 2.")
   (arg)
      register Lisp_Object arg;
 {
+  Lisp_Object result;
+  int hscroll;
+  struct window *w = XWINDOW (selected_window);
+  
   if (NILP (arg))
-    XSETFASTINT (arg, window_internal_width (XWINDOW (selected_window)) - 2);
+    XSETFASTINT (arg, window_internal_width (w) - 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 frame.  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 window.\n\
-If ARG is omitted or nil, erases the entire frame and then\n\
-redraws with point in the center of the current window.")
-  (arg)
-     register Lisp_Object arg;
-{
-  register struct window *w = XWINDOW (selected_window);
-  register int ht = window_internal_height (w);
-  struct position pos;
-  struct buffer *buf = XBUFFER (w->buffer);
-  struct buffer *obuf = current_buffer;
-
-  if (NILP (arg))
-    {
-      extern int frame_garbaged;
-      int i;
-
-      /* Invalidate pixel data calculated for all compositions.  */
-      for (i = 0; i < n_compositions; i++)
-       composition_table[i]->font = NULL;
-
-      Fredraw_frame (w->frame);
-      SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
-      XSETFASTINT (arg, ht / 2);
-    }
-  else if (CONSP (arg)) /* Just C-u. */
-    {
-      XSETFASTINT (arg, ht / 2);
-    }
-  else
-    {
-      arg = Fprefix_numeric_value (arg);
-      CHECK_NUMBER (arg, 0);
-    }
-
-  if (XINT (arg) < 0)
-    XSETINT (arg, XINT (arg) + ht);
-
-  set_buffer_internal (buf);
-  pos = *vmotion (PT, - XINT (arg), w);
-
-  set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
-  w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
-                          || FETCH_BYTE (pos.bytepos - 1) == '\n')
-                         ? Qt : Qnil);
-  w->force_start = Qt;
-  set_buffer_internal (obuf);
+  hscroll = XINT (w->hscroll) - XINT (arg);
+  result = Fset_window_hscroll (selected_window, make_number (hscroll));
+  
+  if (interactive_p (0))
+    w->min_hscroll = w->hscroll;
 
-  return Qnil;
+  return result;
 }
 
-
 /* Value is the number of lines actually displayed in window W,
    as opposed to its height.  */
 
@@ -4507,29 +4492,156 @@ displayed_window_lines (w)
   else
     old_buffer = NULL;
 
-  SET_TEXT_POS_FROM_MARKER (start, w->start);
+  /* In case W->start is out of the accessible range, do something
+     reasonable.  This happens in Info mode when Info-scroll-down
+     calls (recenter -1) while W->start is 1.  */
+  if (XMARKER (w->start)->charpos < BEGV)
+    SET_TEXT_POS (start, BEGV, BEGV_BYTE);
+  else if (XMARKER (w->start)->charpos > ZV)
+    SET_TEXT_POS (start, ZV, ZV_BYTE);
+  else
+    SET_TEXT_POS_FROM_MARKER (start, w->start);
+
   start_display (&it, w, start);
   move_it_vertically (&it, height);
+  bottom_y = line_bottom_y (&it);
+
+  /* Add in empty lines at the bottom of the window.  */
+  if (bottom_y < height)
+    {
+      int uy = CANON_Y_UNIT (it.f);
+      it.vpos += (height - bottom_y + uy - 1) / uy;
+    }
 
   if (old_buffer)
     set_buffer_internal (old_buffer);
 
-  bottom_y = it.current_y + it.max_ascent + it.max_descent;
+  return it.vpos;
+}
 
-  if (bottom_y > it.current_y && bottom_y <= it.last_visible_y)
-    /* Hit a line without a terminating newline.  */
-    it.vpos++;
 
-  /* Add in empty lines at the bottom of the window.  */
-  if (bottom_y < height)
+DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P",
+  "Center point in window and redisplay frame.\n\
+With prefix argument ARG, recenter putting point on screen line ARG\n\
+relative to the current window.  If ARG is negative, it counts up from the\n\
+bottom of the window.  (ARG should be less than the height of the window.)\n\
+\n\
+If ARG is omitted or nil, erase the entire frame and then\n\
+redraw with point in the center of the current window.\n\
+Just C-u as prefix means put point in the center of the window\n\
+and redisplay normally--don't erase and redraw the frame.")
+  (arg)
+     register Lisp_Object arg;
+{
+  struct window *w = XWINDOW (selected_window);
+  struct buffer *buf = XBUFFER (w->buffer);
+  struct buffer *obuf = current_buffer;
+  int center_p = 0;
+  int charpos, bytepos;
+
+  if (NILP (arg))
+    {
+      int i;
+
+      /* Invalidate pixel data calculated for all compositions.  */
+      for (i = 0; i < n_compositions; i++)
+       composition_table[i]->font = NULL;
+
+      Fredraw_frame (w->frame);
+      SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
+      center_p = 1;
+    }
+  else if (CONSP (arg)) /* Just C-u. */
+    center_p = 1;
+  else
+    {
+      arg = Fprefix_numeric_value (arg);
+      CHECK_NUMBER (arg, 0);
+    }
+
+  set_buffer_internal (buf);
+
+  /* Handle centering on a graphical frame specially.  Such frames can
+     have variable-height lines and centering point on the basis of
+     line counts would lead to strange effects.  */
+  if (FRAME_WINDOW_P (XFRAME (w->frame)))
+    {
+      if (center_p)
+       {
+         struct it it;
+         struct text_pos pt;
+         
+         SET_TEXT_POS (pt, PT, PT_BYTE);
+         start_display (&it, w, pt);
+         move_it_vertically (&it, - window_box_height (w) / 2);
+         charpos = IT_CHARPOS (it);
+         bytepos = IT_BYTEPOS (it);
+       }
+      else if (XINT (arg) < 0)
+       {
+         struct it it;
+         struct text_pos pt;
+         int y0, y1, h, nlines;
+         
+         SET_TEXT_POS (pt, PT, PT_BYTE);
+         start_display (&it, w, pt);
+         y0 = it.current_y;
+
+         /* The amount of pixels we have to move back is the window
+            height minus what's displayed in the line containing PT,
+            and the lines below.  */
+         nlines = - XINT (arg) - 1;
+         move_it_by_lines (&it, nlines, 1);
+
+         y1 = it.current_y - y0;
+         h = line_bottom_y (&it) - y1;
+
+         /* If we can't move down NLINES lines because we hit
+            the end of the buffer, count in some empty lines.  */
+         if (it.vpos < nlines)
+           y1 += (nlines - it.vpos) * CANON_Y_UNIT (it.f);
+         
+         y0 = it.last_visible_y - y1 - h;
+         
+         start_display (&it, w, pt);
+         move_it_vertically (&it, - y0);
+         charpos = IT_CHARPOS (it);
+         bytepos = IT_BYTEPOS (it);
+       }
+      else
+       {
+         struct position pos;
+         pos = *vmotion (PT, - XINT (arg), w);
+         charpos = pos.bufpos;
+         bytepos = pos.bytepos;
+       }
+    }
+  else
     {
-      struct frame *f = XFRAME (w->frame);
-      int rest = height - bottom_y;
-      int lines = rest / CANON_Y_UNIT (f);
-      it.vpos += lines;
+      struct position pos;
+      int ht = window_internal_height (w);
+
+      if (center_p)
+       arg = make_number (ht / 2);
+      else if (XINT (arg) < 0)
+       arg = make_number (XINT (arg) + ht);
+      
+      pos = *vmotion (PT, - XINT (arg), w);
+      charpos = pos.bufpos;
+      bytepos = pos.bytepos;
     }
 
-  return it.vpos;
+  /* Set the new window start.  */
+  set_marker_both (w->start, w->buffer, charpos, bytepos);
+  w->window_end_valid = Qnil;
+  w->force_start = Qt;
+  if (bytepos == BEGV_BYTE || FETCH_BYTE (bytepos - 1) == '\n')
+    w->start_at_line_beg = Qt;
+  else
+    w->start_at_line_beg = Qnil;
+  
+  set_buffer_internal (obuf);
+  return Qnil;
 }
 
 
@@ -4585,8 +4697,8 @@ zero means top of window, negative means relative to bottom of window.")
        XSETINT (arg, XINT (arg) + lines);
     }
 
+  /* Skip past a partially visible first line.  */
   if (w->vscroll)
-    /* Skip past a partially visible first line.  */
     XSETINT (arg, XINT (arg) + 1);
 
   return Fvertical_motion (arg, window);
@@ -4620,20 +4732,21 @@ struct save_window_data
 
 /* This is saved as a Lisp_Vector  */
 struct saved_window
-  {
-    /* these first two must agree with struct Lisp_Vector in lisp.h */
-    EMACS_INT size_from_Lisp_Vector_struct;
-    struct Lisp_Vector *next_from_Lisp_Vector_struct;
+{
+  /* these first two must agree with struct Lisp_Vector in lisp.h */
+  EMACS_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;
-    Lisp_Object orig_top, orig_height;
-  };
-#define SAVED_WINDOW_VECTOR_SIZE 16 /* Arg to Fmake_vector */
+  Lisp_Object window;
+  Lisp_Object buffer, start, pointm, mark;
+  Lisp_Object left, top, width, height, hscroll, min_hscroll;
+  Lisp_Object parent, prev;
+  Lisp_Object start_at_line_beg;
+  Lisp_Object display_table;
+  Lisp_Object orig_top, orig_height;
+};
+
+#define SAVED_WINDOW_VECTOR_SIZE 17 /* Arg to Fmake_vector */
 
 #define SAVED_WINDOW_N(swv,n) \
   ((struct saved_window *) (XVECTOR ((swv)->contents[(n)])))
@@ -4699,7 +4812,6 @@ the return value is nil.  Otherwise the value is t.")
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
   f = XFRAME (frame);
-  ensure_frame_matrix (f);
   
   /* If f is a dead frame, don't bother rebuilding its window tree.
      However, there is other stuff we should still try to do below.  */
@@ -4825,6 +4937,7 @@ the return value is nil.  Otherwise the value is t.")
          w->width = p->width;
          w->height = p->height;
          w->hscroll = p->hscroll;
+         w->min_hscroll = p->min_hscroll;
          w->display_table = p->display_table;
          w->orig_top = p->orig_top;
          w->orig_height = p->orig_height;
@@ -4899,7 +5012,7 @@ the return value is nil.  Otherwise the value is t.")
          when the frame's old selected window has been deleted.  */
       if (f != selected_frame && FRAME_WINDOW_P (f))
        do_switch_frame (WINDOW_FRAME (XWINDOW (data->root_window)),
-                        Qnil, 0);
+                        0, 0);
 #endif
 
       /* Set the screen height to the value it had before this function.  */
@@ -4927,7 +5040,6 @@ the return value is nil.  Otherwise the value is t.")
              xassert (NILP (leaf_windows[i]->hchild) 
                       && NILP (leaf_windows[i]->vchild));
              free_window_matrices (leaf_windows[i]);
-             SET_FRAME_GARBAGED (f);
            }
          else if (EQ (leaf_windows[i]->buffer, new_current_buffer))
            ++n;
@@ -4948,7 +5060,7 @@ the return value is nil.  Otherwise the value is t.")
         Fselect_window above totally superfluous; it still sets f's
         selected window.  */
       if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
-       do_switch_frame (data->selected_frame, Qnil, 0);
+       do_switch_frame (data->selected_frame, 0, 0);
 
       if (! NILP (Vwindow_configuration_change_hook)
          && ! NILP (Vrun_hooks))
@@ -5092,6 +5204,7 @@ save_window_save (window, vector, i)
       p->width = w->width;
       p->height = w->height;
       p->hscroll = w->hscroll;
+      p->min_hscroll = w->min_hscroll;
       p->display_table = w->display_table;
       p->orig_top = w->orig_top;
       p->orig_height = w->orig_height;
@@ -5169,10 +5282,7 @@ redirection (see `redirect-frame-focus').")
   f = XFRAME (frame);
 
   n_windows = count_windows (XWINDOW (FRAME_ROOT_WINDOW (f)));
-  vec = allocate_vectorlike (VECSIZE (struct save_window_data));
-  for (i = 0; i < VECSIZE (struct save_window_data); i++)
-    vec->contents[i] = Qnil;
-  vec->size = VECSIZE (struct save_window_data);
+  vec = allocate_other_vector (VECSIZE (struct save_window_data));
   data = (struct save_window_data *)vec;
 
   XSETFASTINT (data->frame_width, FRAME_WIDTH (f));
@@ -5392,7 +5502,7 @@ foreach_window_1 (w, fn, user_data)
 }
 
 
-/* Freeze or unfreeze the window start of W if unless it is a
+/* Freeze or unfreeze the window start of W unless it is a
    mini-window or the selected window.  FREEZE_P non-null means freeze
    the window start.  */
 
@@ -5520,6 +5630,8 @@ compare_window_configurations (c1, c2, ignore_positions)
        {
          if (! EQ (p1->hscroll, p2->hscroll))
            return 0;
+         if (!EQ (p1->min_hscroll, p2->min_hscroll))
+           return 0;
          if (! EQ (p1->start_at_line_beg, p2->start_at_line_beg))
            return 0;
          if (NILP (Fequal (p1->start, p2->start)))
@@ -5569,10 +5681,10 @@ init_window ()
 void
 syms_of_window ()
 {
-  Qleft_bitmap_area = intern ("left-bitmap-area");
-  staticpro (&Qleft_bitmap_area);
-  Qright_bitmap_area = intern ("right-bitmap-area");
-  staticpro (&Qright_bitmap_area);
+  Qleft_fringe = intern ("left-fringe");
+  staticpro (&Qleft_fringe);
+  Qright_fringe = intern ("right-fringe");
+  staticpro (&Qright_fringe);
   
   Qwindow_size_fixed = intern ("window-size-fixed");
   staticpro (&Qwindow_size_fixed);
@@ -5742,7 +5854,7 @@ If there is only one window, it is split regardless of this value.");
 
   DEFVAR_LISP ("scroll-preserve-screen-position",
               &Vscroll_preserve_screen_position,
-    "*Nonzero means scroll commands move point to keep its screen line unchanged.");
+    "*Non-nil means scroll commands move point to keep its screen line unchanged.");
   Vscroll_preserve_screen_position = Qnil;
 
   DEFVAR_LISP ("window-configuration-change-hook",
@@ -5751,6 +5863,14 @@ If there is only one window, it is split regardless of this value.");
 The selected frame is the one whose configuration has changed.");
   Vwindow_configuration_change_hook = Qnil;
 
+  DEFVAR_BOOL ("window-size-fixed", &window_size_fixed,
+    "Non-nil in a buffer means windows displaying the buffer are fixed-size.\n\
+Emacs won't change the size of any window displaying that buffer,\n\
+unless you explicitly change the size, or Emacs has no other choice.\n\
+This variable automatically becomes buffer-local when set.");
+  Fmake_variable_buffer_local (Qwindow_size_fixed);
+  window_size_fixed = 0;
+
   defsubr (&Sselected_window);
   defsubr (&Sminibuffer_window);
   defsubr (&Swindow_minibuffer_p);