]> code.delx.au - gnu-emacs/blobdiff - src/window.c
*** empty log message ***
[gnu-emacs] / src / window.c
index c696d7f9e2735609acf5ca9c0e3ad7287f7afe98..2663b8d0d903ad7702feff558cf85a6f8e96cbbe 100644 (file)
@@ -1,7 +1,7 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
-   Copyright (C) 1985,86,87, 1993,94,95,96,97,98, 2000,01,02,03,04
-   Free Software Foundation, Inc.
+   Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
+                 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -17,8 +17,8 @@ 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, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include <config.h>
 #include "lisp.h"
@@ -50,6 +50,7 @@ Boston, MA 02111-1307, USA.  */
 
 
 Lisp_Object Qwindowp, Qwindow_live_p, Qwindow_configuration_p;
+Lisp_Object Qscroll_up, Qscroll_down;
 Lisp_Object Qwindow_size_fixed;
 extern Lisp_Object Qleft_margin, Qright_margin;
 
@@ -65,7 +66,7 @@ static int window_min_size P_ ((struct window *, int, int, int *));
 static void size_window P_ ((Lisp_Object, int, int, int));
 static int freeze_window_start P_ ((struct window *, void *));
 static int window_fixed_size_p P_ ((struct window *, int, int));
-static void enlarge_window P_ ((Lisp_Object, int, int, int));
+static void enlarge_window P_ ((Lisp_Object, int, int));
 static Lisp_Object window_list P_ ((void));
 static int add_window_to_list P_ ((struct window *, void *));
 static int candidate_window_p P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
@@ -79,10 +80,6 @@ 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
@@ -124,6 +121,11 @@ Lisp_Object Vother_window_scroll_buffer;
 
 Lisp_Object Vtemp_buffer_show_function;
 
+/* Non-zero means line and page scrolling on tall lines (with images)
+   does partial scrolling by modifying window-vscroll.  */
+
+int auto_window_vscroll_p;
+
 /* Non-zero means to use mode-line-inactive face in all windows but the
    selected-window and the minibuffer-scroll-window when the
    minibuffer is active.  */
@@ -204,11 +206,19 @@ static int window_initialized;
 Lisp_Object Qwindow_configuration_change_hook;
 Lisp_Object Vwindow_configuration_change_hook;
 
-/* Nonzero means scroll commands try to put point
+/* Non-nil means scroll commands try to put point
    at the same screen height as previously.  */
 
 Lisp_Object Vscroll_preserve_screen_position;
 
+/* Incremented by 1 whenever a window is deleted.  */
+
+int window_deletion_count;
+
+/* Used by the function window_scroll_pixel_based */
+
+static int window_scroll_pixel_based_preserve_y;
+
 #if 0 /* This isn't used anywhere.  */
 /* Nonzero means we can split a frame even if it is "unsplittable".  */
 static int inhibit_frame_unsplittable;
@@ -241,7 +251,8 @@ make_window ()
   register struct window *p;
 
   p = allocate_window ();
-  XSETFASTINT (p->sequence_number, ++sequence_number);
+  ++sequence_number;
+  XSETFASTINT (p->sequence_number, sequence_number);
   XSETFASTINT (p->left_col, 0);
   XSETFASTINT (p->top_line, 0);
   XSETFASTINT (p->total_lines, 0);
@@ -271,7 +282,6 @@ make_window ()
   XSETWINDOW (val, p);
   XSETFASTINT (p->last_point, 0);
   p->frozen_window_start_p = 0;
-  p->height_fixed_p = 0;
   p->last_cursor_off_p = p->cursor_off_p = 0;
   p->left_margin_cols = Qnil;
   p->right_margin_cols = Qnil;
@@ -280,7 +290,6 @@ make_window ()
   p->fringes_outside_margins = Qnil;
   p->scroll_bar_width = Qnil;
   p->vertical_scroll_bar_type = Qt;
-  p->overlay_arrow_bitmap = 0;
 
   Vwindow_list = Qnil;
   return val;
@@ -327,9 +336,10 @@ If POS is only out of view because of horizontal scrolling, return non-nil.
 POS defaults to point in WINDOW; WINDOW defaults to the selected window.
 
 If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil,
-return value is a list (X Y FULLY) where X and Y are the pixel coordinates
-relative to the top left corner of the window, and FULLY is t if the
-character after POS is fully visible and nil otherwise.  */)
+return value is a list (X Y PARTIAL) where X and Y are the pixel coordinates
+relative to the top left corner of the window. PARTIAL is nil if the character
+after POS is fully visible; otherwise it is a cons (RTOP . RBOT) where RTOP
+and RBOT are the number of pixels invisible at the top and bottom of the row.  */)
      (pos, window, partially)
      Lisp_Object pos, window, partially;
 {
@@ -338,7 +348,7 @@ character after POS is fully visible and nil otherwise.  */)
   register struct buffer *buf;
   struct text_pos top;
   Lisp_Object in_window = Qnil;
-  int fully_p = 1;
+  int rtop, rbot, fully_p = 1;
   int x, y;
 
   w = decode_window (window);
@@ -361,14 +371,17 @@ character after POS is fully visible and nil otherwise.  */)
       && posint <= BUF_ZV (buf)
       && CHARPOS (top) >= BUF_BEGV (buf)
       && CHARPOS (top) <= BUF_ZV (buf)
-      && pos_visible_p (w, posint, &fully_p, &x, &y, NILP (partially))
-      && (!NILP (partially) || fully_p))
+      && pos_visible_p (w, posint, &x, &y, &rtop, &rbot, NILP (partially))
+      && (fully_p = !rtop && !rbot, (!NILP (partially) || fully_p)))
     in_window = Qt;
 
   if (!NILP (in_window) && !NILP (partially))
     in_window = Fcons (make_number (x),
                       Fcons (make_number (y),
-                             Fcons (fully_p ? Qt : Qnil, Qnil)));
+                             Fcons ((fully_p ? Qnil
+                                    : Fcons (make_number (rtop),
+                                             make_number (rbot))),
+                                    Qnil)));
   return in_window;
 }
 
@@ -539,7 +552,7 @@ display margins, fringes, header line, and/or mode line.  */)
                             + WINDOW_LEFT_FRINGE_COLS (w)),
                make_number (WINDOW_TOP_EDGE_LINE (w)
                             + WINDOW_HEADER_LINE_LINES (w)),
-               make_number (WINDOW_RIGHT_EDGE_COL (w)
+               make_number (WINDOW_BOX_RIGHT_EDGE_COL (w)
                             - WINDOW_RIGHT_MARGIN_COLS (w)
                             - WINDOW_RIGHT_FRINGE_COLS (w)),
                make_number (WINDOW_BOTTOM_EDGE_LINE (w)
@@ -563,7 +576,7 @@ display margins, fringes, header line, and/or mode line.  */)
                             + WINDOW_LEFT_FRINGE_WIDTH (w)),
                make_number (WINDOW_TOP_EDGE_Y (w)
                             + WINDOW_HEADER_LINE_HEIGHT (w)),
-               make_number (WINDOW_RIGHT_EDGE_X (w)
+               make_number (WINDOW_BOX_RIGHT_EDGE_X (w)
                             - WINDOW_RIGHT_MARGIN_WIDTH (w)
                             - WINDOW_RIGHT_FRINGE_WIDTH (w)),
                make_number (WINDOW_BOTTOM_EDGE_Y (w)
@@ -607,9 +620,6 @@ coordinates_in_window (w, x, y)
   int grabbable_width = ux;
   int lmargin_width, rmargin_width, text_left, text_right;
 
-  if (*x < x0 || *x >= x1)
-    return ON_NOTHING;
-
   /* In what's below, we subtract 1 when computing right_x because we
      want the rightmost pixel, which is given by left_pixel+width-1.  */
   if (w->pseudo_window_p)
@@ -651,14 +661,29 @@ coordinates_in_window (w, x, y)
          || WINDOW_RIGHTMOST_P (w))
        {
          if (!WINDOW_LEFTMOST_P (w) && abs (*x - x0) < grabbable_width)
-           return ON_VERTICAL_BORDER;
+           {
+             /* Convert X and Y to window relative coordinates.
+                Vertical border is at the left edge of window.  */
+             *x = max (0, *x - x0);
+             *y -= top_y;
+             return ON_VERTICAL_BORDER;
+           }
        }
       else
        {
          if (abs (*x - x1) < grabbable_width)
-           return ON_VERTICAL_BORDER;
+           {
+             /* Convert X and Y to window relative coordinates.
+                Vertical border is at the right edge of window.  */
+             *x = min (x1, *x) - x0;
+             *y -= top_y;
+             return ON_VERTICAL_BORDER;
+           }
        }
 
+      if (*x < x0 || *x >= x1)
+       return ON_NOTHING;
+
       /* Convert X and Y to window relative coordinates.
         Mode line starts at left edge of window.  */
       *x -= x0;
@@ -673,9 +698,15 @@ coordinates_in_window (w, x, y)
       goto header_vertical_border_check;
     }
 
+  if (*x < x0 || *x >= x1)
+    return ON_NOTHING;
+
   /* Outside any interesting column?  */
   if (*x < left_x || *x > right_x)
-    return ON_SCROLL_BAR;
+    {
+      *y -= top_y;
+      return ON_SCROLL_BAR;
+    }
 
   lmargin_width = window_box_width (w, LEFT_MARGIN_AREA);
   rmargin_width = window_box_width (w, RIGHT_MARGIN_AREA);
@@ -689,7 +720,13 @@ coordinates_in_window (w, x, y)
          && !WINDOW_HAS_VERTICAL_SCROLL_BAR (w)
          && !WINDOW_RIGHTMOST_P (w)
          && (abs (*x - right_x) < grabbable_width))
-       return ON_VERTICAL_BORDER;
+       {
+         /* Convert X and Y to window relative coordinates.
+            Vertical border is at the right edge of window.  */
+         *x = min (right_x, *x) - left_x;
+         *y -= top_y;
+         return ON_VERTICAL_BORDER;
+       }
     }
   else
     {
@@ -701,6 +738,8 @@ coordinates_in_window (w, x, y)
        {
          /* On the border on the right side of the window?  Assume that
             this area begins at RIGHT_X minus a canonical char width.  */
+         *x = min (right_x, *x) - left_x;
+         *y -= top_y;
          return ON_VERTICAL_BORDER;
        }
     }
