]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
(redisplay_window): If window-scroll-functions change
[gnu-emacs] / src / xdisp.c
index b9ef666f869c8fb49f5c65e87316408e7f75b5d0..7fc3425dc0d6dca41bd9c587b52bc99b01ec9bc0 100644 (file)
@@ -1,5 +1,5 @@
 /* Display generation from window structure and buffer text.
-   Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86, 87, 88, 93, 94, 95 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -15,10 +15,11 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
-#include "config.h"
+#include <config.h>
 #include <stdio.h>
 /*#include <ctype.h>*/
 #undef NULL
@@ -34,16 +35,36 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "disptab.h"
 #include "termhooks.h"
 #include "intervals.h"
+#include "keyboard.h"
+
+#if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
+extern void set_frame_menubar ();
+#endif
 
 extern int interrupt_input;
 extern int command_loop_level;
 
-/* Nonzero means print newline before next minibuffer message.  */
+extern Lisp_Object Qface;
+
+extern Lisp_Object Voverriding_local_map;
+extern Lisp_Object Voverriding_local_map_menu_flag;
+
+Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
+Lisp_Object Qwindow_scroll_functions, Vwindow_scroll_functions;
+Lisp_Object Qredisplay_end_trigger_functions;
+
+/* Nonzero means print newline to stdout before next minibuffer message.  */
 
 int noninteractive_need_newline;
 
+/* Nonzero means print newline to message log before next message.  */
+
+static int message_log_need_newline;
+
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #define max(a, b) ((a) > (b) ? (a) : (b))
+#define minmax(floor, val, ceil) \
+       ((val) < (floor) ? (floor) : (val) > (ceil) ? (ceil) : (val))
 
 /* The buffer position of the first character appearing
  entirely or partially on the current frame line.
@@ -65,11 +86,6 @@ static int this_line_start_hpos;
 /* Buffer that this_line variables are describing. */
 static struct buffer *this_line_buffer;
 
-/* Set by try_window_id to the vpos of first of any lines
-   scrolled on to the bottom of the frame.  These lines should
-   not be included in any general scroll computation.  */
-static int scroll_bottom_vpos;
-
 /* Value of echo_area_glyphs when it was last acted on.
   If this is nonzero, there is a message on the frame
   in the minibuffer and it should be erased as soon
@@ -79,6 +95,10 @@ char *previous_echo_glyphs;
 /* Nonzero means truncate lines in all windows less wide than the frame */
 int truncate_partial_width_windows;
 
+/* Nonzero means we have more than one non-minibuffer-only frame.
+   Not guaranteed to be accurate except while parsing frame-title-format.  */
+int multiple_frames;
+
 Lisp_Object Vglobal_mode_string;
 
 /* Marker for where to display an arrow on top of the buffer text.  */
@@ -87,24 +107,43 @@ Lisp_Object Voverlay_arrow_position;
 /* String to display for the arrow.  */
 Lisp_Object Voverlay_arrow_string;
 
+/* Like mode-line-format, but for the titlebar on a visible frame.  */
+Lisp_Object Vframe_title_format;
+
+/* Like mode-line-format, but for the titlebar on an iconified frame.  */
+Lisp_Object Vicon_title_format;
+
+/* List of functions to call when a window's size changes.  These
+   functions get one arg, a frame on which one or more windows' sizes
+   have changed.  */
+static Lisp_Object Vwindow_size_change_functions;
+
 /* Values of those variables at last redisplay.  */
 static Lisp_Object last_arrow_position, last_arrow_string;
 
+Lisp_Object Qmenu_bar_update_hook;
+
 /* Nonzero if overlay arrow has been displayed once in this window.  */
 static int overlay_arrow_seen;
 
+/* Nonzero if visible end of buffer has already been displayed once
+   in this window.  (We need this variable in case there are overlay
+   strings that get displayed there.)  */
+static int zv_strings_seen;
+
 /* Nonzero means highlight the region even in nonselected windows.  */
 static int highlight_nonselected_windows;
 
 /* If cursor motion alone moves point off frame,
    Try scrolling this many lines up or down if that will bring it back.  */
-int scroll_step;
+static int scroll_step;
 
 /* Nonzero if try_window_id has made blank lines at window bottom
  since the last redisplay that paused */
 static int blank_end_of_window;
 
-/* Number of windows showing the buffer of the selected window.
+/* Number of windows showing the buffer of the selected window
+   (or another buffer with the same base buffer).
    keyboard.c refers to this.  */
 int buffer_shared;
 
@@ -115,28 +154,29 @@ int buffer_shared;
 static int cursor_vpos;
 static int cursor_hpos;
 
-int debug_end_pos;
+static int debug_end_pos;
 
 /* Nonzero means display mode line highlighted */
 int mode_line_inverse_video;
 
+static int message_log_check_duplicate ();
 static void echo_area_display ();
 void mark_window_display_accurate ();
 static void redisplay_windows ();
 static void redisplay_window ();
+static void update_menu_bar ();
 static void try_window ();
 static int try_window_id ();
 static struct position *display_text_line ();
 static void display_mode_line ();
 static int display_mode_element ();
-static char *fmodetrunc ();
 static char *decode_mode_spec ();
 static int display_string ();
 static void display_menu_bar ();
 static int display_count_lines ();
 
 /* Prompt to display in front of the minibuffer contents */
-char *minibuf_prompt;
+Lisp_Object minibuf_prompt;
 
 /* Width in columns of current minibuffer prompt.  */
 int minibuf_prompt_width;
@@ -147,6 +187,14 @@ int minibuf_prompt_width;
    It overrides the minibuf_prompt as well as the buffer.  */
 char *echo_area_glyphs;
 
+/* This is the length of the message in echo_area_glyphs.  */
+int echo_area_glyphs_length;
+
+/* This is the window where the echo area message was displayed.
+   It is always a minibuffer window, but it may not be the
+   same window currently active as a minibuffer.  */
+Lisp_Object echo_area_window;
+
 /* true iff we should redraw the mode lines on the next redisplay */
 int update_mode_lines;
 
@@ -165,10 +213,6 @@ int end_unchanged;
    contain no useful information */
 int unchanged_modified;
 
-/* Nonzero if head_clip or tail_clip of current buffer has changed
-   since last redisplay that finished */
-int clip_changed;
-
 /* Nonzero if window sizes or contents have changed
    since last redisplay that finished */
 int windows_or_buffers_changed;
@@ -178,23 +222,185 @@ int windows_or_buffers_changed;
 int line_number_displayed;
 
 /* Maximum buffer size for which to display line numbers.  */
-int line_number_display_limit;
+static int line_number_display_limit;
+
+/* Number of lines to keep in the message log buffer.
+   t means infinite.  nil means don't log at all.  */
+Lisp_Object Vmessage_log_max;
 \f
-/* Specify m, a string, as a message in the minibuf.  If m is 0, clear out
-   any existing message, and let the minibuffer text show through.  */
+/* Output a newline in the *Messages* buffer if "needs" one.  */
+
 void
-message1 (m)
+message_log_maybe_newline ()
+{
+  if (message_log_need_newline)
+    message_dolog ("", 0, 1);
+}
+
+
+/* Add a string to the message log, optionally terminated with a newline.
+   This function calls low-level routines in order to bypass text property
+   hooks, etc. which might not be safe to run.  */
+
+void
+message_dolog (m, len, nlflag)
+     char *m;
+     int len, nlflag;
+{
+  if (!NILP (Vmessage_log_max))
+    {
+      struct buffer *oldbuf;
+      int oldpoint, oldbegv, oldzv;
+      int old_windows_or_buffers_changed = windows_or_buffers_changed;
+
+      oldbuf = current_buffer;
+      Fset_buffer (Fget_buffer_create (build_string ("*Messages*")));
+      current_buffer->undo_list = Qt;
+      oldpoint = PT;
+      oldbegv = BEGV;
+      oldzv = ZV;
+      BEGV = BEG;
+      ZV = Z;
+      if (oldpoint == Z)
+       oldpoint += len + nlflag;
+      if (oldzv == Z)
+       oldzv += len + nlflag;
+      TEMP_SET_PT (Z);
+      if (len)
+       insert_1 (m, len, 1, 0);
+      if (nlflag)
+       {
+         int this_bol, prev_bol, dup;
+         insert_1 ("\n", 1, 1, 0);
+
+         this_bol = scan_buffer ('\n', Z, 0, -2, 0, 0);
+         if (this_bol > BEG)
+           {
+             prev_bol = scan_buffer ('\n', this_bol, 0, -2, 0, 0);
+             dup = message_log_check_duplicate (prev_bol, this_bol);
+             if (dup)
+               {
+                 if (oldpoint > prev_bol)
+                   oldpoint -= min (this_bol, oldpoint) - prev_bol;
+                 if (oldbegv > prev_bol)
+                   oldbegv -= min (this_bol, oldbegv) - prev_bol;
+                 if (oldzv > prev_bol)
+                   oldzv -= min (this_bol, oldzv) - prev_bol;
+                 del_range_1 (prev_bol, this_bol, 0);
+                 if (dup > 1)
+                   {
+                     char dupstr[40];
+                     int duplen;
+
+                     /* If you change this format, don't forget to also
+                        change message_log_check_duplicate.  */
+                     sprintf (dupstr, " [%d times]", dup);
+                     duplen = strlen (dupstr);
+                     TEMP_SET_PT (Z-1);
+                     if (oldpoint == Z)
+                       oldpoint += duplen;
+                     if (oldzv == Z)
+                       oldzv += duplen;
+                     insert_1 (dupstr, duplen, 1, 0);
+                   }
+               }
+           }
+
+         if (NATNUMP (Vmessage_log_max))
+           {
+             int pos = scan_buffer ('\n', Z, 0,
+                                    -XFASTINT (Vmessage_log_max) - 1, 0, 0);
+             oldpoint -= min (pos, oldpoint) - BEG;
+             oldbegv -= min (pos, oldbegv) - BEG;
+             oldzv -= min (pos, oldzv) - BEG;
+             del_range_1 (BEG, pos, 0);
+           }
+       }
+      BEGV = oldbegv;
+      ZV = oldzv;
+      TEMP_SET_PT (oldpoint);
+      set_buffer_internal (oldbuf);
+      windows_or_buffers_changed = old_windows_or_buffers_changed;
+      message_log_need_newline = !nlflag;
+    }
+}
+
+/* We are at the end of the buffer after just having inserted a newline.
+   (Note: We depend on the fact we won't be crossing the gap.)
+   Check to see if the most recent message looks a lot like the previous one.
+   Return 0 if different, 1 if the new one should just replace it, or a
+   value N > 1 if we should also append " [N times]".  */
+
+static int
+message_log_check_duplicate (prev_bol, this_bol)
+     int prev_bol, this_bol;
+{
+  int i;
+  int len = Z - 1 - this_bol;
+  int seen_dots = 0;
+  unsigned char *p1 = BUF_CHAR_ADDRESS (current_buffer, prev_bol);
+  unsigned char *p2 = BUF_CHAR_ADDRESS (current_buffer, this_bol);
+
+  for (i = 0; i < len; i++)
+    {
+      if (i >= 3 && p1[i-3] == '.' && p1[i-2] == '.' && p1[i-1] == '.'
+         && p1[i] != '\n')
+       seen_dots = 1;
+      if (p1[i] != p2[i])
+       return seen_dots;
+    }
+  p1 += len;
+  if (*p1 == '\n')
+    return 2;
+  if (*p1++ == ' ' && *p1++ == '[')
+    {
+      int n = 0;
+      while (*p1 >= '0' && *p1 <= '9')
+       n = n * 10 + *p1++ - '0';
+      if (strncmp (p1, " times]\n", 8) == 0)
+       return n+1;
+    }
+  return 0;
+}
+\f
+/* Display an echo area message M with a specified length of LEN chars.
+   The string may include null characters.  If M is 0, clear out any
+   existing message, and let the minibuffer text show through.
+
+   The buffer M must continue to exist until after the echo area
+   gets cleared or some other message gets displayed there.
+
+   Do not pass text that is stored in a Lisp string.
+   Do not pass text in a buffer that was alloca'd.  */
+
+void
+message2 (m, len)
+     char *m;
+     int len;
+{
+  /* First flush out any partial line written with print.  */
+  message_log_maybe_newline ();
+  if (m)
+    message_dolog (m, len, 1);
+  message2_nolog (m, len);
+}
+
+
+/* The non-logging counterpart of message2.  */
+
+void
+message2_nolog (m, len)
      char *m;
+     int len;
 {
   if (noninteractive)
     {
       if (noninteractive_need_newline)
        putc ('\n', stderr);
       noninteractive_need_newline = 0;
-      if (cursor_in_echo_area != 0)
-       fprintf (stderr, "%s", m);
-      else
-       fprintf (stderr, "%s\n", m);
+      fwrite (m, len, 1, stderr);
+      if (cursor_in_echo_area == 0)
+       fprintf (stderr, "\n");
       fflush (stderr);
     }
   /* A null message buffer means that the frame hasn't really been
@@ -202,39 +408,87 @@ message1 (m)
      cmd_error, so this must be just an informative message; toss it.  */
   else if (INTERACTIVE && FRAME_MESSAGE_BUF (selected_frame))
     {
-#ifdef MULTI_FRAME
-      Lisp_Object minibuf_frame;
+      Lisp_Object mini_window;
+      FRAME_PTR f;
+
+      /* Get the frame containing the minibuffer
+        that the selected frame is using.  */
+      mini_window = FRAME_MINIBUF_WINDOW (selected_frame);
+      f = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
 
-      choose_minibuf_frame ();
-      minibuf_frame = WINDOW_FRAME (XWINDOW (minibuf_window));
-      FRAME_SAMPLE_VISIBILITY (XFRAME (minibuf_frame));
+#ifdef MULTI_FRAME
+      FRAME_SAMPLE_VISIBILITY (f);
       if (FRAME_VISIBLE_P (selected_frame)
-         && ! FRAME_VISIBLE_P (XFRAME (minibuf_frame)))
-       Fmake_frame_visible (WINDOW_FRAME (XWINDOW (minibuf_window)));
+         && ! FRAME_VISIBLE_P (f))
+       Fmake_frame_visible (WINDOW_FRAME (XWINDOW (mini_window)));
 #endif
 
       if (m)
-       echo_area_glyphs = m;
+       {
+         echo_area_glyphs = m;
+         echo_area_glyphs_length = len;
+       }
       else
        echo_area_glyphs = previous_echo_glyphs = 0;
 
       do_pending_window_change ();
       echo_area_display ();
-      update_frame (XFRAME (XWINDOW (minibuf_window)->frame), 1, 1);
+      update_frame (f, 1, 1);
       do_pending_window_change ();
+      if (frame_up_to_date_hook != 0 && ! gc_in_progress)
+       (*frame_up_to_date_hook) (f);
     }
 }
+\f
+/* Display a null-terminated echo area message M.  If M is 0, clear out any
+   existing message, and let the minibuffer text show through.
+
+   The buffer M must continue to exist until after the echo area
+   gets cleared or some other message gets displayed there.
+
+   Do not pass text that is stored in a Lisp string.
+   Do not pass text in a buffer that was alloca'd.  */
+
+void
+message1 (m)
+     char *m;
+{
+  message2 (m, (m ? strlen (m) : 0));
+}
+
+void
+message1_nolog (m)
+     char *m;
+{
+  message2_nolog (m, (m ? strlen (m) : 0));
+}
+
+/* Truncate what will be displayed in the echo area
+   the next time we display it--but don't redisplay it now.  */
+
+void
+truncate_echo_area (len)
+     int len;
+{
+  /* A null message buffer means that the frame hasn't really been
+     initialized yet.  Error messages get reported properly by
+     cmd_error, so this must be just an informative message; toss it.  */
+  if (!noninteractive && INTERACTIVE && FRAME_MESSAGE_BUF (selected_frame))
+    echo_area_glyphs_length = len;
+}
 
 /* Nonzero if FRAME_MESSAGE_BUF (selected_frame) is being used by print;
    zero if being used by message.  */
 int message_buf_print;
 
-/* Dump an informative message to the minibuf.  If m is 0, clear out
+/* Dump an informative message to the minibuf.  If M is 0, clear out
    any existing message, and let the minibuffer text show through.  */
+
 /* VARARGS 1 */
 void
 message (m, a1, a2, a3)
      char *m;
+     EMACS_INT a1, a2, a3;
 {
   if (noninteractive)
     {
@@ -254,37 +508,36 @@ message (m, a1, a2, a3)
       /* The frame whose minibuffer we're going to display the message on.
         It may be larger than the selected frame, so we need
         to use its buffer, not the selected frame's buffer.  */
-      FRAME_PTR echo_frame;
-#ifdef MULTI_FRAME
-      choose_minibuf_frame ();
-      echo_frame = XFRAME (WINDOW_FRAME (XWINDOW (minibuf_window)));
-#else
-      echo_frame = selected_frame;
-#endif
+      Lisp_Object mini_window;
+      FRAME_PTR f;
+
+      /* Get the frame containing the minibuffer
+        that the selected frame is using.  */
+      mini_window = FRAME_MINIBUF_WINDOW (selected_frame);
+      f = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
 
       /* A null message buffer means that the frame hasn't really been
         initialized yet.  Error messages get reported properly by
         cmd_error, so this must be just an informative message; toss it.  */
-      if (FRAME_MESSAGE_BUF (echo_frame))
+      if (FRAME_MESSAGE_BUF (f))
        {
          if (m)
            {
-             {
+             int len;
 #ifdef NO_ARG_ARRAY
-               int a[3];
-               a[0] = a1;
-               a[1] = a2;
-               a[2] = a3;
+             EMACS_INT a[3];
+             a[0] = a1;
+             a[1] = a2;
+             a[2] = a3;
 
-               doprnt (FRAME_MESSAGE_BUF (echo_frame),
-                       FRAME_WIDTH (echo_frame), m, 0, 3, a);
+             len = doprnt (FRAME_MESSAGE_BUF (f),
+                           FRAME_WIDTH (f), m, (char *)0, 3, a);
 #else
-               doprnt (FRAME_MESSAGE_BUF (echo_frame),
-                       FRAME_WIDTH (echo_frame), m, 0, 3, &a1);
+             len = doprnt (FRAME_MESSAGE_BUF (f),
+                           FRAME_WIDTH (f), m, (char *)0, 3, &a1);
 #endif /* NO_ARG_ARRAY */
-             }
 
-             message1 (FRAME_MESSAGE_BUF (echo_frame));
+             message2 (FRAME_MESSAGE_BUF (f), len);
            }
          else
            message1 (0);
@@ -296,17 +549,37 @@ message (m, a1, a2, a3)
     }
 }
 
+/* The non-logging version of message.  */
+void
+message_nolog (m, a1, a2, a3)
+     char *m;
+     EMACS_INT a1, a2, a3;
+{
+  Lisp_Object old_log_max;
+  old_log_max = Vmessage_log_max;
+  Vmessage_log_max = Qnil;
+  message (m, a1, a2, a3);
+  Vmessage_log_max = old_log_max;
+}
+
+void
+update_echo_area ()
+{
+  message2 (echo_area_glyphs, echo_area_glyphs_length);
+}
+\f
 static void
 echo_area_display ()
 {
   register int vpos;
   FRAME_PTR f;
+  Lisp_Object mini_window;
 
-#ifdef MULTI_FRAME
-  choose_minibuf_frame ();
-#endif
-
-  f = XFRAME (WINDOW_FRAME (XWINDOW (minibuf_window)));
+  /* Choose the minibuffer window for this display.
+     It is the minibuffer window used by the selected frame.  */
+  mini_window = FRAME_MINIBUF_WINDOW (selected_frame);
+  /* This is the frame that window is in.  */
+  f = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
 
   if (! FRAME_VISIBLE_P (f))
     return;
@@ -319,38 +592,192 @@ echo_area_display ()
 
   if (echo_area_glyphs || minibuf_level == 0)
     {
-      vpos = XFASTINT (XWINDOW (minibuf_window)->top);
+      echo_area_window = mini_window;
+
+      vpos = XFASTINT (XWINDOW (mini_window)->top);
       get_display_line (f, vpos, 0);
-      display_string (XWINDOW (minibuf_window), vpos,
+      display_string (XWINDOW (mini_window), vpos,
                      echo_area_glyphs ? echo_area_glyphs : "",
-                     0, 0, 0, FRAME_WIDTH (f));
+                     echo_area_glyphs ? echo_area_glyphs_length : -1,
+                     0, 0, 0, 0, FRAME_WIDTH (f));
 
+#if 0 /* This just gets in the way.  update_frame does the job.  */
       /* If desired cursor location is on this line, put it at end of text */
+      if (cursor_in_echo_area)
+       FRAME_CURSOR_Y (f) = vpos;
       if (FRAME_CURSOR_Y (f) == vpos)
        FRAME_CURSOR_X (f) = FRAME_DESIRED_GLYPHS (f)->used[vpos];
+#endif
 
       /* Fill the rest of the minibuffer window with blank lines.  */
       {
        int i;
 
        for (i = vpos + 1;
-            i < vpos + XFASTINT (XWINDOW (minibuf_window)->height); i++)
+            i < vpos + XFASTINT (XWINDOW (mini_window)->height); i++)
          {
            get_display_line (f, i, 0);
-           display_string (XWINDOW (minibuf_window), vpos,
-                           "", 0, 0, 0, FRAME_WIDTH (f));
+           display_string (XWINDOW (mini_window), vpos,
+                           "", 0, 0, 0, 0, 0, FRAME_WIDTH (f));
          }
       }
     }
-  else if (!EQ (minibuf_window, selected_window))
+  else if (!EQ (mini_window, selected_window))
     windows_or_buffers_changed++;
 
-  if (EQ (minibuf_window, selected_window))
+  if (EQ (mini_window, selected_window))
     this_line_bufpos = 0;
 
   previous_echo_glyphs = echo_area_glyphs;
 }
 \f
