(mail-setup): Call set-buffer-multibyte because
[bpt/emacs.git] / src / window.c
index 223ef07..baa55a5 100644 (file)
@@ -244,6 +244,7 @@ make_window ()
   XSETFASTINT (p->height, 0);
   XSETFASTINT (p->width, 0);
   XSETFASTINT (p->hscroll, 0);
+  XSETFASTINT (p->min_hscroll, 0);
   p->orig_top = p->orig_height = Qnil;
   p->start = Fmake_marker ();
   p->pointm = Fmake_marker ();
@@ -417,17 +418,19 @@ DEFUN ("set-window-hscroll", Fset_window_hscroll, Sset_window_hscroll, 2, 2, 0,
   "Set number of columns WINDOW is scrolled from left margin to NCOL.\n\
 NCOL should be zero or positive.")
   (window, ncol)
-     register Lisp_Object window, ncol;
+     Lisp_Object window, ncol;
 {
-  register struct window *w;
+  struct window *w = decode_window (window);
+  int hscroll;
 
   CHECK_NUMBER (ncol, 1);
-  if (XINT (ncol) < 0) XSETFASTINT (ncol, 0);
-  w = decode_window (window);
-  if (XINT (w->hscroll) != XINT (ncol))
-    /* Prevent redisplay shortcuts */
+  hscroll = max (0, XINT (ncol));
+  
+  /* Prevent redisplay shortcuts when changing the hscroll.  */
+  if (XINT (w->hscroll) != hscroll)
     XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
-  w->hscroll = ncol;
+  
+  w->hscroll = make_number (hscroll);
   return ncol;
 }
 
@@ -497,21 +500,28 @@ coordinates_in_window (w, x, y)
      register struct window *w;
      register int *x, *y;
 {
-  struct frame *f = XFRAME (WINDOW_FRAME (w));
-  int left_x, right_x, top_y, bottom_y;
-  int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
-
   /* Let's make this a global enum later, instead of using numbers
      everywhere.  */
   enum {ON_NOTHING, ON_TEXT, ON_MODE_LINE, ON_VERTICAL_BORDER,
        ON_HEADER_LINE, ON_LEFT_FRINGE, ON_RIGHT_FRINGE};
 
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  int left_x, right_x, top_y, bottom_y;
+  int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
+  int part;
+  int ux = CANON_X_UNIT (f), uy = CANON_Y_UNIT (f);
+  int x0 = XFASTINT (w->left) * ux;
+  int x1 = x0 + XFASTINT (w->width) * ux;
+
+  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)
     {
       left_x = 0;
-      right_x = XFASTINT (w->width) * CANON_Y_UNIT (f) - 1;
+      right_x = XFASTINT (w->width) * CANON_X_UNIT (f) - 1;
       top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w);
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
     }
@@ -530,50 +540,59 @@ coordinates_in_window (w, x, y)
      sibling, say it's on the vertical line.  That's to be able
      to resize windows horizontally in case we're using toolkit
      scroll bars.  */
-  
+
   if (WINDOW_WANTS_MODELINE_P (w)
-      && *y < bottom_y
-      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w))
+      && *y >= bottom_y - CURRENT_MODE_LINE_HEIGHT (w)
+      && *y < bottom_y)
     {
-      if (!WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - ((XFASTINT (w->left) + XFASTINT (w->width))
-                        * CANON_X_UNIT (f)))
-             < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-      return ON_MODE_LINE;
+      /* We're somewhere on the mode line.  We consider the place
+        between mode lines of horizontally adjacent mode lines
+        as the vertical border.    If scroll bars on the left,
+        return the right window.  */
+      part = ON_MODE_LINE;
+      
+      if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
+       {
+         if (abs (*x - x0) < ux / 2)
+           part = ON_VERTICAL_BORDER;
+       }
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+       part = ON_VERTICAL_BORDER;
     }