@@ -732,9 +771,9 @@ coordinates_in_window (w, x, y)
              ? (*x < right_x - WINDOW_RIGHT_FRINGE_WIDTH (w))
              : (*x >= right_x - rmargin_width)))
        {
-         *x -= right_x;
-         if (!WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w))
-           *x -= WINDOW_RIGHT_FRINGE_WIDTH (w);
+         *x -= right_x - rmargin_width;
+         if (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w))
+           *x += WINDOW_RIGHT_FRINGE_WIDTH (w);
          *y -= top_y;
          return ON_RIGHT_MARGIN;
        }
@@ -746,7 +785,7 @@ coordinates_in_window (w, x, y)
     }
 
   /* Everything special ruled out - must be on text area */
-  *x -= left_x + WINDOW_LEFT_FRINGE_WIDTH (w);
+  *x -= text_left;
   *y -= top_y;
   return ON_TEXT;
 }
@@ -1022,7 +1061,8 @@ if it isn't already recorded.  */)
 
   if (! NILP (update)
       && ! (! NILP (w->window_end_valid)
-           && XFASTINT (w->last_modified) >= MODIFF))
+           && XFASTINT (w->last_modified) >= MODIFF)
+      && !noninteractive)
     {
       struct text_pos startp;
       struct it it;
@@ -1322,7 +1362,7 @@ delete_window (window)
     CHECK_WINDOW (window);
   p = XWINDOW (window);
 
-  /* It's okay to delete an already-deleted window.  */
+  /* It's a no-op to delete an already-deleted window.  */
   if (NILP (p->buffer)
       && NILP (p->hchild)
       && NILP (p->vchild))
@@ -1386,6 +1426,9 @@ delete_window (window)
       }
   }
 
+  /* Now we know we can delete this one.  */
+  window_deletion_count++;
+
   tem = p->buffer;
   /* tem is null for dummy parent windows
      (which have inferiors but not any contents themselves) */
@@ -1444,8 +1487,10 @@ delete_window (window)
   tem = par->hchild;
   if (NILP (tem))
     tem = par->vchild;
-  if (NILP (XWINDOW (tem)->next))
+  if (NILP (XWINDOW (tem)->next)) {
     replace_window (parent, tem);
+    par = XWINDOW (tem);
+  }
 
   /* Since we may be deleting combination windows, we must make sure that
      not only p but all its children have been marked as deleted.  */
@@ -1457,6 +1502,51 @@ delete_window (window)
   /* Mark this window as deleted.  */
   p->buffer = p->hchild = p->vchild = Qnil;
 
+  if (! NILP (par->parent))
+    par = XWINDOW (par->parent);
+
+  /* Check if we have a v/hchild with a v/hchild.  In that case remove
+     one of them.  */
+
+  if (! NILP (par->vchild) && ! NILP (XWINDOW (par->vchild)->vchild))
+    {
+      p = XWINDOW (par->vchild);
+      par->vchild = p->vchild;
+      tem = p->vchild;
+    }
+  else if (! NILP (par->hchild) && ! NILP (XWINDOW (par->hchild)->hchild))
+    {
+      p = XWINDOW (par->hchild);
+      par->hchild = p->hchild;
+      tem = p->hchild;
+    }
+  else
+    p = 0;
+
+  if (p)
+    {
+      while (! NILP (tem)) {
+        XWINDOW (tem)->parent = p->parent;
+        if (NILP (XWINDOW (tem)->next))
+          break;
+        tem = XWINDOW (tem)->next;
+      }
+      if (! NILP (tem)) {
+        /* The next of the v/hchild we are removing is now the next of the
+           last child for the v/hchild:
+           Before v/hchild -> v/hchild -> next1 -> next2
+                    |
+                     -> next3
+           After:  v/hchild -> next1 -> next2 -> next3
+        */
+        XWINDOW (tem)->next = p->next;
+        if (! NILP (p->next))
+          XWINDOW (p->next)->prev = tem;
+      }
+      p->next = p->prev = p->vchild = p->hchild = p->buffer = Qnil;
+    }
+
+
   /* Adjust glyph matrices. */
   adjust_glyphs (f);
   UNBLOCK_INPUT;
@@ -1607,7 +1697,7 @@ decode_next_window_args (window, minibuf, all_frames)
                   : Qnil);
   else if (EQ (*all_frames, Qvisible))
     ;
-  else if (XFASTINT (*all_frames) == 0)
+  else if (EQ (*all_frames, make_number (0)))
     ;
   else if (FRAMEP (*all_frames))
     ;