+/* Update frame titles.  */
+
+#ifdef HAVE_WINDOW_SYSTEM
+static char frame_title_buf[512];
+static char *frame_title_ptr;
+
+static int
+store_frame_title (str, mincol, maxcol)
+     char *str;
+     int mincol, maxcol;
+{
+  char *limit;
+  if (maxcol < 0 || maxcol >= sizeof(frame_title_buf))
+    maxcol = sizeof (frame_title_buf);
+  limit = &frame_title_buf[maxcol];
+  while (*str != '\0' && frame_title_ptr < limit)
+    *frame_title_ptr++ = *str++;
+  while (frame_title_ptr < &frame_title_buf[mincol])
+    *frame_title_ptr++ = ' ';
+  return frame_title_ptr - frame_title_buf;
+}
+
+static void
+x_consider_frame_title (frame)
+     Lisp_Object frame;
+{
+  Lisp_Object fmt;
+  struct buffer *obuf;
+  int len;
+  FRAME_PTR f = XFRAME (frame);
+
+  if (!(FRAME_WINDOW_P (f) || FRAME_MINIBUF_ONLY_P (f) || f->explicit_name))
+    return;
+
+  /* Do we have more than one visible frame on this X display?  */
+  {
+    Lisp_Object tail;
+
+    for (tail = Vframe_list; CONSP (tail); tail = XCONS (tail)->cdr)
+      {
+       FRAME_PTR tf = XFRAME (XCONS (tail)->car);
+
+       if (tf != f && FRAME_KBOARD (tf) == FRAME_KBOARD (f)
+           && !FRAME_MINIBUF_ONLY_P (tf)
+           && (FRAME_VISIBLE_P (tf) || FRAME_ICONIFIED_P (tf)))
+         break;
+      }
+
+    multiple_frames = CONSP (tail);
+  }
+
+  obuf = current_buffer;
+  Fset_buffer (XWINDOW (f->selected_window)->buffer);
+  fmt = (FRAME_ICONIFIED_P (f) ? Vicon_title_format : Vframe_title_format);
+  frame_title_ptr = frame_title_buf;
+  len = display_mode_element (XWINDOW (f->selected_window), 0, 0, 0,
+                             0, sizeof (frame_title_buf), fmt);
+  frame_title_ptr = 0;
+  set_buffer_internal (obuf);
+  /* Set the name only if it's changed.  This avoids consing
+     in the common case where it hasn't.  (If it turns out that we've
+     already wasted too much time by walking through the list with
+     display_mode_element, then we might need to optimize at a higher
+     level than this.)  */
+  if (! STRINGP (f->name) || XSTRING (f->name)->size != len
+      || bcmp (frame_title_buf, XSTRING (f->name)->data, len) != 0)
+    x_implicitly_set_name (f, make_string (frame_title_buf, len), Qnil);
+}
+#else
+#define frame_title_ptr ((char *)0)
+#define store_frame_title(str, mincol, maxcol) 0
+#endif
+\f
+/* Prepare for redisplay by updating menu-bar item lists when appropriate.
+   This can call eval.  */
+
+void
+prepare_menu_bars ()
+{
+  register struct window *w = XWINDOW (selected_window);
+  int all_windows;
+  struct gcpro gcpro1, gcpro2;
+
+  all_windows = (update_mode_lines || buffer_shared > 1
+                || windows_or_buffers_changed);
+
+  /* Update all frame titles based on their buffer names, etc.
+     We do this before the menu bars so that the buffer-menu
+     will show the up-to-date frame titles.
+
+     This used to be done after the menu bars, for a reason that
+     was stated as follows but which I do not understand:
+     "We do this after the menu bars so that the frame will first
+     create its menu bar using the name `emacs' if no other name
+     has yet been specified."
+     I think that is no longer a concern.  */
+#ifdef HAVE_WINDOW_SYSTEM
+  if (windows_or_buffers_changed || update_mode_lines)
+    {
+      Lisp_Object tail, frame;
+
+      FOR_EACH_FRAME (tail, frame)
+       if (FRAME_VISIBLE_P (XFRAME (frame))
+           || FRAME_ICONIFIED_P (XFRAME (frame)))
+         x_consider_frame_title (frame);
+    }
+#endif
+
+  /* Update the menu bar item lists, if appropriate.
+     This has to be done before any actual redisplay
+     or generation of display lines.  */
+  if (all_windows)
+    {
+      Lisp_Object tail, frame;
+      int count = specpdl_ptr - specpdl;
+
+      record_unwind_protect (Fstore_match_data, Fmatch_data ());
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         /* If a window on this frame changed size,
+            report that to the user and clear the size-change flag.  */
+         if (FRAME_WINDOW_SIZES_CHANGED (XFRAME (frame)))
+           {
+             Lisp_Object functions;
+             /* Clear flag first in case we get error below.  */
+             FRAME_WINDOW_SIZES_CHANGED (XFRAME (frame)) = 0;
+             functions = Vwindow_size_change_functions;
+             GCPRO2 (tail, functions);
+             while (CONSP (functions))
+               {
+                 call1 (XCONS (functions)->car, frame);
+                 functions = XCONS (functions)->cdr;
+               }
+             UNGCPRO;
+           }
+         GCPRO1 (tail);
+         update_menu_bar (XFRAME (frame), 0);
+         UNGCPRO;
+       }
+
+      unbind_to (count, Qnil);
+    }
+  else
+    update_menu_bar (selected_frame, 1);
+}
+\f
 /* Do a frame update, taking possible shortcuts into account.
    This is the main external entry point for redisplay.
 
@@ -358,14 +785,26 @@ echo_area_display ()
    message is no longer requested, we clear the echo area
    or bring back the minibuffer if that is in use.
 
-   Everyone would like to have a hook here to call eval,
-   but that cannot be done safely without a lot of changes elsewhere.
-   This can be called from signal handlers; with alarms set up;
+   Do not call eval from within this function.
+   Calls to eval after the call to echo_area_display would confuse
+   the display_line mechanism and would cause a crash.
+   Calls to eval before that point will work most of the time,
+   but can still lose, because  this function
+   can be called from signal handlers; with alarms set up;
    or with synchronous processes running.
-   See the function `echo' in keyboard.c.
+
    See Fcall_process; if you called it from here, it could be
    entered recursively.  */
 
+static int do_verify_charstarts;
+
+/* Counter is used to clear the face cache
+   no more than once ever 1000 redisplays.  */
+static int clear_face_cache_count;
+
+/* Record the previous terminal frame we displayed.  */
+static FRAME_PTR previous_terminal_frame;
+
 void
 redisplay ()
 {
@@ -375,11 +814,28 @@ redisplay ()
   int all_windows;
   register int tlbufpos, tlendpos;
   struct position pos;
-  extern int input_pending;
 
   if (noninteractive)
     return;
 
+#ifdef USE_X_TOOLKIT
+  if (popup_activated ())
+    return;
+#endif
+
+#ifdef MULTI_FRAME
+  if (FRAME_TERMCAP_P (selected_frame)
+      && previous_terminal_frame != selected_frame)
+    {
+      /* Since frames on an ASCII terminal share the same display area,
+        displaying a different frame means redisplay the whole thing.  */
+      windows_or_buffers_changed++;
+      SET_FRAME_GARBAGED (selected_frame);
+      XSETFRAME (Vterminal_frame, selected_frame);
+    }
+  previous_terminal_frame = selected_frame;
+#endif
+
   /* Set the visible flags for all frames.
      Do this before checking for resized or garbaged frames; they want
      to know if their frames are visible.
@@ -388,7 +844,13 @@ redisplay ()
     Lisp_Object tail, frame;
 
     FOR_EACH_FRAME (tail, frame)
-      FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
+      {
+       FRAME_SAMPLE_VISIBILITY (XFRAME (frame));
+
+       /* Clear out all the display lines in which we will generate the
+          glyphs to display.  */
+       init_desired_glyphs (XFRAME (frame));
+      }
   }
 
   /* Notice any pending interrupt request to change frame size.  */
@@ -400,28 +862,29 @@ redisplay ()
       frame_garbaged = 0;
     }
 
-  /* Normally the message* functions will have already displayed and
-     updated the echo area, but the frame may have been trashed, or
-     the update may have been preempted, so display the echo area
-     again here.  */
-  if (echo_area_glyphs || previous_echo_glyphs)
-    {
-      echo_area_display ();
-      must_finish = 1;
-    }
+  prepare_menu_bars ();
 
-  if (clip_changed || windows_or_buffers_changed)
+  if (windows_or_buffers_changed)
     update_mode_lines++;
 
   /* Detect case that we need to write a star in the mode line.  */
   if (XFASTINT (w->last_modified) < MODIFF
-      && XFASTINT (w->last_modified) <= current_buffer->save_modified)
+      && XFASTINT (w->last_modified) <= SAVE_MODIFF)
     {
       w->update_mode_line = Qt;
       if (buffer_shared > 1)
        update_mode_lines++;
     }
 
+  /* If %c is in use, update it if needed.  */
+  if (!NILP (w->column_number_displayed)
+      /* This alternative quickly identifies a common case
+        where no change is needed.  */
+      && !(PT == XFASTINT (w->last_point)
+          && XFASTINT (w->last_modified) >= MODIFF)
+      && XFASTINT (w->column_number_displayed) != current_column ())
+    w->update_mode_line = Qt; 
+
   FRAME_SCROLL_BOTTOM_VPOS (XFRAME (w->frame)) = -1;
 
   all_windows = update_mode_lines || buffer_shared > 1;
@@ -430,7 +893,17 @@ redisplay ()
      to ensure we remove any arrow that should no longer exist.  */
   if (! EQ (Voverlay_arrow_position, last_arrow_position)
       || ! EQ (Voverlay_arrow_string, last_arrow_string))
-    all_windows = 1, clip_changed = 1;
+    all_windows = 1;
+
+  /* Normally the message* functions will have already displayed and
+     updated the echo area, but the frame may have been trashed, or
+     the update may have been preempted, so display the echo area
+     again here.  */
+  if (echo_area_glyphs || previous_echo_glyphs)
+    {
+      echo_area_display ();
+      must_finish = 1;
+    }
 
   /* If showing region, and mark has changed, must redisplay whole window.  */
   if (((!NILP (Vtransient_mark_mode)
@@ -444,14 +917,15 @@ redisplay ()
   tlbufpos = this_line_bufpos;
   tlendpos = this_line_endpos;
   if (!all_windows && tlbufpos > 0 && NILP (w->update_mode_line)
+      && !current_buffer->clip_changed
       && FRAME_VISIBLE_P (XFRAME (w->frame))
       /* Make sure recorded data applies to current buffer, etc */
       && this_line_buffer == current_buffer
       && current_buffer == XBUFFER (w->buffer)
       && NILP (w->force_start)
       /* Point must be on the line that we have info recorded about */
-      && point >= tlbufpos
-      && point <= Z - tlendpos
+      && PT >= tlbufpos
+      && PT <= Z - tlendpos
       /* All text outside that line, including its final newline,
         must be unchanged */
       && (XFASTINT (w->last_modified) >= MODIFF
@@ -459,7 +933,7 @@ redisplay ()
              && GPT >= tlbufpos
              /* If selective display, can't optimize
                 if the changes start at the beginning of the line.  */
-             && ((XTYPE (current_buffer->selective_display) == Lisp_Int
+             && ((INTEGERP (current_buffer->selective_display)
                   && XINT (current_buffer->selective_display) > 0
                   ? (beg_unchanged >= tlbufpos
                      && GPT > tlbufpos)
@@ -477,6 +951,7 @@ redisplay ()
        {
          cursor_vpos = -1;
          overlay_arrow_seen = 0;
+         zv_strings_seen = 0;
          display_text_line (w, tlbufpos, this_line_vpos, this_line_start_hpos,
                             pos_tab_offset (w, tlbufpos));
          /* If line contains point, is not continued,
@@ -484,6 +959,29 @@ redisplay ()
          if (cursor_vpos >= 0 && this_line_bufpos
              && this_line_endpos == tlendpos)
            {
+             /* If this is not the window's last line,
+                we must adjust the charstarts of the lines below.  */
+             if (this_line_vpos + 1
+                 < XFASTINT (w->top) + window_internal_height (w))
+               {
+                 int left = XFASTINT (w->left);
+                 int *charstart_next_line
+                   = FRAME_CURRENT_GLYPHS (XFRAME (WINDOW_FRAME (w)))->charstarts[this_line_vpos + 1];
+                 int adjust;
+
+                 if (Z - tlendpos == ZV)
+                   /* This line ends at end of (accessible part of) buffer.
+                      There is no newline to count.  */
+                   adjust = Z - tlendpos - charstart_next_line[left];
+                 else
+                   /* This line ends in a newline.
+                      Must take account of the newline and the rest of the
+                      text that follows.  */
+                   adjust = Z - tlendpos + 1 - charstart_next_line[left];
+
+                 adjust_window_charstarts (w, this_line_vpos, adjust);
+               }
+
              if (XFASTINT (w->width) != FRAME_WIDTH (XFRAME (WINDOW_FRAME (w))))
                preserve_other_columns (w);
              goto update;
@@ -491,7 +989,12 @@ redisplay ()
          else
            goto cancel;
        }
-      else if (point == XFASTINT (w->last_point))
+      else if (PT == XFASTINT (w->last_point)
+              /* Make sure the cursor was last displayed
+                 in this window.  Otherwise we have to reposition it.  */
+              && XINT (w->top) <= FRAME_CURSOR_Y (selected_frame)
+              && (XINT (w->top) + XINT (w->height)
+                  > FRAME_CURSOR_Y (selected_frame)))
        {
          if (!must_finish)
            {
@@ -500,21 +1003,25 @@ redisplay ()
            }
          goto update;
        }
-      /* If highlighting the region, we can't just move the cursor.  */
+      /* If highlighting the region, or if the cursor is in the echo area,
+        then we can't just move the cursor.  */
       else if (! (!NILP (Vtransient_mark_mode)
                  && !NILP (current_buffer->mark_active))
-              && NILP (w->region_showing))
+              && NILP (w->region_showing)
+              && !cursor_in_echo_area)
        {
          pos = *compute_motion (tlbufpos, 0,
                                 XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0,
-                                point, 2, - (1 << (SHORTBITS - 1)),
+                                0,
+                                PT, 2, - (1 << (BITS_PER_SHORT - 1)),
                                 window_internal_width (w) - 1,
                                 XINT (w->hscroll),
-                                pos_tab_offset (w, tlbufpos));
+                                pos_tab_offset (w, tlbufpos), w);
          if (pos.vpos < 1)
            {
+             int width = window_internal_width (w) - 1;
              FRAME_CURSOR_X (selected_frame)
-               = XFASTINT (w->left) + max (pos.hpos, 0);
+               = XFASTINT (w->left) + minmax (0, pos.hpos, width);
              FRAME_CURSOR_Y (selected_frame) = this_line_vpos;
              goto update;
            }
@@ -529,14 +1036,20 @@ redisplay ()
   this_line_bufpos = 0;
   all_windows |= buffer_shared > 1;
 
+  clear_face_cache_count++;
+
   if (all_windows)
     {
       Lisp_Object tail, frame;
 
-#ifdef HAVE_X_WINDOWS
-      /* Since we're doing a thorough redisplay, we might as well
-        recompute all our display faces.  */
-      clear_face_vector ();
+#ifdef HAVE_FACES
+      /* Clear the face cache, only when we do a full redisplay
+        and not too often either.  */
+      if (clear_face_cache_count > 1000)
+       {
+         clear_face_cache ();
+         clear_face_cache_count = 0;
+       }
 #endif
 
       /* Recompute # windows showing selected buffer.
@@ -546,19 +1059,22 @@ redisplay ()
       FOR_EACH_FRAME (tail, frame)
        {
          FRAME_PTR f = XFRAME (frame);
+         if (! FRAME_TERMCAP_P (f) || f == selected_frame)
+           {
 
-         /* Mark all the scroll bars to be removed; we'll redeem the ones
-            we want when we redisplay their windows.  */
-         if (condemn_scroll_bars_hook)
-           (*condemn_scroll_bars_hook) (f);
+             /* Mark all the scroll bars to be removed; we'll redeem the ones
+                we want when we redisplay their windows.  */
+             if (condemn_scroll_bars_hook)
+               (*condemn_scroll_bars_hook) (f);
 
-         if (FRAME_VISIBLE_P (f))
-           redisplay_windows (FRAME_ROOT_WINDOW (f));
+             if (FRAME_VISIBLE_P (f))
+               redisplay_windows (FRAME_ROOT_WINDOW (f));
 
-         /* Any scroll bars which redisplay_windows should have nuked
-            should now go away.  */
-         if (judge_scroll_bars_hook)
-           (*judge_scroll_bars_hook) (f);
+             /* Any scroll bars which redisplay_windows should have nuked
+                should now go away.  */
+             if (judge_scroll_bars_hook)
+               (*judge_scroll_bars_hook) (f);
+           }
        }
     }
   else if (FRAME_VISIBLE_P (selected_frame))
@@ -587,15 +1103,21 @@ update:
        {
          FRAME_PTR f;
 
-         if (XTYPE (XCONS (tail)->car) != Lisp_Frame)
+         if (!FRAMEP (XCONS (tail)->car))
            continue;
 
          f = XFRAME (XCONS (tail)->car);
-         if (FRAME_VISIBLE_P (f))
+
+         if ((! FRAME_TERMCAP_P (f) || f == selected_frame)
+             && FRAME_VISIBLE_P (f))
            {
              pause |= update_frame (f, 0, 0);
              if (!pause)
-               mark_window_display_accurate (f->root_window, 1);
+               {
+                 mark_window_display_accurate (f->root_window, 1);
+                 if (frame_up_to_date_hook != 0)
+                   (*frame_up_to_date_hook) (f);
+               }
            }
        }
     }
@@ -604,6 +1126,8 @@ update:
     {
       if (FRAME_VISIBLE_P (selected_frame))
        pause = update_frame (selected_frame, 0, 0);
+      else
+       pause = 0;
 
       /* We may have called echo_area_display at the top of this
         function.  If the echo area is on another frame, that may
@@ -611,10 +1135,14 @@ update:
         above call to update_frame would not have caught it.  Catch
         it here.  */
       {
-       FRAME_PTR mini_frame =
-         XFRAME (WINDOW_FRAME (XWINDOW (minibuf_window)));
+       Lisp_Object mini_window;
+       FRAME_PTR mini_frame;
+
+       mini_window = FRAME_MINIBUF_WINDOW (selected_frame);
+       mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window)));
        
-       if (mini_frame != selected_frame)
+       if (mini_frame != selected_frame
+           && ! FRAME_TERMCAP_P (mini_frame))
          pause |= update_frame (mini_frame, 0, 0);
       }
     }
@@ -645,24 +1173,28 @@ update:
       register struct buffer *b = XBUFFER (w->buffer);
 
       blank_end_of_window = 0;
-      clip_changed = 0;
       unchanged_modified = BUF_MODIFF (b);
       beg_unchanged = BUF_GPT (b) - BUF_BEG (b);
       end_unchanged = BUF_Z (b) - BUF_GPT (b);
 
-      XFASTINT (w->last_point) = BUF_PT (b);
-      XFASTINT (w->last_point_x) = FRAME_CURSOR_X (selected_frame);
-      XFASTINT (w->last_point_y) = FRAME_CURSOR_Y (selected_frame);
+      XSETFASTINT (w->last_point, BUF_PT (b));
+      XSETFASTINT (w->last_point_x, FRAME_CURSOR_X (selected_frame));
+      XSETFASTINT (w->last_point_y, FRAME_CURSOR_Y (selected_frame));
 
       if (all_windows)
        mark_window_display_accurate (FRAME_ROOT_WINDOW (selected_frame), 1);
       else
        {
+         b->clip_changed = 0;
          w->update_mode_line = Qnil;
-         XFASTINT (w->last_modified) = BUF_MODIFF (b);
-         w->window_end_valid = Qt;
+         XSETFASTINT (w->last_modified, BUF_MODIFF (b));
+         w->window_end_valid = w->buffer;
          last_arrow_position = Voverlay_arrow_position;
          last_arrow_string = Voverlay_arrow_string;
+         if (do_verify_charstarts)
+           verify_charstarts (w);
+         if (frame_up_to_date_hook != 0)
+           (*frame_up_to_date_hook) (selected_frame);
        }
       update_mode_lines = 0;
       windows_or_buffers_changed = 0;
@@ -681,6 +1213,11 @@ update:
 
   /* Change frame size now if a change is pending.  */
   do_pending_window_change ();
+
+  /* If we just did a pending size change, redisplay again
+     for the new size.  */
+  if (windows_or_buffers_changed && !pause)
+    redisplay ();
 }
 
 /* Redisplay, but leave alone any recent echo area message
@@ -712,15 +1249,13 @@ mark_window_display_accurate (window, flag)
 
   for (;!NILP (window); window = w->next)
     {
-      if (XTYPE (window) != Lisp_Window) abort ();
+      if (!WINDOWP (window)) abort ();
       w = XWINDOW (window);
 
       if (!NILP (w->buffer))
        {
-         XFASTINT (w->last_modified)
-           = !flag ? 0
-             : XBUFFER (w->buffer) == current_buffer
-               ? MODIFF : BUF_MODIFF (XBUFFER (w->buffer));
+         XSETFASTINT (w->last_modified,
+                      !flag ? 0 : BUF_MODIFF (XBUFFER (w->buffer)));
 
          /* Record if we are showing a region, so can make sure to
             update it fully at next redisplay.  */
@@ -730,8 +1265,10 @@ mark_window_display_accurate (window, flag)
                               : Qnil);
        }
 