-  
-  if (WINDOW_WANTS_HEADER_LINE_P (w)
-      && *y >= top_y
-      && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w))
+  else if (WINDOW_WANTS_HEADER_LINE_P (w)
+          && *y < top_y + CURRENT_HEADER_LINE_HEIGHT (w)
+          && *y >= top_y)
     {
-      if (!WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - ((XFASTINT (w->left) + XFASTINT (w->width))
-                        * CANON_X_UNIT (f)))
-             < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-      return ON_HEADER_LINE;
+      part = ON_HEADER_LINE;
+      
+      if (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f))
+       {
+         if (abs (*x - x0) < ux / 2)
+           part = ON_VERTICAL_BORDER;
+       }
+      else if (!WINDOW_RIGHTMOST_P (w) && abs (*x - x1) < ux / 2)
+       part = ON_VERTICAL_BORDER;
     }
-
-  /* Completely outside anything interesting?  */
-  if (*y < top_y
-      || *y >= bottom_y
-      || *x < (left_x
-              - flags_area_width
-              - (FRAME_LEFT_SCROLL_BAR_WIDTH (f)
-                 * CANON_X_UNIT (f)))
-      || *x > right_x + flags_area_width)
-    return ON_NOTHING;
-  
-  if (FRAME_WINDOW_P (f))
+  /* Outside anything interesting?  */
+  else if (*y < top_y
+          || *y >= bottom_y
+          || *x < (left_x
+                   - flags_area_width
+                   - FRAME_LEFT_SCROLL_BAR_WIDTH (f) * ux)
+          || *x > right_x + flags_area_width)
+    {
+      part = ON_NOTHING;
+    }
+  else if (FRAME_WINDOW_P (f))
     {
       if (!w->pseudo_window_p
          && !FRAME_HAS_VERTICAL_SCROLL_BARS (f)
          && !WINDOW_RIGHTMOST_P (w)
-         && (abs (*x - right_x - flags_area_width) < CANON_X_UNIT (f) / 2))
-       return ON_VERTICAL_BORDER;
-
-      if (*x < left_x || *x > right_x)
+         && (abs (*x - right_x - flags_area_width) < ux / 2))
+       {
+         part = ON_VERTICAL_BORDER;
+       }
+      else if (*x < left_x || *x > right_x)
        {
          /* Other lines than the mode line don't include flags areas and
             scroll bars on the left.  */
@@ -581,7 +600,13 @@ coordinates_in_window (w, x, y)
          /* Convert X and Y to window-relative pixel coordinates.  */
          *x -= left_x;
          *y -= top_y;
-         return *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+       }
+      else
+       {
+         *x -= left_x;
+         *y -= top_y;
+         part = ON_TEXT;
        }
     }
   else
@@ -596,24 +621,30 @@ coordinates_in_window (w, x, y)
          /* Convert X and Y to window-relative pixel coordinates.  */
          *x -= left_x;
          *y -= top_y;
-         return *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
+         part = *x < left_x ? ON_LEFT_FRINGE : ON_RIGHT_FRINGE;
        }
-  
       /* Here, too, "*x > right_x" is because of character terminals.  */
-      if (!w->pseudo_window_p
-         && !WINDOW_RIGHTMOST_P (w)
-         && *x > right_x - CANON_X_UNIT (f))
-       /* On the border on the right side of the window?  Assume that
-          this area begins at RIGHT_X minus a canonical char width.  */
-       return ON_VERTICAL_BORDER;
+      else if (!w->pseudo_window_p
+              && !WINDOW_RIGHTMOST_P (w)
+              && *x > right_x - ux)
+       {
+         /* On the border on the right side of the window?  Assume that
+            this area begins at RIGHT_X minus a canonical char width.  */
+         part = ON_VERTICAL_BORDER;
+       }
+      else
+       {
+         /* Convert X and Y to window-relative pixel coordinates.  */
+         *x -= left_x;
+         *y -= top_y;
+         part = ON_TEXT;
+       }
     }
-  
-  /* Convert X and Y to window-relative pixel coordinates.  */
-  *x -= left_x;
-  *y -= top_y;
-  return ON_TEXT;
+
+  return part;
 }
 