@@ -1764,7 +1854,7 @@ DEFUN ("other-window", Fother_window, Sother_window, 1, 2, "p",
 All windows on current frame are arranged in a cyclic order.
 This command selects the window ARG steps away in that order.
 A negative ARG moves in the opposite order.  The optional second
-argument ALL_FRAMES has the same meaning as in `next-window', which see.  */)
+argument ALL-FRAMES has the same meaning as in `next-window', which see.  */)
      (arg, all_frames)
      Lisp_Object arg, all_frames;
 {
@@ -1796,7 +1886,8 @@ MINIBUF neither nil nor t means never include the minibuffer window.  */)
      Lisp_Object frame, minibuf, window;
 {
   if (NILP (window))
-    window = selected_window;
+    window = FRAMEP (frame) ? XFRAME (frame)->selected_window : selected_window;
+  CHECK_WINDOW (window);
   if (NILP (frame))
     frame = selected_frame;
 
@@ -1881,7 +1972,7 @@ window_loop (type, obj, mini, frames)
 
   if (f)
     frame_arg = Qlambda;
-  else if (XFASTINT (frames) == 0)
+  else if (EQ (frames, make_number (0)))
     frame_arg = frames;
   else if (EQ (frames, Qvisible))
     frame_arg = frames;
@@ -1904,7 +1995,7 @@ window_loop (type, obj, mini, frames)
   GCPRO1 (windows);
   best_window = Qnil;
 
-  for (; CONSP (windows); windows = CDR (windows))
+  for (; CONSP (windows); windows = XCDR (windows))
     {
       struct window *w;
 
@@ -1938,11 +2029,13 @@ window_loop (type, obj, mini, frames)
            break;
 
          case GET_LRU_WINDOW:
-           /* t as arg means consider only full-width windows */
-           if (!NILP (obj) && !WINDOW_FULL_WIDTH_P (w))
-             break;
-           /* Ignore dedicated windows and minibuffers.  */
-           if (MINI_WINDOW_P (w) || EQ (w->dedicated, Qt))
+           /* `obj' is an integer encoding a bitvector.
+              `obj & 1' means consider only full-width windows.
+              `obj & 2' means consider also dedicated windows. */
+           if (((XINT (obj) & 1) && !WINDOW_FULL_WIDTH_P (w))
+               || (!(XINT (obj) & 2) && !NILP (w->dedicated))
+               /* Minibuffer windows are always ignored.  */
+               || MINI_WINDOW_P (w))
              break;
            if (NILP (best_window)
                || (XFASTINT (XWINDOW (best_window)->use_time)
@@ -1993,9 +2086,9 @@ window_loop (type, obj, mini, frames)
            break;
 
          case GET_LARGEST_WINDOW:
-           {
+           { /* nil `obj' means to ignore dedicated windows.  */
              /* Ignore dedicated windows and minibuffers.  */
-             if (MINI_WINDOW_P (w) || EQ (w->dedicated, Qt))
+             if (MINI_WINDOW_P (w) || (NILP (obj) && !NILP (w->dedicated)))
                break;
 
              if (NILP (best_window))
@@ -2089,39 +2182,47 @@ check_all_windows ()
   window_loop (CHECK_ALL_WINDOWS, Qnil, 1, Qt);
 }
 
-DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 1, 0,
+DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 2, 0,
        doc: /* Return the window least recently selected or used for display.
 Return a full-width window if possible.
 A minibuffer window is never a candidate.
+A dedicated window is never a candidate, unless DEDICATED is non-nil,
+  so if all windows are dedicated, the value is nil.
 If optional argument FRAME is `visible', search all visible frames.
 If FRAME is 0, search all visible and iconified frames.
 If FRAME is t, search all frames.
 If FRAME is nil, search only the selected frame.
 If FRAME is a frame, search only that frame.  */)
-     (frame)
-     Lisp_Object frame;
+  (frame, dedicated)
+     Lisp_Object frame, dedicated;
 {
   register Lisp_Object w;
   /* First try for a window that is full-width */
-  w = window_loop (GET_LRU_WINDOW, Qt, 0, frame);
+  w = window_loop (GET_LRU_WINDOW,
+                  NILP (dedicated) ? make_number (1) : make_number (3),
+                  0, frame);
   if (!NILP (w) && !EQ (w, selected_window))
     return w;
   /* If none of them, try the rest */
-  return window_loop (GET_LRU_WINDOW, Qnil, 0, frame);
+  return window_loop (GET_LRU_WINDOW,
+                     NILP (dedicated) ? make_number (0) : make_number (2),
+                     0, frame);
 }
 
-DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 1, 0,
+DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 2, 0,
        doc: /* Return the largest window in area.
 A minibuffer window is never a candidate.
+A dedicated window is never a candidate unless DEDICATED is non-nil,
+  so if all windows are dedicated, the value is nil.
 If optional argument FRAME is `visible', search all visible frames.
 If FRAME is 0, search all visible and iconified frames.
 If FRAME is t, search all frames.
 If FRAME is nil, search only the selected frame.
 If FRAME is a frame, search only that frame.  */)
-     (frame)
-     Lisp_Object frame;
+    (frame, dedicated)
+     Lisp_Object frame, dedicated;
 {
-  return window_loop (GET_LARGEST_WINDOW, Qnil, 0,
+  return window_loop (GET_LARGEST_WINDOW, dedicated, 0,
                      frame);
 }
 
@@ -2379,27 +2480,22 @@ window_fixed_size_p (w, width_p, check_siblings_p)
     }
   else if (BUFFERP (w->buffer))
     {
-      if (w->height_fixed_p && !width_p)
-       fixed_p = 1;
-      else
-       {
-         struct buffer *old = current_buffer;
-         Lisp_Object val;
+      struct buffer *old = current_buffer;
+      Lisp_Object val;
 
-         current_buffer = XBUFFER (w->buffer);
-         val = find_symbol_value (Qwindow_size_fixed);
-         current_buffer = old;
+      current_buffer = XBUFFER (w->buffer);
+      val = find_symbol_value (Qwindow_size_fixed);
+      current_buffer = old;
 
-         fixed_p = 0;
-         if (!EQ (val, Qunbound))
-           {
-             fixed_p = !NILP (val);
+      fixed_p = 0;
+      if (!EQ (val, Qunbound))
+       {
+         fixed_p = !NILP (val);
 
-             if (fixed_p
-                 && ((EQ (val, Qheight) && width_p)
-                     || (EQ (val, Qwidth) && !width_p)))
-               fixed_p = 0;
-           }
+         if (fixed_p
+             && ((EQ (val, Qheight) && width_p)
+                 || (EQ (val, Qwidth) && !width_p)))
+           fixed_p = 0;
        }
 
       /* Can't tell if this one is resizable without looking at
@@ -2498,7 +2594,10 @@ window_min_size_1 (w, width_p)
   else
     {
       if (width_p)
-       size = window_min_width;
+       size = max (window_min_width,
+                   (MIN_SAFE_WINDOW_WIDTH
+                    + WINDOW_FRINGE_COLS (w)
+                    + WINDOW_SCROLL_BAR_COLS (w)));
       else
        {
          if (MINI_WINDOW_P (w)
@@ -2654,6 +2753,9 @@ shrink_windows (total, size, nchildren, shrinkable,
             --shrinkable;
             total_removed += smallest;
 
+            /* We don't know what the smallest is now.  */
+            smallest = total;
+
             /* Out of for, just remove one window at the time and
                check again if we have enough space.  */
             break;
@@ -2678,6 +2780,16 @@ shrink_windows (total, size, nchildren, shrinkable,
      that are left and still can be shrunk.  */
   while (total_shrink > total_removed)
     {
+      int nonzero_sizes = 0;
+      int nonzero_idx = -1;
+
+      for (i = 0; i < nchildren; ++i)
+        if (new_sizes[i] > 0)
+          {
+            ++nonzero_sizes;
+            nonzero_idx = i;
+          }
+
       for (i = 0; i < nchildren; ++i)
         if (new_sizes[i] > min_size)
           {
@@ -2688,6 +2800,25 @@ shrink_windows (total, size, nchildren, shrinkable,
                check again if we have enough space.  */
             break;
           }
+
+
+      /* Special case, only one window left.  */
+      if (nonzero_sizes == 1)
+        break;
+    }
+
+  /* Any surplus due to rounding, we add to windows that are left.  */
+  while (total_shrink < total_removed)
+    {
+      for (i = 0; i < nchildren; ++i)
+        {
+          if (new_sizes[i] != 0 && total_shrink < total_removed)
+            {
+              ++new_sizes[i];
+              --total_removed;
+              break;
+            }
+        }
     }
 
   return new_sizes;
@@ -2976,6 +3107,9 @@ set_window_buffer (window, buffer, run_hooks_p, keep_margins_p)
   if (EQ (window, selected_window))
     b->last_selected_window = window;
 
+  /* Let redisplay errors through.  */
+  b->display_error_modiff = 0;
+
   /* Update time stamps of buffer display.  */
   if (INTEGERP (b->display_count))
     XSETINT (b->display_count, XINT (b->display_count) + 1);
@@ -3114,10 +3248,14 @@ selects the buffer of the selected window before each command.  */)
   w = XWINDOW (window);
   w->frozen_window_start_p = 0;
 
-  XSETFASTINT (w->use_time, ++window_select_count);
+  ++window_select_count;
+  XSETFASTINT (w->use_time, window_select_count);
   if (EQ (window, selected_window))
     return window;
 
+  /* Store the current buffer's actual point into the
+     old selected window.  It belongs to that window,
+     and when the window is not selected, must be in the window.  */
   if (!NILP (selected_window))
     {
       ow = XWINDOW (selected_window);
@@ -3136,7 +3274,7 @@ selects the buffer of the selected window before each command.  */)
         so that FRAME_FOCUS_FRAME is moved appropriately as we
         move around in the state where a minibuffer in a separate
         frame is active.  */
-      Fselect_frame (WINDOW_FRAME (w), Qnil);
+      Fselect_frame (WINDOW_FRAME (w));
     }
   else
     sf->selected_window = window;
@@ -3211,10 +3349,14 @@ display_buffer_1 (window)
 }
 
 DEFUN ("special-display-p", Fspecial_display_p, Sspecial_display_p, 1, 1, 0,
-       doc: /* Returns non-nil if a buffer named BUFFER-NAME would be created specially.
-The value is actually t if the frame should be called with default frame
-parameters, and a list of frame parameters if they were specified.
-See `special-display-buffer-names', and `special-display-regexps'.  */)
+       doc: /* Returns non-nil if a buffer named BUFFER-NAME gets a special frame.
+If the value is t, `display-buffer' or `pop-to-buffer' would create a
+special frame for that buffer using the default frame parameters.
+
+If the value is a list, it is a list of frame parameters that would be used
+to make a frame for that buffer.
+The variables `special-display-buffer-names'
+and `special-display-regexps' control this.  */)
      (buffer_name)
      Lisp_Object buffer_name;
 {
@@ -3245,7 +3387,9 @@ See `special-display-buffer-names', and `special-display-regexps'.  */)
 }
 
 DEFUN ("same-window-p", Fsame_window_p, Ssame_window_p, 1, 1, 0,
-       doc: /* Returns non-nil if a new buffer named BUFFER-NAME would use the same window.
+       doc: /* Returns non-nil if a buffer named BUFFER-NAME would use the same window.
+More precisely, if `display-buffer' or `pop-to-buffer' would display
+that buffer in the selected window rather than (as usual) in some other window.
 See `same-window-buffer-names' and `same-window-regexps'.  */)
      (buffer_name)
      Lisp_Object buffer_name;
@@ -3293,7 +3437,7 @@ displaying BUFFER, then simply raise that frame.
 The variables `special-display-buffer-names',
 `special-display-regexps', `same-window-buffer-names', and
 `same-window-regexps' customize how certain buffer names are handled.
-The latter two take effect only if NOT-THIS-WINDOW is t.
+The latter two take effect only if NOT-THIS-WINDOW is nil.
 
 If optional argument FRAME is `visible', search all visible frames.
 If FRAME is 0, search all visible and iconified frames.
@@ -3401,15 +3545,17 @@ displayed.  */)
       if (FRAME_NO_SPLIT_P (NILP (frames) ? f : last_nonminibuf_frame))
        {
          /* Try visible frames first.  */
-         window = Fget_largest_window (Qvisible);
+         window = Fget_largest_window (Qvisible, Qt);
          /* If that didn't work, try iconified frames.  */
          if (NILP (window))
-           window = Fget_largest_window (make_number (0));
+           window = Fget_largest_window (make_number (0), Qt);
+#if 0     /* Don't try windows on other displays.  */
          if (NILP (window))
-           window = Fget_largest_window (Qt);
+           window = Fget_largest_window (Qt, Qt);
+#endif
        }
       else
-       window = Fget_largest_window (frames);
+       window = Fget_largest_window (frames, Qt);
 
       /* If we got a tall enough full-width window that can be split,
         split it.  */
@@ -3422,7 +3568,7 @@ displayed.  */)
        {
          Lisp_Object upper, lower, other;
 
-         window = Fget_lru_window (frames);
+         window = Fget_lru_window (frames, Qt);
          /* If the LRU window is selected, and big enough,
             and can be split, split it.  */
          if (!NILP (window)
@@ -3431,23 +3577,27 @@ displayed.  */)
                  || EQ (XWINDOW (window)->parent, Qnil))
              && window_height (window) >= window_min_height << 1)
            window = Fsplit_window (window, Qnil, Qnil);
+         else
+           window = Fget_lru_window (frames, Qnil);
          /* If Fget_lru_window returned nil, try other approaches.  */
 
          /* Try visible frames first.  */
          if (NILP (window))
            window = Fget_buffer_window (buffer, Qvisible);
          if (NILP (window))
-           window = Fget_largest_window (Qvisible);
+           window = Fget_largest_window (Qvisible, Qnil);
          /* If that didn't work, try iconified frames.  */
          if (NILP (window))
            window = Fget_buffer_window (buffer, make_number (0));
          if (NILP (window))
-           window = Fget_largest_window (make_number (0));
-         /* Try invisible frames.  */
+           window = Fget_largest_window (make_number (0), Qnil);
+
+#if 0     /* Don't try frames on other displays.  */
          if (NILP (window))
            window = Fget_buffer_window (buffer, Qt);
          if (NILP (window))
-           window = Fget_largest_window (Qt);
+           window = Fget_largest_window (Qt, Qnil);
+#endif
          /* As a last resort, make a new frame.  */
          if (NILP (window))
            window = Fframe_selected_window (call0 (Vpop_up_frame_function));
@@ -3469,12 +3619,12 @@ displayed.  */)
                           + XFASTINT (XWINDOW (window)->total_lines));
              enlarge_window (upper,
                              total / 2 - XFASTINT (XWINDOW (upper)->total_lines),
-                             0, 0);
+                             0);
            }
        }
     }
   else
-    window = Fget_lru_window (Qnil);
+    window = Fget_lru_window (Qnil, Qnil);
 
   Fset_window_buffer (window, buffer, Qnil);
   return display_buffer_1 (window);
@@ -3546,7 +3696,7 @@ temp_output_buffer_show (buf)
 #endif
   set_buffer_internal (old);
 