-      w->window_end_valid = Qt;
+      w->window_end_valid = w->buffer;
       w->update_mode_line = Qnil;
+      if (!NILP (w->buffer) && flag)
+       XBUFFER (w->buffer)->clip_changed = 0;
 
       if (!NILP (w->vchild))
        mark_window_display_accurate (w->vchild, flag);
@@ -752,8 +1289,87 @@ mark_window_display_accurate (window, flag)
     }
 }
 \f
+/* Update the menu bar item list for frame F.
+   This has to be done before we start to fill in any display lines,
+   because it can call eval.
+
+   If SAVE_MATCH_DATA is 1, we must save and restore it here.  */
+
+static void
+update_menu_bar (f, save_match_data)
+     FRAME_PTR f;
+     int save_match_data;
+{
+  struct buffer *old = current_buffer;
+  Lisp_Object window;
+  register struct window *w;
+
+  window = FRAME_SELECTED_WINDOW (f);
+  w = XWINDOW (window);
+  
+  if (update_mode_lines)
+    w->update_mode_line = Qt;
+
+  if (FRAME_WINDOW_P (f)
+      ?
+#if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
+      FRAME_EXTERNAL_MENU_BAR (f) 
+#else
+      FRAME_MENU_BAR_LINES (f) > 0
+#endif
+      : FRAME_MENU_BAR_LINES (f) > 0)
+    {
+      /* If the user has switched buffers or windows, we need to
+        recompute to reflect the new bindings.  But we'll
+        recompute when update_mode_lines is set too; that means
+        that people can use force-mode-line-update to request
+        that the menu bar be recomputed.  The adverse effect on
+        the rest of the redisplay algorithm is about the same as
+        windows_or_buffers_changed anyway.  */
+      if (windows_or_buffers_changed
+         || !NILP (w->update_mode_line)
+         || (XFASTINT (w->last_modified) < MODIFF
+             && (XFASTINT (w->last_modified)
+                 <= BUF_SAVE_MODIFF (XBUFFER (w->buffer))))
+         || ((!NILP (Vtransient_mark_mode)
+              && !NILP (XBUFFER (w->buffer)->mark_active))
+             != !NILP (w->region_showing)))
+       {
+         struct buffer *prev = current_buffer;
+         int count = specpdl_ptr - specpdl;
+
+         set_buffer_internal_1 (XBUFFER (w->buffer));
+         if (save_match_data)
+           record_unwind_protect (Fstore_match_data, Fmatch_data ());
+         if (NILP (Voverriding_local_map_menu_flag))
+           {
+             specbind (Qoverriding_terminal_local_map, Qnil);
+             specbind (Qoverriding_local_map, Qnil);
+           }
+
+         /* Run the Lucid hook.  */
+         call1 (Vrun_hooks, Qactivate_menubar_hook);
+         /* If it has changed current-menubar from previous value,
+            really recompute the menubar from the value.  */
+         if (! NILP (Vlucid_menu_bar_dirty_flag))
+           call0 (Qrecompute_lucid_menubar);
+         safe_run_hooks (Qmenu_bar_update_hook);
+         FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+#if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
+         if (FRAME_WINDOW_P (f))
+           set_frame_menubar (f, 0, 0);
+#endif /* USE_X_TOOLKIT || HAVE_NTGUI */
+
+         unbind_to (count, Qnil);
+         set_buffer_internal_1 (prev);
+       }
+    }
+}
+\f
 int do_id = 1;
 
+/* Redisplay WINDOW and its subwindows and siblings.  */
+
 static void
 redisplay_windows (window)
      Lisp_Object window;
@@ -762,6 +1378,8 @@ redisplay_windows (window)
     redisplay_window (window, 0);
 }
 
+/* Redisplay window WINDOW and its subwindows.  */
+
 static void
 redisplay_window (window, just_this_one)
      Lisp_Object window;
@@ -770,15 +1388,16 @@ redisplay_window (window, just_this_one)
   register struct window *w = XWINDOW (window);
   FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
   int height;
-  register int lpoint = point;
+  register int lpoint = PT;
   struct buffer *old = current_buffer;
   register int width = window_internal_width (w) - 1;
   register int startp;
   register int hscroll = XINT (w->hscroll);
   struct position pos;
-  int opoint = point;
+  int opoint = PT;
   int tem;
-  int window_needs_modeline;
+  int update_mode_line;
+  struct Lisp_Char_Table *dp = window_display_table (w);
 
   if (FRAME_HEIGHT (f) == 0) abort (); /* Some bug zeros some core */
 
@@ -798,62 +1417,103 @@ redisplay_window (window, just_this_one)
     abort ();
   
   height = window_internal_height (w);
+  update_mode_line = (!NILP (w->update_mode_line) || update_mode_lines);
+  if (XBUFFER (w->buffer)->clip_changed)
+    update_mode_line = 1;
 
   if (MINI_WINDOW_P (w))
     {
-      if (w == XWINDOW (minibuf_window))
-       {
-         if (echo_area_glyphs)
-           /* We've already displayed the echo area glyphs, if any.  */
-           goto finish_scroll_bars;
-       }
-      else
+      if (w == XWINDOW (echo_area_window) && echo_area_glyphs)
+       /* We've already displayed the echo area glyphs in this window.  */
+       goto finish_scroll_bars;
+      else if (w != XWINDOW (minibuf_window))
        {
-         /* This is a minibuffer, but it's not the currently active one, so
-            clear it.  */
-         int vpos = XFASTINT (XWINDOW (FRAME_MINIBUF_WINDOW (f))->top);
+         /* This is a minibuffer, but it's not the currently active one,
+            so clear it.  */
+         int vpos = XFASTINT (w->top);
          int i;
 
          for (i = 0; i < height; i++)
            {
              get_display_line (f, vpos + i, 0);
-             display_string (w, vpos + i, "", 0, 0, 0, width);
+             display_string (w, vpos + i, "", 0, 0, 0, 1, 0, width);
            }
          
          goto finish_scroll_bars;
        }
     }
 
-  if (update_mode_lines)
-    w->update_mode_line = Qt;
-
   /* Otherwise set up data on this window; select its buffer and point value */
 
-  current_buffer = XBUFFER (w->buffer);
-  opoint = point;
+  if (update_mode_line)
+    set_buffer_internal_1 (XBUFFER (w->buffer));
+  else
+    set_buffer_temp (XBUFFER (w->buffer));
 
-  /* Count number of windows showing the selected buffer.  */
+  opoint = PT;
 
-  if (!just_this_one
-      && current_buffer == XBUFFER (XWINDOW (selected_window)->buffer))
-    buffer_shared++;
+  /* If %c is in mode line, update it if needed.  */
+  if (!NILP (w->column_number_displayed)
+      /* This alternative quickly identifies a common case
+        where no change is needed.  */
+      && !(PT == XFASTINT (w->last_point)
+          && XFASTINT (w->last_modified) >= MODIFF)
+      && XFASTINT (w->column_number_displayed) != current_column ())
+    update_mode_line = 1; 
 
-  /* POINT refers normally to the selected window.
-     For any other window, set up appropriate value.  */
+  /* Count number of windows showing the selected buffer.
+     An indirect buffer counts as its base buffer.  */
 
-  if (!EQ (window, selected_window))
+  if (!just_this_one)
     {
-      SET_PT (marker_position (w->pointm));
-      if (point < BEGV)
+      struct buffer *current_base, *window_base;
+      current_base = current_buffer;
+      window_base = XBUFFER (XWINDOW (selected_window)->buffer);
+      if (current_base->base_buffer)
+       current_base = current_base->base_buffer;
+      if (window_base->base_buffer)
+       window_base = window_base->base_buffer;
+      if (current_base == window_base)
+       buffer_shared++;
+    }
+
+  /* POINT refers normally to the selected window.
+     For any other window, set up appropriate value.  */
+
+  if (!EQ (window, selected_window))
+    {
+      int new_pt = marker_position (w->pointm);
+      if (new_pt < BEGV)
        {
-         SET_PT (BEGV);
-         Fset_marker (w->pointm, make_number (point), Qnil);
+         new_pt = BEGV;
+         Fset_marker (w->pointm, make_number (new_pt), Qnil);
        }
-      else if (point > (ZV - 1))
+      else if (new_pt > (ZV - 1))
        {
-         SET_PT (ZV);
-         Fset_marker (w->pointm, make_number (point), Qnil);
+         new_pt = ZV;
+         Fset_marker (w->pointm, make_number (new_pt), Qnil);
        }
+      /* We don't use SET_PT so that the point-motion hooks don't run.  */
+      BUF_PT (current_buffer) = new_pt;
+    }
+
+  /* If any of the character widths specified in the display table
+     have changed, invalidate the width run cache.  It's true that this
+     may be a bit late to catch such changes, but the rest of
+     redisplay goes (non-fatally) haywire when the display table is
+     changed, so why should we worry about doing any better?  */
+  if (current_buffer->width_run_cache)
+    {
+      struct Lisp_Char_Table *disptab = buffer_display_table ();
+
+      if (! disptab_matches_widthtab (disptab,
+                                      XVECTOR (current_buffer->width_table)))
+        {
+          invalidate_region_cache (current_buffer,
+                                   current_buffer->width_run_cache,
+                                   BEG, Z);
+          recompute_width_table (current_buffer, disptab);
+        }
     }
 
   /* If window-start is screwed up, choose a new one.  */
@@ -866,41 +1526,73 @@ redisplay_window (window, just_this_one)
      unless the specified location is outside the accessible range.  */
   if (!NILP (w->force_start))
     {
+      w->force_start = Qnil;
       /* Forget any recorded base line for line number display.  */
       w->base_line_number = Qnil;
-      w->update_mode_line = Qt;
-      w->force_start = Qnil;
-      XFASTINT (w->last_modified) = 0;
+      /* Redisplay the mode line.  Select the buffer properly for that.
+        Also, run the hook window-scroll-functions
+        because we have scrolled.  */
+      /* Note, we do this after clearing force_start because
+        if there's an error, it is better to forget about force_start
+        than to get into an infinite loop calling the hook functions
+        and having them get more errors.  */
+      if (!update_mode_line
+         || ! NILP (Vwindow_scroll_functions))
+       {
+         Lisp_Object temp[3];
+
+         set_buffer_temp (old);
+         set_buffer_internal_1 (XBUFFER (w->buffer));
+         update_mode_line = 1;
+         w->update_mode_line = Qt;
+         if (! NILP (Vwindow_scroll_functions))
+           {
+             run_hook_with_args_2 (Qwindow_scroll_functions, window,
+                                   make_number (startp));
+             startp = marker_position (w->start);
+           }
+       }
+      XSETFASTINT (w->last_modified, 0);
       if (startp < BEGV) startp = BEGV;
       if (startp > ZV)   startp = ZV;
       try_window (window, startp);
       if (cursor_vpos < 0)
        {
-         /* ??? What should happen here if highlighting a region?  */
          /* If point does not appear, move point so it does appear */
          pos = *compute_motion (startp, 0,
-                               ((EQ (window, minibuf_window) && startp == 1)
-                                ? minibuf_prompt_width : 0)
-                               +
-                               (hscroll ? 1 - hscroll : 0),
-                               ZV, height / 2,
-                               - (1 << (SHORTBITS - 1)),
-                               width, hscroll, pos_tab_offset (w, startp));
-         SET_PT (pos.bufpos);
-         if (w != XWINDOW (FRAME_SELECTED_WINDOW (f)))
-           Fset_marker (w->pointm, make_number (point), Qnil);
+                                (((EQ (window, minibuf_window)
+                                   && startp == BEG)
+                                  ? minibuf_prompt_width : 0)
+                                 + (hscroll ? 1 - hscroll : 0)),
+                                0,
+                                ZV, height / 2,
+                                - (1 << (BITS_PER_SHORT - 1)),
+                                width, hscroll, pos_tab_offset (w, startp), w);
+         BUF_PT (current_buffer) = pos.bufpos;
+         if (w != XWINDOW (selected_window))
+           Fset_marker (w->pointm, make_number (PT), Qnil);
          else
            {
-             lpoint = point;
-             FRAME_CURSOR_X (f) = max (0, pos.hpos) + XFASTINT (w->left);
+             if (current_buffer == old)
+               lpoint = PT;
+             FRAME_CURSOR_X (f) = (XFASTINT (w->left)
+                                   + minmax (0, pos.hpos, width));
              FRAME_CURSOR_Y (f) = pos.vpos + XFASTINT (w->top);
            }
+         /* If we are highlighting the region,
+            then we just changed the region, so redisplay to show it.  */
+         if (!NILP (Vtransient_mark_mode)
+             && !NILP (current_buffer->mark_active))
+           {
+             cancel_my_columns (XWINDOW (window));
+             try_window (window, startp);
+           }
        }
       goto done;
     }
 
   /* Handle case where text has not changed, only point,
-     and it has not moved off the frame */
+     and it has not moved off the frame */
 
   /* This code is not used for minibuffer for the sake of
      the case of redisplaying to replace an echo area message;
@@ -910,16 +1602,23 @@ redisplay_window (window, just_this_one)
      in redisplay handles the same cases.  */
 
   if (XFASTINT (w->last_modified) >= MODIFF
-      && point >= startp && !clip_changed
+      && PT >= startp && !current_buffer->clip_changed
       && (just_this_one || XFASTINT (w->width) == FRAME_WIDTH (f))
+      /* If force-mode-line-update was called, really redisplay;
+        that's how redisplay is forced after e.g. changing
+        buffer-invisibility-spec.  */
+      && NILP (w->update_mode_line)
       /* Can't use this case if highlighting a region.  */
       && !(!NILP (Vtransient_mark_mode) && !NILP (current_buffer->mark_active))
       && NILP (w->region_showing)
+      /* If end pos is out of date, scroll bar and percentage will be wrong */
+      && INTEGERP (w->window_end_vpos)
+      && XFASTINT (w->window_end_vpos) < XFASTINT (w->height)
       && !EQ (window, minibuf_window))
     {
-      pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0),
-                           point, height + 1, 10000, width, hscroll,
-                           pos_tab_offset (w, startp));
+      pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0), 0,
+                           PT, height, 0, width, hscroll,
+                           pos_tab_offset (w, startp), w);
 
       if (pos.vpos < height)
        {
@@ -927,7 +1626,8 @@ redisplay_window (window, just_this_one)
          if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)))
            {
              /* These variables are supposed to be origin 1 */
-             FRAME_CURSOR_X (f) = max (0, pos.hpos) + XFASTINT (w->left);
+             FRAME_CURSOR_X (f) = (XFASTINT (w->left)
+                                   + minmax (0, pos.hpos, width));
              FRAME_CURSOR_Y (f) = pos.vpos + XFASTINT (w->top);
            }
          /* This doesn't do the trick, because if a window to the right of
@@ -945,23 +1645,26 @@ redisplay_window (window, just_this_one)
   /* If current starting point was originally the beginning of a line
      but no longer is, find a new starting point.  */
   else if (!NILP (w->start_at_line_beg)
-          && !(startp == BEGV
+          && !(startp <= BEGV
                || FETCH_CHAR (startp - 1) == '\n'))
     {
       goto recenter;
     }
   else if (just_this_one && !MINI_WINDOW_P (w)
-          && point >= startp
+          && PT >= startp
           && XFASTINT (w->last_modified)
           /* or else vmotion on first line won't work.  */
           && ! NILP (w->start_at_line_beg)
           && ! EQ (w->window_end_valid, Qnil)
-          && do_id && !clip_changed
+          && do_id && !current_buffer->clip_changed
           && !blank_end_of_window
           && XFASTINT (w->width) == FRAME_WIDTH (f)
           /* Can't use this case if highlighting a region.  */
           && !(!NILP (Vtransient_mark_mode)
                && !NILP (current_buffer->mark_active))
+          /* Don't use try_window_id if newline
+             doesn't display as the end of a line.  */
+          && !(dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, '\n')))
           && NILP (w->region_showing)
           && EQ (last_arrow_position, Voverlay_arrow_position)
           && EQ (last_arrow_string, Voverlay_arrow_string)