+
 DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p,
   Scoordinates_in_window_p, 2, 2, 0,
   "Return non-nil if COORDINATES are in WINDOW.\n\
@@ -833,7 +864,7 @@ DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0,
 This is updated by redisplay, when it runs to completion.\n\
 Simply changing the buffer text or setting `window-start'\n\
 does not update this value.\n\
-If UP-TO-DATE is non-nil, compute the up-to-date position\n\
+If UPDATE is non-nil, compute the up-to-date position\n\
 if it isn't already recorded.")
   (window, update)
      Lisp_Object window, update;
@@ -877,6 +908,7 @@ if it isn't already recorded.")
         cope with variable-height lines.  */
       start_display (&it, w, startp);
       move_it_vertically (&it, window_box_height (w));
+      move_it_past_eol (&it);
       value = make_number (IT_CHARPOS (it));
     }
   else
@@ -898,6 +930,11 @@ DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0,
     Fgoto_char (pos);
   else
     set_marker_restricted (w->pointm, pos, w->buffer);
+
+  /* We have to make sure that redisplay updates the window to show
+     the new value of point.  */
+  if (!EQ (window, selected_window))
+    ++windows_or_buffers_changed;
   
   return pos;
 }
@@ -1128,7 +1165,7 @@ delete_window (window)
   register Lisp_Object tem, parent, sib;
   register struct window *p;
   register struct window *par;
-  FRAME_PTR frame;
+  struct frame *f;
 
   /* Because this function is called by other C code on non-leaf
      windows, the CHECK_LIVE_WINDOW macro would choke inappropriately,
@@ -1152,18 +1189,17 @@ delete_window (window)
 
   windows_or_buffers_changed++;
   Vwindow_list = Qnil;
-  frame = XFRAME (WINDOW_FRAME (p));
-  FRAME_WINDOW_SIZES_CHANGED (frame) = 1;
+  f = XFRAME (WINDOW_FRAME (p));
+  FRAME_WINDOW_SIZES_CHANGED (f) = 1;
 
   /* Are we trying to delete any frame's selected window?  */
   {
-    Lisp_Object frame, pwindow;
+    Lisp_Object pwindow;
 
     /* See if the frame's selected window is either WINDOW
        or any subwindow of it, by finding all that window's parents
        and comparing each one with WINDOW.  */
-    frame = WINDOW_FRAME (XWINDOW (window));
-    pwindow = FRAME_SELECTED_WINDOW (XFRAME (frame));
+    pwindow = FRAME_SELECTED_WINDOW (f);
 
     while (!NILP (pwindow))
       {
@@ -1185,7 +1221,7 @@ delete_window (window)
        if (EQ (window, selected_window))
          Fselect_window (alternative);
        else
-         FRAME_SELECTED_WINDOW (XFRAME (frame)) = alternative;
+         FRAME_SELECTED_WINDOW (f) = alternative;
       }
   }
 
@@ -1204,7 +1240,7 @@ delete_window (window)
      events and other events that access glyph matrices are not
      processed while we are changing them.  */
   BLOCK_INPUT;
-  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (frame)));
+  free_window_matrices (XWINDOW (FRAME_ROOT_WINDOW (f)));
 
   tem = p->next;
   if (!NILP (tem))
@@ -1261,7 +1297,7 @@ delete_window (window)
   p->buffer = p->hchild = p->vchild = Qnil;
 
   /* Adjust glyph matrices. */
-  adjust_glyphs (frame);
+  adjust_glyphs (f);
   UNBLOCK_INPUT;
 }
 
@@ -1615,7 +1651,6 @@ MINIBUF neither nil nor t means never include the minibuffer window.")
   (frame, minibuf, window)
      Lisp_Object frame, minibuf, window;
 {
-  Lisp_Object list;
 
   if (NILP (window))
     window = selected_window;
@@ -1656,8 +1691,7 @@ window_list_1 (window, minibuf, all_frames)
                 Qnil, look at just the selected frame;
                Qvisible, look at visible frames;
                a frame, just look at windows on that frame.
-   If MINI is non-zero, perform the operation on minibuffer windows too.
-*/
+   If MINI is non-zero, perform the operation on minibuffer windows too.  */
 
 enum window_loop
 {
@@ -1954,13 +1988,12 @@ value is reasonable when this function is called.")
 {
   struct window *w;
   int startpos;
-  int top;
+  int top, new_top;
 
   if (NILP (window))
     window = selected_window;
   else
     CHECK_LIVE_WINDOW (window, 0);
-
   w = XWINDOW (window);
 
   startpos = marker_position (w->start);
@@ -1976,7 +2009,9 @@ value is reasonable when this function is called.")
      on the frame.  But don't try to do this if the window start is
      outside the visible portion (as might happen when the display is
      not current, due to typeahead).  */
-  if (startpos >= BUF_BEGV (XBUFFER (w->buffer))
+  new_top = XFASTINT (w->top) - FRAME_TOP_MARGIN (XFRAME (WINDOW_FRAME (w)));
+  if (new_top != top
+      && startpos >= BUF_BEGV (XBUFFER (w->buffer))
       && startpos <= BUF_ZV (XBUFFER (w->buffer)))
     {
       struct position pos;
@@ -1988,6 +2023,7 @@ value is reasonable when this function is called.")
       pos = *vmotion (startpos, -top, w);
 
       set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
+      w->window_end_valid = Qnil;
       w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
                               || FETCH_BYTE (pos.bytepos - 1) == '\n') ? Qt
                              : Qnil);
@@ -2370,14 +2406,12 @@ size_window (window, size, width_p, nodelete_p)
       min_size = window_min_height;
     }
   
-  if (old_size < window_min_width)
+  if (old_size < min_size)
     w->too_small_ok = Qt;
 
   /* Maybe delete WINDOW if it's too small.  */
   if (!nodelete_p && !NILP (w->parent))
     {
-      int min_size;
-
       if (!MINI_WINDOW_P (w) && !NILP (w->too_small_ok))
        min_size = width_p ? MIN_SAFE_WINDOW_WIDTH : MIN_SAFE_WINDOW_HEIGHT;
       else
@@ -2575,6 +2609,7 @@ set_window_buffer (window, buffer, run_hooks_p)
   bzero (&w->last_cursor, sizeof w->last_cursor);
   w->window_end_valid = Qnil;
   XSETFASTINT (w->hscroll, 0);
+  XSETFASTINT (w->min_hscroll, 0);
   set_marker_both (w->pointm, buffer, BUF_PT (b), BUF_PT_BYTE (b));
   set_marker_restricted (w->start,
                         make_number (b->last_window_start),
@@ -2854,7 +2889,7 @@ If FRAME is nil, search only the selected frame\n\
  which means search visible and iconified frames.\n\
 \n\
 If `even-window-heights' is non-nil, window heights will be evened out\n\
-if displaying the buffer causes two vertically\ adjacent windows to be\n\
+if displaying the buffer causes two vertically adjacent windows to be\n\
 displayed.")
   (buffer, not_this_window, frame)
      register Lisp_Object buffer, not_this_window, frame;