-  if (!EQ (Vtemp_buffer_show_function, Qnil))
+  if (!NILP (Vtemp_buffer_show_function))
     call1 (Vtemp_buffer_show_function, buf);
   else
     {
@@ -3602,7 +3752,8 @@ make_dummy_parent (window)
       = ((struct Lisp_Vector *)o)->contents[i];
   XSETWINDOW (new, p);
 
-  XSETFASTINT (p->sequence_number, ++sequence_number);
+  ++sequence_number;
+  XSETFASTINT (p->sequence_number, sequence_number);
 
   /* Put new into window structure in place of window */
   replace_window (window, new);
@@ -3759,20 +3910,18 @@ See Info node `(elisp)Splitting Windows' for more details and examples.*/)
   return new;
 }
 \f
-DEFUN ("enlarge-window", Fenlarge_window, Senlarge_window, 1, 3, "p",
+DEFUN ("enlarge-window", Fenlarge_window, Senlarge_window, 1, 2, "p",
        doc: /* Make current window ARG lines bigger.
 From program, optional second arg non-nil means grow sideways ARG columns.
 Interactively, if an argument is not given, make the window one line bigger.
-
-Optional third arg PRESERVE-BEFORE, if non-nil, means do not change the size
-of the siblings above or to the left of the selected window.  Only
-siblings to the right or below are changed.  */)
-     (arg, side, preserve_before)
-     register Lisp_Object arg, side, preserve_before;
+If HORIZONTAL is non-nil, enlarge horizontally instead of vertically.
+This function can delete windows, even the second window, if they get
+too small.  */)
+     (arg, horizontal)
+     Lisp_Object arg, horizontal;
 {
   CHECK_NUMBER (arg);
-  enlarge_window (selected_window, XINT (arg), !NILP (side),
-                 !NILP (preserve_before));
+  enlarge_window (selected_window, XINT (arg), !NILP (horizontal));
 
   if (! NILP (Vwindow_configuration_change_hook))
     call1 (Vrun_hooks, Qwindow_configuration_change_hook);
@@ -3780,20 +3929,16 @@ siblings to the right or below are changed.  */)
   return Qnil;
 }
 
-DEFUN ("shrink-window", Fshrink_window, Sshrink_window, 1, 3, "p",
+DEFUN ("shrink-window", Fshrink_window, Sshrink_window, 1, 2, "p",
        doc: /* Make current window ARG lines smaller.
 From program, optional second arg non-nil means shrink sideways arg columns.
-Interactively, if an argument is not given, make the window one line smaller.
-
-Optional third arg PRESERVE-BEFORE, if non-nil, means do not change the size
-of the siblings above or to the left of the selected window.  Only
+Interactively, if an argument is not given, make the window one line smaller.  Only
 siblings to the right or below are changed.  */)
-     (arg, side, preserve_before)
-     register Lisp_Object arg, side, preserve_before;
+     (arg, side)
+     Lisp_Object arg, side;
 {
   CHECK_NUMBER (arg);
-  enlarge_window (selected_window, -XINT (arg), !NILP (side),
-                 !NILP (preserve_before));
+  enlarge_window (selected_window, -XINT (arg), !NILP (side));
 
   if (! NILP (Vwindow_configuration_change_hook))
     call1 (Vrun_hooks, Qwindow_configuration_change_hook);
@@ -3819,40 +3964,40 @@ window_width (window)
 
 
 #define CURBEG(w) \
-  *(widthflag ? &(XWINDOW (w)->left_col) : &(XWINDOW (w)->top_line))
+  *(horiz_flag ? &(XWINDOW (w)->left_col) : &(XWINDOW (w)->top_line))
 
 #define CURSIZE(w) \
-  *(widthflag ? &(XWINDOW (w)->total_cols) : &(XWINDOW (w)->total_lines))
+  *(horiz_flag ? &(XWINDOW (w)->total_cols) : &(XWINDOW (w)->total_lines))
 
 
-/* Enlarge WINDOW by DELTA.  WIDTHFLAG non-zero means
-   increase its width.  Siblings of the selected window are resized to
-   fulfill the size request.  If they become too small in the process,
-   they will be deleted.
+/* Enlarge WINDOW by DELTA.
+   HORIZ_FLAG nonzero means enlarge it horizontally;
+   zero means do it vertically.
 
-   If PRESERVE_BEFORE is nonzero, that means don't alter
-   the siblings to the left or above WINDOW.  */
+   Siblings of the selected window are resized to fulfill the size
+   request.  If they become too small in the process, they will be
+   deleted.  */
 
 static void
-enlarge_window (window, delta, widthflag, preserve_before)
+enlarge_window (window, delta, horiz_flag)
      Lisp_Object window;
-     int delta, widthflag, preserve_before;
+     int delta, horiz_flag;
 {
   Lisp_Object parent, next, prev;
   struct window *p;
   Lisp_Object *sizep;
   int maximum;
   int (*sizefun) P_ ((Lisp_Object))
-    = widthflag ? window_width : window_height;
+    = horiz_flag ? window_width : window_height;
   void (*setsizefun) P_ ((Lisp_Object, int, int))
-    = (widthflag ? set_window_width : set_window_height);
+    = (horiz_flag ? set_window_width : set_window_height);
 
   /* Check values of window_min_width and window_min_height for
      validity.  */
   check_min_window_sizes ();
 
   /* Give up if this window cannot be resized.  */
-  if (window_fixed_size_p (XWINDOW (window), widthflag, 1))
+  if (window_fixed_size_p (XWINDOW (window), horiz_flag, 1))
     error ("Window is not resizable");
 
   /* Find the parent of the selected window.  */
@@ -3863,12 +4008,12 @@ enlarge_window (window, delta, widthflag, preserve_before)
 
       if (NILP (parent))
        {
-         if (widthflag)
+         if (horiz_flag)
            error ("No other window to side of this one");
          break;
        }
 
-      if (widthflag
+      if (horiz_flag
          ? !NILP (XWINDOW (parent)->hchild)
          : !NILP (XWINDOW (parent)->vchild))
        break;
@@ -3883,33 +4028,18 @@ enlarge_window (window, delta, widthflag, preserve_before)
 
     /* Compute the maximum size increment this window can have.  */
 
-    if (preserve_before)
-      {
-       if (!NILP (parent))
-         {
-           maxdelta = (*sizefun) (parent) - XINT (*sizep);
-           /* Subtract size of siblings before, since we can't take that.  */
-           maxdelta -= XINT (CURBEG (window)) - XINT (CURBEG (parent));
-         }
-       else
-         maxdelta = (!NILP (p->next) ? ((*sizefun) (p->next)
-                                        - window_min_size (XWINDOW (p->next),
-                                                           widthflag, 0, 0))
-                     : (delta = 0));
-      }
-    else
-      maxdelta = (!NILP (parent) ? (*sizefun) (parent) - XINT (*sizep)
-                 /* This is a main window followed by a minibuffer.  */
-                 : !NILP (p->next) ? ((*sizefun) (p->next)
-                                      - window_min_size (XWINDOW (p->next),
-                                                         widthflag, 0, 0))
-                 /* This is a minibuffer following a main window.  */
-                 : !NILP (p->prev) ? ((*sizefun) (p->prev)
-                                      - window_min_size (XWINDOW (p->prev),
-                                                         widthflag, 0, 0))
-                 /* This is a frame with only one window, a minibuffer-only
-                    or a minibufferless frame.  */
-                 : (delta = 0));
+    maxdelta = (!NILP (parent) ? (*sizefun) (parent) - XINT (*sizep)
+               /* This is a main window followed by a minibuffer.  */
+               : !NILP (p->next) ? ((*sizefun) (p->next)
+                                    - window_min_size (XWINDOW (p->next),
+                                                       horiz_flag, 0, 0))
+               /* This is a minibuffer following a main window.  */
+               : !NILP (p->prev) ? ((*sizefun) (p->prev)
+                                    - window_min_size (XWINDOW (p->prev),
+                                                       horiz_flag, 0, 0))
+               /* This is a frame with only one window, a minibuffer-only
+                  or a minibufferless frame.  */
+               : (delta = 0));
 
     if (delta > maxdelta)
       /* This case traps trying to make the minibuffer
@@ -3918,7 +4048,7 @@ enlarge_window (window, delta, widthflag, preserve_before)
       delta = maxdelta;
   }
 
-  if (XINT (*sizep) + delta < window_min_size (XWINDOW (window), widthflag, 0, 0))
+  if (XINT (*sizep) + delta < window_min_size (XWINDOW (window), horiz_flag, 0, 0))
     {
       delete_window (window);
       return;
@@ -3931,11 +4061,10 @@ enlarge_window (window, delta, widthflag, preserve_before)
   maximum = 0;
   for (next = p->next; ! NILP (next); next = XWINDOW (next)->next)
     maximum += (*sizefun) (next) - window_min_size (XWINDOW (next),
-                                                   widthflag, 0, 0);
-  if (! preserve_before)
-    for (prev = p->prev; ! NILP (prev); prev = XWINDOW (prev)->prev)
-      maximum += (*sizefun) (prev) - window_min_size (XWINDOW (prev),
-                                                     widthflag, 0, 0);
+                                                   horiz_flag, 0, 0);
+  for (prev = p->prev; ! NILP (prev); prev = XWINDOW (prev)->prev)
+    maximum += (*sizefun) (prev) - window_min_size (XWINDOW (prev),
+                                                   horiz_flag, 0, 0);
 
   /* If we can get it all from them without deleting them, do so.  */
   if (delta <= maximum)
@@ -3951,13 +4080,13 @@ enlarge_window (window, delta, widthflag, preserve_before)
         moving away from this window in both directions alternately,
         and take as much as we can get without deleting that sibling.  */
       while (delta != 0
-            && (!NILP (next) || (!preserve_before && !NILP (prev))))
+            && (!NILP (next) || !NILP (prev)))
        {
          if (! NILP (next))
            {
              int this_one = ((*sizefun) (next)
                              - window_min_size (XWINDOW (next),
-                                                widthflag, 0, &fixed_p));
+                                                horiz_flag, 0, &fixed_p));
              if (!fixed_p)
                {
                  if (this_one > delta)
@@ -3975,11 +4104,11 @@ enlarge_window (window, delta, widthflag, preserve_before)
          if (delta == 0)
            break;
 
-         if (!preserve_before && ! NILP (prev))
+         if (! NILP (prev))
            {
              int this_one = ((*sizefun) (prev)
                              - window_min_size (XWINDOW (prev),
-                                                widthflag, 0, &fixed_p));
+                                                horiz_flag, 0, &fixed_p));
              if (!fixed_p)
                {
                  if (this_one > delta)
@@ -4075,17 +4204,17 @@ enlarge_window (window, delta, widthflag, preserve_before)
 
             The number of children n equals the number of resizable
             children of this window + 1 because we know window itself
-            is resizable (otherwise we would have signalled an error.  */
+            is resizable (otherwise we would have signalled an error).  */
 
          struct window *w = XWINDOW (window);
          Lisp_Object s;
          int n = 1;
 
          for (s = w->next; !NILP (s); s = XWINDOW (s)->next)
-           if (!window_fixed_size_p (XWINDOW (s), widthflag, 0))
+           if (!window_fixed_size_p (XWINDOW (s), horiz_flag, 0))
              ++n;
          for (s = w->prev; !NILP (s); s = XWINDOW (s)->prev)
-           if (!window_fixed_size_p (XWINDOW (s), widthflag, 0))
+           if (!window_fixed_size_p (XWINDOW (s), horiz_flag, 0))
              ++n;
 
          delta1 = n * delta;
@@ -4112,9 +4241,153 @@ enlarge_window (window, delta, widthflag, preserve_before)
   adjust_glyphs (XFRAME (WINDOW_FRAME (XWINDOW (window))));
 }
 
+
+/* Adjust the size of WINDOW by DELTA, moving only its trailing edge.
+   HORIZ_FLAG nonzero means adjust the width, moving the right edge.
+   zero means adjust the height, moving the bottom edge.
+
+   Following siblings of the selected window are resized to fulfill
+   the size request.  If they become too small in the process, they
+   are not deleted; instead, we signal an error.  */
+
+static void
+adjust_window_trailing_edge (window, delta, horiz_flag)
+     Lisp_Object window;
+     int delta, horiz_flag;
+{
+  Lisp_Object parent, child;
+  struct window *p;
+  Lisp_Object old_config = Fcurrent_window_configuration (Qnil);
+  int delcount = window_deletion_count;
+
+  /* Check values of window_min_width and window_min_height for
+     validity.  */
+  check_min_window_sizes ();
+
+  if (NILP (window))
+    window = Fselected_window ();
+
+  CHECK_WINDOW (window);
+
+  /* Give up if this window cannot be resized.  */
+  if (window_fixed_size_p (XWINDOW (window), horiz_flag, 1))
+    error ("Window is not resizable");
+
+  while (1)
+    {
+      Lisp_Object first_parallel = Qnil;
+
+      p = XWINDOW (window);
+      parent = p->parent;
+
+      if (NILP (XWINDOW (window)->next))
+       {
+         Fset_window_configuration (old_config);
+         error ("No other window following this one");
+       }
+
+      /* See if this level has windows in parallel in the specified
+        direction.  If so, set FIRST_PARALLEL to the first one.  */
+      if (horiz_flag)
+       {
+         if (! NILP (parent) && !NILP (XWINDOW (parent)->vchild))
+           first_parallel = XWINDOW (parent)->vchild;
+       }
+      else
+       {
+         if (! NILP (parent) && !NILP (XWINDOW (parent)->hchild))
+           first_parallel = XWINDOW (parent)->hchild;
+       }
+
+      /* Don't make this window too small.  */
+      if (XINT (CURSIZE (window)) + delta
+         < (horiz_flag ? window_min_width : window_min_height))
+       {
+         Fset_window_configuration (old_config);
+         error ("Cannot adjust window size as specified");
+       }
+
+      /* Clear out some redisplay caches.  */
+      XSETFASTINT (p->last_modified, 0);
+      XSETFASTINT (p->last_overlay_modified, 0);
+
+      /* Adjust this window's edge.  */
+      XSETINT (CURSIZE (window),
+              XINT (CURSIZE (window)) + delta);
+
+      /* If this window has following siblings in the desired dimension,
+        make them smaller, and exit the loop.
+
+        (If we reach the top of the tree and can never do this,
+        we will fail and report an error, above.)  */
+      if (NILP (first_parallel))
+       {
+         if (!NILP (XWINDOW (window)->next))
+           {
+              /* This may happen for the minibuffer.  In that case
+                 the window_deletion_count check below does not work.  */
+              if (XINT (CURSIZE (p->next)) - delta <= 0)
+                {
+                  Fset_window_configuration (old_config);
+                  error ("Cannot adjust window size as specified");
+                }
+
+             XSETINT (CURBEG (p->next),
+                      XINT (CURBEG (p->next)) + delta);
+             size_window (p->next, XINT (CURSIZE (p->next)) - delta,
+                          horiz_flag, 0);
+             break;
+           }
+       }
+      else
+       /* Here we have a chain of parallel siblings, in the other dimension.
+          Change the size of the other siblings.  */
+       for (child = first_parallel;
+            ! NILP (child);
+            child = XWINDOW (child)->next)
+         if (! EQ (child, window))
+           size_window (child, XINT (CURSIZE (child)) + delta,
+                        horiz_flag, 0);
+
+      window = parent;
+    }
+
+  /* If we made a window so small it got deleted,
+     we failed.  Report failure.  */
+  if (delcount != window_deletion_count)
+    {
+      Fset_window_configuration (old_config);
+      error ("Cannot adjust window size as specified");
+    }
+
+  /* Adjust glyph matrices. */
+  adjust_glyphs (XFRAME (WINDOW_FRAME (XWINDOW (window))));
+}
+
 #undef CURBEG
 #undef CURSIZE
 
+DEFUN ("adjust-window-trailing-edge", Fadjust_window_trailing_edge,
+       Sadjust_window_trailing_edge, 3, 3, 0,
+       doc: /* Adjust the bottom or right edge of WINDOW by DELTA.
+If HORIZONTAL is non-nil, that means adjust the width, moving the right edge.
+Otherwise, adjust the height, moving the bottom edge.
+
+Following siblings of the selected window are resized to fulfill
+the size request.  If they become too small in the process, they
+are not deleted; instead, we signal an error.  */)
+  (window, delta, horizontal)
+  Lisp_Object window, delta, horizontal;
+{
+  CHECK_NUMBER (delta);
+  adjust_window_trailing_edge (window, XINT (delta), !NILP (horizontal));
+
+  if (! NILP (Vwindow_configuration_change_hook))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
+  return Qnil;
+}
+
 
 \f
 /***********************************************************************
@@ -4349,7 +4622,7 @@ shrink_mini_window (w)
         among the other windows.  */
       Lisp_Object window;
       XSETWINDOW (window, w);
-      enlarge_window (window, 1 - XFASTINT (w->total_lines), 0, 0);
+      enlarge_window (window, 1 - XFASTINT (w->total_lines), 0);
     }
 }
 
@@ -4482,7 +4755,6 @@ window_scroll_pixel_based (window, n, whole, noerror)
   struct text_pos start;
   Lisp_Object tem;
   int this_scroll_margin;
-  int preserve_y;
   /* True if we fiddled the window vscroll field without really scrolling.   */
   int vscrolled = 0;
 
@@ -4500,7 +4772,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, - window_box_height (w) / 2);
+      move_it_vertically_backward (&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
@@ -4510,41 +4782,93 @@ window_scroll_pixel_based (window, n, whole, noerror)
       if (it.current_y <= 0)
        {
          init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
-         move_it_vertically (&it, 0);
+         move_it_vertically_backward (&it, 0);
          it.current_y = 0;
        }
 
       start = it.current.pos;
     }
+  else if (auto_window_vscroll_p)
+    {
+      if (tem = XCAR (XCDR (XCDR (tem))), CONSP (tem))
+       {
+         int px;
+         int dy = WINDOW_FRAME_LINE_HEIGHT (w);
+         if (whole)
+           dy = max ((window_box_height (w)
+                      - next_screen_context_lines * dy),
+                     dy);
+         dy *= n;
+
+         if (n < 0 && (px = XINT (XCAR (tem))) > 0)
+           {
+             px = max (0, -w->vscroll - min (px, -dy));
+             Fset_window_vscroll (window, make_number (px), Qt);
+             return;
+           }
+         if (n > 0 && (px = XINT (XCDR (tem))) > 0)
+           {
+             px = max (0, -w->vscroll + min (px, dy));
+             Fset_window_vscroll (window, make_number (px), Qt);
+             return;
+           }
+       }
+      Fset_window_vscroll (window, make_number (0), Qt);
+    }
 
-  /* If scroll_preserve_screen_position is non-zero, we try to set
+  /* If scroll_preserve_screen_position is non-nil, we try to set
      point in the same window line as it is now, so get that line.  */
   if (!NILP (Vscroll_preserve_screen_position))
     {
-      start_display (&it, w, start);
-      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
-      preserve_y = it.current_y;
+      /* We preserve the goal pixel coordinate across consecutive
+        calls to scroll-up or scroll-down.  This avoids the
+        possibility of point becoming "stuck" on a tall line when
+        scrolling by one line.  */
+      if (window_scroll_pixel_based_preserve_y < 0
+         || (!EQ (current_kboard->Vlast_command, Qscroll_up)
+             && !EQ (current_kboard->Vlast_command, Qscroll_down)))
+       {
+         start_display (&it, w, start);
+         move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+         window_scroll_pixel_based_preserve_y = it.current_y;
+       }
     }
   else
-    preserve_y = -1;
+    window_scroll_pixel_based_preserve_y = -1;
 
   /* Move iterator it from start the specified distance forward or
      backward.  The result is the new window start.  */
   start_display (&it, w, start);
   if (whole)
     {
-      int screen_full = (window_box_height (w)
-                        - next_screen_context_lines * FRAME_LINE_HEIGHT (it.f));
-      int dy = n * screen_full;
+      int start_pos = IT_CHARPOS (it);
+      int dy = WINDOW_FRAME_LINE_HEIGHT (w);
+      dy = max ((window_box_height (w)
+                - next_screen_context_lines * dy),
+               dy) * n;
 
       /* 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,
         we would end up at the start of the line ending at ZV.  */
       if (dy <= 0)
-       move_it_vertically_backward (&it, -dy);
+       {
+         move_it_vertically_backward (&it, -dy);
+         /* Ensure we actually does move, e.g. in case we are currently
+            looking at an image that is taller that the window height.  */
+         while (start_pos == IT_CHARPOS (it)
+                && start_pos > BEGV)
+           move_it_by_lines (&it, -1, 1);
+       }
       else if (dy > 0)
-       move_it_to (&it, ZV, -1, it.current_y + dy, -1,
-                   MOVE_TO_POS | MOVE_TO_Y);
+       {
+         move_it_to (&it, ZV, -1, it.current_y + dy, -1,
+                     MOVE_TO_POS | MOVE_TO_Y);
+         /* Ensure we actually does move, e.g. in case we are currently
+            looking at an image that is taller that the window height.  */
+         while (start_pos == IT_CHARPOS (it)
+                && start_pos < ZV)
+           move_it_by_lines (&it, 1, 1);
+       }
     }
   else
     move_it_by_lines (&it, n, 1);
@@ -4559,7 +4883,7 @@ window_scroll_pixel_based (window, n, whole, noerror)
        {
          if (it.current_y < it.last_visible_y
              && (it.current_y + it.max_ascent + it.max_descent
-                 >= it.last_visible_y))
+                 > it.last_visible_y))
            {
              /* The last line was only partially visible, make it fully
                 visible.  */
@@ -4619,26 +4943,39 @@ window_scroll_pixel_based (window, n, whole, noerror)
       w->force_start = Qt;
     }
 
+  /* The rest of this function uses current_y in a nonstandard way,
+     not including the height of the header line if any.  */
   it.current_y = it.vpos = 0;
 
-  /* Preserve the screen position if we must.  */
-  if (preserve_y >= 0)
-    {
-      move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y);
-      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-    }
-  else
-    {
-      /* Move PT out of scroll margins.  */
-      this_scroll_margin = max (0, scroll_margin);
-      this_scroll_margin = min (this_scroll_margin, XFASTINT (w->total_lines) / 4);
-      this_scroll_margin *= FRAME_LINE_HEIGHT (it.f);
+  /* Move PT out of scroll margins.
+     This code wants current_y to be zero at the window start position
+     even if there is a header line.  */
+  this_scroll_margin = max (0, scroll_margin);
+  this_scroll_margin = min (this_scroll_margin, XFASTINT (w->total_lines) / 4);
+  this_scroll_margin *= FRAME_LINE_HEIGHT (it.f);
 
-      if (n > 0)
+  if (n > 0)
+    {
+      /* We moved the window start towards ZV, so PT may be now
+        in the scroll margin at the top.  */
+      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+      if (IT_CHARPOS (it) == PT && it.current_y >= this_scroll_margin
+          && (NILP (Vscroll_preserve_screen_position)
+             || EQ (Vscroll_preserve_screen_position, Qt)))
+       /* We found PT at a legitimate height.  Leave it alone.  */
+       ;
+      else if (window_scroll_pixel_based_preserve_y >= 0)
+       {
+         /* If we have a header line, take account of it.
+            This is necessary because we set it.current_y to 0, above.  */
+         move_it_to (&it, -1, -1,
+                     window_scroll_pixel_based_preserve_y
+                     - (WINDOW_WANTS_HEADER_LINE_P (w) ? 1 : 0 ),
+                     -1, MOVE_TO_Y);
+         SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+       }
+      else
        {
-         /* We moved the window start towards ZV, so PT may be now
-            in the scroll margin at the top.  */
-         move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
          while (it.current_y < this_scroll_margin)
            {
              int prev = it.current_y;
@@ -4648,23 +4985,57 @@ window_scroll_pixel_based (window, n, whole, noerror)
            }
          SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
        }
-      else if (n < 0)
+    }
+  else if (n < 0)
+    {
+      int charpos, bytepos;
+      int partial_p;
+
+      /* Save our position, for the
+        window_scroll_pixel_based_preserve_y case.  */
+      charpos = IT_CHARPOS (it);
+      bytepos = IT_BYTEPOS (it);
+
+      /* We moved the window start towards BEGV, so PT may be now
+        in the scroll margin at the bottom.  */
+      move_it_to (&it, PT, -1,
+                 (it.last_visible_y - CURRENT_HEADER_LINE_HEIGHT (w)
+                  - this_scroll_margin - 1),
+                 -1,
+                 MOVE_TO_POS | MOVE_TO_Y);
+
+      /* Save our position, in case it's correct.  */
+      charpos = IT_CHARPOS (it);
+      bytepos = IT_BYTEPOS (it);
+
+      /* See if point is on a partially visible line at the end.  */
+      if (it.what == IT_EOB)
+       partial_p = it.current_y + it.ascent + it.descent > it.last_visible_y;
+      else
        {
-         int charpos, bytepos;
-
-         /* We moved the window start towards BEGV, so PT may be now
-            in the scroll margin at the bottom.  */
-         move_it_to (&it, PT, -1,
-                     it.last_visible_y - this_scroll_margin - 1, -1,
-                     MOVE_TO_POS | MOVE_TO_Y);
-
-         /* Save our position, in case it's correct.  */
-         charpos = IT_CHARPOS (it);
-         bytepos = IT_BYTEPOS (it);
-
-         /* See if point is on a partially visible line at the end.  */
          move_it_by_lines (&it, 1, 1);
-         if (it.current_y > it.last_visible_y)
+         partial_p = it.current_y > it.last_visible_y;
+       }
+
+      if (charpos == PT && !partial_p
+          && (NILP (Vscroll_preserve_screen_position)
+             || EQ (Vscroll_preserve_screen_position, Qt)))
+       /* We found PT before we found the display margin, so PT is ok.  */
+       ;
+      else if (window_scroll_pixel_based_preserve_y >= 0)
+       {
+         SET_TEXT_POS_FROM_MARKER (start, w->start);
+         start_display (&it, w, start);
+         /* It would be wrong to subtract CURRENT_HEADER_LINE_HEIGHT
+            here because we called start_display again and did not
+            alter it.current_y this time.  */
+         move_it_to (&it, -1, -1, window_scroll_pixel_based_preserve_y, -1,
+                     MOVE_TO_Y);
+         SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+       }
+      else
+       {
+         if (partial_p)
            /* The last line was only partially visible, so back up two
               lines to make sure we're on a fully visible line.  */
            {
@@ -4758,7 +5129,8 @@ window_scroll_line_based (window, n, whole, noerror)
         the window-scroll-functions.  */
       w->force_start = Qt;
 
-      if (whole && !NILP (Vscroll_preserve_screen_position))
+      if (!NILP (Vscroll_preserve_screen_position)
+         && (whole || !EQ (Vscroll_preserve_screen_position, Qt)))
        {
          SET_PT_BOTH (pos, pos_byte);
          Fvertical_motion (make_number (original_vpos), window);
@@ -4984,17 +5356,17 @@ specifies the window to scroll.  This takes precedence over
   return Qnil;
 }
 \f
-DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 0, 1, "P",
+DEFUN ("scroll-left", Fscroll_left, Sscroll_left, 0, 2, "P\np",
        doc: /* Scroll selected window display ARG columns left.
 Default for ARG is window width minus 2.
 Value is the total amount of leftward horizontal scrolling in
 effect after the change.
-If `automatic-hscrolling' is non-nil, the argument ARG modifies
-lower bound for automatic scrolling, i.e. automatic scrolling
+If SET_MINIMUM is non-nil, the new scroll amount becomes the
+lower bound for automatic scrolling, i.e. automatic scrolling
 will not scroll a window to a column less than the value returned
-by this function.  */)
-     (arg)
-     register Lisp_Object arg;
+by this function.  This happens in an interactive call.  */)
+     (arg, set_minimum)
+     register Lisp_Object arg, set_minimum;
 {
   Lisp_Object result;
   int hscroll;
@@ -5008,23 +5380,23 @@ by this function.  */)
   hscroll = XINT (w->hscroll) + XINT (arg);
   result = Fset_window_hscroll (selected_window, make_number (hscroll));
 