@@ -985,7 +1688,8 @@ redisplay_window (window, just_this_one)
       try_window (window, startp);
       if (cursor_vpos >= 0)
        {
-         if (!just_this_one || clip_changed || beg_unchanged < startp)
+         if (!just_this_one || current_buffer->clip_changed
+             || beg_unchanged < startp)
            /* Forget any recorded base line for line number display.  */
            w->base_line_number = Qnil;
          goto done;
@@ -994,30 +1698,43 @@ redisplay_window (window, just_this_one)
        cancel_my_columns (w);
     }
 
-  XFASTINT (w->last_modified) = 0;
-  w->update_mode_line = Qt;
+  XSETFASTINT (w->last_modified, 0);
+  /* Redisplay the mode line.  Select the buffer properly for that.  */
+  if (!update_mode_line)
+    {
+      set_buffer_temp (old);
+      set_buffer_internal_1 (XBUFFER (w->buffer));
+      update_mode_line = 1;
+      w->update_mode_line = Qt;
+    }
 
   /* Try to scroll by specified few lines */
 
-  if (scroll_step && !clip_changed)
+  if (scroll_step && !current_buffer->clip_changed
+      && startp >= BEGV && startp <= ZV)
     {
-      if (point > startp)
+      if (PT > startp)
        {
-         pos = *vmotion (Z - XFASTINT (w->window_end_pos),
-                         scroll_step, width, hscroll, window);
+         pos = *vmotion (Z - XFASTINT (w->window_end_pos), scroll_step, w);
          if (pos.vpos >= height)
            goto scroll_fail;
        }
 
-      pos = *vmotion (startp, point < startp ? - scroll_step : scroll_step,
-                     width, hscroll, window);
+      pos = *vmotion (startp, (PT < startp ? - scroll_step : scroll_step), w);
 
-      if (point >= pos.bufpos)
+      if (PT >= pos.bufpos)
        {
+         if (! NILP (Vwindow_scroll_functions))
+           {
+             run_hook_with_args_2 (Qwindow_scroll_functions, window,
+                                   make_number (pos.bufpos));
+             pos.bufpos = marker_position (w->start);
+           }
          try_window (window, pos.bufpos);
          if (cursor_vpos >= 0)
            {
-             if (!just_this_one || clip_changed || beg_unchanged < startp)
+             if (!just_this_one || current_buffer->clip_changed
+                 || beg_unchanged < startp)
                /* Forget any recorded base line for line number display.  */
                w->base_line_number = Qnil;
              goto done;
@@ -1034,19 +1751,30 @@ recenter:
   /* Forget any previously recorded base line for line number display.  */
   w->base_line_number = Qnil;
 
-  pos = *vmotion (point, - height / 2, width, hscroll, window);
+  pos = *vmotion (PT, - (height / 2), w);
+  /* Set startp here explicitly in case that helps avoid an infinite loop
+     in case the window-scroll-functions functions get errors.  */
+  Fset_marker (w->start, make_number (pos.bufpos), Qnil);
+  if (! NILP (Vwindow_scroll_functions))
+    {
+      run_hook_with_args_2 (Qwindow_scroll_functions, window,
+                           make_number (pos.bufpos));
+      pos.bufpos = marker_position (w->start);
+    }
   try_window (window, pos.bufpos);
 
   startp = marker_position (w->start);
-  w->start_at_line_beg = 
-    (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
+  w->start_at_line_beg
+    (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
 
 done:
-  if ((!NILP (w->update_mode_line)
+  if ((update_mode_line
        /* If window not full width, must redo its mode line
          if the window to its side is being redone */
        || (!just_this_one && width < FRAME_WIDTH (f) - 1)
-       || INTEGERP (w->base_line_pos))
+       || INTEGERP (w->base_line_pos)
+       || (!NILP (w->column_number_displayed)
+          && XFASTINT (w->column_number_displayed) != current_column ()))
       && height != XFASTINT (w->height))
     display_mode_line (w);
   if (! line_number_displayed
@@ -1057,8 +1785,15 @@ done:
     }
 
   /* When we reach a frame's selected window, redo the frame's menu bar.  */
-  if (!NILP (w->update_mode_line)
-      && FRAME_MENU_BAR_LINES (f) > 0
+  if (update_mode_line
+      && (FRAME_WINDOW_P (f)
+         ?
+#if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
+         FRAME_EXTERNAL_MENU_BAR (f) 
+#else
+         FRAME_MENU_BAR_LINES (f) > 0
+#endif
+         : FRAME_MENU_BAR_LINES (f) > 0)
       && EQ (FRAME_SELECTED_WINDOW (f), window))
     display_menu_bar (w);
 
@@ -1078,7 +1813,7 @@ done:
          || (w == XWINDOW (minibuf_window) && ! echo_area_glyphs))
        {
          whole = ZV - BEGV;
-         start = startp - BEGV;
+         start = marker_position (w->start) - BEGV;
          /* I don't think this is guaranteed to be right.  For the
             moment, we'll pretend it is.  */
          end = (Z - XINT (w->window_end_pos)) - BEGV;
@@ -1097,9 +1832,12 @@ done:
       (*redeem_scroll_bar_hook) (w);
     }
 
-  SET_PT (opoint);
-  current_buffer = old;
-  SET_PT (lpoint);
+  BUF_PT (current_buffer) = opoint;
+  if (update_mode_line)
+    set_buffer_internal_1 (old);
+  else
+    set_buffer_temp (old);
+  BUF_PT (current_buffer) = lpoint;
 }
 \f
 /* Do full redisplay on one window, starting at position `pos'. */
@@ -1121,21 +1859,37 @@ try_window (window, pos)
   Fset_marker (w->start, make_number (pos), Qnil);
   cursor_vpos = -1;
   overlay_arrow_seen = 0;
+  zv_strings_seen = 0;
   val.hpos = XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0;
 
   while (--height >= 0)
     {
       val = *display_text_line (w, pos, vpos, val.hpos, tab_offset);
       tab_offset += width;
+      /* For the first line displayed, display_text_line
+        subtracts the prompt width from the tab offset.
+        But it does not affect the value of our variable tab_offset.
+        So we do the subtraction again,
+        for the sake of continuation lines of that first line.  */
+      if (MINI_WINDOW_P (w) && vpos == XFASTINT (w->top))
+       tab_offset -= minibuf_prompt_width;
+
       if (val.vpos) tab_offset = 0;
       vpos++;
       if (pos != val.bufpos)
-       last_text_vpos
-         /* Next line, unless prev line ended in end of buffer with no cr */
-         = vpos - (val.vpos && (FETCH_CHAR (val.bufpos - 1) != '\n'
-                                || ! NILP (Fget_text_property (val.bufpos-1,
-                                                               Qinvisible,
-                                                               Fcurrent_buffer ()))));
+       {
+         int invis = 0;
+#ifdef USE_TEXT_PROPERTIES
+         Lisp_Object invis_prop;
+         invis_prop = Fget_char_property (val.bufpos-1, Qinvisible, window);
+         invis = TEXT_PROP_MEANS_INVISIBLE (invis_prop);
+#endif
+
+         last_text_vpos
+           /* Next line, unless prev line ended in end of buffer with no cr */
+           = vpos - (val.vpos
+                     && (FETCH_CHAR (val.bufpos - 1) != '\n' || invis));
+       }
       pos = val.bufpos;
     }
 
@@ -1150,8 +1904,8 @@ try_window (window, pos)
     w->update_mode_line = Qt;
 
   /* Say where last char on frame will be, once redisplay is finished.  */
-  XFASTINT (w->window_end_pos) = Z - pos;
-  XFASTINT (w->window_end_vpos) = last_text_vpos - XFASTINT (w->top);
+  XSETFASTINT (w->window_end_pos, Z - pos);
+  XSETFASTINT (w->window_end_vpos, last_text_vpos - XFASTINT (w->top));
   /* But that is not valid info until redisplay finishes.  */
   w->window_end_valid = Qnil;
 }
@@ -1176,42 +1930,46 @@ try_window_id (window)
   int width = window_internal_width (w) - 1;
   int hscroll = XINT (w->hscroll);
   int lmargin = hscroll > 0 ? 1 - hscroll : 0;
+  int did_motion;
   register int vpos;
   register int i, tem;
   int last_text_vpos = 0;
   int stop_vpos;
+  int selective = (INTEGERP (current_buffer->selective_display)
+                  ? XINT (current_buffer->selective_display)
+                  : !NILP (current_buffer->selective_display) ? -1 : 0);
 
   struct position val, bp, ep, xp, pp;
   int scroll_amount = 0;
   int delta;
-  int tab_offset, epto;
+  int tab_offset, epto, old_tick;
 
   if (GPT - BEG < beg_unchanged)
     beg_unchanged = GPT - BEG;
   if (Z - GPT < end_unchanged)
     end_unchanged = Z - GPT;
 
-  if (beg_unchanged + 1 < start)
+  if (beg_unchanged + BEG < start)
     return 0;                  /* Give up if changes go above top of window */
 
   /* Find position before which nothing is changed.  */
-  bp = *compute_motion (start, 0, lmargin,
-                       beg_unchanged + 1, height + 1, 0, width, hscroll,
-                       pos_tab_offset (w, start));
+  bp = *compute_motion (start, 0, lmargin, 0,
+                       min (ZV, beg_unchanged + BEG), height, 0,
+                       width, hscroll, pos_tab_offset (w, start), w);
   if (bp.vpos >= height)
     {
-      if (point < bp.bufpos && !bp.contin)
+      if (PT < bp.bufpos)
        {
-         /* All changes are below the frame, and point is on the frame.
-            We don't need to change the frame at all.
+         /* All changes are beyond the window end, and point is on the screen.
+            We don't need to change the text at all.
             But we need to update window_end_pos to account for
             any change in buffer size.  */
-         bp = *compute_motion (start, 0, lmargin,
-                               Z, height, 0,
-                               width, hscroll, pos_tab_offset (w, start));
-         XFASTINT (w->window_end_vpos) = height;
-         XFASTINT (w->window_end_pos) = Z - bp.bufpos;
-         return 1;
+         bp = *compute_motion (start, 0, lmargin, 0,
+                               ZV, height, 0,
+                               width, hscroll, pos_tab_offset (w, start), w);
+         XSETFASTINT (w->window_end_vpos, height);
+         XSETFASTINT (w->window_end_pos, Z - bp.bufpos);
+         goto findpoint;
        }
       return 0;
     }
@@ -1219,24 +1977,23 @@ try_window_id (window)
   vpos = bp.vpos;
 
   /* Find beginning of that frame line.  Must display from there.  */
-  bp = *vmotion (bp.bufpos, 0, width, hscroll, window);
+  bp = *vmotion (bp.bufpos, 0, w);
 
   pos = bp.bufpos;
   val.hpos = lmargin;
   if (pos < start)
     return -1;
 
+  did_motion = 0;
   /* If about to start displaying at the beginning of a continuation line,
      really start with previous frame line, in case it was not
      continued when last redisplayed */
   if ((bp.contin && bp.bufpos - 1 == beg_unchanged && vpos > 0)
       ||
       /* Likewise if we have to worry about selective display.  */
-      (XTYPE (current_buffer->selective_display) == Lisp_Int
-       && XINT (current_buffer->selective_display) > 0
-       && bp.bufpos - 1 == beg_unchanged && vpos > 0))
+      (selective > 0 && bp.bufpos - 1 == beg_unchanged && vpos > 0))
     {
-      bp = *vmotion (bp.bufpos, -1, width, hscroll, window);
+      bp = *vmotion (bp.bufpos, -1, w);
       --vpos;
       pos = bp.bufpos;
     }
@@ -1244,6 +2001,7 @@ try_window_id (window)
   if (bp.contin && bp.hpos != lmargin)
     {
       val.hpos = bp.prevhpos - width + lmargin;
+      did_motion = 1;
       pos--;
     }
 
@@ -1251,17 +2009,14 @@ try_window_id (window)
 
   /* Find first visible newline after which no more is changed.  */
   tem = find_next_newline (Z - max (end_unchanged, Z - ZV), 1);
-  if (XTYPE (current_buffer->selective_display) == Lisp_Int
-      && XINT (current_buffer->selective_display) > 0)
-    while (tem < ZV - 1
-          && (position_indentation (tem)
-              >= XINT (current_buffer->selective_display)))
+  if (selective > 0)
+    while (tem < ZV - 1 && (indented_beyond_p (tem, selective)))
       tem = find_next_newline (tem, 1);
 
   /* Compute the cursor position after that newline.  */
-  ep = *compute_motion (pos, vpos, val.hpos, tem,
-                       height, - (1 << (SHORTBITS - 1)),
-                       width, hscroll, pos_tab_offset (w, bp.bufpos));
+  ep = *compute_motion (pos, vpos, val.hpos, did_motion, tem,
+                       height, - (1 << (BITS_PER_SHORT - 1)),
+                       width, hscroll, pos_tab_offset (w, bp.bufpos), w);
 
   /* If changes reach past the text available on the frame,
      just display rest of frame.  */
@@ -1283,6 +2038,7 @@ try_window_id (window)
 
   cursor_vpos = -1;
   overlay_arrow_seen = 0;
+  zv_strings_seen = 0;
 
   /* If changes do not reach to bottom of window,
      figure out how much to scroll the rest of the window */
@@ -1290,9 +2046,9 @@ try_window_id (window)
     {
       /* Now determine how far up or down the rest of the window has moved */
       epto = pos_tab_offset (w, ep.bufpos);
-      xp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos,
+      xp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos, 1,
                            Z - XFASTINT (w->window_end_pos),
-                           10000, 0, width, hscroll, epto);
+                           10000, 0, width, hscroll, epto, w);
       scroll_amount = xp.vpos - XFASTINT (w->window_end_vpos);
 
       /* Is everything on frame below the changes whitespace?
@@ -1306,27 +2062,29 @@ try_window_id (window)
       if (i == xp.bufpos)
        return -2;
 
-      XFASTINT (w->window_end_vpos) += scroll_amount;
+      XSETFASTINT (w->window_end_vpos,
+                  XFASTINT (w->window_end_vpos) + scroll_amount);
 
       /* Before doing any scrolling, verify that point will be on frame. */
-      if (point > ep.bufpos && !(point <= xp.bufpos && xp.bufpos < height))
+      if (PT > ep.bufpos && !(PT <= xp.bufpos && xp.bufpos < height))
        {
-         if (point <= xp.bufpos)
+         if (PT <= xp.bufpos)
            {
-             pp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos,
-                                   point, height, - (1 << (SHORTBITS - 1)),
-                                   width, hscroll, epto);
+             pp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos, 1,
+                                   PT, height, - (1 << (BITS_PER_SHORT - 1)),
+                                   width, hscroll, epto, w);
            }
          else
            {
-             pp = *compute_motion (xp.bufpos, xp.vpos, xp.hpos,
-                                   point, height, - (1 << (SHORTBITS - 1)),
-                                   width, hscroll, pos_tab_offset (w, xp.bufpos));
+             pp = *compute_motion (xp.bufpos, xp.vpos, xp.hpos, 1,
+                                   PT, height, - (1 << (BITS_PER_SHORT - 1)),
+                                   width, hscroll,
+                                   pos_tab_offset (w, xp.bufpos), w);
            }
-         if (pp.bufpos < point || pp.vpos == height)
+         if (pp.bufpos < PT || pp.vpos == height)
            return 0;
          cursor_vpos = pp.vpos + top;
-         cursor_hpos = pp.hpos + XFASTINT (w->left);
+         cursor_hpos = XFASTINT (w->left) + minmax (0, pp.hpos, width);
        }
 
       if (stop_vpos - scroll_amount >= height
@@ -1344,7 +2102,14 @@ try_window_id (window)
          blank_end_of_window = 1;
        }
       else if (!scroll_amount)
-       {}
+       {
+         /* Even if we don't need to scroll, we must adjust the
+            charstarts of subsequent lines (that we won't redisplay)
+            according to the amount of text inserted or deleted.  */
+         int oldpos = FRAME_CURRENT_GLYPHS (f)->charstarts[ep.vpos + top][0];
+         int adjust = ep.bufpos - oldpos;
+         adjust_window_charstarts (w, ep.vpos + top - 1, adjust);
+       }
       else if (bp.bufpos == Z - end_unchanged)
        {
          /* If reprinting everything is nearly as fast as scrolling,
@@ -1361,9 +2126,26 @@ try_window_id (window)
             following line from being overwritten by scrolling
             and therefore having to be redrawn.  */
          tem = scroll_frame_lines (f, bp.vpos + top - scroll_amount,
-                                    top + height - max (0, scroll_amount),
-                                    scroll_amount);
-         if (!tem) stop_vpos = height;
+                                   top + height - max (0, scroll_amount),
+                                   scroll_amount, bp.bufpos);
+         if (!tem)
+           stop_vpos = height;
+         else
+           {
+             /* scroll_frame_lines did not properly adjust subsequent
+                lines' charstarts in the case where the text of the
+                screen line at bp.vpos has changed.
+                (This can happen in a deletion that ends in mid-line.)
+                To adjust properly, we need to make things consistent
+                at the position ep.
+                So do a second adjust to make that happen.
+                Note that stop_vpos >= ep.vpos, so it is sufficient
+                to update the charstarts for lines at ep.vpos and below.  */
+             int oldstart
+               = FRAME_CURRENT_GLYPHS (f)->charstarts[ep.vpos + top][0];
+             adjust_window_charstarts (w, ep.vpos + top - 1,
+                                       ep.bufpos - oldstart);
+           }
        }
       else if (scroll_amount)
        {
@@ -1381,7 +2163,7 @@ try_window_id (window)
            return -2;
          tem = scroll_frame_lines (f, ep.vpos + top - scroll_amount,
                                     top + height - max (0, scroll_amount),
-                                    scroll_amount);
+                                    scroll_amount, ep.bufpos);
          if (!tem) stop_vpos = height;
        }
     }
@@ -1405,9 +2187,15 @@ try_window_id (window)
      to account for passing the line that that character really starts in.  */
   if (val.hpos < lmargin)
     tab_offset += width;