@@ -3054,6 +3089,7 @@ temp_output_buffer_show (buf)
       Vminibuf_scroll_window = window;
       w = XWINDOW (window);
       XSETFASTINT (w->hscroll, 0);
+      XSETFASTINT (w->min_hscroll, 0);
       set_marker_restricted_both (w->start, buf, 1, 1);
       set_marker_restricted_both (w->pointm, buf, 1, 1);
 
@@ -3893,8 +3929,7 @@ window_scroll_pixel_based (window, n, whole, noerror)
   
   /* If PT is not visible in WINDOW, move back one half of
      the screen.  */
-  XSETFASTINT (tem, PT);
-  tem = Fpos_visible_in_window_p (tem, window, Qnil);
+  tem = Fpos_visible_in_window_p (make_number (PT), window, Qnil);
   if (NILP (tem))
     {
       /* Move backward half the height of the window.  Performance note:
@@ -3938,7 +3973,16 @@ window_scroll_pixel_based (window, n, whole, noerror)
       int screen_full = (it.last_visible_y
                         - next_screen_context_lines * CANON_Y_UNIT (it.f));
       int direction = n < 0 ? -1 : 1;
-      move_it_vertically (&it, direction * screen_full);
+      int dy = direction * screen_full;
+
+      /* Note that move_it_vertically always moves the iterator to the
+         start of a line.  So, if the last line doesn't have a newline,
+        we would end up at the start of the line ending at ZV.  */
+      if (dy <= 0)
+       move_it_vertically_backward (&it, -dy);
+      else if (dy > 0)
+       move_it_to (&it, ZV, -1, it.current_y + dy, -1,
+                   MOVE_TO_POS | MOVE_TO_Y);
     }
   else
     move_it_by_lines (&it, n, 1);
