]> code.delx.au - gnu-emacs/blobdiff - src/window.c
(set_case_table): If standard is nonzero, setup
[gnu-emacs] / src / window.c
index ad22e1df1079b7cd2c0dfd4f496cefe80664a00d..93495afb1a532f1e18a5d3b3f8825f7e0509da12 100644 (file)
@@ -124,6 +124,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,7 +209,7 @@ 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;
@@ -241,7 +246,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);
@@ -327,9 +333,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 +345,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 +368,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;
 }
 
@@ -607,9 +617,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)
@@ -659,6 +666,9 @@ coordinates_in_window (w, x, 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,6 +683,9 @@ 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;
@@ -1607,7 +1620,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))
     ;
@@ -1828,7 +1841,7 @@ window_list_1 (window, minibuf, all_frames)
   rest = Fmemq (window, list);
   if (!NILP (rest) && !EQ (rest, list))
     {
-      for (tail = list; XCDR (tail) != rest; tail = XCDR (tail))
+      for (tail = list; !EQ (XCDR (tail), rest); tail = XCDR (tail))
        ;
       XSETCDR (tail, Qnil);
       list = nconc2 (rest, list);
@@ -1881,7 +1894,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;
@@ -2093,6 +2106,8 @@ DEFUN ("get-lru-window", Fget_lru_window, Sget_lru_window, 0, 1, 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, 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.
@@ -2113,6 +2128,8 @@ If FRAME is a frame, search only that frame.  */)
 DEFUN ("get-largest-window", Fget_largest_window, Sget_largest_window, 0, 1, 0,
        doc: /* Return the largest window in area.
 A minibuffer window is never a candidate.
+A dedicated window is never a candidate, 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.
@@ -2654,6 +2671,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 +2698,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 +2718,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 +3025,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);
@@ -3057,7 +3109,9 @@ BUFFER can be a buffer or the name of an existing buffer.
 Optional third arg KEEP-MARGINS non-nil means that WINDOW's current
 display margins, fringe widths, and scroll bar settings are maintained;
 the default is to reset these from BUFFER's local settings or the frame
-defaults.  */)
+defaults.
+
+This function runs the hook `window-scroll-functions'.  */)
      (window, buffer, keep_margins)
      register Lisp_Object window, buffer, keep_margins;
 {
@@ -3112,7 +3166,8 @@ 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;
 
@@ -3209,10 +3264,13 @@ 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, a frame would be created 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;
 {
@@ -3600,7 +3658,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);
@@ -4498,7 +4557,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
@@ -4508,14 +4567,41 @@ 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))
     {
@@ -4531,18 +4617,34 @@ window_scroll_pixel_based (window, n, whole, noerror)
   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);
@@ -4617,26 +4719,37 @@ 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 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)
     {
-      /* 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);
+      /* 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)
+       /* We found PT at a legitimate height.  Leave it alone.  */
+       ;
+      else if (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);
 
-      if (n > 0)
+         move_it_to (&it, -1, -1, preserve_y, -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;
@@ -4646,23 +4759,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 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 - 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);
+         move_it_by_lines (&it, 1, 1);
+         partial_p = it.current_y > it.last_visible_y;
+       }
 
-         /* Save our position, in case it's correct.  */
-         charpos = IT_CHARPOS (it);
-         bytepos = IT_BYTEPOS (it);
+      if (charpos == PT && !partial_p)
+       /* We found PT before we found the display margin, so PT is ok.  */
+       ;
+      else if (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
 
-         /* 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)
+         move_it_to (&it, -1, -1, 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.  */
            {
@@ -4982,17 +5129,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;
@@ -5006,23 +5153,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;
@@ -5036,7 +5183,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;
@@ -5169,7 +5316,7 @@ 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);
        }
@@ -5177,29 +5324,62 @@ and redisplay normally--don't erase and redraw the frame.  */)
        {
          struct it it;
          struct text_pos pt;
-         int y0, y1, h, nlines;
+         int nlines = - XINT (arg);
+         int extra_line_spacing;
+         int h = window_box_height (w);
 
          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);
        }
@@ -5801,7 +5981,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;
@@ -6032,9 +6212,9 @@ display marginal areas and the text area.  */)
   struct window *w = decode_window (window);
 
   if (!NILP (left))
-    CHECK_NUMBER (left);
+    CHECK_NATNUM (left);
   if (!NILP (right))
-    CHECK_NUMBER (right);
+    CHECK_NATNUM (right);
 
   if (!EQ (w->left_fringe_width, left)
       || !EQ (w->right_fringe_width, right)
@@ -6094,10 +6274,12 @@ If TYPE is t, use the frame's scroll-bar 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)
@@ -6212,13 +6394,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);
@@ -6530,6 +6715,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.  */);
@@ -6660,7 +6849,9 @@ 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: /* *Non-nil means scroll commands move point to keep its screen line unchanged.
+This is only when it is impossible to keep point fixed and still
+scroll as specified.  */);
   Vscroll_preserve_screen_position = Qnil;
 
   DEFVAR_LISP ("window-configuration-change-hook",