+  old_tick = MODIFF;
   while (vpos < stop_vpos)
     {
       val = *display_text_line (w, pos, top + vpos++, val.hpos, tab_offset);
+      /* If display_text_line ran a hook and changed some text,
+        redisplay all the way to bottom of buffer
+        So that we show the changes.  */
+      if (old_tick != MODIFF)
+       stop_vpos = height;
       tab_offset += width;
       if (val.vpos) tab_offset = 0;
       if (pos != val.bufpos)
@@ -1427,8 +2215,8 @@ try_window_id (window)
         include the split character in the text considered on the frame */
       if (val.hpos < lmargin)
        val.bufpos++;
-      XFASTINT (w->window_end_vpos) = last_text_vpos;
-      XFASTINT (w->window_end_pos) = Z - val.bufpos;
+      XSETFASTINT (w->window_end_vpos, last_text_vpos);
+      XSETFASTINT (w->window_end_pos, Z - val.bufpos);
     }
 
   /* If scrolling made blank lines at window bottom,
@@ -1467,7 +2255,7 @@ try_window_id (window)
       /* Here is a case where display_text_line sets cursor_vpos wrong.
         Make it be fixed up, below.  */
       if (xp.bufpos == ZV
-         && xp.bufpos == point)
+         && xp.bufpos == PT)
        cursor_vpos = -1;
     }
 
@@ -1484,10 +2272,10 @@ try_window_id (window)
          || (delta > 0 && xp.bufpos <= ZV)
          || (delta == 0 && xp.hpos))
        {
-         val = *vmotion (Z - XFASTINT (w->window_end_pos),
-                         delta, width, hscroll, window);
-         XFASTINT (w->window_end_pos) = Z - val.bufpos;
-         XFASTINT (w->window_end_vpos) += val.vpos;
+         val = *vmotion (Z - XFASTINT (w->window_end_pos), delta, w);
+         XSETFASTINT (w->window_end_pos, Z - val.bufpos);
+         XSETFASTINT (w->window_end_vpos,
+                      XFASTINT (w->window_end_vpos) + val.vpos);
        }
     }
 
@@ -1496,8 +2284,9 @@ try_window_id (window)
   /* If point was not in a line that was displayed, find it */
   if (cursor_vpos < 0)
     {
-      val = *compute_motion (start, 0, lmargin, point, 10000, 10000,
-                            width, hscroll, pos_tab_offset (w, start));
+    findpoint:
+      val = *compute_motion (start, 0, lmargin, 0, PT, 10000, 10000,
+                            width, hscroll, pos_tab_offset (w, start), w);
       /* Admit failure if point is off frame now */
       if (val.vpos >= height)
        {
@@ -1506,17 +2295,17 @@ try_window_id (window)
          return 0;
        }
       cursor_vpos = val.vpos + top;
-      cursor_hpos = val.hpos + XFASTINT (w->left);
+      cursor_hpos = XFASTINT (w->left) + minmax (0, val.hpos, width);
     }
 
-  FRAME_CURSOR_X (f) = max (0, cursor_hpos);
+  FRAME_CURSOR_X (f) = cursor_hpos;
   FRAME_CURSOR_Y (f) = cursor_vpos;
 
   if (debug_end_pos)
     {
-      val = *compute_motion (start, 0, lmargin, ZV,
-                            height, - (1 << (SHORTBITS - 1)),
-                            width, hscroll, pos_tab_offset (w, start));
+      val = *compute_motion (start, 0, lmargin, 0, ZV,
+                            height, - (1 << (BITS_PER_SHORT - 1)),
+                            width, hscroll, pos_tab_offset (w, start), w);
       if (val.vpos != XFASTINT (w->window_end_vpos))
        abort ();
       if (XFASTINT (w->window_end_pos)
@@ -1560,7 +2349,15 @@ redisplay_region (buf, start, end)
       start = end; end = temp;
     }
 
-  if (buf != current_buffer)
+  /* If this is a buffer not in the selected window,
+     we must do other windows.  */
+  if (buf != XBUFFER (XWINDOW (selected_window)->buffer))
+    windows_or_buffers_changed = 1;
+  /* If it's not current, we can't use beg_unchanged, end_unchanged for it.  */
+  else if (buf != current_buffer)
+    windows_or_buffers_changed = 1;
+  /* If multiple windows show this buffer, we must do other windows.  */
+  else if (buffer_shared > 1)
     windows_or_buffers_changed = 1;
   else
     {
@@ -1579,9 +2376,9 @@ redisplay_region (buf, start, end)
     }
 
   /* Increment the buffer's time stamp, but also increment the save
-     and autosave timestamps, so as not to screw up that timekeeping. */
-  if (BUF_MODIFF (buf) == buf->save_modified)
-    buf->save_modified++;
+     and autosave timestamps, so as not to screw up that timekeeping.  */
+  if (BUF_MODIFF (buf) == BUF_SAVE_MODIFF (buf))
+    BUF_SAVE_MODIFF (buf)++;
   if (BUF_MODIFF (buf) == buf->auto_save_modified)
     buf->auto_save_modified++;
 
@@ -1589,71 +2386,96 @@ redisplay_region (buf, start, end)
 }
 
 \f
-/* Copy glyphs from the vector FROM to the rope T.
+/* Copy LEN glyphs starting address FROM to the rope TO.
    But don't actually copy the parts that would come in before S.
-   Value is T, advanced past the copied data.  */
+   Value is TO, advanced past the copied data.
+   F is the frame we are displaying in.  */
 
-GLYPH *
-copy_rope (t, s, from, face)
-     register GLYPH *t; /* Copy to here. */
+static GLYPH *
+copy_part_of_rope (f, to, s, from, len, face)
+     FRAME_PTR f;
+     register GLYPH *to; /* Copy to here. */
      register GLYPH *s; /* Starting point. */
-     Lisp_Object from;  /* Data to copy; known to be a vector.  */
+     Lisp_Object *from;  /* Data to copy. */
+     int len;
      int face;         /* Face to apply to glyphs which don't specify one. */
 {
-  register int n = XVECTOR (from)->size;
-  register Lisp_Object *f = XVECTOR (from)->contents;
-
-  while (n--)
-    {
-      int glyph = XFASTINT (*f);
+  int n = len;
+  register Lisp_Object *fp = from;
+  /* These cache the results of the last call to compute_glyph_face.  */
+  int last_code = -1;
+  int last_merged = 0;
+
+#ifdef HAVE_FACES
+  if (! FRAME_TERMCAP_P (f))
+    while (n--)
+      {
+       int glyph = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
+       int facecode;
+
+       if (FAST_GLYPH_FACE (glyph) == 0)
+         /* If GLYPH has no face code, use FACE.  */
+         facecode = face;
+       else if (FAST_GLYPH_FACE (glyph) == last_code)
+         /* If it's same as previous glyph, use same result.  */
+         facecode = last_merged;
+       else
+         {
+           /* Merge this glyph's face and remember the result.  */
+           last_code = FAST_GLYPH_FACE (glyph);
+           last_merged = facecode = compute_glyph_face (f, last_code, face);
+         }
 
-      if (t >= s) *t = MAKE_GLYPH (GLYPH_CHAR (glyph),
-                                  (GLYPH_FACE (glyph)
-                                   ? GLYPH_FACE (glyph)
-                                   : face));
-      ++t;
-      ++f;
-    }
-  return t;
+       if (to >= s)
+         *to = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (glyph), facecode);
+       ++to;
+       ++fp;
+      }
+  else
+#endif
+    while (n--)
+      {
+       if (to >= s) *to = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
+       ++to;
+       ++fp;
+      }
+  return to;
 }
 
-/* Copy exactly LEN glyphs from FROM into data at T.
-   But don't alter words before S.  */
+/* Correct a glyph by replacing its specified user-level face code
+   with a displayable computed face code.  */
 
-GLYPH *
-copy_part_of_rope (t, s, from, len, face)
-     register GLYPH *t; /* Copy to here. */
-     register GLYPH *s; /* Starting point. */
-     Lisp_Object *from;  /* Data to copy. */
-     int len;
-     int face;         /* Face to apply to glyphs which don't specify one. */
+static GLYPH
+fix_glyph (f, glyph, cface)
+     FRAME_PTR f;
+     GLYPH glyph;
+     int cface;
 {
-  int n = len;
-  register Lisp_Object *f = from;
-
-  while (n--)
+#ifdef HAVE_FACES
+  if (! FRAME_TERMCAP_P (f))
     {
-      int glyph = XFASTINT (*f);
-
-      if (t >= s) *t = MAKE_GLYPH (GLYPH_CHAR (glyph),
-                                  (GLYPH_FACE (glyph)
-                                   ? GLYPH_FACE (glyph)
-                                   : face));
-      ++t;
-      ++f;
+      if (FAST_GLYPH_FACE (glyph) != 0)
+       cface = compute_glyph_face (f, FAST_GLYPH_FACE (glyph), cface);
+      glyph = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (glyph), cface);
     }
-  return t;
+#endif
+  return glyph;
 }
 \f
-/* Display one line of window w, starting at position START in W's buffer.
-   Display starting at horizontal position HPOS, which is normally zero
-   or negative.  A negative value causes output up to hpos = 0 to be discarded.
-   This is done for negative hscroll, or when this is a continuation line
-   and the continuation occurred in the middle of a multi-column character.
+/* Display one line of window W, starting at position START in W's buffer.
+
+   Display starting at horizontal position HPOS, expressed relative to
+   W's left edge.  In situations where the text at START shouldn't
+   start at the left margin (i.e. when the window is hscrolled, or
+   we're continuing a line which left off in the midst of a
+   multi-column character), HPOS should be negative; we throw away
+   characters up 'til hpos = 0.  So, HPOS must take hscrolling into
+   account.
 
    TABOFFSET is an offset for ostensible hpos, used in tab stop calculations.
 
-   Display on position VPOS on the frame.  (origin 0).
+   Display on position VPOS on the frame.  It is origin 0, relative to
+   the top of the frame, not W.
 
    Returns a STRUCT POSITION giving character to start next line with
    and where to display it, including a zero or negative hpos.
@@ -1672,12 +2494,14 @@ display_text_line (w, start, vpos, hpos, taboffset)
   register int pos = start;
   register int c;
   register GLYPH *p1;
-  int end;
   register int pause;
   register unsigned char *p;
   GLYPH *endp;
-  register GLYPH *startp;
-  register GLYPH *p1prev = 0;
+  register GLYPH *leftmargin;
+  register GLYPH *p1prev;
+  register GLYPH *p1start;
+  int prevpos;
+  int *charstart;
   FRAME_PTR f = XFRAME (w->frame);
   int tab_width = XINT (current_buffer->tab_width);
   int ctl_arrow = !NILP (current_buffer->ctl_arrow);
@@ -1685,58 +2509,59 @@ display_text_line (w, start, vpos, hpos, taboffset)
   struct position val;
   int lastpos;
   int invis;
+  int last_invis_skip = 0;
+  Lisp_Object last_invis_prop;
   int hscroll = XINT (w->hscroll);
-  int truncate = hscroll
-    || (truncate_partial_width_windows
-       && XFASTINT (w->width) < FRAME_WIDTH (f))
-    || !NILP (current_buffer->truncate_lines);
+  int truncate = (hscroll
+                 || (truncate_partial_width_windows
+                     && XFASTINT (w->width) < FRAME_WIDTH (f))
+                 || !NILP (current_buffer->truncate_lines));
 
   /* 1 if we should highlight the region.  */
   int highlight_region
     = !NILP (Vtransient_mark_mode) && !NILP (current_buffer->mark_active);
   int region_beg, region_end;
 
-  int selective
-    = XTYPE (current_buffer->selective_display) == Lisp_Int
-      ? XINT (current_buffer->selective_display)
-       : !NILP (current_buffer->selective_display) ? -1 : 0;
+  int selective = (INTEGERP (current_buffer->selective_display)
+                  ? XINT (current_buffer->selective_display)
+                  : !NILP (current_buffer->selective_display) ? -1 : 0);
   register struct frame_glyphs *desired_glyphs = FRAME_DESIRED_GLYPHS (f);
-  register struct Lisp_Vector *dp = window_display_table (w);
+  register struct Lisp_Char_Table *dp = window_display_table (w);
 
   Lisp_Object default_invis_vector[3];
-  /* Nonzero means display something where there are invisible lines.
-     The precise value is the number of glyphs to display.  */
+  /* Number of characters of ellipsis to display after an invisible line
+     if it calls for an ellipsis.
+     Note that this value can be nonzero regardless of whether
+     selective display is enabled--you must check that separately.  */
   int selective_rlen
-    = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
+    = (dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? XVECTOR (DISP_INVIS_VECTOR (dp))->size
-       : selective && !NILP (current_buffer->selective_display_ellipses)
-       ? 3 : 0);
+       : !NILP (current_buffer->selective_display_ellipses) ? 3 : 0);
   /* This is the sequence of Lisp objects to display
      when there are invisible lines.  */
   Lisp_Object *invis_vector_contents