@@ -3951,11 +3995,13 @@ window_scroll_pixel_based (window, n, whole, noerror)
        {
          if (it.current_y + it.max_ascent + it.max_descent
              > it.last_visible_y)
-           /* The last line was only partially visible, make it fully
-              visible.  */
-           w->vscroll = 
-             it.last_visible_y
-             - it.current_y + it.max_ascent + it.max_descent;
+           {
+             /* The last line was only partially visible, make it fully
+                visible.  */
+             w->vscroll = (it.last_visible_y
+                           - it.current_y + it.max_ascent + it.max_descent);
+             adjust_glyphs (it.f);
+           }
          else if (noerror)
            return;
          else
@@ -4361,16 +4407,22 @@ Default for ARG is window width minus 2.")
   (arg)
      register Lisp_Object arg;
 {
-
+  Lisp_Object result;
+  int hscroll;
+  struct window *w = XWINDOW (selected_window);
+  
   if (NILP (arg))
-    XSETFASTINT (arg, window_internal_width (XWINDOW (selected_window)) - 2);
+    XSETFASTINT (arg, window_internal_width (w) - 2);
   else
     arg = Fprefix_numeric_value (arg);
 
-  return
-    Fset_window_hscroll (selected_window,
-                        make_number (XINT (XWINDOW (selected_window)->hscroll)
-                                     + XINT (arg)));
+  hscroll = XINT (w->hscroll) + XINT (arg);
+  result = Fset_window_hscroll (selected_window, make_number (hscroll));
+
+  if (!NILP (Finteractive_p ()))
+    w->min_hscroll = w->hscroll;
+
+  return result;
 }
 
 DEFUN ("scroll-right", Fscroll_right, Sscroll_right, 0, 1, "P",
@@ -4379,72 +4431,24 @@ Default for ARG is window width minus 2.")
   (arg)
      register Lisp_Object arg;
 {
+  Lisp_Object result;
+  int hscroll;
+  struct window *w = XWINDOW (selected_window);
+  
   if (NILP (arg))
-    XSETFASTINT (arg, window_internal_width (XWINDOW (selected_window)) - 2);
+    XSETFASTINT (arg, window_internal_width (w) - 2);
   else
     arg = Fprefix_numeric_value (arg);
 
-  return
-    Fset_window_hscroll (selected_window,
-                        make_number (XINT (XWINDOW (selected_window)->hscroll)
-                                     - XINT (arg)));
-}
-
-DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P",
-  "Center point in window and redisplay frame.  With ARG, put point on line ARG.\n\
-The desired position of point is always relative to the current window.\n\
-Just C-u as prefix means put point in the center of the window.\n\
-If ARG is omitted or nil, erases the entire frame and then\n\
-redraws with point in the center of the current window.")
-  (arg)
-     register Lisp_Object arg;
-{
-  register struct window *w = XWINDOW (selected_window);
-  register int ht = window_internal_height (w);
-  struct position pos;
-  struct buffer *buf = XBUFFER (w->buffer);
-  struct buffer *obuf = current_buffer;
-
-  if (NILP (arg))
-    {
-      extern int frame_garbaged;
-      int i;
-
-      /* Invalidate pixel data calculated for all compositions.  */
-      for (i = 0; i < n_compositions; i++)
-       composition_table[i]->font = NULL;
-
-      Fredraw_frame (w->frame);
-      SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
-      XSETFASTINT (arg, ht / 2);
-    }
-  else if (CONSP (arg)) /* Just C-u. */
-    {
-      XSETFASTINT (arg, ht / 2);
-    }
-  else
-    {
-      arg = Fprefix_numeric_value (arg);
-      CHECK_NUMBER (arg, 0);
-    }
-
-  if (XINT (arg) < 0)
-    XSETINT (arg, XINT (arg) + ht);
-
-  set_buffer_internal (buf);
-  pos = *vmotion (PT, - XINT (arg), w);
-
-  set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
-  w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
-                          || FETCH_BYTE (pos.bytepos - 1) == '\n')
-                         ? Qt : Qnil);
-  w->force_start = Qt;
-  set_buffer_internal (obuf);
+  hscroll = XINT (w->hscroll) - XINT (arg);
+  result = Fset_window_hscroll (selected_window, make_number (hscroll));
+  
+  if (!NILP (Finteractive_p ()))
+    w->min_hscroll = w->hscroll;
 
-  return Qnil;
+  return result;
 }
 