-  if (interactive_p (0))
+  if (!NILP (set_minimum))
     w->min_hscroll = w->hscroll;
 
   return result;
 }
 
-DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 1, "P",
+DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 2, "P\np",
        doc: /* Scroll selected window display ARG columns right.
 Default for ARG is window width minus 2.
 Value is the total amount of leftward horizontal scrolling in
 effect after the change.
-If `automatic-hscrolling' is non-nil, the argument ARG modifies
-lower bound for automatic scrolling, i.e. automatic scrolling
+If SET_MINIMUM is non-nil, the new scroll amount becomes the
+lower bound for automatic scrolling, i.e. automatic scrolling
 will not scroll a window to a column less than the value returned
-by this function.  */)
-     (arg)
-     register Lisp_Object arg;
+by this function.  This happens in an interactive call.  */)
+     (arg, set_minimum)
+     register Lisp_Object arg, set_minimum;
 {
   Lisp_Object result;
   int hscroll;
@@ -5038,7 +5410,7 @@ by this function.  */)
   hscroll = XINT (w->hscroll) - XINT (arg);
   result = Fset_window_hscroll (selected_window, make_number (hscroll));
 
-  if (interactive_p (0))
+  if (!NILP (set_minimum))
     w->min_hscroll = w->hscroll;
 
   return result;