-    = (dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
+    = (dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? XVECTOR (DISP_INVIS_VECTOR (dp))->contents
        : default_invis_vector);
 
-  GLYPH truncator = (dp == 0 || XTYPE (DISP_TRUNC_GLYPH (dp)) != Lisp_Int
-                   ? '$' : XINT (DISP_TRUNC_GLYPH (dp)));
-  GLYPH continuer = (dp == 0 || XTYPE (DISP_CONTINUE_GLYPH (dp)) != Lisp_Int
-                   ? '\\' : XINT (DISP_CONTINUE_GLYPH (dp)));
+  GLYPH truncator = (dp == 0 || !INTEGERP (DISP_TRUNC_GLYPH (dp))
+                    ? '$' : XINT (DISP_TRUNC_GLYPH (dp)));
+  GLYPH continuer = (dp == 0 || !INTEGERP (DISP_CONTINUE_GLYPH (dp))
+                    ? '\\' : XINT (DISP_CONTINUE_GLYPH (dp)));
 
   /* The next buffer location at which the face should change, due
      to overlays or text property changes.  */
   int next_face_change;
 
-#ifdef USE_TEXT_PROPERTIES
-  /* The next location where the `invisible' property changes */
-  int next_invisible;
-  Lisp_Object prop, position, endpos;
-#endif
-  
+  /* The next location where the `invisible' property changes, or an
+     overlay starts or ends.  */
+  int next_boundary;
+
   /* The face we're currently using.  */
   int current_face = 0;
+  int i;
 
-  XFASTINT (default_invis_vector[2]) = '.';
+  XSETFASTINT (default_invis_vector[2], '.');
   default_invis_vector[0] = default_invis_vector[1] = default_invis_vector[2];
 
   hpos += XFASTINT (w->left);
@@ -1762,21 +2587,69 @@ display_text_line (w, start, vpos, hpos, taboffset)
   else
     region_beg = region_end = -1;
 
-  if (MINI_WINDOW_P (w) && start == 1
+  if (MINI_WINDOW_P (w)
+      && start == BEG
       && vpos == XFASTINT (w->top))
     {
-      if (minibuf_prompt)
-       hpos = display_string (w, vpos, minibuf_prompt, hpos,
-                              (!truncate ? continuer : truncator),
-                              -1, -1);
-      minibuf_prompt_width = hpos;
+      if (! NILP (minibuf_prompt))
+       {
+         minibuf_prompt_width
+           = (display_string (w, vpos, XSTRING (minibuf_prompt)->data,
+                              XSTRING (minibuf_prompt)->size, hpos,
+                              /* Display a space if we truncate.  */
+                              ' ',
+                              1, -1,
+                              /* Truncate the prompt a little before the
+                                 margin, so user input can at least start
+                                 on the first line.  */
+                              w->width > 10 ? w->width - 4 : -1)
+              - hpos);
+         hpos += minibuf_prompt_width;
+         taboffset -= minibuf_prompt_width;
+       }
+      else
+       minibuf_prompt_width = 0;
     }
 
-  desired_glyphs->bufp[vpos] = pos;
+  /* If we're hscrolled at all, use compute_motion to skip over any
+     text off the left edge of the window.  compute_motion may know
+     tricks to do this faster than we can.  */
+  if (hpos < 0)
+    {
+      struct position *left_edge
+        = compute_motion (pos, vpos, hpos, 0,
+                          ZV, vpos, 0,
+                          width, hscroll, taboffset, w);
+
+      /* Retrieve the buffer position and column provided by
+         compute_motion.  We can't assume that the column will be
+         zero, because you may have multi-column characters crossing
+         the left margin.  
+
+         compute_motion may have moved us past the screen position we
+         requested, if we hit a multi-column character, or the end of
+         the line.  If so, back up.  */
+      if (left_edge->vpos > vpos
+          || left_edge->hpos > 0)
+        {
+          pos = left_edge->bufpos - 1;
+          hpos = left_edge->prevhpos;
+        }
+      else
+        {
+          pos = left_edge->bufpos;
+          hpos = left_edge->hpos;
+        }
+    }
+
+  desired_glyphs->bufp[vpos] = start;
   p1 = desired_glyphs->glyphs[vpos] + hpos;
-  end = ZV;
-  startp = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
-  endp = startp + width;
+  p1start = p1;
+  charstart = desired_glyphs->charstarts[vpos] + hpos;
+  /* In case we don't ever write anything into it...  */
+  desired_glyphs->charstarts[vpos][XFASTINT (w->left)] = -1;
+  leftmargin = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
+  endp = leftmargin + width;
 
   /* Arrange the overlays nicely for our purposes.  Usually, we call
      display_text_line on only one line at a time, in which case this
@@ -1791,130 +2664,243 @@ display_text_line (w, start, vpos, hpos, taboffset)
      or at face change.  */
   pause = pos;
   next_face_change = pos;
-#ifdef USE_TEXT_PROPERTIES
-  next_invisible = pos;
-#endif
-  while (p1 < endp)
+  next_boundary = pos;
+  p1prev = p1;
+  prevpos = pos;
+  while (1)
     {
-      p1prev = p1;
       if (pos >= pause)
        {
-         /* Did we hit the end of the visible region of the buffer?
-            Stop here.  */
-         if (pos >= end)
-           break;
+         int e_t_h;
+
+         while (pos == next_boundary)
+           {
+             Lisp_Object position, limit, prop, ww;
+
+             /* Display the overlay strings here, unless we're at ZV
+                and have already displayed the appropriate strings
+                on an earlier line.  */
+             if (pos < ZV || !zv_strings_seen++)
+               {
+                 int ovlen;
+                 char *ovstr;
+                 ovlen = overlay_strings (pos, w, &ovstr);
+                 for (; ovlen; ovlen--, ovstr++)
+                   {
+                     if (p1 >= leftmargin && p1 < endp)
+                       *p1 = MAKE_GLYPH (f, *ovstr, current_face);
+                     p1++;
+                   }
+               }
+
+             /* Did we reach point?  Record the cursor location.  */
+             if (pos == PT && cursor_vpos < 0)
+               {
+                 cursor_vpos = vpos;
+                 cursor_hpos = p1 - leftmargin;
+               }
+
+             if (pos >= ZV)
+               break;
+
+             XSETFASTINT (position, pos);
+             limit = Fnext_overlay_change (position);
+#ifdef USE_TEXT_PROPERTIES
+             /* This is just an estimate to give reasonable
+                performance; nothing should go wrong if it is too small.  */
+             if (XFASTINT (limit) > pos + 50)
+               XSETFASTINT (limit, pos + 50);
+             limit = Fnext_single_property_change (position, Qinvisible,
+                                                   Fcurrent_buffer (), limit);
+#endif
+             next_boundary = XFASTINT (limit);
+             /* if the `invisible' property is set, we can skip to
+                the next property change.  */
+             XSETWINDOW (ww, w);
+             prop = Fget_char_property (position, Qinvisible, ww);
+             if (TEXT_PROP_MEANS_INVISIBLE (prop))
+               {
+                 if (pos < PT && next_boundary >= PT)
+                   {
+                     cursor_vpos = vpos;
+                     cursor_hpos = p1 - leftmargin;
+                   }
+                 pos = next_boundary;
+                 last_invis_skip = pos;
+                 last_invis_prop = prop;
+               }
+           }
 
          /* Did we reach point?  Record the cursor location.  */
-         if (pos == point && cursor_vpos < 0)
+         if (pos == PT && cursor_vpos < 0)
            {
              cursor_vpos = vpos;
-             cursor_hpos = p1 - startp;
+             cursor_hpos = p1 - leftmargin;
            }
 
-#ifdef USE_TEXT_PROPERTIES
-         /* if the `invisible' property is set to t, we can skip to
-            the next property change */
-         while (pos == next_invisible && pos < end)
-         {
-           XFASTINT (position) = pos;
-           prop = Fget_text_property (position,
-                                      Qinvisible,
-                                      Fcurrent_buffer ());
-           endpos = Fnext_single_property_change (position,
-                                                  Qinvisible,
-                                                  Fcurrent_buffer ());
-           if (INTEGERP (endpos))
-             next_invisible = XINT (endpos);
-           else
-             next_invisible = end;
-           if (! NILP (prop))
+         /* Did we hit the end of the visible region of the buffer?
+            Stop here.  */
+         if (pos >= ZV)
            {
-             if (pos < point && next_invisible >= point)
-             {
-               cursor_vpos = vpos;
-               cursor_hpos = p1 - startp;
-             }
-             pos = next_invisible;
+             /* Update charstarts for the end of this line.  */
+             /* Do nothing if off the left edge or at the right edge.  */
+             if (p1 >= leftmargin && p1 + 1 != endp)
+               {
+                 int *p2x = &charstart[(p1 < leftmargin
+                                        ? leftmargin : p1)
+                                       - p1start];
+                 *p2x++ = pos;
+               }
+             break;
            }
-         }
-         if (pos >= end)
-           break;
-#endif
 
-#ifdef HAVE_X_WINDOWS
+         /* Figure out where (if at all) the
+            redisplay_end_trigger-hook should run.  */
+         if (MARKERP (w->redisplay_end_trigger)
+             && XMARKER (w->redisplay_end_trigger)->buffer != 0)
+           e_t_h = marker_position (w->redisplay_end_trigger);
+         else if (INTEGERP (w->redisplay_end_trigger))
+           e_t_h = XINT (w->redisplay_end_trigger);
+         else
+           e_t_h = ZV;
+
+         /* If we've gone past the place to run a hook,
+            run the hook.  */
+         if (pos >= e_t_h && e_t_h != ZV)
+           {
+             Lisp_Object args[3];
+
+             args[0] = Qredisplay_end_trigger_functions;
+             XSETWINDOW (args[1], w);
+             XSETINT (args[2], e_t_h);
+
+             /* Since we are *trying* to run these functions,
+                don't try to run them again, even if they get an error.  */
+             w->redisplay_end_trigger = Qnil;
+             Frun_hook_with_args (3, args);
+
+             e_t_h = ZV;
+             /* Notice if it changed the face of this character.  */
+             next_face_change = pos;
+           }
+
+#ifdef HAVE_FACES
          /* Did we hit a face change?  Figure out what face we should
             use now.  We also hit this the first time through the
             loop, to see what face we should start with.  */
-         if (pos >= next_face_change && FRAME_X_P (f))
+         if (pos >= next_face_change && (FRAME_WINDOW_P (f)))
            current_face = compute_char_face (f, w, pos,
                                              region_beg, region_end,
-                                             &next_face_change);
+                                             &next_face_change, pos + 50, 0);
 #endif
 
-         pause = end;
+         /* Compute the next place we need to stop
+            and do something special; set PAUSE.  */
 
-#ifdef USE_TEXT_PROPERTIES       
-         if (pos < next_invisible && next_invisible < pause)
-           pause = next_invisible;
-#endif
+         pause = ZV;
+
+         if (pos < next_boundary && next_boundary < pause)
+           pause = next_boundary;
          if (pos < next_face_change && next_face_change < pause)
            pause = next_face_change;
 
+         if (e_t_h < pause)
+           pause = e_t_h;
+
          /* Wouldn't you hate to read the next line to someone over
              the phone?  */
-         if (pos < point && point < pause)
-           pause = point;
+         if (pos < PT && PT < pause)
+           pause = PT;
          if (pos < GPT && GPT < pause)
            pause = GPT;
 
          p = &FETCH_CHAR (pos);
        }
+
+      if (p1 >= endp)
+       break;
+
+      p1prev = p1;
+
       c = *p++;
-      if (c >= 040 && c < 0177
-         && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
+      /* Let a display table override all standard display methods.  */
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         p1 = copy_part_of_rope (f, p1, leftmargin,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
+                                 current_face);
+       }
+      else if (c >= 040 && c < 0177)
        {
-         if (p1 >= startp)
-           *p1 = MAKE_GLYPH (c, current_face);
+         if (p1 >= leftmargin)
+           *p1 = MAKE_GLYPH (f, c, current_face);
          p1++;
        }
       else if (c == '\n')
        {
          invis = 0;
-         while (pos < end
+         if (last_invis_skip == pos
+             && TEXT_PROP_MEANS_INVISIBLE_WITH_ELLIPSIS (last_invis_prop))
+           invis = 1;
+         while (pos + 1 < ZV
                 && selective > 0
-                && position_indentation (pos + 1) >= selective)
+                && indented_beyond_p (pos + 1, selective))
            {
              invis = 1;
              pos = find_next_newline (pos + 1, 1);
              if (FETCH_CHAR (pos - 1) == '\n')
                pos--;
            }
-         if (invis && selective_rlen > 0 && p1 >= startp)
+         if (invis && selective_rlen > 0 && p1 >= leftmargin)
            {
              p1 += selective_rlen;
-             if (p1 - startp > width)
+             if (p1 - leftmargin > width)
                p1 = endp;
-             copy_part_of_rope (p1prev, p1prev, invis_vector_contents,
+             copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
                                 (p1 - p1prev), current_face);
            }
-#if 1
+#ifdef HAVE_FACES
          /* Draw the face of the newline character as extending all the 
             way to the end of the frame line.  */
          if (current_face)
-           while (p1 < endp)
-             *p1++ = MAKE_GLYPH (' ', current_face);
+           {
+             if (p1 < leftmargin)
+               p1 = leftmargin;
+             while (p1 < endp)
+               *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+           }
 #endif
+
+         /* Update charstarts for the newline that ended this line.  */
+         /* Do nothing here for a char that's entirely off the left edge
+            or if it starts at the right edge.  */
+         if (p1 >= leftmargin && p1prev != endp)
+           {
+             /* Store the newline's position into charstarts
+                for the column where the newline starts.
+                Store -1 for the rest of the glyphs it occupies.  */
+             int *p2x = &charstart[(p1prev < leftmargin
+                                    ? leftmargin : p1prev)
+                                   - p1start];
+             int *p2 = &charstart[(p1 < endp ? p1 : endp) - p1start];
+
+             *p2x++ = pos;
+             while (p2x < p2)
+               *p2x++ = -1;
+           }
+
          break;
        }
       else if (c == '\t')
        {
          do
            {
-             if (p1 >= startp && p1 < endp)
-               *p1 = MAKE_GLYPH (' ', current_face);
+             if (p1 >= leftmargin && p1 < endp)
+               *p1 = MAKE_GLYPH (f, ' ', current_face);
              p1++;
            }
-         while ((p1 - startp + taboffset + hscroll - (hscroll > 0))
+         while ((p1 - leftmargin + taboffset + hscroll - (hscroll > 0))
                 % tab_width);
        }
       else if (c == Ctl ('M') && selective == -1)
@@ -1925,54 +2911,95 @@ display_text_line (w, start, vpos, hpos, taboffset)
          if (selective_rlen > 0)
            {
              p1 += selective_rlen;
-             if (p1 - startp > width)
+             if (p1 - leftmargin > width)
                p1 = endp;
-             copy_part_of_rope (p1prev, p1prev, invis_vector_contents,
+             copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
                                 (p1 - p1prev), current_face);
            }
-#if 1
+#ifdef HAVE_FACES
          /* Draw the face of the newline character as extending all the 
             way to the end of the frame line.  */
          if (current_face)
-           while (p1 < endp)
-             *p1++ = MAKE_GLYPH (' ', current_face);
+           {
+             if (p1 < leftmargin)
+               p1 = leftmargin;
+             while (p1 < endp)
+               *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+           }
 #endif
+
+         /* Update charstarts for the ^M that ended this line.  */
+         /* Do nothing here for a char that's entirely off the left edge
+            or if it starts at the right edge.  */
+         if (p1 >= leftmargin && p1prev != endp)
+           {
+             /* Store the newline's position into charstarts
+                for the column where the newline starts.
+                Store -1 for the rest of the glyphs it occupies.  */
+             int *p2x = &charstart[(p1prev < leftmargin
+                                    ? leftmargin : p1prev)
+                                   - p1start];
+             int *p2 = &charstart[(p1 < endp ? p1 : endp) - p1start];
+
+             *p2x++ = pos;
+             while (p2x < p2)
+               *p2x++ = -1;
+           }
          break;
        }
-      else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
-       {
-         p1 = copy_rope (p1, startp, DISP_CHAR_VECTOR (dp, c), current_face);
-       }
       else if (c < 0200 && ctl_arrow)
        {
-         if (p1 >= startp)
-           *p1 = MAKE_GLYPH ((dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
-                              ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
-                             current_face);
+         if (p1 >= leftmargin)
+           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
+                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                            current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = MAKE_GLYPH (c ^ 0100, current_face);
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, c ^ 0100, current_face);
          p1++;
        }
       else
        {
-         if (p1 >= startp)
-           *p1 = MAKE_GLYPH ((dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
-                              ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
-                             current_face);
+         if (p1 >= leftmargin)
+           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
+                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                            current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = MAKE_GLYPH ((c >> 6) + '0', current_face);
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (c >> 6) + '0', current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = MAKE_GLYPH ((7 & (c >> 3)) + '0', current_face);
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (7 & (c >> 3)) + '0', current_face);
          p1++;
-         if (p1 >= startp && p1 < endp)
-           *p1 = MAKE_GLYPH ((7 & c) + '0', current_face);
+         if (p1 >= leftmargin && p1 < endp)
+           *p1 = MAKE_GLYPH (f, (7 & c) + '0', current_face);
          p1++;
        }
 
+      prevpos = pos;
       pos++;
+
+      /* Update charstarts for the character just output.  */
+
+      /* Do nothing here for a char that's entirely off the left edge.  */
+      if (p1 >= leftmargin)
+       {
+         /* Store the char's position into charstarts
+            for the first glyph occupied by this char.
+            Store -1 for the rest of the glyphs it occupies.  */
+         if (p1 != p1prev)
+           {
+             int *p2x = &charstart[(p1prev < leftmargin
+                                    ? leftmargin : p1prev)
+                                   - p1start];
+             int *p2 = &charstart[(p1 < endp ? p1 : endp) - p1start];
+
+             if (p2x < p2)
+               *p2x++ = prevpos;
+             while (p2x < p2)
+               *p2x++ = -1;
+           }
+       }
     }
 
   val.hpos = - XINT (w->hscroll);
@@ -1983,6 +3010,18 @@ display_text_line (w, start, vpos, hpos, taboffset)
 
   lastpos = pos;
 
+  /* Store 0 in this charstart line for the positions where
+     there is no character.  But do leave what was recorded
+     for the character that ended the line.  */
+  /* Add 1 in the endtest to compensate for the fact that ENDP was
+     made from WIDTH, which is 1 less than the window's actual
+     internal width.  */
+  i = p1 - p1start + 1;
+  if (p1 < leftmargin)
+    i += leftmargin - p1;
+  for (; i < endp - p1start + 1; i++)
+    charstart[i] = 0;
+
   /* Handle continuation in middle of a character */
   /* by backing up over it */
   if (p1 > endp)
@@ -2009,25 +3048,35 @@ display_text_line (w, start, vpos, hpos, taboffset)
   if (pos < ZV)
     {
       if (FETCH_CHAR (pos) == '\n')
-       /* If stopped due to a newline, start next line after it */
-       pos++;
+       {
+         /* If stopped due to a newline, start next line after it */
+         pos++;
+         /* Check again for hidden lines, in case the newline occurred exactly
+            at the right margin.  */
+         while (pos < ZV && selective > 0
+                && indented_beyond_p (pos, selective))
+           pos = find_next_newline (pos, 1);
+       }
       else
        /* Stopped due to right margin of window */
        {
          if (truncate)
            {
-             *p1++ = truncator;
+             *p1++ = fix_glyph (f, truncator, 0);
              /* Truncating => start next line after next newline,
                 and point is on this line if it is before the newline,
                 and skip none of first char of next line */
-             pos = find_next_newline (pos, 1);
+             do
+               pos = find_next_newline (pos, 1);
+             while (pos < ZV && selective > 0
+                    && indented_beyond_p (pos, selective));
              val.hpos = XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0;
 
              lastpos = pos - (FETCH_CHAR (pos - 1) == '\n');
            }
          else
            {
-             *p1++ = continuer;
+             *p1++ = fix_glyph (f, continuer, 0);
              val.vpos = 0;
              lastpos--;
            }
@@ -2037,10 +3086,10 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If point is at eol or in invisible text at eol,
      record its frame location now.  */
 
-  if (start <= point && point <= lastpos && cursor_vpos < 0)
+  if (start <= PT && PT <= lastpos && cursor_vpos < 0)
     {
       cursor_vpos = vpos;
-      cursor_hpos = p1 - startp;
+      cursor_hpos = p1 - leftmargin;
     }
 
   if (cursor_vpos == vpos)
@@ -2050,8 +3099,12 @@ display_text_line (w, start, vpos, hpos, taboffset)
       cursor_hpos += XFASTINT (w->left);
       if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)))
        {
-         FRAME_CURSOR_Y (f) = cursor_vpos;
-         FRAME_CURSOR_X (f) = cursor_hpos;
+         if (!(cursor_in_echo_area && FRAME_HAS_MINIBUF_P (f)
+               && EQ (FRAME_MINIBUF_WINDOW (f), minibuf_window)))
+           {
+             FRAME_CURSOR_Y (f) = cursor_vpos;
+             FRAME_CURSOR_X (f) = cursor_hpos;
+           }
 
          if (w == XWINDOW (selected_window))
            {
@@ -2076,23 +3129,31 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If hscroll and line not empty, insert truncation-at-left marker */
   if (hscroll && lastpos != start)
     {
-      *startp = truncator;
-      if (p1 <= startp)
-       p1 = startp + 1;
+      *leftmargin = fix_glyph (f, truncator, 0);
+      if (p1 <= leftmargin)
+       p1 = leftmargin + 1;
     }
 
   if (XFASTINT (w->width) + XFASTINT (w->left) != FRAME_WIDTH (f))
     {
       endp++;
-      if (p1 < startp) p1 = startp;
+      if (p1 < leftmargin) p1 = leftmargin;
       while (p1 < endp) *p1++ = SPACEGLYPH;
 
       /* Don't draw vertical bars if we're using scroll bars.  They're
          covered up by the scroll bars, and it's distracting to see
          them when the scroll bar windows are flickering around to be
          reconfigured.  */
-      *p1++ = (FRAME_HAS_VERTICAL_SCROLL_BARS (f)
-              ? ' ' : '|');
+      if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
+       {
+         int i;
+         for (i = 0; i < FRAME_SCROLL_BAR_COLS (f); i++)
+           *p1++ = SPACEGLYPH;
+       }
+      else
+       *p1++ = (dp && INTEGERP (DISP_BORDER_GLYPH (dp))
+                ? DISP_BORDER_GLYPH (dp)
+                : '|');
     }
   desired_glyphs->used[vpos] = max (desired_glyphs->used[vpos],
                                   p1 - desired_glyphs->glyphs[vpos]);
@@ -2101,10 +3162,10 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If the start of this line is the overlay arrow-position,
      then put the arrow string into the display-line.  */
 
-  if (XTYPE (Voverlay_arrow_position) == Lisp_Marker
+  if (MARKERP (Voverlay_arrow_position)
       && current_buffer == XMARKER (Voverlay_arrow_position)->buffer
       && start == marker_position (Voverlay_arrow_position)
-      && XTYPE (Voverlay_arrow_string) == Lisp_String
+      && STRINGP (Voverlay_arrow_string)
       && ! overlay_arrow_seen)
     {
       unsigned char *p = XSTRING (Voverlay_arrow_string)->data;
@@ -2114,11 +3175,31 @@ display_text_line (w, start, vpos, hpos, taboffset)
 
       if (len > width)
        len = width;
-      for (i = 0; i < len; i++)
-       startp[i] = p[i];
+#ifdef HAVE_FACES
+      if (!NULL_INTERVAL_P (XSTRING (Voverlay_arrow_string)->intervals))
+       {
+         /* If the arrow string has text props, obey them when displaying.  */
+         for (i = 0; i < len; i++)
+           {
+             int c = p[i];
+             Lisp_Object face, ilisp;
+             int newface;
+
+             XSETFASTINT (ilisp, i);
+             face = Fget_text_property (ilisp, Qface, Voverlay_arrow_string);
+             newface = compute_glyph_face_1 (f, face, 0);
+             leftmargin[i] = FAST_MAKE_GLYPH (c, newface);
+           }
+       }
+      else
+#endif /* HAVE_FACES */
+       {
+         for (i = 0; i < len; i++)
+           leftmargin[i] = p[i];
+       }
 
       /* Bug in SunOS 4.1.1 compiler requires this intermediate variable.  */
-      arrow_end = (startp - desired_glyphs->glyphs[vpos]) + len;
+      arrow_end = (leftmargin - desired_glyphs->glyphs[vpos]) + len;
       if (desired_glyphs->used[vpos] < arrow_end)
        desired_glyphs->used[vpos] = arrow_end;
 
@@ -2141,49 +3222,39 @@ display_menu_bar (w)
   register FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
   int maxendcol = FRAME_WIDTH (f);
   int hpos = 0;
+  int i;
 
-  if (FRAME_MENU_BAR_LINES (f) <= 0)
+#ifdef HAVE_NTGUI
+  return;
+#endif
+
+#ifdef USE_X_TOOLKIT
+  if (FRAME_X_P (f))
     return;
+#endif /* USE_X_TOOLKIT */
 
   get_display_line (f, vpos, 0);
 
-  /* If the user has switched buffers or windows, we need to
-     recompute to reflect the new bindings.  But we'll
-     recompute when update_mode_lines is set too; that means
-     that people can use force-mode-line-update to request
-     that the menu bar be recomputed.  The adverse effect on
-     the rest of the redisplay algorithm is about the same as
-     windows_or_buffers_changed anyway.  */
-  if (windows_or_buffers_changed
-      || update_mode_lines
-      || (XFASTINT (w->last_modified) < MODIFF
-         && (XFASTINT (w->last_modified)
-             <= XBUFFER (w->buffer)->save_modified)))
+  items = FRAME_MENU_BAR_ITEMS (f);
+  for (i = 0; i < XVECTOR (items)->size; i += 3)
     {
-      struct buffer *prev = current_buffer;
-      current_buffer = XBUFFER (w->buffer);
-      FRAME_MENU_BAR_ITEMS (f) = menu_bar_items ();
-      current_buffer = prev;
-    }
+      Lisp_Object pos, string;
+      string = XVECTOR (items)->contents[i + 1];
+      if (NILP (string))
+       break;
 
-  for (tail = FRAME_MENU_BAR_ITEMS (f); CONSP (tail); tail = XCONS (tail)->cdr)
-    {
-      Lisp_Object string;
-
-      string = XCONS (XCONS (XCONS (tail)->car)->cdr)->car;
-
-      /* Record in each item its hpos.  */
-      XFASTINT (XCONS (XCONS (XCONS (tail)->car)->cdr)->cdr) = hpos;
+      XSETFASTINT (XVECTOR (items)->contents[i + 2], hpos);
 
       if (hpos < maxendcol)
        hpos = display_string (XWINDOW (FRAME_ROOT_WINDOW (f)), vpos,
                               XSTRING (string)->data,
-                              hpos, 0, hpos, maxendcol);
-      /* Put a gap of 3 spaces between items.  */
+                              XSTRING (string)->size,
+                              hpos, 0, 0, hpos, maxendcol);
+      /* Put a space between items.  */
       if (hpos < maxendcol)
        {
-         int hpos1 = hpos + 3;
-         hpos = display_string (w, vpos, "", hpos, 0,
+         int hpos1 = hpos + 1;
+         hpos = display_string (w, vpos, "", 0, hpos, 0, 0,
                                 min (hpos1, maxendcol), maxendcol);
        }
     }
@@ -2193,7 +3264,12 @@ display_menu_bar (w)
 
   /* Fill out the line with spaces.  */
   if (maxendcol > hpos)
-    hpos = display_string (w, vpos, "", hpos, 0, maxendcol, -1);
+    hpos = display_string (w, vpos, "", 0, hpos, 0, 0, maxendcol, maxendcol);
+
+  /* Clear the rest of the lines allocated to the menu bar.  */
+  vpos++;
+  while (vpos < FRAME_MENU_BAR_LINES (f))
+    get_display_line (f, vpos++, 0);
 }
 \f
 /* Display the mode line for window w */
@@ -2208,10 +3284,20 @@ display_mode_line (w)
   register FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
 
   line_number_displayed = 0;
+  w->column_number_displayed = Qnil;
 
   get_display_line (f, vpos, left);
+
+  /* Temporarily make frame F's kboard the current kboard
+     so that kboard-local variables in the mode_line_format
+     will get the right values.  */
+  push_frame_kboard (f);
+
   display_mode_element (w, vpos, left, 0, right, right,
                        current_buffer->mode_line_format);
+
+  pop_frame_kboard ();
+
   FRAME_DESIRED_GLYPHS (f)->bufp[vpos] = 0;
 
   /* Make the mode line inverse video if the entire line
@@ -2223,25 +3309,15 @@ display_mode_line (w)
   if (XFASTINT (w->width) == FRAME_WIDTH (f)
       || XFASTINT (XWINDOW (w->parent)->width) == FRAME_WIDTH (f))
     FRAME_DESIRED_GLYPHS (f)->highlight[vpos] = mode_line_inverse_video;
-
-#ifdef HAVE_X_WINDOWS
-  /* I'm trying this out because I saw Unimpress use it, but it's
-     possible that this may mess adversely with some window managers.  -jla
-
-     Wouldn't it be nice to use something like mode-line-format to
-     describe frame titles?  -JimB  */
-
-  /* Change the title of the frame to the name of the buffer displayed
-     in the currently selected window.  Don't do this for minibuffer frames,
-     and don't do it when there's only one non-minibuffer frame.  */
-  if (FRAME_X_P (f)
-      && ! FRAME_MINIBUF_ONLY_P (f)
-      && w == XWINDOW (f->selected_window))
-    x_implicitly_set_name (f, (EQ (Fnext_frame (WINDOW_FRAME (w), Qnil),
-                                  WINDOW_FRAME (w))
-                              ? Qnil
-                              : XBUFFER (w->buffer)->name),
-                          Qnil);
+#ifdef HAVE_FACES
+  else if (! FRAME_TERMCAP_P (f) && mode_line_inverse_video)
+    {
+      /* For a partial width window, explicitly set face of each glyph. */
+      int i;
+      GLYPH *ptr = FRAME_DESIRED_GLYPHS (f)->glyphs[vpos];
+      for (i = left; i < right; ++i)
+       ptr[i] = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (ptr[i]), 1);
+    }
 #endif
 }
 
@@ -2284,11 +3360,7 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
 
   depth++;
 
-#ifdef SWITCH_ENUM_BUG
-  switch ((int) XTYPE (elt))
-#else
-  switch (XTYPE (elt))
-#endif
+  switch (SWITCH_ENUM_CAST (XTYPE (elt)))
     {
     case Lisp_String:
       {
@@ -2304,11 +3376,15 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
            if (this - 1 != last)
              {
                register int lim = --this - last + hpos;
-               hpos = display_string (w, vpos, last, hpos, 0, hpos,
-                                      min (lim, maxendcol));
+               if (frame_title_ptr)
+                 hpos = store_frame_title (last, hpos, min (lim, maxendcol));
+               else
+                 hpos = display_string (w, vpos, last, -1, hpos, 0, 1,
+                                        hpos, min (lim, maxendcol));
              }
            else /* c == '%' */
              {
+               register int minendcol;
                register int spec_width = 0;
 
                /* We can't allow -ve args due to the "%-" construct */
@@ -2321,19 +3397,28 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
                    spec_width = spec_width * 10 + (c - '0');
                  }
 
-               spec_width += hpos;
-               if (spec_width > maxendcol)
-                 spec_width = maxendcol;
+               minendcol = hpos + spec_width;
+               if (minendcol > maxendcol)
+                 {
+                   spec_width = maxendcol - hpos;
+                   minendcol = maxendcol;
+                 }
 
                if (c == 'M')
                  hpos = display_mode_element (w, vpos, hpos, depth,
                                               spec_width, maxendcol,
                                               Vglobal_mode_string);
                else if (c != 0)
-                 hpos = display_string (w, vpos,
-                                        decode_mode_spec (w, c,
-                                                          maxendcol - hpos),
-                                        hpos, 0, spec_width, maxendcol);
+                 {
+                   char *spec = decode_mode_spec (w, c, spec_width,
+                                                  maxendcol - hpos);
+                   if (frame_title_ptr)
+                     hpos = store_frame_title (spec, minendcol, maxendcol);
+                   else
+                     hpos = display_string (w, vpos, spec, -1,
+                                            hpos, 0, 1,
+                                            minendcol, maxendcol);
+                 }
              }
          }
       }