-
 /* Value is the number of lines actually displayed in window W,
    as opposed to its height.  */
 
@@ -4473,13 +4477,18 @@ displayed_window_lines (w)
   if (old_buffer)
     set_buffer_internal (old_buffer);
 
-  /* Add in empty lines at the bottom of the window.  */
   bottom_y = it.current_y + it.max_ascent + it.max_descent;
+
+  if (bottom_y > it.current_y && bottom_y <= it.last_visible_y)
+    /* Hit a line without a terminating newline.  */
+    it.vpos++;
+
+  /* Add in empty lines at the bottom of the window.  */
   if (bottom_y < height)
     {
       struct frame *f = XFRAME (w->frame);
       int rest = height - bottom_y;
-      int lines = (rest + CANON_Y_UNIT (f) - 1) / CANON_Y_UNIT (f);
+      int lines = rest / CANON_Y_UNIT (f);
       it.vpos += lines;
     }
 
@@ -4487,6 +4496,110 @@ displayed_window_lines (w)
 }
 
 
+DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P",
+  "Center point in window and redisplay frame.\n\
+With prefix argument ARG, recenter putting point on screen line ARG\n\
+relative to the current window.  If ARG is negative, it counts up from the\n\
+bottom of the window.  (ARG should be less than the height of the window.)\n\
+\n\
+If ARG is omitted or nil, erase the entire frame and then\n\
+redraw with point in the center of the current window.\n\
+Just C-u as prefix means put point in the center of the window\n\
+and redisplay normally--don't erase and redraw the frame.")
+  (arg)
+     register Lisp_Object arg;
+{
+  struct window *w = XWINDOW (selected_window);
+  struct buffer *buf = XBUFFER (w->buffer);
+  struct buffer *obuf = current_buffer;
+  int center_p = 0;
+  int charpos, bytepos;
+
+  if (NILP (arg))
+    {
+      int i;
+
+      /* Invalidate pixel data calculated for all compositions.  */
+      for (i = 0; i < n_compositions; i++)
+       composition_table[i]->font = NULL;
+
+      Fredraw_frame (w->frame);
+      SET_FRAME_GARBAGED (XFRAME (WINDOW_FRAME (w)));
+      center_p = 1;
+    }
+  else if (CONSP (arg)) /* Just C-u. */
+    center_p = 1;
+  else
+    {
+      arg = Fprefix_numeric_value (arg);
+      CHECK_NUMBER (arg, 0);
+    }
+
+  set_buffer_internal (buf);
+
+  /* Handle centering on a gfaphical frame specially.  Such frames can
+     have variable-height lines and centering point on the basis of
+     line counts would lead to strange effects.  */
+  if (center_p && FRAME_WINDOW_P (XFRAME (w->frame)))
+    {
+      struct it it;
+      struct text_pos pt;
+      
+      SET_TEXT_POS (pt, PT, PT_BYTE);
+      start_display (&it, w, pt);
+      move_it_vertically (&it, - it.last_visible_y / 2);
+      charpos = IT_CHARPOS (it);
+      bytepos = IT_BYTEPOS (it);
+    }
+  else
+    {
+      struct position pos;
+      
+      if (center_p)
+       {
+         int ht = displayed_window_lines (w);
+         arg = make_number (ht / 2);
+       }
+      else if (XINT (arg) < 0)
+       {
+         int ht = displayed_window_lines (w);
+         XSETINT (arg, XINT (arg) + ht);
+       }
+      
+      pos = *vmotion (PT, - XINT (arg), w);
+      charpos = pos.bufpos;
+      bytepos = pos.bytepos;
+    }
+
+  /* Set the new window start.  */
+  set_marker_both (w->start, w->buffer, charpos, bytepos);
+  w->window_end_valid = Qnil;
+  w->force_start = Qt;
+  if (bytepos == BEGV_BYTE || FETCH_BYTE (bytepos - 1) == '\n')
+    w->start_at_line_beg = Qt;
+  else
+    w->start_at_line_beg = Qnil;
+  
+  set_buffer_internal (obuf);
+  return Qnil;
+}
+
+
+DEFUN ("window-text-height", Fwindow_text_height, Swindow_text_height,
+  0, 1, 0,
+  "Return the height in lines of the text display area of WINDOW.\n\
+This doesn't include the mode-line (or header-line if any) or any\n\
+partial-height lines in the text display area.")
+  (window)
+     Lisp_Object window;
+{
+  struct window *w = decode_window (window);
+  int pixel_height = window_box_height (w);
+  int line_height = pixel_height / CANON_Y_UNIT (XFRAME (w->frame));
+  return make_number (line_height);
+}
+
+
 \f
 DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line,
   1, 1, "P",