@@ -5133,6 +5505,8 @@ and redisplay normally--don't erase and redraw the frame.  */)
   struct buffer *obuf = current_buffer;
   int center_p = 0;
   int charpos, bytepos;
+  int iarg;
+  int this_scroll_margin;
 
   /* If redisplay is suppressed due to an error, try again.  */
   obuf->display_error_modiff = 0;
@@ -5155,10 +5529,17 @@ and redisplay normally--don't erase and redraw the frame.  */)
     {
       arg = Fprefix_numeric_value (arg);
       CHECK_NUMBER (arg);
+      iarg = XINT (arg);
     }
 
   set_buffer_internal (buf);
 
+  /* Do this after making BUF current
+     in case scroll_margin is buffer-local.  */
+  this_scroll_margin = max (0, scroll_margin);
+  this_scroll_margin = min (this_scroll_margin,
+                           XFASTINT (w->total_lines) / 4);
+
   /* 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.  */
@@ -5171,44 +5552,82 @@ and redisplay normally--don't erase and redraw the frame.  */)
 
          SET_TEXT_POS (pt, PT, PT_BYTE);
          start_display (&it, w, pt);
-         move_it_vertically (&it, - window_box_height (w) / 2);
+         move_it_vertically_backward (&it, window_box_height (w) / 2);
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
        }
-      else if (XINT (arg) < 0)
+      else if (iarg < 0)
        {
          struct it it;
          struct text_pos pt;
-         int y0, y1, h, nlines;
+         int nlines = -iarg;
+         int extra_line_spacing;
+         int h = window_box_height (w);
+
+         iarg = - max (-iarg, this_scroll_margin);
 
          SET_TEXT_POS (pt, PT, PT_BYTE);
          start_display (&it, w, pt);
-         y0 = it.current_y;
+
+         /* Be sure we have the exact height of the full line containing PT.  */
+         move_it_by_lines (&it, 0, 1);
 
          /* 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;
+         it.current_y = 0;
+         it.vpos = 0;
          move_it_by_lines (&it, nlines, 1);
 
-         y1 = line_bottom_y (&it);
+         if (it.vpos == nlines)
+           h -= it.current_y;
+         else
+           {
+             /* Last line has no newline */
+             h -= line_bottom_y (&it);
+             it.vpos++;
+           }
+
+         /* Don't reserve space for extra line spacing of last line.  */
+         extra_line_spacing = it.max_extra_line_spacing;
 
          /* 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) * FRAME_LINE_HEIGHT (it.f);
-
-         h = window_box_height (w) - (y1 - y0);
+           {
+             nlines -= it.vpos;
+             extra_line_spacing = it.extra_line_spacing;
+             h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
+           }
+         if (h <= 0)
+           return Qnil;
 
+         /* Now find the new top line (starting position) of the window.  */
          start_display (&it, w, pt);