@@ -2352,9 +3437,16 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
            tem = Fsymbol_value (elt);
            /* If value is a string, output that string literally:
               don't check for % within it.  */
-           if (XTYPE (tem) == Lisp_String)
-             hpos = display_string (w, vpos, XSTRING (tem)->data,
-                                    hpos, 0, minendcol, maxendcol);
+           if (STRINGP (tem))
+             {
+               if (frame_title_ptr)
+                 hpos = store_frame_title (XSTRING (tem)->data,
+                                           minendcol, maxendcol);
+               else
+                 hpos = display_string (w, vpos, XSTRING (tem)->data,
+                                        XSTRING (tem)->size,
+                                        hpos, 0, 1, minendcol, maxendcol);
+             }
            /* Give up right away for nil or t.  */
            else if (!EQ (tem, elt))
              { elt = tem; goto tail_recurse; }
@@ -2375,11 +3467,11 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
           If first element is a symbol, process the cadr or caddr recursively
           according to whether the symbol's value is non-nil or nil.  */
        car = XCONS (elt)->car;
-       if (XTYPE (car) == Lisp_Symbol)
+       if (SYMBOLP (car))
          {
            tem = Fboundp (car);
            elt = XCONS (elt)->cdr;
-           if (XTYPE (elt) != Lisp_Cons)
+           if (!CONSP (elt))
              goto invalid;
            /* elt is now the cdr, and we know it is a cons cell.
               Use its car if CAR has a non-nil value.  */
@@ -2395,12 +3487,12 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
            elt = XCONS (elt)->cdr;
            if (NILP (elt))
              break;
-           else if (XTYPE (elt) != Lisp_Cons)
+           else if (!CONSP (elt))
              goto invalid;
            elt = XCONS (elt)->car;
            goto tail_recurse;
          }
-       else if (XTYPE (car) == Lisp_Int)
+       else if (INTEGERP (car))
          {
            register int lim = XINT (car);
            elt = XCONS (elt)->cdr;
@@ -2425,11 +3517,11 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
              }
            goto tail_recurse;
          }