@@ -4559,20 +4672,21 @@ struct save_window_data
 
 /* This is saved as a Lisp_Vector  */
 struct saved_window
-  {
-    /* these first two must agree with struct Lisp_Vector in lisp.h */
-    EMACS_INT size_from_Lisp_Vector_struct;
-    struct Lisp_Vector *next_from_Lisp_Vector_struct;
+{
+  /* these first two must agree with struct Lisp_Vector in lisp.h */
+  EMACS_INT size_from_Lisp_Vector_struct;
+  struct Lisp_Vector *next_from_Lisp_Vector_struct;
 
-    Lisp_Object window;
-    Lisp_Object buffer, start, pointm, mark;
-    Lisp_Object left, top, width, height, hscroll;
-    Lisp_Object parent, prev;
-    Lisp_Object start_at_line_beg;
-    Lisp_Object display_table;
-    Lisp_Object orig_top, orig_height;
-  };
-#define SAVED_WINDOW_VECTOR_SIZE 16 /* Arg to Fmake_vector */
+  Lisp_Object window;
+  Lisp_Object buffer, start, pointm, mark;
+  Lisp_Object left, top, width, height, hscroll, min_hscroll;
+  Lisp_Object parent, prev;
+  Lisp_Object start_at_line_beg;
+  Lisp_Object display_table;
+  Lisp_Object orig_top, orig_height;
+};
+
+#define SAVED_WINDOW_VECTOR_SIZE 17 /* Arg to Fmake_vector */
 
 #define SAVED_WINDOW_N(swv,n) \
   ((struct saved_window *) (XVECTOR ((swv)->contents[(n)])))
@@ -4638,7 +4752,7 @@ the return value is nil.  Otherwise the value is t.")
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
   f = XFRAME (frame);
-
+  
   /* If f is a dead frame, don't bother rebuilding its window tree.
      However, there is other stuff we should still try to do below.  */
   if (FRAME_LIVE_P (f))
@@ -4763,6 +4877,7 @@ the return value is nil.  Otherwise the value is t.")
          w->width = p->width;
          w->height = p->height;
          w->hscroll = p->hscroll;
+         w->min_hscroll = p->min_hscroll;
          w->display_table = p->display_table;
          w->orig_top = p->orig_top;
          w->orig_height = p->orig_height;
@@ -4865,7 +4980,6 @@ the return value is nil.  Otherwise the value is t.")
              xassert (NILP (leaf_windows[i]->hchild) 
                       && NILP (leaf_windows[i]->vchild));
              free_window_matrices (leaf_windows[i]);
-             SET_FRAME_GARBAGED (f);
            }
          else if (EQ (leaf_windows[i]->buffer, new_current_buffer))
            ++n;
@@ -5030,6 +5144,7 @@ save_window_save (window, vector, i)
       p->width = w->width;
       p->height = w->height;
       p->hscroll = w->hscroll;
+      p->min_hscroll = w->min_hscroll;
       p->display_table = w->display_table;
       p->orig_top = w->orig_top;
       p->orig_height = w->orig_height;
@@ -5458,6 +5573,8 @@ compare_window_configurations (c1, c2, ignore_positions)
        {
          if (! EQ (p1->hscroll, p2->hscroll))
            return 0;
+         if (!EQ (p1->min_hscroll, p2->min_hscroll))
+           return 0;
          if (! EQ (p1->start_at_line_beg, p2->start_at_line_beg))
            return 0;
          if (NILP (Fequal (p1->start, p2->start)))
@@ -5739,6 +5856,7 @@ The selected frame is the one whose configuration has changed.");
   defsubr (&Sother_window_for_scrolling);
   defsubr (&Sscroll_other_window);
   defsubr (&Srecenter);
+  defsubr (&Swindow_text_height);
   defsubr (&Smove_to_window_line);
   defsubr (&Swindow_configuration_p);
   defsubr (&Swindow_configuration_frame);