-         move_it_vertically (&it, - h);
+         it.current_y = 0;
+         move_it_vertically_backward (&it, h);
+
+         /* If extra line spacing is present, we may move too far
+            back.  This causes the last line to be only partially
+            visible (which triggers redisplay to recenter that line
+            in the middle), so move forward.
+            But ignore extra line spacing on last line, as it is not
+            considered to be part of the visible height of the line.
+         */
+         h += extra_line_spacing;
+         while (-it.current_y > h)
+           move_it_by_lines (&it, 1, 1);
+
          charpos = IT_CHARPOS (it);
          bytepos = IT_BYTEPOS (it);
        }
       else
        {
          struct position pos;
-         pos = *vmotion (PT, - XINT (arg), w);
+
+         iarg = max (iarg, this_scroll_margin);
+
+         pos = *vmotion (PT, -iarg, w);
          charpos = pos.bufpos;
          bytepos = pos.bytepos;
        }
@@ -5219,11 +5638,15 @@ and redisplay normally--don't erase and redraw the frame.  */)
       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);
+       iarg = ht / 2;
+      else if (iarg < 0)
+       iarg += ht;
 
-      pos = *vmotion (PT, - XINT (arg), w);
+      /* Don't let it get into the margin at either top or bottom.  */
+      iarg = max (iarg, this_scroll_margin);
+      iarg = min (iarg, ht - this_scroll_margin - 1);
+
+      pos = *vmotion (PT, - iarg, w);
       charpos = pos.bufpos;
       bytepos = pos.bytepos;
     }
@@ -5272,6 +5695,9 @@ zero means top of window, negative means relative to bottom of window.  */)
   struct window *w = XWINDOW (selected_window);
   int lines, start;
   Lisp_Object window;
+#if 0
+  int this_scroll_margin;
+#endif
 
   window = selected_window;
   start = marker_position (w->start);
@@ -5287,13 +5713,33 @@ zero means top of window, negative means relative to bottom of window.  */)
     Fgoto_char (w->start);
 
   lines = displayed_window_lines (w);
+
+#if 0
+  this_scroll_margin = max (0, scroll_margin);
+  this_scroll_margin = min (this_scroll_margin, lines / 4);
+#endif
+
   if (NILP (arg))
     XSETFASTINT (arg, lines / 2);
   else
     {
-      arg = Fprefix_numeric_value (arg);
-      if (XINT (arg) < 0)
-       XSETINT (arg, XINT (arg) + lines);
+      int iarg = XINT (Fprefix_numeric_value (arg));
+
+      if (iarg < 0)
+       iarg = iarg + lines;
+
+#if 0  /* This code would prevent move-to-window-line from moving point
+         to a place inside the scroll margins (which would cause the
+         next redisplay to scroll).  I wrote this code, but then concluded
+         it is probably better not to install it.  However, it is here
+         inside #if 0 so as not to lose it.  -- rms.  */
+
+      /* Don't let it get into the margin at either top or bottom.  */
+      iarg = max (iarg, this_scroll_margin);
+      iarg = min (iarg, lines - this_scroll_margin - 1);
+#endif
+
+      arg = make_number (iarg);
     }
 
   /* Skip past a partially visible first line.  */
@@ -5350,8 +5796,6 @@ struct saved_window
   Lisp_Object scroll_bar_width, vertical_scroll_bar_type;
 };
 
-#define SAVED_WINDOW_VECTOR_SIZE 24 /* Arg to Fmake_vector */
-
 #define SAVED_WINDOW_N(swv,n) \
   ((struct saved_window *) (XVECTOR ((swv)->contents[(n)])))
 
@@ -5411,9 +5855,38 @@ the return value is nil.  Otherwise the value is t.  */)
   else
     {
       if (XBUFFER (new_current_buffer) == current_buffer)
-       old_point = PT;
+       /* The code further down "preserves point" by saving here PT in
+          old_point and then setting it later back into PT.  When the
+          current-selected-window and the final-selected-window both show
+          the current buffer, this suffers from the problem that the
+          current PT is the window-point of the current-selected-window,
+          while the final PT is the point of the final-selected-window, so
+          this copy from one PT to the other would end up moving the
+          window-point of the final-selected-window to the window-point of
+          the current-selected-window.  So we have to be careful which
+          point of the current-buffer we copy into old_point.  */
+       if (EQ (XWINDOW (data->current_window)->buffer, new_current_buffer)
+           && WINDOWP (selected_window)
+           && EQ (XWINDOW (selected_window)->buffer, new_current_buffer)
+           && !EQ (selected_window, data->current_window))
+         old_point = XMARKER (XWINDOW (data->current_window)->pointm)->charpos;
+       else
+         old_point = PT;
       else
-       old_point = BUF_PT (XBUFFER (new_current_buffer));
+       /* BUF_PT (XBUFFER (new_current_buffer)) gives us the position of
+          point in new_current_buffer as of the last time this buffer was
+          used.  This can be non-deterministic since it can be changed by
+          things like jit-lock by mere temporary selection of some random
+          window that happens to show this buffer.
+          So if possible we want this arbitrary choice of "which point" to
+          be the one from the to-be-selected-window so as to prevent this
+          window's cursor from being copied from another window.  */
+       if (EQ (XWINDOW (data->current_window)->buffer, new_current_buffer)
+           /* If current_window = selected_window, its point is in BUF_PT.  */
+           && !EQ (selected_window, data->current_window))
+         old_point = XMARKER (XWINDOW (data->current_window)->pointm)->charpos;
+       else
+         old_point = BUF_PT (XBUFFER (new_current_buffer));
     }
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
@@ -5458,8 +5931,9 @@ the return value is nil.  Otherwise the value is t.  */)
 #endif
 #endif
 
-      /* "Swap out" point from the selected window
-        into its buffer.  We do this now, before
+      /* "Swap out" point from the selected window's buffer
+        into the window itself.  (Normally the pointm of the selected
+        window holds garbage.)  We do this now, before
         restoring the window contents, and prevent it from
         being done later on when we select a new window.  */
       if (! NILP (XWINDOW (selected_window)->buffer))
@@ -5609,10 +6083,11 @@ the return value is nil.  Otherwise the value is t.  */)
       FRAME_ROOT_WINDOW (f) = data->root_window;
       /* Prevent "swapping out point" in the old selected window
         using the buffer that has been restored into it.
-        Use the point value from the beginning of this function
-        since unshow_buffer (called from delete_all_subwindows)
-        could have altered it.  */
+        We already swapped out point that from that window's old buffer.  */
       selected_window = Qnil;
+
+      /* Arrange *not* to restore point in the buffer that was
+        current when the window configuration was saved.  */
       if (EQ (XWINDOW (data->current_window)->buffer, new_current_buffer))
        set_marker_restricted (XWINDOW (data->current_window)->pointm,
                               make_number (old_point),
@@ -5803,7 +6278,7 @@ save_window_save (window, vector, i)
       p = SAVED_WINDOW_N (vector, i);
       w = XWINDOW (window);
 
-      XSETFASTINT (w->temslot, i++);
+      XSETFASTINT (w->temslot, i); i++;
       p->window = window;
       p->buffer = w->buffer;
       p->left_col = w->left_col;
@@ -5916,7 +6391,7 @@ redirection (see `redirect-frame-focus').  */)
   data->saved_windows = tem;
   for (i = 0; i < n_windows; i++)
     XVECTOR (tem)->contents[i]
-      = Fmake_vector (make_number (SAVED_WINDOW_VECTOR_SIZE), Qnil);
+      = Fmake_vector (make_number (VECSIZE (struct saved_window)), Qnil);
   save_window_save (FRAME_ROOT_WINDOW (f), XVECTOR (tem), 0);
   XSETWINDOW_CONFIGURATION (tem, data);
   return (tem);
@@ -5944,6 +6419,84 @@ usage: (save-window-excursion BODY ...)  */)
   return unbind_to (count, val);
 }
 
+
+\f
+/***********************************************************************
+                           Window Split Tree
+ ***********************************************************************/
+
+static Lisp_Object
+window_tree (w)
+     struct window *w;
+{
+  Lisp_Object tail = Qnil;
+  Lisp_Object result = Qnil;
+
+  while (w)
+    {
+      Lisp_Object wn;
+
+      XSETWINDOW (wn, w);
+      if (!NILP (w->hchild))
+       wn = Fcons (Qnil, Fcons (Fwindow_edges (wn),
+                                window_tree (XWINDOW (w->hchild))));
+      else if (!NILP (w->vchild))
+       wn = Fcons (Qt, Fcons (Fwindow_edges (wn),
+                              window_tree (XWINDOW (w->vchild))));
+
+      if (NILP (result))
+       {
+         result = tail = Fcons (wn, Qnil);
+       }
+      else
+       {
+         XSETCDR (tail, Fcons (wn, Qnil));
+         tail = XCDR (tail);
+       }
+
+      w = NILP (w->next) ? 0 : XWINDOW (w->next);
+    }
+
+  return result;
+}
+
+
+
+DEFUN ("window-tree", Fwindow_tree, Swindow_tree,
+       0, 1, 0,
+       doc: /* Return the window tree for frame FRAME.
+
+The return value is a list of the form (ROOT MINI), where ROOT
+represents the window tree of the frame's root window, and MINI
+is the frame's minibuffer window.
+
+If the root window is not split, ROOT is the root window itself.
+Otherwise, ROOT is a list (DIR EDGES W1 W2 ...) where DIR is nil for a
+horizontal split, and t for a vertical split, EDGES gives the combined
+size and position of the subwindows in the split, and the rest of the
+elements are the subwindows in the split.  Each of the subwindows may
+again be a window or a list representing a window split, and so on.
+EDGES is a list \(LEFT TOP RIGHT BOTTOM) as returned by `window-edges'.
+
+If FRAME is nil or omitted, return information on the currently
+selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  FRAME_PTR f;
+
+  if (NILP (frame))
+    frame = selected_frame;
+
+  CHECK_FRAME (frame);
+  f = XFRAME (frame);
+
+  if (!FRAME_LIVE_P (f))
+    return Qnil;
+
+  return window_tree (XWINDOW (FRAME_ROOT_WINDOW (f)));
+}
+
 \f
 /***********************************************************************
                            Marginal Areas
@@ -5957,33 +6510,33 @@ Second arg LEFT-WIDTH specifies the number of character cells to
 reserve for the left marginal area.  Optional third arg RIGHT-WIDTH
 does the same for the right marginal area.  A nil width parameter
 means no margin.  */)