-       else if (XTYPE (car) == Lisp_String || XTYPE (car) == Lisp_Cons)
+       else if (STRINGP (car) || CONSP (car))
          {
            register int limit = 50;
            /* LIMIT is to protect against circular lists.  */
-           while (XTYPE (elt) == Lisp_Cons && --limit > 0
+           while (CONSP (elt) && --limit > 0
                   && hpos < maxendcol)
              {
                hpos = display_mode_element (w, vpos, hpos, depth,
@@ -2443,38 +3535,146 @@ display_mode_element (w, vpos, hpos, depth, minendcol, maxendcol, elt)
 
     default:
     invalid:
-      return (display_string (w, vpos, "*invalid*", hpos, 0,
-                             minendcol, maxendcol));
+      if (frame_title_ptr)
+       hpos = store_frame_title ("*invalid*", minendcol, maxendcol);
+      else
+       hpos = display_string (w, vpos, "*invalid*", -1, hpos, 0, 1,
+                              minendcol, maxendcol);
+      return hpos;
     }
 
- end:
   if (minendcol > hpos)
-    hpos = display_string (w, vpos, "", hpos, 0, minendcol, -1);
+    if (frame_title_ptr)
+      hpos = store_frame_title ("", minendcol, maxendcol);
+    else
+      hpos = display_string (w, vpos, "", 0, hpos, 0, 1, minendcol, maxendcol);
   return hpos;
 }
 \f
+/* Write a null-terminated, right justified decimal representation of
+   the positive integer D to BUF using a minimal field width WIDTH.  */
+
+static void
+pint2str (buf, width, d)
+     register char *buf;
+     register int width;
+     register int d;
+{
+  register char *p = buf;
+  
+  if (d <= 0)
+      *p++ = '0';
+  else
+      while (d > 0)
+      {
+         *p++ = d % 10 + '0';
+         d /= 10;
+      }
+  for (width -= (int) (p - buf); width > 0; --width) *p++ = ' ';
+  *p-- = '\0';
+  while (p > buf)
+  {
+      d = *buf;
+      *buf++ = *p;
+      *p-- = d;
+  }
+}
+
 /* Return a string for the output of a mode line %-spec for window W,
-   generated by character C and width MAXWIDTH.  */
+   generated by character C.  SPEC_WIDTH is the field width when
+   padding to the left (%c, %l).  The value returned from this
+   function will later be truncated to width MAXWIDTH. */
 
 static char lots_of_dashes[] = "--------------------------------------------------------------------------------------------------------------------------------------------";
 
 static char *
-decode_mode_spec (w, c, maxwidth)
+decode_mode_spec (w, c, spec_width, maxwidth)
      struct window *w;
      register char c;
+     register int spec_width;
      register int maxwidth;
 {
-  Lisp_Object obj = Qnil;
+  Lisp_Object obj;
   FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
   char *decode_mode_spec_buf = (char *) FRAME_TEMP_GLYPHS (f)->total_contents;
+  struct buffer *b = XBUFFER (w->buffer);
 
+  obj = Qnil;
   if (maxwidth > FRAME_WIDTH (f))
     maxwidth = FRAME_WIDTH (f);
 
   switch (c)
     {
+    case '*':
+      if (!NILP (b->read_only))
+       return "%";
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      return "-";
+
+    case '+':
+      /* This differs from %* only for a modified read-only buffer.  */
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      if (!NILP (b->read_only))
+       return "%";
+      return "-";
+
+    case '&':
+      /* This differs from %* in ignoring read-only-ness.  */
+      if (BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
+       return "*";
+      return "-";
+
+    case '%':
+      return "%";
+
+    case '[': 
+      {
+       int i;
+       char *p;
+
+       if (command_loop_level > 5)
+         return "[[[... ";
+       p = decode_mode_spec_buf;
+       for (i = 0; i < command_loop_level; i++)
+         *p++ = '[';
+       *p = 0;
+       return decode_mode_spec_buf;
+      }
+
+    case ']': 
+      {
+       int i;
+       char *p;
+
+       if (command_loop_level > 5)
+         return " ...]]]";
+       p = decode_mode_spec_buf;
+       for (i = 0; i < command_loop_level; i++)
+         *p++ = ']';
+       *p = 0;
+       return decode_mode_spec_buf;
+      }
+
+    case '-':
+      {
+       register char *p;
+       register int i;
+       
+       if (maxwidth < sizeof (lots_of_dashes))
+         return lots_of_dashes;
+       else
+         {
+           for (p = decode_mode_spec_buf, i = maxwidth; i > 0; i--)
+             *p++ = '-';
+           *p = '\0';
+         }
+       return decode_mode_spec_buf;
+      }
+
     case 'b': 
-      obj = current_buffer->name;
+      obj = b->name;
 #if 0
       if (maxwidth >= 3 && XSTRING (obj)->size > maxwidth)
        {
@@ -2486,12 +3686,30 @@ decode_mode_spec (w, c, maxwidth)
 #endif
       break;
 
+    case 'c':
+      {
+       int col = current_column ();
+       XSETFASTINT (w->column_number_displayed, col);
+       pint2str (decode_mode_spec_buf, spec_width, col);
+       return decode_mode_spec_buf;
+      }
+
+    case 'F':
+      /* %F displays the frame name.  */
+#ifdef MULTI_FRAME
+      if (!NILP (selected_frame->title))
+       return (char *) XSTRING (selected_frame->title)->data;
+      return (char *) XSTRING (selected_frame->name)->data;
+#else
+      return "Emacs";
+#endif
+
     case 'f': 
-      obj = current_buffer->filename;
+      obj = b->filename;
 #if 0
       if (NILP (obj))
        return "[none]";
-      else if (XTYPE (obj) == Lisp_String && XSTRING (obj)->size > maxwidth)
+      else if (STRINGP (obj) && XSTRING (obj)->size > maxwidth)
        {
          bcopy ("...", decode_mode_spec_buf, 3);
          bcopy (XSTRING (obj)->data + XSTRING (obj)->size - maxwidth + 3,
@@ -2512,14 +3730,14 @@ decode_mode_spec (w, c, maxwidth)
        /* If we decided that this buffer isn't suitable for line numbers, 
           don't forget that too fast.  */
        if (EQ (w->base_line_pos, w->buffer))
-         return "??";
+         goto no_value;
 
        /* If the buffer is very big, don't waste time.  */
-       if (ZV - BEGV > line_number_display_limit)
+       if (BUF_ZV (b) - BUF_BEGV (b) > line_number_display_limit)
          {
            w->base_line_pos = Qnil;
            w->base_line_number = Qnil;
-           return "??";
+           goto no_value;
          }
 
        if (!NILP (w->base_line_number)
@@ -2532,7 +3750,7 @@ decode_mode_spec (w, c, maxwidth)
        else
          {
            line = 1;
-           linepos = BEGV;
+           linepos = BUF_BEGV (b);
          }
 
        /* Count lines from base line to window start position.  */
@@ -2544,15 +3762,15 @@ decode_mode_spec (w, c, maxwidth)
           or too far away, or if we did not have one.
           "Too close" means it's plausible a scroll-down would
           go back past it.  */
-       if (startpos == BEGV)
+       if (startpos == BUF_BEGV (b))
          {
-           XFASTINT (w->base_line_number) = topline;
-           XFASTINT (w->base_line_pos) = BEGV;
+           XSETFASTINT (w->base_line_number, topline);
+           XSETFASTINT (w->base_line_pos, BUF_BEGV (b));
          }
        else if (nlines < height + 25 || nlines > height * 3 + 50
-                || linepos == BEGV)
+                || linepos == BUF_BEGV (b))
          {
-           int limit = BEGV;
+           int limit = BUF_BEGV (b);
            int position;
            int distance = (height * 2 + 30) * 200;
 
@@ -2569,11 +3787,11 @@ decode_mode_spec (w, c, maxwidth)
              {
                w->base_line_pos = w->buffer;
                w->base_line_number = Qnil;
-               return "??";
+               goto no_value;
              }
 
-           XFASTINT (w->base_line_number) = topline - nlines;
-           XFASTINT (w->base_line_pos) = position;
+           XSETFASTINT (w->base_line_number, topline - nlines);
+           XSETFASTINT (w->base_line_pos, position);
          }
 
        /* Now count lines from the start pos to point.  */
@@ -2583,52 +3801,48 @@ decode_mode_spec (w, c, maxwidth)
        line_number_displayed = 1;
 
        /* Make the string to show.  */
-       sprintf (decode_mode_spec_buf, "%d", topline + nlines);
+       pint2str (decode_mode_spec_buf, spec_width, topline + nlines);
        return decode_mode_spec_buf;
+    no_value:
+        {
+         char* p = decode_mode_spec_buf;
+         for (spec_width -= 2; spec_width > 0; --spec_width) *p++ = ' ';
+         strcpy (p, "??");
+         return decode_mode_spec_buf;
+       }
       }
       break;
 
     case 'm': 
-      obj = current_buffer->mode_name;
+      obj = b->mode_name;
       break;
 
     case 'n':
-      if (BEGV > BEG || ZV < Z)
+      if (BUF_BEGV (b) > BUF_BEG (b) || BUF_ZV (b) < BUF_Z (b))
        return " Narrow";
       break;
 
-    case '*':
-      if (!NILP (current_buffer->read_only))
-       return "%";
-      if (MODIFF > current_buffer->save_modified)
-       return "*";
-      return "-";
-
-    case 's':
-      /* status of process */
-      obj = Fget_buffer_process (Fcurrent_buffer ());
-      if (NILP (obj))
-       return "no process";
-      obj = Fsymbol_name (Fprocess_status (obj));
-      break;
-
     case 'p':
       {
        int pos = marker_position (w->start);
-       int total = ZV - BEGV;
+       int total = BUF_ZV (b) - BUF_BEGV (b);
 
-       if (XFASTINT (w->window_end_pos) <= Z - ZV)
+       if (XFASTINT (w->window_end_pos) <= BUF_Z (b) - BUF_ZV (b))
          {
-           if (pos <= BEGV)
+           if (pos <= BUF_BEGV (b))
              return "All";
            else
              return "Bottom";
          }
-       else if (pos <= BEGV)
+       else if (pos <= BUF_BEGV (b))
          return "Top";
        else
          {
-           total = ((pos - BEGV) * 100 + total - 1) / total;
+           if (total > 1000000)
+             /* Do it differently for a large value, to avoid overflow.  */
+             total = ((pos - BUF_BEGV (b)) + (total / 100) - 1) / (total / 100);
+           else
+             total = ((pos - BUF_BEGV (b)) * 100 + total - 1) / total;
            /* We can't normally display a 3-digit number,
               so get us a 2-digit number that is close.  */
            if (total == 100)
@@ -2638,59 +3852,163 @@ decode_mode_spec (w, c, maxwidth)
          }
       }
 
-    case '%':
-      return "%";
-
-    case '[': 
+      /* Display percentage of size above the bottom of the screen.  */
+    case 'P':
       {
-       int i;
-       char *p;
+       int toppos = marker_position (w->start);
+       int botpos = BUF_Z (b) - XFASTINT (w->window_end_pos);
+       int total = BUF_ZV (b) - BUF_BEGV (b);
 
-       if (command_loop_level > 5)
-         return "[[[... ";
-       p = decode_mode_spec_buf;
-       for (i = 0; i < command_loop_level; i++)
-         *p++ = '[';
-       *p = 0;
-       return decode_mode_spec_buf;
-      }
-
-    case ']': 
-      {
-       int i;
-       char *p;
-
-       if (command_loop_level > 5)
-         return " ...]]]";
-       p = decode_mode_spec_buf;
-       for (i = 0; i < command_loop_level; i++)
-         *p++ = ']';
-       *p = 0;
-       return decode_mode_spec_buf;
-      }
-      
-    case '-':
-      {
-       register char *p;
-       register int i;
-       
-       if (maxwidth < sizeof (lots_of_dashes))
-         return lots_of_dashes;
+       if (botpos >= BUF_ZV (b))
+         {
+           if (toppos <= BUF_BEGV (b))
+             return "All";
+           else
+             return "Bottom";
+         }
        else
          {
-           for (p = decode_mode_spec_buf, i = maxwidth; i > 0; i--)
-             *p++ = '-';
-           *p = '\0';
+           if (total > 1000000)
+             /* Do it differently for a large value, to avoid overflow.  */
+             total = ((botpos - BUF_BEGV (b)) + (total / 100) - 1) / (total / 100);
+           else
+             total = ((botpos - BUF_BEGV (b)) * 100 + total - 1) / total;
+           /* We can't normally display a 3-digit number,
+              so get us a 2-digit number that is close.  */
+           if (total == 100)
+             total = 99;
+           if (toppos <= BUF_BEGV (b))
+             sprintf (decode_mode_spec_buf, "Top%2d%%", total);
+           else
+             sprintf (decode_mode_spec_buf, "%2d%%", total);
+           return decode_mode_spec_buf;
          }
-       return decode_mode_spec_buf;
       }
+
+    case 's':
+      /* status of process */
+      obj = Fget_buffer_process (w->buffer);
+      if (NILP (obj))
+       return "no process";
+#ifdef subprocesses
+      obj = Fsymbol_name (Fprocess_status (obj));
+#endif
+      break;
+
+    case 't':                  /* indicate TEXT or BINARY */
+#ifdef MODE_LINE_BINARY_TEXT
+      return MODE_LINE_BINARY_TEXT (b);
+#else
+      return "T";
+#endif
     }
-  
-  if (XTYPE (obj) == Lisp_String)
+
+  if (STRINGP (obj))
     return (char *) XSTRING (obj)->data;
   else
     return "";
 }
+\f
+/* Search for COUNT instances of a line boundary, which means either a
+   newline or (if selective display enabled) a carriage return.
+   Start at START.  If COUNT is negative, search backwards.
+
+   If we find COUNT instances, set *SHORTAGE to zero, and return the
+   position after the COUNTth match.  Note that for reverse motion
+   this is not the same as the usual convention for Emacs motion commands.
+
+   If we don't find COUNT instances before reaching the end of the
+   buffer (or the beginning, if scanning backwards), set *SHORTAGE to
+   the number of line boundaries left unfound, and return the end of the
+   buffer we bumped up against.  */
+
+static int
+display_scan_buffer (start, count, shortage)
+     int *shortage, start;
+     register int count;
+{
+  int limit = ((count > 0) ? ZV - 1 : BEGV);
+  int direction = ((count > 0) ? 1 : -1);
+
+  register unsigned char *cursor;
+  unsigned char *base;
+
+  register int ceiling;
+  register unsigned char *ceiling_addr;
+
+  /* If we are not in selective display mode,
+     check only for newlines.  */
+  if (! (!NILP (current_buffer->selective_display)
+        && !INTEGERP (current_buffer->selective_display)))
+    return scan_buffer ('\n', start, 0, count, shortage, 0);
+
+  /* The code that follows is like scan_buffer
+     but checks for either newline or carriage return.  */
+
+  if (shortage != 0)
+    *shortage = 0;
+
+  if (count > 0)
+    while (start != limit + 1)
+      {
+       ceiling =  BUFFER_CEILING_OF (start);
+       ceiling = min (limit, ceiling);
+       ceiling_addr = &FETCH_CHAR (ceiling) + 1;
+       base = (cursor = &FETCH_CHAR (start));
+       while (1)
+         {
+           while (*cursor != '\n' && *cursor != 015 && ++cursor != ceiling_addr)
+             ;
+           if (cursor != ceiling_addr)
+             {
+               if (--count == 0)
+                 {
+                   immediate_quit = 0;
+                   return (start + cursor - base + 1);
+                 }
+               else
+                 if (++cursor == ceiling_addr)
+                   break;
+             }
+           else
+             break;
+         }
+       start += cursor - base;
+      }
+  else
+    {
+      start--;                 /* first character we scan */
+      while (start > limit - 1)
+       {                       /* we WILL scan under start */
+         ceiling =  BUFFER_FLOOR_OF (start);
+         ceiling = max (limit, ceiling);
+         ceiling_addr = &FETCH_CHAR (ceiling) - 1;
+         base = (cursor = &FETCH_CHAR (start));
+         cursor++;
+         while (1)
+           {
+             while (--cursor != ceiling_addr
+                    && *cursor != '\n' && *cursor != 015)
+               ;
+             if (cursor != ceiling_addr)
+               {
+                 if (++count == 0)
+                   {
+                     immediate_quit = 0;
+                     return (start + cursor - base + 1);
+                   }
+               }
+             else
+               break;
+           }
+         start += cursor - base;
+       }
+    }
+
+  if (shortage != 0)
+    *shortage = count * direction;
+  return (start + ((direction == 1 ? 0 : 1)));
+}
 
 /* Count up to N lines starting from FROM.
    But don't go beyond LIMIT.
@@ -2711,7 +4029,7 @@ display_count_lines (from, limit, n, pos_ptr)
   else
     ZV = limit;
 
-  *pos_ptr = scan_buffer ('\n', from, n, &shortage);
+  *pos_ptr = display_scan_buffer (from, n, &shortage);
 
   ZV = oldzv;
   BEGV = oldbegv;
@@ -2726,6 +4044,7 @@ display_count_lines (from, limit, n, pos_ptr)
 /* Display STRING on one line of window W, starting at HPOS.
    Display at position VPOS.  Caller should have done get_display_line.
    If VPOS == -1, display it as the current frame's title.
+   LENGTH is the length of STRING, or -1 meaning STRING is null-terminated.
 
   TRUNCATE is GLYPH to display at end if truncated.  Zero for none.
 
@@ -2736,17 +4055,26 @@ display_count_lines (from, limit, n, pos_ptr)
   The right edge of W is an implicit maximum.
   If TRUNCATE is nonzero, the implicit maximum is one column before the edge.
 
-  Returns ending hpos */
+  OBEY_WINDOW_WIDTH says to put spaces or vertical bars
+  at the place where the current window ends in this line
+  and not display anything beyond there.  Otherwise, only MAXCOL
+  controls where to stop output.
+
+  Returns ending hpos.  */
 
 static int
-display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
+display_string (w, vpos, string, length, hpos, truncate,
+               obey_window_width, mincol, maxcol)
      struct window *w;
      unsigned char *string;
+     int length;
      int vpos, hpos;
      GLYPH truncate;
+     int obey_window_width;
      int mincol, maxcol;
 {
   register int c;
+  int truncated;
   register GLYPH *p1;
   int hscroll = XINT (w->hscroll);
   int tab_width = XINT (XBUFFER (w->buffer)->tab_width);
@@ -2759,42 +4087,75 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
 
   /* Use the standard display table, not the window's display table.
      We don't want the mode line in rot13.  */
-  register struct Lisp_Vector *dp = 0;
+  register struct Lisp_Char_Table *dp = 0;
+  int i;
 
-  if (XTYPE (Vstandard_display_table) == Lisp_Vector
-      && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
-    dp = XVECTOR (Vstandard_display_table);
+  if (DISP_TABLE_P (Vstandard_display_table))
+    dp = XCHAR_TABLE (Vstandard_display_table);
 
   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
 
   p1 = p1start;
   start = desired_glyphs->glyphs[vpos] + XFASTINT (w->left);
-  end = start + window_width - (truncate != 0);
 
-  if ((window_width + XFASTINT (w->left)) != FRAME_WIDTH (f))
+  if (obey_window_width)
     {
-      if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
+      end = start + window_width - (truncate != 0);
+
+      if ((window_width + XFASTINT (w->left)) != FRAME_WIDTH (f))
        {
-         int i;
+         if (FRAME_HAS_VERTICAL_SCROLL_BARS (f))
+           {
+             int i;
 
-         for (i = 0; i < VERTICAL_SCROLL_BAR_WIDTH; i++)
-           *end-- = ' ';
+             for (i = 0; i < FRAME_SCROLL_BAR_COLS (f); i++)
+               *end-- = ' ';
+           }
+         else
+           *end-- = '|';
        }
-      else
-       *end-- = '|';
     }
 
-  if (maxcol >= 0 && end - desired_glyphs->glyphs[vpos] > maxcol)
+  if (! obey_window_width
+      || (maxcol >= 0 && end - desired_glyphs->glyphs[vpos] > maxcol))
     end = desired_glyphs->glyphs[vpos] + maxcol;
+
+  /* Store 0 in charstart for these columns.  */
+  for (i = (hpos >= 0 ? hpos : 0); i < end - p1start + hpos; i++)
+    desired_glyphs->charstarts[vpos][i] = 0;
+
   if (maxcol >= 0 && mincol > maxcol)
     mincol = maxcol;
 
-  while (p1 < end)
+  /* We set truncated to 1 if we get stopped by trying to pass END
+     (that is, trying to pass MAXCOL.)  */
+  truncated = 0;
+  while (1)
     {
+      if (length == 0)
+       break;
       c = *string++;
-      if (!c) break;
-      if (c >= 040 && c < 0177
-         && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
+      /* Specified length.  */
+      if (length >= 0)
+       length--;
+      /* Unspecified length (null-terminated string).  */
+      else if (c == 0)
+       break;
+
+      if (p1 >= end)
+       {
+         truncated = 1;
+         break;
+       }
+
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         p1 = copy_part_of_rope (f, p1, start,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
+                                 XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
+                                 0);
+       }
+      else if (c >= 040 && c < 0177)
        {
          if (p1 >= start)
            *p1 = c;
@@ -2810,13 +4171,12 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
            }
          while ((p1 - start + hscroll - (hscroll > 0)) % tab_width);
        }
-      else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
-        p1 = copy_rope (p1, start, DISP_CHAR_VECTOR (dp, c), 0);
       else if (c < 0200 && ! NILP (buffer_defaults.ctl_arrow))
        {
          if (p1 >= start)
-           *p1 = (dp && XTYPE (DISP_CTRL_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_CTRL_GLYPH (dp)) : '^');
+           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
+                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                            0);
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = c ^ 0100;
@@ -2825,8 +4185,9 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
       else
        {
          if (p1 >= start)
-           *p1 = (dp && XTYPE (DISP_ESCAPE_GLYPH (dp)) == Lisp_Int
-                  ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\');
+           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
+                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                            0);
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = (c >> 6) + '0';
@@ -2840,10 +4201,10 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
        }
     }
 
-  if (c)
+  if (truncated)
     {
       p1 = end;
-      if (truncate) *p1++ = truncate;
+      if (truncate) *p1++ = fix_glyph (f, truncate, 0);
     }
   else if (mincol >= 0)
     {
@@ -2863,9 +4224,104 @@ display_string (w, vpos, string, hpos, truncate, mincol, maxcol)
   }
 }
 \f
+/* This is like a combination of memq and assq.
+   Return 1 if PROPVAL appears as an element of LIST
+   or as the car of an element of LIST.
+   If PROPVAL is a list, compare each element against LIST
+   in that way, and return 1 if any element of PROPVAL is found in LIST.
+   Otherwise return 0.
+   This function cannot quit.  */
+
+int
+invisible_p (propval, list)
+     register Lisp_Object propval;
+     Lisp_Object list;
+{
+  register Lisp_Object tail, proptail;
+  for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      register Lisp_Object tem;
+      tem = XCONS (tail)->car;
+      if (EQ (propval, tem))
+       return 1;
+      if (CONSP (tem) && EQ (propval, XCONS (tem)->car))
+       return 1;
+    }
+  if (CONSP (propval))
+    for (proptail = propval; CONSP (proptail);
+        proptail = XCONS (proptail)->cdr)
+      {
+       Lisp_Object propelt;
+       propelt = XCONS (proptail)->car;
+       for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+         {
+           register Lisp_Object tem;
+           tem = XCONS (tail)->car;
+           if (EQ (propelt, tem))
+             return 1;
+           if (CONSP (tem) && EQ (propelt, XCONS (tem)->car))
+             return 1;
+         }
+      }
+  return 0;
+}
+
+/* Return 1 if PROPVAL appears as the car of an element of LIST
+   and the cdr of that element is non-nil.
+   If PROPVAL is a list, check each element of PROPVAL in that way,
+   and the first time some element is found,
+   return 1 if the cdr of that element is non-nil.
+   Otherwise return 0.
+   This function cannot quit.  */
+
+int
+invisible_ellipsis_p (propval, list)
+     register Lisp_Object propval;
+     Lisp_Object list;
+{
+  register Lisp_Object tail, proptail;
+  for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      register Lisp_Object tem;
+      tem = XCONS (tail)->car;
+      if (CONSP (tem) && EQ (propval, XCONS (tem)->car))
+       return ! NILP (XCONS (tem)->cdr);
+    }
+  if (CONSP (propval))
+    for (proptail = propval; CONSP (proptail);
+        proptail = XCONS (proptail)->cdr)
+      {
+       Lisp_Object propelt;
+       propelt = XCONS (proptail)->car;
+       for (tail = list; CONSP (tail); tail = XCONS (tail)->cdr)
+         {
+           register Lisp_Object tem;
+           tem = XCONS (tail)->car;
+           if (CONSP (tem) && EQ (propelt, XCONS (tem)->car))
+             return ! NILP (XCONS (tem)->cdr);
+         }
+      }
+  return 0;
+}
+\f
 void
 syms_of_xdisp ()
 {
+  staticpro (&Qmenu_bar_update_hook);
+  Qmenu_bar_update_hook = intern ("menu-bar-update-hook");
+
+  staticpro (&Qoverriding_terminal_local_map);
+  Qoverriding_terminal_local_map = intern ("overriding-terminal-local-map");
+
+  staticpro (&Qoverriding_local_map);
+  Qoverriding_local_map = intern ("overriding-local-map");
+
+  staticpro (&Qwindow_scroll_functions);
+  Qwindow_scroll_functions = intern ("window-scroll-functions");
+
+  staticpro (&Qredisplay_end_trigger_functions);
+  Qredisplay_end_trigger_functions = intern ("redisplay-end-trigger-functions");
+
   staticpro (&last_arrow_position);
   staticpro (&last_arrow_string);
   last_arrow_position = Qnil;
@@ -2908,6 +4364,56 @@ If this is zero, point is always centered after it moves off frame.");
   DEFVAR_BOOL ("highlight-nonselected-windows", &highlight_nonselected_windows,
     "*Non-nil means highlight region even in nonselected windows.");
   highlight_nonselected_windows = 1;
+
+  DEFVAR_BOOL ("multiple-frames", &multiple_frames,
+    "Non-nil if more than one frame is visible on this display.\n\
+Minibuffer-only frames don't count, but iconified frames do.\n\
+This variable is not guaranteed to be accurate except while processing\n\
+`frame-title-format' and `icon-title-format'.");
+
+  DEFVAR_LISP ("frame-title-format", &Vframe_title_format,
+    "Template for displaying the titlebar of visible frames.\n\
+\(Assuming the window manager supports this feature.)\n\
+This variable has the same structure as `mode-line-format' (which see),\n\
+and is used only on frames for which no explicit name has been set\n\
+\(see `modify-frame-parameters').");
+  DEFVAR_LISP ("icon-title-format", &Vicon_title_format,
+    "Template for displaying the titlebar of an iconified frame.\n\
+\(Assuming the window manager supports this feature.)\n\
+This variable has the same structure as `mode-line-format' (which see),\n\
+and is used only on frames for which no explicit name has been set\n\
+\(see `modify-frame-parameters').");
+  Vicon_title_format
+    = Vframe_title_format
+    = Fcons (intern ("multiple-frames"),
+            Fcons (build_string ("%b"),
+                   Fcons (Fcons (build_string (""),
+                                 Fcons (intern ("invocation-name"),
+                                        Fcons (build_string ("@"),
+                                               Fcons (intern ("system-name"),
+                                                              Qnil)))),
+                          Qnil)));
+
+  DEFVAR_LISP ("message-log-max", &Vmessage_log_max,
+    "Maximum number of lines to keep in the message log buffer.\n\
+If nil, disable message logging.  If t, log messages but don't truncate\n\
+the buffer when it becomes large.");
+  XSETFASTINT (Vmessage_log_max, 50);
+
+  DEFVAR_LISP ("window-size-change-functions", &Vwindow_size_change_functions,
+    "Functions called before redisplay, if window sizes have changed.\n\
+The value should be a list of functions that take one argument.\n\
+Just before redisplay, for each frame, if any of its windows have changed\n\
+size since the last redisplay, or have been split or deleted,\n\
+all the functions in the list are called, with the frame as argument.");
+  Vwindow_size_change_functions = Qnil;
+
+  DEFVAR_LISP ("window-scroll-functions", &Vwindow_scroll_functions,
+    "List of Functions to call before redisplaying a window with scrolling.\n\
+Each function is called with two arguments, the window\n\
+and its new display-start position.  Note that the value of `window-end'\n\
+is not valid when these functions are called.");
+  Vwindow_scroll_functions = Qnil;
 }
 
 /* initialize the window system */
@@ -2930,12 +4436,12 @@ init_xdisp ()
   if (!noninteractive)
     {
       FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (root_window)));
-      XFASTINT (XWINDOW (root_window)->top) = 0;
+      XSETFASTINT (XWINDOW (root_window)->top, 0);
       set_window_height (root_window, FRAME_HEIGHT (f) - 1, 0);
-      XFASTINT (mini_w->top) = FRAME_HEIGHT (f) - 1;
+      XSETFASTINT (mini_w->top, FRAME_HEIGHT (f) - 1);
       set_window_height (minibuf_window, 1, 0);
 
-      XFASTINT (XWINDOW (root_window)->width) = FRAME_WIDTH (f);
-      XFASTINT (mini_w->width) = FRAME_WIDTH (f);
+      XSETFASTINT (XWINDOW (root_window)->width, FRAME_WIDTH (f));
+      XSETFASTINT (mini_w->width, FRAME_WIDTH (f));
     }
 }