Merged from emacs@sv.gnu.org
[bpt/emacs.git] / src / window.c
index 69cee51..966960e 100644 (file)
@@ -1,7 +1,7 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
    Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
-                 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+                 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -52,6 +52,7 @@ Boston, MA 02110-1301, 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;
 
@@ -67,7 +68,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,
@@ -212,6 +213,14 @@ Lisp_Object Vwindow_configuration_change_hook;
 
 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;
@@ -654,12 +663,24 @@ 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)
@@ -701,7 +722,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
     {
@@ -713,6 +740,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;
        }
     }
@@ -1335,7 +1364,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))
@@ -1399,6 +1428,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) */
@@ -1824,7 +1856,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;
 {
@@ -1856,7 +1888,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;
 
@@ -1964,7 +1997,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;
 
@@ -2002,7 +2035,7 @@ window_loop (type, obj, mini, frames)
               `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) && EQ (w->dedicated, Qt))
+               || (!(XINT (obj) & 2) && !NILP (w->dedicated))
                /* Minibuffer windows are always ignored.  */
                || MINI_WINDOW_P (w))
              break;
@@ -2057,7 +2090,7 @@ window_loop (type, obj, mini, frames)
          case GET_LARGEST_WINDOW:
            { /* nil `obj' means to ignore dedicated windows.  */
              /* Ignore dedicated windows and minibuffers.  */
-             if (MINI_WINDOW_P (w) || (NILP (obj) && EQ (w->dedicated, Qt)))
+             if (MINI_WINDOW_P (w) || (NILP (obj) && !NILP (w->dedicated)))
                break;
 
              if (NILP (best_window))
@@ -2563,7 +2596,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)
@@ -3403,7 +3439,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.
@@ -3585,7 +3621,7 @@ displayed.  */)
                           + XFASTINT (XWINDOW (window)->total_lines));
              enlarge_window (upper,
                              total / 2 - XFASTINT (XWINDOW (upper)->total_lines),
-                             0, 0);
+                             0);
            }
        }
     }
@@ -3662,7 +3698,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
     {
@@ -3876,21 +3912,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.
 If HORIZONTAL is non-nil, enlarge horizontally instead of vertically.
-
-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, horizontal, preserve_before)
-     register Lisp_Object arg, horizontal, preserve_before;
+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 (horizontal),
-                 !NILP (preserve_before));
+  enlarge_window (selected_window, XINT (arg), !NILP (horizontal));
 
   if (! NILP (Vwindow_configuration_change_hook))
     call1 (Vrun_hooks, Qwindow_configuration_change_hook);
@@ -3898,20 +3931,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);
@@ -3949,15 +3978,12 @@ window_width (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.
-
-   If PRESERVE_BEFORE is nonzero, that means don't alter
-   the siblings to the left or above WINDOW.  */
+   deleted.  */
 
 static void
-enlarge_window (window, delta, horiz_flag, preserve_before)
+enlarge_window (window, delta, horiz_flag)
      Lisp_Object window;
-     int delta, horiz_flag, preserve_before;
+     int delta, horiz_flag;
 {
   Lisp_Object parent, next, prev;
   struct window *p;
@@ -4004,33 +4030,18 @@ enlarge_window (window, delta, horiz_flag, 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),
-                                                           horiz_flag, 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),
-                                                         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));
+    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
@@ -4053,10 +4064,9 @@ enlarge_window (window, delta, horiz_flag, preserve_before)
   for (next = p->next; ! NILP (next); next = XWINDOW (next)->next)
     maximum += (*sizefun) (next) - window_min_size (XWINDOW (next),
                                                    horiz_flag, 0, 0);
-  if (! preserve_before)
-    for (prev = p->prev; ! NILP (prev); prev = XWINDOW (prev)->prev)
-      maximum += (*sizefun) (prev) - window_min_size (XWINDOW (prev),
-                                                     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)
@@ -4072,7 +4082,7 @@ enlarge_window (window, delta, horiz_flag, 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))
            {
@@ -4096,7 +4106,7 @@ enlarge_window (window, delta, horiz_flag, preserve_before)
          if (delta == 0)
            break;
 
-         if (!preserve_before && ! NILP (prev))
+         if (! NILP (prev))
            {
              int this_one = ((*sizefun) (prev)
                              - window_min_size (XWINDOW (prev),
@@ -4233,9 +4243,153 @@ enlarge_window (window, delta, horiz_flag, 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
 /***********************************************************************
@@ -4470,7 +4624,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);
     }
 }
 
@@ -4603,7 +4757,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;
 
@@ -4669,12 +4822,21 @@ window_scroll_pixel_based (window, n, whole, noerror)
      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.  */
@@ -4723,7 +4885,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.  */
@@ -4804,14 +4966,14 @@ window_scroll_pixel_based (window, n, whole, noerror)
              || EQ (Vscroll_preserve_screen_position, Qt)))
        /* We found PT at a legitimate height.  Leave it alone.  */
        ;
-      else if (preserve_y >= 0)
+      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.  */
-         if (WINDOW_WANTS_HEADER_LINE_P (w))
-           preserve_y -= CURRENT_HEADER_LINE_HEIGHT (w);
-
-         move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y);
+         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
@@ -4831,7 +4993,8 @@ window_scroll_pixel_based (window, n, whole, noerror)
       int charpos, bytepos;
       int partial_p;
 
-      /* Save our position, for the preserve_y case.  */
+      /* Save our position, for the
+        window_scroll_pixel_based_preserve_y case.  */
       charpos = IT_CHARPOS (it);
       bytepos = IT_BYTEPOS (it);
 
@@ -4861,20 +5024,15 @@ window_scroll_pixel_based (window, n, whole, noerror)
              || EQ (Vscroll_preserve_screen_position, Qt)))
        /* We found PT before we found the display margin, so PT is ok.  */
        ;
-      else if (preserve_y >= 0)
+      else if (window_scroll_pixel_based_preserve_y >= 0)
        {
          SET_TEXT_POS_FROM_MARKER (start, w->start);
          start_display (&it, w, start);
-#if 0  /* It's wrong to subtract this here
-         because we called start_display again
-         and did not alter it.current_y this time.  */
-
-         /* If we have a header line, take account of it.  */
-         if (WINDOW_WANTS_HEADER_LINE_P (w))
-           preserve_y -= CURRENT_HEADER_LINE_HEIGHT (w);
-#endif
-
-         move_it_to (&it, -1, -1, preserve_y, -1, MOVE_TO_Y);
+         /* 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
@@ -5699,7 +5857,23 @@ 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
        /* BUF_PT (XBUFFER (new_current_buffer)) gives us the position of
           point in new_current_buffer as of the last time this buffer was
@@ -6470,7 +6644,8 @@ 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;
 {
@@ -6538,7 +6713,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;
 {
@@ -6568,7 +6743,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.
@@ -6853,6 +7028,12 @@ 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);
@@ -6878,6 +7059,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.
@@ -7116,6 +7299,7 @@ The selected frame is the one whose configuration has changed.  */);
   defsubr (&Ssplit_window);
   defsubr (&Senlarge_window);
   defsubr (&Sshrink_window);
+  defsubr (&Sadjust_window_trailing_edge);
   defsubr (&Sscroll_up);
   defsubr (&Sscroll_down);
   defsubr (&Sscroll_left);