-     (window, left, right)
-     Lisp_Object window, left, right;
+     (window, left_width, right_width)
+     Lisp_Object window, left_width, right_width;
 {
   struct window *w = decode_window (window);
 
   /* Translate negative or zero widths to nil.
      Margins that are too wide have to be checked elsewhere.  */
 
-  if (!NILP (left))
+  if (!NILP (left_width))
     {
-      CHECK_NUMBER (left);
-      if (XINT (left) <= 0)
-       left = Qnil;
+      CHECK_NUMBER (left_width);
+      if (XINT (left_width) <= 0)
+       left_width = Qnil;
     }
 
-  if (!NILP (right))
+  if (!NILP (right_width))
     {
-      CHECK_NUMBER (right);
-      if (XINT (right) <= 0)
-       right = Qnil;
+      CHECK_NUMBER (right_width);
+      if (XINT (right_width) <= 0)
+       right_width = Qnil;
     }
 
-  if (!EQ (w->left_margin_cols, left)
-      || !EQ (w->right_margin_cols, right))
+  if (!EQ (w->left_margin_cols, left_width)
+      || !EQ (w->right_margin_cols, right_width))
     {
-      w->left_margin_cols = left;
-      w->right_margin_cols = right;
+      w->left_margin_cols = left_width;
+      w->right_margin_cols = right_width;
 
       adjust_window_margins (w);
 
@@ -6028,22 +6581,22 @@ the command `set-fringe-style'.
 If optional fourth arg OUTSIDE-MARGINS is non-nil, draw the fringes
 outside of the display margins.  By default, fringes are drawn between
 display marginal areas and the text area.  */)
-     (window, left, right, outside_margins)
-     Lisp_Object window, left, right, outside_margins;
+     (window, left_width, right_width, outside_margins)
+     Lisp_Object window, left_width, right_width, outside_margins;
 {
   struct window *w = decode_window (window);
 
-  if (!NILP (left))
-    CHECK_NUMBER (left);
-  if (!NILP (right))
-    CHECK_NUMBER (right);
+  if (!NILP (left_width))
+    CHECK_NATNUM (left_width);
+  if (!NILP (right_width))
+    CHECK_NATNUM (right_width);
 
-  if (!EQ (w->left_fringe_width, left)
-      || !EQ (w->right_fringe_width, right)
+  if (!EQ (w->left_fringe_width, left_width)
+      || !EQ (w->right_fringe_width, right_width)
       || !EQ (w->fringes_outside_margins, outside_margins))
     {
-      w->left_fringe_width = left;
-      w->right_fringe_width = right;
+      w->left_fringe_width = left_width;
+      w->right_fringe_width = right_width;
       w->fringes_outside_margins = outside_margins;
 
       adjust_window_margins (w);
@@ -6089,17 +6642,20 @@ this is automatically adjusted to a multiple of the frame column width.
 Third parameter VERTICAL-TYPE specifies the type of the vertical scroll
 bar: left, right, or nil.
 If WIDTH is nil, use the frame's scroll-bar width.
-If TYPE is t, use the frame's scroll-bar type.  */)
+If VERTICAL-TYPE is t, use the frame's scroll-bar type.
+Fourth parameter HORIZONTAL-TYPE is currently unused.  */)
      (window, width, vertical_type, horizontal_type)
      Lisp_Object window, width, vertical_type, horizontal_type;
 {
   struct window *w = decode_window (window);
 
   if (!NILP (width))
-    CHECK_NUMBER (width);
+    {
+      CHECK_NATNUM (width);
 
-  if (XINT (width) == 0)
-    vertical_type = Qnil;
+      if (XINT (width) == 0)
+       vertical_type = Qnil;
+    }
 
   if (!(EQ (vertical_type, Qnil)
        || EQ (vertical_type, Qleft)
@@ -6155,7 +6711,7 @@ DEFUN ("window-vscroll", Fwindow_vscroll, Swindow_vscroll, 0, 2, 0,
        doc: /* Return the amount by which WINDOW is scrolled vertically.
 Use the selected window if WINDOW is nil or omitted.
 Normally, value is a multiple of the canonical character height of WINDOW;
-optional second arg PIXELS_P means value is measured in pixels.  */)
+optional second arg PIXELS-P means value is measured in pixels.  */)
   (window, pixels_p)
      Lisp_Object window, pixels_p;
 {
@@ -6185,7 +6741,7 @@ DEFUN ("set-window-vscroll", Fset_window_vscroll, Sset_window_vscroll,
        doc: /* Set amount by which WINDOW should be scrolled vertically to VSCROLL.
 WINDOW nil means use the selected window.  Normally, VSCROLL is a
 non-negative multiple of the canonical character height of WINDOW;
-optional third arg PIXELS_P non-nil means that VSCROLL is in pixels.
+optional third arg PIXELS-P non-nil means that VSCROLL is in pixels.
 If PIXELS-P is nil, VSCROLL may have to be rounded so that it
 corresponds to an integral number of pixels.  The return value is the
 result of this rounding.
@@ -6214,13 +6770,16 @@ If PIXELS-P is non-nil, the return value is VSCROLL.  */)
                      : XFLOATINT (vscroll));
       w->vscroll = min (w->vscroll, 0);
 
-      /* Adjust glyph matrix of the frame if the virtual display
-        area becomes larger than before.  */
-      if (w->vscroll < 0 && w->vscroll < old_dy)
-       adjust_glyphs (f);
+      if (w->vscroll != old_dy)
+       {
+         /* Adjust glyph matrix of the frame if the virtual display
+            area becomes larger than before.  */
+         if (w->vscroll < 0 && w->vscroll < old_dy)
+           adjust_glyphs (f);
 
-      /* Prevent redisplay shortcuts.  */
-      XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
+         /* Prevent redisplay shortcuts.  */
+         XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
+       }
     }
 
   return Fwindow_vscroll (window, pixels_p);
@@ -6467,8 +7026,15 @@ init_window ()
 void
 syms_of_window ()
 {
+  Qscroll_up = intern ("scroll-up");
+  staticpro (&Qscroll_up);
+
+  Qscroll_down = intern ("scroll-down");
+  staticpro (&Qscroll_down);
+
   Qwindow_size_fixed = intern ("window-size-fixed");
   staticpro (&Qwindow_size_fixed);
+  Fset (Qwindow_size_fixed, Qnil);
 
   staticpro (&Qwindow_configuration_change_hook);
   Qwindow_configuration_change_hook
@@ -6491,6 +7057,8 @@ syms_of_window ()
   minibuf_selected_window = Qnil;
   staticpro (&minibuf_selected_window);
 
+  window_scroll_pixel_based_preserve_y = -1;
+
   DEFVAR_LISP ("temp-buffer-show-function", &Vtemp_buffer_show_function,
               doc: /* Non-nil means call as function to display a help buffer.
 The function is called with one argument, the buffer to be displayed.
@@ -6532,6 +7100,10 @@ is displayed in the `mode-line' face.  */);
               doc: /* *Non-nil means `display-buffer' should make a separate frame.  */);
   pop_up_frames = 0;
 
+  DEFVAR_BOOL ("auto-window-vscroll", &auto_window_vscroll_p,
+              doc: /* *Non-nil means to automatically adjust `window-vscroll' to view tall lines.  */);
+  auto_window_vscroll_p = 1;
+
   DEFVAR_BOOL ("display-buffer-reuse-frames", &display_buffer_reuse_frames,
               doc: /* *Non-nil means `display-buffer' should reuse frames.
 If the buffer in question is already displayed in a frame, raise that frame.  */);
@@ -6547,7 +7119,8 @@ where `pop-up-frame-alist' would hold the default frame parameters.  */);
 
   DEFVAR_LISP ("special-display-buffer-names", &Vspecial_display_buffer_names,
               doc: /* *List of buffer names that should have their own special frames.
-Displaying a buffer whose name is in this list makes a special frame for it
+Displaying a buffer with `display-buffer' or `pop-to-buffer',
+if its name is in this list, makes a special frame for it
 using `special-display-function'.  See also `special-display-regexps'.
 
 An element of the list can be a list instead of just a string.
@@ -6572,9 +7145,9 @@ Those variables take precedence over this one.  */);
 
   DEFVAR_LISP ("special-display-regexps", &Vspecial_display_regexps,
               doc: /* *List of regexps saying which buffers should have their own special frames.
-If a buffer name matches one of these regexps, it gets its own frame.
-Displaying a buffer whose name is in this list makes a special frame for it
-using `special-display-function'.
+When displaying a buffer with `display-buffer' or `pop-to-buffer',
+if any regexp in this list matches the buffer name, it makes a
+special frame for the buffer by calling `special-display-function'.
 
 An element of the list can be a list instead of just a string.
 There are two ways to use a list as an element:
@@ -6662,7 +7235,13 @@ If there is only one window, it is split regardless of this value.  */);
 
   DEFVAR_LISP ("scroll-preserve-screen-position",
               &Vscroll_preserve_screen_position,
-              doc: /* *Non-nil means scroll commands move point to keep its screen line unchanged.  */);
+              doc: /* *Controls if scroll commands move point to keep its screen line unchanged.
+A value of nil means point does not keep its screen position except
+at the scroll margin or window boundary respectively.
+A value of t means point keeps its screen position if the scroll
+command moved it vertically out of the window, e.g. when scrolling
+by full screens.
+Any other value means point always keeps its screen position.  */);
   Vscroll_preserve_screen_position = Qnil;
 
   DEFVAR_LISP ("window-configuration-change-hook",
@@ -6671,16 +7250,6 @@ 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,
-              doc: /* Non-nil in a buffer means windows displaying the buffer are fixed-size.
-If the value is`height', then only the window's height is fixed.
-If the value is `width', then only the window's width is fixed.
-Any other non-nil value fixes both the width and the height.
-Emacs won't change the size of any window displaying that buffer,
-unless you explicitly change the size, or Emacs has no other choice.  */);
-  Fmake_variable_buffer_local (Qwindow_size_fixed);
-  window_size_fixed = 0;
-
   defsubr (&Sselected_window);
   defsubr (&Sminibuffer_window);
   defsubr (&Swindow_minibuffer_p);
@@ -6728,6 +7297,7 @@ unless you explicitly change the size, or Emacs has no other choice.  */);
   defsubr (&Ssplit_window);
   defsubr (&Senlarge_window);
   defsubr (&Sshrink_window);
+  defsubr (&Sadjust_window_trailing_edge);
   defsubr (&Sscroll_up);
   defsubr (&Sscroll_down);
   defsubr (&Sscroll_left);
@@ -6743,6 +7313,7 @@ unless you explicitly change the size, or Emacs has no other choice.  */);
   defsubr (&Sset_window_configuration);
   defsubr (&Scurrent_window_configuration);
   defsubr (&Ssave_window_excursion);
+  defsubr (&Swindow_tree);
   defsubr (&Sset_window_margins);
   defsubr (&Swindow_margins);
   defsubr (&Sset_window_fringes);