Merge r21-4-11-chise-0_20-=ucs.
[chise/xemacs-chise.git.1] / src / redisplay.c
index 895f520..e40c348 100644 (file)
@@ -40,16 +40,17 @@ Boston, MA 02111-1307, USA.  */
 
 #include <config.h>
 #include "lisp.h"
-#include <limits.h>
 
 #include "buffer.h"
 #include "commands.h"
 #include "debug.h"
 #include "device.h"
+#include "elhash.h"
 #include "extents.h"
 #include "faces.h"
 #include "frame.h"
 #include "glyphs.h"
+#include "gutter.h"
 #include "insdel.h"
 #include "menubar.h"
 #include "objects.h"
@@ -62,11 +63,10 @@ Boston, MA 02111-1307, USA.  */
 #include "file-coding.h"
 #endif
 
+#include "sysfile.h"
+
 #ifdef HAVE_TTY
 #include "console-tty.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h> /* for isatty() */
-#endif
 #endif /* HAVE_TTY */
 
 /* Note: We have to be careful throughout this code to properly handle
@@ -85,16 +85,10 @@ Boston, MA 02111-1307, USA.  */
 #define LEFT_GLYPHS    2
 #define RIGHT_GLYPHS   3
 
-/* Set the vertical clip to 0 if we are currently updating the line
-   start cache.  Otherwise for buffers of line height 1 it may fail to
-   be able to work properly because regenerate_window will not layout
-   a single line.  */
 #define VERTICAL_CLIP(w, display)                                      \
-  (updating_line_start_cache                                           \
-   ? 0                                                                 \
-   : ((WINDOW_TTY_P (w) | (!display && scroll_on_clipped_lines))       \
+    ((WINDOW_TTY_P (w) | (!display && scroll_on_clipped_lines))                \
       ? INT_MAX                                                                \
-      : vertical_clip))
+      : vertical_clip)
 
 /* The following structures are completely private to redisplay.c so
    we put them here instead of in a header file, for modularity. */
@@ -106,6 +100,9 @@ typedef struct position_redisplay_data_type
   /* This information is normally filled in by the create_*_block
      routines and is used by the add_*_rune routines. */
   Lisp_Object window;
+  /* if we are working with strings rather than buffers we need a
+     handle to the string */
+  Lisp_Object string;
   struct device *d;
   struct display_block *db;
   struct display_line *dl;
@@ -149,6 +146,10 @@ typedef struct position_redisplay_data_type
                           (those off the left side of the screen) need
                           to be skipped before anything is displayed. */
   Bytind bi_start_col_enabled;
+  int start_col_xoffset;       /* Number of pixels that still need to
+                                   be skipped.  This is used for
+                                   horizontal scrolling of glyphs, where we want
+                                   to be able to scroll over part of the glyph. */
 
   int hscroll_glyph_width_adjust;  /* how much the width of the hscroll
                                      glyph differs from space_width (w).
@@ -162,18 +163,20 @@ typedef struct position_redisplay_data_type
   struct extent_fragment *ef;
   face_index findex;
 
-  /* The height of a pixmap may either be predetermined if the user
-     has set a baseline value, or it may be dependent on whatever the
-     line ascent and descent values end up being, based just on font
-     information.  In the first case we can immediately update the
-     values, thus their inclusion here.  In the last case we cannot
-     determine the actual contribution to the line height until we
-     have finished laying out all text on the line.  Thus we propagate
-     the max height of such pixmaps and do a final calculation after
-     all text has been added to the line. */
+  /* The height of a pixmap may either be predetermined if the user has set a
+     baseline value, or it may be dependent on whatever the line ascent and
+     descent values end up being, based just on font and pixmap-ascent
+     information.  In the first case we can immediately update the values, thus
+     their inclusion here.  In the last case we cannot determine the actual
+     contribution to the line height until we have finished laying out all text
+     on the line.  Thus we propagate the max height of such pixmaps and do a
+     final calculation (in calculate_baseline()) after all text has been added
+     to the line. */
   int new_ascent;
   int new_descent;
   int max_pixmap_height;
+  int need_baseline_computation;
+  int end_glyph_width;         /* Well, it is the kitchen sink after all ... */
 
   Lisp_Object result_str; /* String where we put the result of
                             generating a formatted string in the modeline. */
@@ -191,15 +194,24 @@ enum prop_type
   PROP_STRING,
   PROP_CHAR,
   PROP_MINIBUF_PROMPT,
-  PROP_BLANK
+  PROP_BLANK,
+  PROP_GLYPH
 };
 
 /* Data that should be propagated to the next line.  Either a single
-   Emchar or a string of Bufbyte's.
+   Emchar, a string of Bufbyte's or a glyph.
 
    The actual data that is propagated ends up as a Dynarr of these
    blocks.
 
+   prop_blocks are used to indicate that data that was supposed to go
+   on the previous line couldn't actually be displayed. Generally this
+   shouldn't happen if we are clipping the end of lines. If we are
+   wrapping then we need to display the propagation data before moving
+   on. Its questionable whether we should wrap or clip glyphs in this
+   instance. Most e-lisp relies on clipping so we preserve this
+   behavior.
+
    #### It's unclean that both Emchars and Bufbytes are here.
    */
 
@@ -228,6 +240,14 @@ struct prop_block
       int width;
       face_index findex;
     } p_blank;
+
+    struct
+    {
+      /* Not used as yet, but could be used to wrap rather than clip glyphs. */
+      int width;               
+      Lisp_Object glyph;
+    } p_glyph;
+
   } data;
 };
 
@@ -237,25 +257,20 @@ typedef struct
 } prop_block_dynarr;
 
 
-static void generate_formatted_string_db (Lisp_Object format_str,
-                                         Lisp_Object result_str,
-                                         struct window *w,
-                                         struct display_line *dl,
-                                         struct display_block *db,
-                                         face_index findex, int min_pixpos,
-                                         int max_pixpos, int type);
 static Charcount generate_fstring_runes (struct window *w, pos_data *data,
                                         Charcount pos, Charcount min_pos,
                                         Charcount max_pos, Lisp_Object elt,
                                         int depth, int max_pixsize,
-                                        face_index findex, int type);
+                                        face_index findex, int type,
+                                        Charcount *offset,
+                                        Lisp_Object cur_ext);
 static prop_block_dynarr *add_glyph_rune (pos_data *data,
                                          struct glyph_block *gb,
                                          int pos_type, int allow_cursor,
                                          struct glyph_cachel *cachel);
 static Bytind create_text_block (struct window *w, struct display_line *dl,
-                                Bytind bi_start_pos, int start_col,
-                                prop_block_dynarr **prop, int type);
+                                Bytind bi_start_pos, prop_block_dynarr **prop,
+                                int type);
 static int create_overlay_glyph_block (struct window *w,
                                       struct display_line *dl);
 static void create_left_glyph_block (struct window *w,
@@ -269,6 +284,9 @@ static void free_display_line (struct display_line *dl);
 static void update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
                                     Bufpos point, int no_regen);
 static int point_visible (struct window *w, Bufpos point, int type);
+static void calculate_yoffset (struct display_line *dl,
+                               struct display_block *fixup);
+static void calculate_baseline (pos_data *data);
 
 /* This used to be 10 but 30 seems to give much better performance. */
 #define INIT_MAX_PREEMPTS      30
@@ -291,10 +309,6 @@ static int max_preempts;
    isn't any reason we need more than a single set. */
 display_line_dynarr *cmotion_display_lines;
 
-/* Used by generate_formatted_string.  Global because they get used so
-   much that the dynamic allocation time adds up. */
-Emchar_dynarr *formatted_string_emchar_dynarr;
-struct display_line formatted_string_display_line;
 /* We store the extents that we need to generate in a Dynarr and then
    frob them all on at the end of generating the string.  We do it
    this way rather than adding them as we generate the string because
@@ -302,13 +316,13 @@ struct display_line formatted_string_display_line;
    (to avoid having to resize the string multiple times), and we don't
    want to go around adding extents to a string when the extents might
    stretch off the end of the string. */
-EXTENT_dynarr *formatted_string_extent_dynarr;
-Bytecount_dynarr *formatted_string_extent_start_dynarr;
-Bytecount_dynarr *formatted_string_extent_end_dynarr;
+static EXTENT_dynarr *formatted_string_extent_dynarr;
+static Bytecount_dynarr *formatted_string_extent_start_dynarr;
+static Bytecount_dynarr *formatted_string_extent_end_dynarr;
 
 
 /* #### probably temporary */
-int cache_adjustment;
+Fixnum cache_adjustment;
 
 /* This holds a string representing the text corresponding to a single
    modeline % spec. */
@@ -323,13 +337,10 @@ int disable_preemption;   /* Used for debugging redisplay and for
 static int preemption_count;
 
 /* Minimum pixel height of clipped bottom display line. */
-int vertical_clip;
+Fixnum vertical_clip;
 
 /* Minimum visible pixel width of clipped glyphs at right margin. */
-int horizontal_clip;
-
-/* Set if currently inside update_line_start_cache. */
-int updating_line_start_cache;
+Fixnum horizontal_clip;
 
 /* Nonzero means reading single-character input with prompt
    so put cursor on minibuffer after the prompt.  */
@@ -364,6 +375,15 @@ int frame_changed;
 int glyphs_changed;
 int glyphs_changed_set;
 
+/* non-zero if any subwindow has been deleted. */
+int subwindows_changed;
+int subwindows_changed_set;
+
+/* non-zero if any displayed subwindow is in need of updating
+   somewhere. */
+int subwindows_state_changed;
+int subwindows_state_changed_set;
+
 /* This variable is 1 if the icon has to be updated.
  It is set to 1 when `frame-icon-glyph' changes. */
 int icon_changed;
@@ -394,6 +414,14 @@ int asynch_device_change_pending;
 int toolbar_changed;
 int toolbar_changed_set;
 
+/* Nonzero if some frame has changed the layout of internal elements
+   (gutters or toolbars). */
+int frame_layout_changed;
+
+/* non-nil if any gutter has changed */
+int gutter_changed;
+int gutter_changed_set;
+
 /* non-nil if any window has changed since the last time redisplay completed */
 int windows_changed;
 
@@ -405,10 +433,9 @@ int windows_structure_changed;
 Lisp_Object Vbar_cursor;
 Lisp_Object Qbar_cursor;
 
-
-int visible_bell;      /* If true and the terminal will support it
-                          then the frame will flash instead of
-                          beeping when an error occurs */
+Lisp_Object Vvisible_bell;     /* If true and the terminal will support it
+                                  then the frame will flash instead of
+                                  beeping when an error occurs */
 
 /* Nonzero means no need to redraw the entire frame on resuming
    a suspended Emacs.  This is useful on terminals with multiple pages,
@@ -424,10 +451,10 @@ Lisp_Object Vglobal_mode_string;
 
 /* The number of lines scroll a window by when point leaves the window; if
   it is <=0 then point is centered in the window */
-int scroll_step;
+Fixnum scroll_step;
 
 /* Scroll up to this many lines, to bring point back on screen. */
-int scroll_conservatively;
+Fixnum scroll_conservatively;
 
 /* Marker for where to display an arrow on top of the buffer text.  */
 Lisp_Object Voverlay_arrow_position;
@@ -435,9 +462,12 @@ Lisp_Object Voverlay_arrow_position;
 Lisp_Object Voverlay_arrow_string;
 
 Lisp_Object Vwindow_size_change_functions;
-Lisp_Object Qwindow_scroll_functions, Vwindow_scroll_functions;
+Lisp_Object Vwindow_scroll_functions;
 Lisp_Object Qredisplay_end_trigger_functions, Vredisplay_end_trigger_functions;
 
+Lisp_Object Qbuffer_list_changed_hook, Vbuffer_list_changed_hook;
+
+
 #define INHIBIT_REDISPLAY_HOOKS  /* #### Until we've thought about
                                    this more. */
 #ifndef INHIBIT_REDISPLAY_HOOKS
@@ -447,7 +477,8 @@ Lisp_Object Vpre_redisplay_hook, Vpost_redisplay_hook;
 Lisp_Object Qpre_redisplay_hook, Qpost_redisplay_hook;
 #endif /* INHIBIT_REDISPLAY_HOOKS */
 
-int last_display_warning_tick, display_warning_tick;
+static Fixnum last_display_warning_tick;
+static Fixnum display_warning_tick;
 Lisp_Object Qdisplay_warning_buffer;
 int inhibit_warning_display;
 
@@ -457,6 +488,11 @@ Lisp_Object Vuse_left_overflow, Vuse_right_overflow;
 Lisp_Object Vtext_cursor_visible_p;
 
 int column_number_start_at_one;
+
+Lisp_Object Qtop_bottom;
+
+#define WINDOW_SCROLLED(w) ((w)->hscroll > 0 || (w)->left_xoffset)
+
 \f
 /***************************************************************************/
 /*                                                                        */
@@ -465,13 +501,13 @@ int column_number_start_at_one;
 /***************************************************************************/
 
 static int
-redisplay_text_width_emchar_string (struct window *w, int findex,
-                                   Emchar *str, Charcount len)
+redisplay_text_width_charc_string (struct window *w, int findex,
+                                  Charc *str, Charcount len)
 {
-  unsigned char charsets[NUM_LEADING_BYTES];
+  Charset_ID charsets[NUM_LEADING_BYTES];
   Lisp_Object window;
 
-  find_charsets_in_emchar_string (charsets, str, len);
+  find_charsets_in_charc_string (charsets, str, len);
   XSETWINDOW (window, w);
   ensure_face_cachel_complete (WINDOW_FACE_CACHEL (w, findex), window,
                               charsets);
@@ -480,24 +516,24 @@ redisplay_text_width_emchar_string (struct window *w, int findex,
                               WINDOW_FACE_CACHEL (w, findex), str, len));
 }
 
-static Emchar_dynarr *rtw_emchar_dynarr;
+static Charc_dynarr *rtw_charc_dynarr;
 
 int
 redisplay_text_width_string (struct window *w, int findex,
                             Bufbyte *nonreloc, Lisp_Object reloc,
                             Bytecount offset, Bytecount len)
 {
-  if (!rtw_emchar_dynarr)
-    rtw_emchar_dynarr = Dynarr_new (Emchar);
-  Dynarr_reset (rtw_emchar_dynarr);
+  if (!rtw_charc_dynarr)
+    rtw_charc_dynarr = Dynarr_new (Charc);
+  Dynarr_reset (rtw_charc_dynarr);
 
   fixup_internal_substring (nonreloc, reloc, offset, &len);
   if (STRINGP (reloc))
     nonreloc = XSTRING_DATA (reloc);
-  convert_bufbyte_string_into_emchar_dynarr (nonreloc, len, rtw_emchar_dynarr);
-  return redisplay_text_width_emchar_string
-    (w, findex, Dynarr_atp (rtw_emchar_dynarr, 0),
-     Dynarr_length (rtw_emchar_dynarr));
+  convert_bufbyte_string_into_charc_dynarr (nonreloc, len, rtw_charc_dynarr);
+  return redisplay_text_width_charc_string
+    (w, findex, Dynarr_atp (rtw_charc_dynarr, 0),
+     Dynarr_length (rtw_charc_dynarr));
 }
 
 int
@@ -505,26 +541,26 @@ redisplay_frame_text_width_string (struct frame *f, Lisp_Object face,
                                   Bufbyte *nonreloc, Lisp_Object reloc,
                                   Bytecount offset, Bytecount len)
 {
-  unsigned char charsets[NUM_LEADING_BYTES];
+  Charset_ID charsets[NUM_LEADING_BYTES];
   Lisp_Object frame;
   struct face_cachel cachel;
 
-  if (!rtw_emchar_dynarr)
-    rtw_emchar_dynarr = Dynarr_new (Emchar);
-  Dynarr_reset (rtw_emchar_dynarr);
+  if (!rtw_charc_dynarr)
+    rtw_charc_dynarr = Dynarr_new (Charc);
+  Dynarr_reset (rtw_charc_dynarr);
 
   fixup_internal_substring (nonreloc, reloc, offset, &len);
   if (STRINGP (reloc))
     nonreloc = XSTRING_DATA (reloc);
-  convert_bufbyte_string_into_emchar_dynarr (nonreloc, len, rtw_emchar_dynarr);
+  convert_bufbyte_string_into_charc_dynarr (nonreloc, len, rtw_charc_dynarr);
   find_charsets_in_bufbyte_string (charsets, nonreloc, len);
   reset_face_cachel (&cachel);
   cachel.face = face;
   XSETFRAME (frame, f);
   ensure_face_cachel_complete (&cachel, frame, charsets);
   return DEVMETH (XDEVICE (FRAME_DEVICE (f)),
-                 text_width, (f, &cachel, Dynarr_atp (rtw_emchar_dynarr, 0),
-                              Dynarr_length (rtw_emchar_dynarr)));
+                 text_width, (f, &cachel, Dynarr_atp (rtw_charc_dynarr, 0),
+                              Dynarr_length (rtw_charc_dynarr)));
 }
 
 /* Return the display block from DL of the given TYPE.  A display line
@@ -626,8 +662,8 @@ next_tab_position (struct window *w, int start_pixpos, int left_pixpos)
   int pix_tab_width = tab_pix_width (w);
 
   /* Adjust n_pos for any hscrolling which has happened. */
-  if (w->hscroll > 1)
-    n_pos -= space_width (w) * (w->hscroll - 1);
+  if (WINDOW_SCROLLED (w))
+    n_pos -= space_width (w) * (w->hscroll - 1) + w->left_xoffset;
 
   while (n_pos <= start_pixpos)
     n_pos += pix_tab_width;
@@ -670,6 +706,108 @@ calculate_display_line_boundaries (struct window *w, int modeline)
   return bounds;
 }
 
+/* This takes a display_block and its containing line and corrects the yoffset
+   of each glyph in the block to cater for the ascent of the line as a
+   whole. Must be called *after* the line-ascent is known! */
+
+static void
+calculate_yoffset (struct display_line *dl, struct display_block *fixup)
+{
+  int i;
+  for (i=0; i<Dynarr_length (fixup->runes); i++)
+    {
+      struct rune *r = Dynarr_atp (fixup->runes,i);
+      if (r->type == RUNE_DGLYPH)
+        {
+          if (r->object.dglyph.ascent < dl->ascent)
+            r->object.dglyph.yoffset = dl->ascent - r->object.dglyph.ascent +
+             r->object.dglyph.descent;
+        }
+    }
+}
+
+/* Calculate the textual baseline (the ascent and descent values for the
+   display_line as a whole).
+
+   If the baseline is completely blank, or contains no manually positioned
+   glyphs, then the textual baseline is simply the baseline of the default font.
+   (The `contains no manually positioned glyphs' part is actually done for
+   us by `add_emchar_rune'.)
+
+   If the baseline contains pixmaps, and they're all manually positioned, then
+   the textual baseline location is constrained that way, and we need do no
+   work.
+
+   If the baseline contains pixmaps, and at least one is automatically
+   positioned, then the textual ascent is the largest ascent on the line, and
+   the textual descent is the largest descent (which is how things are set up at
+   entry to this function anyway): except that if the max_ascent + max_descent
+   is too small for the height of the line (say you've adjusted the baseline of
+   a short glyph, and there's a tall one next to it), then take the ascent and
+   descent for the line individually from the largest of the explicitly set
+   ascent/descent, and the rescaled ascent/descent of the default font, scaled
+   such that the largest glyph will fit.
+
+   This means that if you have a short glyph (but taller than the default
+   font's descent) forced right under the baseline, and a really tall
+   automatically positioned glyph, that the descent for the line is just big
+   enough for the manually positioned short glyph, and the tall one uses as
+   much of that space as the default font would were it as tall as the tall
+   glyph; but that the ascent is big enough for the tall glyph to fit.
+
+   This behaviour means that under no circumstances will changing the baseline
+   of a short glyph cause a tall glyph to move around; nor will it move the
+   textual baseline more than necessary. (Changing a tall glyph's baseline
+   might move the text's baseline arbitrarily, of course.) */
+
+static void
+calculate_baseline (pos_data *data)
+{
+  /* Blank line: baseline is default font's baseline. */
+
+  if (!data->new_ascent && !data->new_descent)
+    {
+      /* We've got a blank line so initialize these values from the default
+         face. */
+      default_face_font_info (data->window, &data->new_ascent,
+                             &data->new_descent, 0, 0, 0);
+    }
+  
+  /* No automatically positioned glyphs? Return at once. */
+  if (!data->need_baseline_computation)
+    return;
+
+  /* Is the tallest glyph on the line automatically positioned?
+     If it's manually positioned, or it's automatically positioned
+     and there's enough room for it anyway, we need do no more work. */
+  if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+    {
+      int default_font_ascent, default_font_descent, default_font_height;
+      int scaled_default_font_ascent, scaled_default_font_descent;
+      
+      default_face_font_info (data->window, &default_font_ascent,
+                             &default_font_descent, &default_font_height,
+                             0, 0);
+
+      scaled_default_font_ascent = data->max_pixmap_height *
+       default_font_ascent / default_font_height;
+
+      data->new_ascent = max (data->new_ascent, scaled_default_font_ascent);
+
+      /* The ascent may have expanded now. Do we still need to grow the descent,
+         or are things big enough?
+
+         The +1 caters for the baseline row itself. */
+      if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+        {
+          scaled_default_font_descent = (data->max_pixmap_height *
+                                        default_font_descent / default_font_height) + 1;
+
+          data->new_descent = max (data->new_descent, scaled_default_font_descent);
+        }
+    }
+}
+
 /* Given a display line and a starting position, ensure that the
    contents of the display line accurately represent the visual
    representation of the buffer contents starting from the given
@@ -679,8 +817,8 @@ calculate_display_line_boundaries (struct window *w, int modeline)
 
 static Bufpos
 generate_display_line (struct window *w, struct display_line *dl, int bounds,
-                      Bufpos start_pos, int start_col,
-                      prop_block_dynarr **prop, int type)
+                      Bufpos start_pos, prop_block_dynarr **prop,
+                      int type)
 {
   Bufpos ret_bufpos;
   int overlay_width;
@@ -712,7 +850,7 @@ generate_display_line (struct window *w, struct display_line *dl, int bounds,
     /* #### urk urk urk!!! Chuck fix this shit! */
     Bytind hacked_up_bytind =
       create_text_block (w, dl, bufpos_to_bytind (b, start_pos),
-                        start_col, prop, type);
+                        prop, type);
     if (hacked_up_bytind > BI_BUF_ZV (b))
       ret_bufpos = BUF_ZV (b) + 1;
     else
@@ -786,7 +924,7 @@ add_hscroll_rune (pos_data *data)
   gb.glyph = Vhscroll_glyph;
   {
     int oldpixpos = data->pixpos;
-    retval = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 1,
+    retval = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0,
                             GLYPH_CACHEL (XWINDOW (data->window),
                                           HSCROLL_GLYPH_INDEX));
     data->hscroll_glyph_width_adjust =
@@ -801,15 +939,18 @@ add_hscroll_rune (pos_data *data)
   return retval;
 }
 
-/* Adds a character rune to a display block.  If there is not enough
-   room to fit the rune on the display block (as determined by the
-   MAX_PIXPOS) then it adds nothing and returns ADD_FAILED. */
+/* Adds a character rune to a display block.  If there is not enough room
+   to fit the rune on the display block (as determined by the MAX_PIXPOS)
+   then it adds nothing and returns ADD_FAILED.  If
+   NO_CONTRIBUTE_TO_LINE_HEIGHT is non-zero, don't allow the char's height
+   to affect the total line height. (See add_intbyte_string_runes()). */
 
 static prop_block_dynarr *
-add_emchar_rune (pos_data *data)
+add_emchar_rune_1 (pos_data *data, int no_contribute_to_line_height)
 {
   struct rune rb, *crb;
   int width, local;
+  Charc char_glyph;
 
   if (data->start_col)
     {
@@ -826,13 +967,17 @@ add_emchar_rune (pos_data *data)
 
   if (data->ch == '\n')
     {
+      char_glyph = ASCII_TO_CHARC ('\n');
       data->font_is_bogus = 0;
       /* Cheesy end-of-line pseudo-character. */
       width = data->blank_width;
     }
   else
     {
-      Lisp_Object charset = CHAR_CHARSET (data->ch);
+      Lisp_Object charset;
+
+      char_glyph = CHAR_TO_CHARC (data->ch);
+      charset = CHARC_CHARSET (char_glyph);
       if (!EQ (charset, data->last_charset) ||
          data->findex != data->last_findex)
        {
@@ -842,7 +987,7 @@ add_emchar_rune (pos_data *data)
          Lisp_Object font_instance =
            ensure_face_cachel_contains_charset (cachel, data->window,
                                                 charset);
-         struct Lisp_Font_Instance *fi;
+         Lisp_Font_Instance *fi;
 
          if (EQ (font_instance, Vthe_null_font_instance))
            {
@@ -858,8 +1003,13 @@ add_emchar_rune (pos_data *data)
            data->last_char_width = fi->width;
          else
            data->last_char_width = -1;
-         data->new_ascent  = max (data->new_ascent,  (int) fi->ascent);
-         data->new_descent = max (data->new_descent, (int) fi->descent);
+
+         if (!no_contribute_to_line_height)
+           {
+             data->new_ascent  = max (data->new_ascent,  (int) fi->ascent);
+             data->new_descent = max (data->new_descent, (int) fi->descent);
+           }
+
          data->last_charset = charset;
          data->last_findex = data->findex;
        }
@@ -868,9 +1018,9 @@ add_emchar_rune (pos_data *data)
       if (width < 0)
        {
          /* bummer.  Proportional fonts. */
-         width = redisplay_text_width_emchar_string (XWINDOW (data->window),
-                                                     data->findex,
-                                                     &data->ch, 1);
+         width = redisplay_text_width_charc_string (XWINDOW (data->window),
+                                                    data->findex,
+                                                    &char_glyph, 1);
        }
     }
 
@@ -894,18 +1044,25 @@ add_emchar_rune (pos_data *data)
   crb->xpos = data->pixpos;
   crb->width = width;
   if (data->bi_bufpos)
-    crb->bufpos =
-      bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
-                       data->bi_bufpos);
+    {
+      if (NILP (data->string))
+       crb->bufpos =
+         bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
+                           data->bi_bufpos);
+      else
+       crb->bufpos =
+         bytecount_to_charcount (XSTRING_DATA (data->string),
+                                 data->bi_bufpos);
+    }
   else if (data->is_modeline)
     crb->bufpos = data->modeline_charpos;
   else
-    /* fuckme if this shouldn't be an abort. */
-    /* abort (); fuckme harder, this abort gets tripped quite often,
-                in propagation and whatnot.  #### fixme */
+    /* Text but not in buffer */
     crb->bufpos = 0;
   crb->type = RUNE_CHAR;
-  crb->object.chr.ch = data->font_is_bogus ? '~' : data->ch;
+  crb->object.cglyph = data->font_is_bogus
+    ? ASCII_TO_CHARC ('~')
+    : char_glyph;
   crb->endpos = 0;
 
   if (data->cursor_type == CURSOR_ON)
@@ -939,13 +1096,24 @@ add_emchar_rune (pos_data *data)
   return NULL;
 }
 
-/* Given a string C_STRING of length C_LENGTH, call add_emchar_rune
-   for each character in the string.  Propagate any left-over data
-   unless NO_PROP is non-zero. */
-
+static prop_block_dynarr *
+add_emchar_rune (pos_data *data)
+{
+  return add_emchar_rune_1 (data, 0);
+}
+  
+/* Given a string C_STRING of length C_LENGTH, call add_emchar_rune for
+   each character in the string.  Propagate any left-over data unless
+   NO_PROP is non-zero.  If NO_CONTRIBUTE_TO_LINE_HEIGHT is non-zero, don't
+   allow this character to increase the total height of the line. (This is
+   used when the character is part of a text glyph.  In that case, the
+   glyph code itself adjusts the line height as necessary, depending on
+   whether glyph-contrib-p is true.) */
+  
 static prop_block_dynarr *
 add_bufbyte_string_runes (pos_data *data, Bufbyte *c_string,
-                         Bytecount c_length, int no_prop)
+                         Bytecount c_length, int no_prop,
+                         int no_contribute_to_line_height)
 {
   Bufbyte *pos, *end = c_string + c_length;
   prop_block_dynarr *prop;
@@ -958,9 +1126,11 @@ add_bufbyte_string_runes (pos_data *data, Bufbyte *c_string,
 
   for (pos = c_string; pos < end;)
     {
+      Bufbyte *old_pos = pos;
+
       data->ch = charptr_emchar (pos);
 
-      prop = add_emchar_rune (data);
+      prop = add_emchar_rune_1 (data, no_contribute_to_line_height);
 
       if (prop)
        {
@@ -983,6 +1153,9 @@ add_bufbyte_string_runes (pos_data *data, Bufbyte *c_string,
        }
       INC_CHARPTR (pos);
       assert (pos <= end);
+      /* #### Duplicate code from add_string_to_fstring_db_runes
+        should we do more?*/
+      data->bytepos += pos - old_pos;
     }
 
   return NULL;
@@ -1092,12 +1265,11 @@ add_blank_rune (pos_data *data, struct window *w, int char_tab_width)
 static prop_block_dynarr *
 add_octal_runes (pos_data *data)
 {
-  prop_block_dynarr *prop, *add_failed;
+  prop_block_dynarr *add_failed, *prop = 0;
   Emchar orig_char = data->ch;
   unsigned int orig_cursor_type = data->cursor_type;
 
   /* Initialize */
-  prop = NULL;
   add_failed = NULL;
 
   if (data->start_col)
@@ -1157,7 +1329,7 @@ add_octal_runes (pos_data *data)
   ADD_NEXT_OCTAL_RUNE_CHAR;
 
   data->cursor_type = orig_cursor_type;
-  return prop;
+  return NULL;
 }
 
 #undef ADD_NEXT_OCTAL_RUNE_CHAR
@@ -1232,71 +1404,17 @@ add_control_char_runes (pos_data *data, struct buffer *b)
     }
 }
 
-/* Given a display table entry, call the appropriate functions to
-   display each element of the entry. */
-
 static prop_block_dynarr *
-add_disp_table_entry_runes (pos_data *data, Lisp_Object entry)
+add_disp_table_entry_runes_1 (pos_data *data, Lisp_Object entry)
 {
   prop_block_dynarr *prop = NULL;
 
-  if (VECTORP (entry))
-    {
-      struct Lisp_Vector *de = XVECTOR (entry);
-      long len = vector_length (de);
-      int elt;
-
-      for (elt = 0; elt < len; elt++)
-       {
-         if (NILP (de->contents[elt]))
-           continue;
-         else if (STRINGP (de->contents[elt]))
-           {
-             prop =
-               add_bufbyte_string_runes
-                 (data,
-                  XSTRING_DATA   (de->contents[elt]),
-                  XSTRING_LENGTH (de->contents[elt]),
-                  0);
-           }
-         else if (GLYPHP (de->contents[elt]))
-           {
-             if (data->start_col)
-               data->start_col--;
-
-             if (!data->start_col && data->bi_start_col_enabled)
-               {
-                 prop = add_hscroll_rune (data);
-               }
-             else
-               {
-                 struct glyph_block gb;
-
-                 gb.glyph = de->contents[elt];
-                 gb.extent = Qnil;
-                 prop = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0, 0);
-               }
-           }
-         else if (CHAR_OR_CHAR_INTP (de->contents[elt]))
-           {
-             data->ch = XCHAR_OR_CHAR_INT (de->contents[elt]);
-             prop = add_emchar_rune (data);
-           }
-         /* Else blow it off because someone added a bad entry and we
-             don't have any safe way of signaling an error. */
-
-         /* #### Still need to add any remaining elements to the
-             propagation information. */
-         if (prop)
-           return prop;
-       }
-    }
-  else if (STRINGP (entry))
+  if (STRINGP (entry))
     {
       prop = add_bufbyte_string_runes (data,
                                       XSTRING_DATA   (entry),
                                       XSTRING_LENGTH (entry),
-                                      0);
+                                      0, 0);
     }
   else if (GLYPHP (entry))
     {
@@ -1321,10 +1439,80 @@ add_disp_table_entry_runes (pos_data *data, Lisp_Object entry)
       data->ch = XCHAR_OR_CHAR_INT (entry);
       prop = add_emchar_rune (data);
     }
+  else if (CONSP (entry))
+    {
+      if (EQ (XCAR (entry), Qformat)
+         && CONSP (XCDR (entry))
+         && STRINGP (XCAR (XCDR (entry))))
+       {
+         Lisp_Object format = XCAR (XCDR (entry));
+         Bytind len = XSTRING_LENGTH (format);
+         Bufbyte *src = XSTRING_DATA (format), *end = src + len;
+         Bufbyte *result = alloca_array (Bufbyte, len);
+         Bufbyte *dst = result;
+
+         while (src < end)
+           {
+             Emchar c = charptr_emchar (src);
+             INC_CHARPTR (src);
+             if (c != '%' || src == end)
+               dst += set_charptr_emchar (dst, c);
+             else
+               {
+                 c = charptr_emchar (src);
+                 INC_CHARPTR (src);
+                 switch (c)
+                   {
+                     /*case 'x':
+                     dst += long_to_string_base ((char *)dst, data->ch, 16);
+                     break;*/
+                   case '%':
+                     dst += set_charptr_emchar (dst, '%');
+                     break;
+                     /* #### unimplemented */
+                   }
+               }
+           }
+         prop = add_bufbyte_string_runes (data, result, dst - result, 0, 0);
+       }
+    }
 
   /* Else blow it off because someone added a bad entry and we don't
-     have any safe way of signaling an error.  Hey, this comment
-     sounds familiar. */
+     have any safe way of signaling an error. */
+  return prop;
+}
+
+/* Given a display table entry, call the appropriate functions to
+   display each element of the entry. */
+
+static prop_block_dynarr *
+add_disp_table_entry_runes (pos_data *data, Lisp_Object entry)
+{
+  prop_block_dynarr *prop = NULL;
+  if (VECTORP (entry))
+    {
+      Lisp_Vector *de = XVECTOR (entry);
+      EMACS_INT len = vector_length (de);
+      int elt;
+
+      for (elt = 0; elt < len; elt++)
+       {
+         if (NILP (vector_data (de)[elt]))
+           continue;
+         else
+           prop = add_disp_table_entry_runes_1 (data, vector_data (de)[elt]);
+         /* Else blow it off because someone added a bad entry and we
+            don't have any safe way of signaling an error.  Hey, this
+            comment sounds familiar. */
+
+         /* #### Still need to add any remaining elements to the
+             propagation information. */
+         if (prop)
+           return prop;
+       }
+    }
+  else
+    prop = add_disp_table_entry_runes_1 (data, entry);
   return prop;
 }
 
@@ -1486,12 +1674,25 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
 {
   struct window *w = XWINDOW (data->window);
 
+  /* If window faces changed, and glyph instance is text, then
+     glyph sizes might have changed too */
+  invalidate_glyph_geometry_maybe (gb->glyph, w);
+
+  /* This makes sure the glyph is in the cachels.
+
+     #### We do this to make sure the glyph is in the glyph cachels,
+     so that the dirty flag can be reset after redisplay has
+     finished. We should do this some other way, maybe by iterating
+     over the window cache of subwindows. */
+  get_glyph_cachel_index (w, gb->glyph);
+
   /* A nil extent indicates a special glyph (ex. truncator). */
   if (NILP (gb->extent)
       || (pos_type == BEGIN_GLYPHS &&
          extent_begin_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT)
       || (pos_type == END_GLYPHS &&
-         extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT))
+         extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT)
+      || pos_type == LEFT_GLYPHS || pos_type == RIGHT_GLYPHS)
     {
       struct rune rb;
       int width;
@@ -1499,18 +1700,20 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
       int ascent, descent;
       Lisp_Object baseline;
       Lisp_Object face;
+      Lisp_Object instance;
+      face_index findex;
+      prop_block_dynarr *retval = 0;
 
       if (cachel)
        width = cachel->width;
       else
-       width = glyph_width (gb->glyph, Qnil, data->findex, data->window);
+       width = glyph_width (gb->glyph, data->window);
 
       if (!width)
        return NULL;
 
-      if (data->start_col)
+      if (data->start_col || data->start_col_xoffset)
        {
-         prop_block_dynarr *retval;
          int glyph_char_width = width / space_width (w);
 
          /* If we still have not fully scrolled horizontally after
@@ -1546,17 +1749,41 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
       if (data->pixpos + width > data->max_pixpos)
        {
          /* If this is the first object we are attempting to add to
-             the line then we ignore the horizontal_clip threshold.
-             Otherwise we will loop until the bottom of the window
-             continually failing to add this glyph because it is wider
-             than the window.  We could alternatively just completely
-             ignore the glyph and proceed from there but I think that
-             this is a better solution. */
+            the line then we ignore the horizontal_clip threshold.
+            Otherwise we will loop until the bottom of the window
+            continually failing to add this glyph because it is wider
+            than the window.  We could alternatively just completely
+            ignore the glyph and proceed from there but I think that
+            this is a better solution.
+            
+            This does, however, create a different problem in that we
+            can end up adding the object to every single line, never
+            getting any further - for instance an extent with a long
+            start-glyph that covers multitple following
+            characters.  */
          if (Dynarr_length (data->db->runes)
              && data->max_pixpos - data->pixpos < horizontal_clip)
            return ADD_FAILED;
-         else
+         else {
+           struct prop_block pb;
+
+           /* We need to account for the width of the end-of-line
+              glyph if there is nothing more in the line to display,
+              since we will not display it in this instance. It seems
+              kind of gross doing it here, but otherwise we have to
+              search the runes in create_text_block(). */
+           if (data->ch == '\n')
+             data->max_pixpos += data->end_glyph_width;
            width = data->max_pixpos - data->pixpos;
+           /* Add the glyph we are displaying, but clipping, to the
+              propagation data so that we don't try and do it
+              again. */ 
+           retval = Dynarr_new (prop_block);
+           pb.type = PROP_GLYPH;
+           pb.data.p_glyph.glyph = gb->glyph;
+           pb.data.p_glyph.width = width;
+           Dynarr_add (retval, pb);
+         }
        }
 
       if (cachel)
@@ -1566,13 +1793,14 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
        }
       else
        {
-         ascent = glyph_ascent (gb->glyph, Qnil, data->findex, data->window);
-         descent = glyph_descent (gb->glyph, Qnil, data->findex,
-                                  data->window);
+         ascent = glyph_ascent (gb->glyph, data->window);
+         descent = glyph_descent (gb->glyph, data->window);
        }
 
       baseline = glyph_baseline (gb->glyph, data->window);
 
+      rb.object.dglyph.descent = 0; /* Gets reset lower down, if it is known. */
+
       if (glyph_contrib_p (gb->glyph, data->window))
        {
          /* A pixmap that has not had a baseline explicitly set.  Its
@@ -1580,6 +1808,7 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
          if (NILP (baseline))
            {
              int height = ascent + descent;
+             data->need_baseline_computation = 1;
              data->max_pixmap_height = max (data->max_pixmap_height, height);
            }
 
@@ -1602,6 +1831,9 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
 
              data->new_ascent = max (data->new_ascent, pix_ascent);
              data->new_descent = max (data->new_descent, pix_descent);
+             data->max_pixmap_height = max (data->max_pixmap_height, height);
+             
+             rb.object.dglyph.descent = pix_descent;
            }
 
          /* Otherwise something is screwed up. */
@@ -1611,10 +1843,32 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
 
       face = glyph_face (gb->glyph, data->window);
       if (NILP (face))
-       rb.findex = data->findex;
+       findex = data->findex;
       else
-       rb.findex = get_builtin_face_cache_index (w, face);
+       findex = get_builtin_face_cache_index (w, face);
+
+      instance = glyph_image_instance (gb->glyph, data->window,
+                                      ERROR_ME_NOT, 1);
+      if (TEXT_IMAGE_INSTANCEP (instance))
+       {
+         Lisp_Object string = XIMAGE_INSTANCE_TEXT_STRING (instance);
+         face_index orig_findex = data->findex;
+         Bytind orig_bufpos = data->bi_bufpos;
+         Bytind orig_start_col_enabled = data->bi_start_col_enabled;
+
+         data->findex = findex;
+         data->bi_start_col_enabled = 0;
+         if (!allow_cursor)
+           data->bi_bufpos = 0;
+         add_bufbyte_string_runes (data, XSTRING_DATA (string),
+                                   XSTRING_LENGTH (string), 0, 1);
+         data->findex = orig_findex;
+         data->bi_bufpos = orig_bufpos;
+         data->bi_start_col_enabled = orig_start_col_enabled;
+         return retval;
+       }
 
+      rb.findex = findex;
       rb.xpos = data->pixpos;
       rb.width = width;
       rb.bufpos = 0;                   /* glyphs are never "at" anywhere */
@@ -1625,16 +1879,12 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
       else
         rb.endpos = 0;
       rb.type = RUNE_DGLYPH;
-      /* #### Ben sez: this is way bogus if the glyph is a string.
-        You should not make the output routines have to cope with
-        this.  The string could contain Mule characters, or non-
-        printable characters, or characters to be passed through
-        the display table, or non-character objects (when this gets
-        implemented), etc.  Instead, this routine here should parse
-        the string into a series of runes. */
       rb.object.dglyph.glyph = gb->glyph;
       rb.object.dglyph.extent = gb->extent;
       rb.object.dglyph.xoffset = xoffset;
+      rb.object.dglyph.ascent = ascent;
+      rb.object.dglyph.yoffset = 0;   /* Until we know better, assume that it has
+                                        a normal (textual) baseline. */
 
       if (allow_cursor)
        {
@@ -1670,7 +1920,7 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
       Dynarr_add (data->db->runes, rb);
       data->pixpos += width;
 
-      return NULL;
+      return retval;
     }
   else
     {
@@ -1699,7 +1949,7 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
        abort ();       /* there are no unknown types */
     }
 
-  return NULL; /* shut up compiler */
+  return NULL;
 }
 
 /* Add all glyphs at position POS_TYPE that are contained in the given
@@ -1742,15 +1992,14 @@ add_glyph_runes (pos_data *data, int pos_type)
 
 static Bytind
 create_text_block (struct window *w, struct display_line *dl,
-                  Bytind bi_start_pos, int start_col,
-                  prop_block_dynarr **prop, int type)
+                  Bytind bi_start_pos, prop_block_dynarr **prop,
+                  int type)
 {
   struct frame *f = XFRAME (w->frame);
   struct buffer *b = XBUFFER (w->buffer);
   struct device *d = XDEVICE (f->device);
 
   pos_data data;
-  struct Lisp_Vector *dt = 0;
 
   /* Don't display anything in the minibuffer if this window is not on
      a selected frame.  We consider all other windows to be active
@@ -1760,7 +2009,6 @@ create_text_block (struct window *w, struct display_line *dl,
                           is_surrogate_for_selected_frame (f));
 
   int truncate_win = window_truncation_on (w);
-  int end_glyph_width;
 
   /* If the buffer's value of selective_display is an integer then
      only lines that start with less than selective_display columns of
@@ -1768,7 +2016,7 @@ create_text_block (struct window *w, struct display_line *dl,
      after a ^M is invisible. */
   int selective = (INTP (b->selective_display)
                   ? XINT (b->selective_display)
-                  : ((!NILP (b->selective_display) ? -1 : 0)));
+                  : (!NILP (b->selective_display) ? -1 : 0));
 
   /* The variable ctl-arrow allows the user to specify what characters
      can actually be displayed and which octal should be used for.
@@ -1783,46 +2031,41 @@ create_text_block (struct window *w, struct display_line *dl,
      into a more general conversion mechanism.  Ideally you
      could specify a Lisp function that converts characters,
      but this violates the Second Golden Rule and besides would
-     make things way way way way slow.  An idea I like is to
-     be able to specify multiple display tables instead of just
-     one.  Each display table can specify conversions for some
-     characters and leave others unchanged.  The way the
-     character gets displayed is determined by the first display
-     table with a binding for that character.  This way, you
-     could call a function `enable-hex-display' that adds a
-     pre-defined hex display-table (or maybe computes one if
-     you give weird parameters to the function) and adds it
-     to the list of display tables for the current buffer.
-
-     Unfortunately there are still problems dealing with Mule
-     characters.  For example, maybe I want to specify that
-     all extended characters (i.e. >= 256) are displayed in hex.
-     It's not reasonable to create a mapping for all possible
-     such characters, because there are about 2^19 of them.
-     One way of dealing with this is to extend the concept
-     of what a display table is.  Currently it's only allowed
-     to be a 256-entry vector.  Instead, it should be something
-     like:
-
-     a) A 256-entry vector, for backward compatibility
-     b) Some sort of hashtable, mapping characters to values
-     c) A list that specifies a range of values and the
-        mapping to provide for those values.
-
-     Also, extend the concept of "mapping" to include a
-     printf-like spec.  Then, you could make all extended
-     characters show up as hex with a display table like
-
-     ((256 . 524288) . "%x")
+     make things way way way way slow.
+
+     So instead, we extend the display-table concept, which was
+     historically limited to 256-byte vectors, to one of the
+     following:
+
+     a) A 256-entry vector, for backward compatibility;
+     b) char-table, mapping characters to values;
+     c) range-table, mapping ranges of characters to values;
+     d) a list of the above.
+
+     The (d) option allows you to specify multiple display tables
+     instead of just one.  Each display table can specify conversions
+     for some characters and leave others unchanged.  The way the
+     character gets displayed is determined by the first display table
+     with a binding for that character.  This way, you could call a
+     function `enable-hex-display' that adds a hex display-table to
+     the list of display tables for the current buffer.
+
+     #### ...not yet implemented...  Also, we extend the concept of
+     "mapping" to include a printf-like spec.  Thus you can make all
+     extended characters show up as hex with a display table like
+     this:
+
+         #s(range-table data ((256 524288) (format "%x")))
 
      Since more than one display table is possible, you have
-     great flexibility in mapping ranges of characters.
-     */
+     great flexibility in mapping ranges of characters.  */
   Emchar printable_min = (CHAR_OR_CHAR_INTP (b->ctl_arrow)
                          ? XCHAR_OR_CHAR_INT (b->ctl_arrow)
                          : ((EQ (b->ctl_arrow, Qt) || EQ (b->ctl_arrow, Qnil))
                             ? 255 : 160));
 
+  Lisp_Object face_dt, window_dt;
+
   /* The text display block for this display line. */
   struct display_block *db = get_display_block_from_line (dl, TEXT);
 
@@ -1840,6 +2083,7 @@ create_text_block (struct window *w, struct display_line *dl,
 
   dl->used_prop_data = 0;
   dl->num_chars = 0;
+  dl->line_continuation = 0;
 
   xzero (data);
   data.ef = extent_fragment_new (w->buffer, f);
@@ -1848,6 +2092,7 @@ create_text_block (struct window *w, struct display_line *dl,
      them to this structure for ease of passing. */
   data.d = d;
   XSETWINDOW (data.window, w);
+  data.string = Qnil;
   data.db = db;
   data.dl = dl;
 
@@ -1861,10 +2106,10 @@ create_text_block (struct window *w, struct display_line *dl,
      glyph.  Save the width of the end glyph for later use. */
   data.max_pixpos = dl->bounds.right_in;
   if (truncate_win)
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
+    data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
   else
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
-  data.max_pixpos -= end_glyph_width;
+    data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
+  data.max_pixpos -= data.end_glyph_width;
 
   if (cursor_in_echo_area && MINI_WINDOW_P (w) && echo_area_active (f))
     {
@@ -1891,6 +2136,7 @@ create_text_block (struct window *w, struct display_line *dl,
   data.cursor_x = -1;
 
   data.start_col = w->hscroll;
+  data.start_col_xoffset = w->left_xoffset;
   data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0);
   data.hscroll_glyph_width_adjust = 0;
 
@@ -1956,15 +2202,30 @@ create_text_block (struct window *w, struct display_line *dl,
       /* Check for face changes. */
       if (initial || (!no_more_frags && data.bi_bufpos == data.ef->end))
        {
+         Lisp_Object last_glyph = Qnil;
+
+         /* Deal with glyphs that we have already displayed. The
+            theory is that if we end up with a PROP_GLYPH in the
+            propagation data then we are clipping the glyph and there
+            can be no propagation data before that point. The theory
+            works because we always recalculate the extent-fragments
+            for propagated data, we never actually propagate the
+            fragments that still need to be displayed. */
+         if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH) 
+           {
+             last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+             Dynarr_free (*prop);
+             *prop = 0;
+           }
          /* Now compute the face and begin/end-glyph information. */
          data.findex =
            /* Remember that the extent-fragment routines deal in Bytind's. */
-           extent_fragment_update (w, data.ef, data.bi_bufpos);
+           extent_fragment_update (w, data.ef, data.bi_bufpos, last_glyph);
+
+         get_display_tables (w, data.findex, &face_dt, &window_dt);
 
          if (data.bi_bufpos == data.ef->end)
            no_more_frags = 1;
-
-         dt = get_display_table (w, data.findex);
        }
       initial = 0;
 
@@ -2029,9 +2290,9 @@ create_text_block (struct window *w, struct display_line *dl,
        }
 
       /* If there is propagation data, then it represents the current
-         buffer position being displayed.  Add them and advance the
-         position counter.  This might also add the minibuffer
-         prompt. */
+        buffer position being displayed.  Add them and advance the
+        position counter.  This might also add the minibuffer
+        prompt. */
       else if (*prop)
        {
          dl->used_prop_data = 1;
@@ -2053,19 +2314,59 @@ create_text_block (struct window *w, struct display_line *dl,
         here rather than doing them at the end of handling the
         previous run so that glyphs at the beginning and end of
         a line are handled correctly. */
-      else if (Dynarr_length (data.ef->end_glyphs) > 0)
+      else if (Dynarr_length (data.ef->end_glyphs) > 0
+              || Dynarr_length (data.ef->begin_glyphs) > 0)
        {
-         *prop = add_glyph_runes (&data, END_GLYPHS);
-         if (*prop)
-           goto done;
-       }
+         glyph_block_dynarr* tmpglyphs = 0;
+         /* #### I think this is safe, but could be wrong. */
+         data.ch = BI_BUF_FETCH_CHAR (b, data.bi_bufpos);
 
-      /* If there are begin glyphs, add them to the line. */
-      else if (Dynarr_length (data.ef->begin_glyphs) > 0)
-       {
-         *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
-         if (*prop)
-           goto done;
+         if (Dynarr_length (data.ef->end_glyphs) > 0) 
+           {
+             *prop = add_glyph_runes (&data, END_GLYPHS);
+             tmpglyphs = data.ef->end_glyphs;
+           }
+
+         /* If there are begin glyphs, add them to the line. */
+         if (!*prop && Dynarr_length (data.ef->begin_glyphs) > 0) 
+           {
+             *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
+             tmpglyphs = data.ef->begin_glyphs;
+           }
+
+         if (*prop) 
+           {
+             /* If we just clipped a glyph and we are at the end of a
+                line and there are more glyphs to display then do
+                appropriate processing to not get a continuation
+                glyph. */
+             if (*prop != ADD_FAILED 
+                 && Dynarr_atp (*prop, 0)->type == PROP_GLYPH
+                 && data.ch == '\n')
+               { 
+                 /* If there are no more glyphs then do the normal
+                    processing. 
+
+                    #### This doesn't actually work if the same glyph is
+                    present more than once in the block. To solve
+                    this we would have to carry the index around
+                    which might be problematic since the fragment is
+                    recalculated for each line. */
+                 if (EQ (Dynarr_end (tmpglyphs)->glyph,
+                         Dynarr_atp (*prop, 0)->data.p_glyph.glyph))
+                   {
+                     Dynarr_free (*prop);
+                     *prop = 0;
+                   }
+                 else {
+                   data.blank_width = DEVMETH (d, eol_cursor_width, ());
+                   add_emchar_rune (&data); /* discard prop data. */
+                   goto done;
+                 }
+               }
+             else
+               goto done;
+           }
        }
 
       /* If at end-of-buffer, we've already processed begin and
@@ -2076,16 +2377,17 @@ create_text_block (struct window *w, struct display_line *dl,
 
       else
        {
+         Lisp_Object entry = Qnil;
          /* Get the character at the current buffer position. */
          data.ch = BI_BUF_FETCH_CHAR (b, data.bi_bufpos);
+         if (!NILP (face_dt) || !NILP (window_dt))
+           entry = display_table_entry (data.ch, face_dt, window_dt);
 
          /* If there is a display table entry for it, hand it off to
              add_disp_table_entry_runes and let it worry about it. */
-         if (dt && !NILP (DISP_CHAR_ENTRY (dt, data.ch)))
+         if (!NILP (entry) && !EQ (entry, make_char (data.ch)))
            {
-             *prop =
-               add_disp_table_entry_runes (&data,
-                                           DISP_CHAR_ENTRY (dt, data.ch));
+             *prop = add_disp_table_entry_runes (&data, entry);
 
              if (*prop)
                goto done;
@@ -2098,7 +2400,7 @@ create_text_block (struct window *w, struct display_line *dl,
              /* We aren't going to be adding an end glyph so give its
                  space back in order to make sure that the cursor can
                  fit. */
-             data.max_pixpos += end_glyph_width;
+             data.max_pixpos += data.end_glyph_width;
 
              if (selective > 0
                  && (bi_spaces_at_point
@@ -2179,7 +2481,7 @@ create_text_block (struct window *w, struct display_line *dl,
 
              /* We won't be adding a truncation or continuation glyph
                  so give up the room allocated for them. */
-             data.max_pixpos += end_glyph_width;
+             data.max_pixpos += data.end_glyph_width;
 
              if (!NILP (b->selective_display_ellipses))
                {
@@ -2209,6 +2511,7 @@ create_text_block (struct window *w, struct display_line *dl,
                  data.blank_width = DEVMETH (d, eol_cursor_width, ());
                  data.findex = DEFAULT_INDEX;
                  data.start_col = 0;
+                 data.start_col_xoffset = 0;
                  data.bi_start_col_enabled = 0;
 
                  add_emchar_rune (&data);
@@ -2242,7 +2545,8 @@ create_text_block (struct window *w, struct display_line *dl,
              int prop_width = 0;
 
              if (data.start_col > 1)
-               tab_start_pixpos -= (space_width (w) * (data.start_col - 1));
+               tab_start_pixpos -= (space_width (w) * (data.start_col - 1))
+                 + data.start_col_xoffset;
 
              next_tab_start =
                next_tab_position (w, tab_start_pixpos,
@@ -2338,7 +2642,7 @@ done:
        {
          /* If data.start_col_enabled is still true, then the window is
              scrolled far enough so that nothing on this line is visible.
-             We need to stick a trunctation glyph at the beginning of the
+             We need to stick a truncation glyph at the beginning of the
              line in that case unless the line is completely blank. */
          if (data.bi_start_col_enabled)
            {
@@ -2386,7 +2690,7 @@ done:
              for the next newline.  We also add the end-of-line glyph which
              we know will fit because we adjusted the right border before
              we starting laying out the line. */
-         data.max_pixpos += end_glyph_width;
+         data.max_pixpos += data.end_glyph_width;
          data.findex = DEFAULT_INDEX;
          gb.extent = Qnil;
 
@@ -2425,11 +2729,12 @@ done:
 
              /* data.bi_bufpos is already at the start of the next line. */
 
+             dl->line_continuation = 1;
              gb.glyph = Vcontinuation_glyph;
              cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX);
            }
 
-         add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 1, cachel);
+         add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, cachel);
 
          if (truncate_win && data.bi_bufpos == BI_BUF_ZV (b)
              && BI_BUF_FETCH_CHAR (b, prev_bytind (b, BI_BUF_ZV (b))) != '\n')
@@ -2449,6 +2754,7 @@ done:
       data.blank_width = DEVMETH (d, eol_cursor_width, ());
       data.findex = DEFAULT_INDEX;
       data.start_col = 0;
+      data.start_col_xoffset = 0;
       data.bi_start_col_enabled = 0;
 
       data.max_pixpos += data.blank_width;
@@ -2470,7 +2776,7 @@ done:
       {
        struct rune *rb = Dynarr_atp (db->runes, elt);
 
-       if ((rb->type == RUNE_CHAR && rb->object.chr.ch == ' ')
+       if ((rb->type == RUNE_CHAR && CHARC_ASCII_EQ (rb->object.cglyph, ' '))
            || rb->type == RUNE_BLANK)
          {
            dl->bounds.left_white += rb->width;
@@ -2490,8 +2796,7 @@ done:
       {
        struct rune *rb = Dynarr_atp (db->runes, elt);
 
-       if (!(rb->type == RUNE_CHAR && rb->object.chr.ch < 0x100
-           && isspace (rb->object.chr.ch))
+       if (!(rb->type == RUNE_CHAR && CHARC_IS_SPACE (rb->object.cglyph))
            && !rb->type == RUNE_BLANK)
          {
            dl->bounds.right_white = rb->xpos + rb->width;
@@ -2519,26 +2824,7 @@ done:
   else
     db->end_pos = dl->bounds.right_white;
 
-  /* update line height parameters */
-  if (!data.new_ascent && !data.new_descent)
-    {
-      /* We've got a blank line so initialize these values from the default
-         face. */
-      default_face_font_info (data.window, &data.new_ascent,
-                             &data.new_descent, 0, 0, 0);
-    }
-
-  if (data.max_pixmap_height)
-    {
-      int height = data.new_ascent + data.new_descent;
-      int pix_ascent, pix_descent;
-
-      pix_descent = data.max_pixmap_height * data.new_descent / height;
-      pix_ascent = data.max_pixmap_height - pix_descent;
-
-      data.new_ascent = max (data.new_ascent, pix_ascent);
-      data.new_descent = max (data.new_descent, pix_descent);
-    }
+  calculate_baseline (&data);
 
   dl->ascent = data.new_ascent;
   dl->descent = data.new_descent;
@@ -2556,6 +2842,8 @@ done:
       dl->descent = descent;
   }
 
+  calculate_yoffset (dl, db);
+
   dl->cursor_elt = data.cursor_x;
   /* #### lossage lossage lossage! Fix this shit! */
   if (data.bi_bufpos > BI_BUF_ZV (b))
@@ -2624,6 +2912,7 @@ create_overlay_glyph_block (struct window *w, struct display_line *dl)
   data.last_charset = Qunbound;
   data.last_findex = DEFAULT_INDEX;
   data.result_str = Qnil;
+  data.string = Qnil;
 
   Dynarr_reset (data.db->runes);
 
@@ -2633,7 +2922,7 @@ create_overlay_glyph_block (struct window *w, struct display_line *dl)
        (&data,
         XSTRING_DATA   (Voverlay_arrow_string),
         XSTRING_LENGTH (Voverlay_arrow_string),
-        1);
+        1, 0);
     }
   else if (GLYPHP (Voverlay_arrow_string))
     {
@@ -2644,17 +2933,7 @@ create_overlay_glyph_block (struct window *w, struct display_line *dl)
       add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, 0);
     }
 
-  if (data.max_pixmap_height)
-    {
-      int height = data.new_ascent + data.new_descent;
-      int pix_ascent, pix_descent;
-
-      pix_descent = data.max_pixmap_height * data.new_descent / height;
-      pix_ascent = data.max_pixmap_height - pix_descent;
-
-      data.new_ascent = max (data.new_ascent, pix_ascent);
-      data.new_descent = max (data.new_descent, pix_descent);
-    }
+  calculate_baseline (&data);
 
   dl->ascent = data.new_ascent;
   dl->descent = data.new_descent;
@@ -2662,6 +2941,8 @@ create_overlay_glyph_block (struct window *w, struct display_line *dl)
   data.db->start_pos = dl->bounds.left_in;
   data.db->end_pos = data.pixpos;
 
+  calculate_yoffset (dl, data.db);
+
   return data.pixpos - dl->bounds.left_in;
 }
 
@@ -2675,8 +2956,26 @@ add_margin_runes (struct display_line *dl, struct display_block *db, int start,
                             ? dl->left_glyphs
                             : dl->right_glyphs);
   int elt, end;
-  int xpos = start;
   int reverse;
+  struct window *w = XWINDOW (window);
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  pos_data data;
+
+  xzero (data);
+  data.d = d;
+  data.window = window;
+  data.db = db;
+  data.dl = dl;
+  data.pixpos = start;
+  data.cursor_type = NO_CURSOR;
+  data.cursor_x = -1;
+  data.last_charset = Qunbound;
+  data.last_findex = DEFAULT_INDEX;
+  data.result_str = Qnil;
+  data.string = Qnil;
+  data.new_ascent = dl->ascent;
+  data.new_descent = dl->descent;
 
   if ((layout == GL_WHITESPACE && side == LEFT_GLYPHS)
       || (layout == GL_INSIDE_MARGIN && side == RIGHT_GLYPHS))
@@ -2705,88 +3004,33 @@ add_margin_runes (struct display_line *dl, struct display_block *db, int start,
           || (side == RIGHT_GLYPHS &&
               extent_end_glyph_layout (XEXTENT (gb->extent)) == layout)))
        {
-         struct rune rb;
-
-         rb.width = gb->width;
-         rb.findex = gb->findex;
-         rb.xpos = xpos;
-         rb.bufpos = -1;
-         rb.endpos = 0;
-         rb.type = RUNE_DGLYPH;
-         rb.object.dglyph.glyph = gb->glyph;
-         rb.object.dglyph.extent = gb->extent;
-         rb.object.dglyph.xoffset = 0;
-         rb.cursor_type = CURSOR_OFF;
-
-         Dynarr_add (db->runes, rb);
-         xpos += rb.width;
+         data.findex = gb->findex;
+         data.max_pixpos = data.pixpos + gb->width;
+         add_glyph_rune (&data, gb, side, 0, NULL);
          count--;
          gb->active = 0;
+       }
 
-         if (glyph_contrib_p (gb->glyph, window))
-           {
-             unsigned short ascent, descent;
-             Lisp_Object baseline = glyph_baseline (gb->glyph, window);
-
-             ascent = glyph_ascent (gb->glyph, Qnil, gb->findex, window);
-             descent = glyph_descent (gb->glyph, Qnil, gb->findex, window);
-
-             /* A pixmap that has not had a baseline explicitly set.
-                 We use the existing ascent / descent ratio of the
-                 line. */
-             if (NILP (baseline))
-               {
-                 int gheight = ascent + descent;
-                 int line_height = dl->ascent + dl->descent;
-                 int pix_ascent, pix_descent;
+      (reverse ? elt-- : elt++);
+    }
 
-                 pix_descent = (int) (gheight * dl->descent) / line_height;
-                 pix_ascent = gheight - pix_descent;
+  calculate_baseline (&data);
 
-                 dl->ascent = max ((int) dl->ascent, pix_ascent);
-                 dl->descent = max ((int) dl->descent, pix_descent);
-               }
+  dl->ascent = data.new_ascent;
+  dl->descent = data.new_descent;
 
-             /* A string so determine contribution normally. */
-             else if (EQ (baseline, Qt))
-               {
-                 dl->ascent = max (dl->ascent, ascent);
-                 dl->descent = max (dl->descent, descent);
-               }
+  calculate_yoffset (dl, data.db);
 
-             /* A pixmap with an explicitly set baseline.  We determine the
-                contribution here. */
-             else if (INTP (baseline))
-               {
-                 int height = ascent + descent;
-                 int pix_ascent, pix_descent;
+  return data.pixpos;
+}
 
-                 pix_ascent = height * XINT (baseline) / 100;
-                 pix_descent = height - pix_ascent;
+/* Add a blank to a margin display block. */
 
-                 dl->ascent = max ((int) dl->ascent, pix_ascent);
-                 dl->descent = max ((int) dl->descent, pix_descent);
-               }
-
-             /* Otherwise something is screwed up. */
-             else
-               abort ();
-           }
-       }
-
-      (reverse ? elt-- : elt++);
-    }
-
-  return xpos;
-}
-
-/* Add a blank to a margin display block. */
-
-static void
-add_margin_blank (struct display_line *dl, struct display_block *db,
-                 struct window *w, int xpos, int width, int side)
-{
-  struct rune rb;
+static void
+add_margin_blank (struct display_line *dl, struct display_block *db,
+                 struct window *w, int xpos, int width, int side)
+{
+  struct rune rb;
 
   rb.findex = (side == LEFT_GLYPHS
               ? get_builtin_face_cache_index (w, Vleft_margin_face)
@@ -2843,7 +3087,7 @@ create_left_glyph_block (struct window *w, struct display_line *dl,
        {
          int width;
 
-         width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+         width = glyph_width (gb->glyph, window);
 
          if (white_in_start - width >= left_in_end)
            {
@@ -2871,7 +3115,7 @@ create_left_glyph_block (struct window *w, struct display_line *dl,
      they should start.  The inside margin glyphs get whatever space
      is left after the whitespace glyphs have been displayed.  These
      are tricky to calculate since if we decide to use the overflow
-     area we basicaly have to start over.  So for these we build up a
+     area we basically have to start over.  So for these we build up a
      list of just the inside margin glyphs and manipulate it to
      determine the needed info. */
   {
@@ -2894,7 +3138,7 @@ create_left_glyph_block (struct window *w, struct display_line *dl,
        if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
            GL_INSIDE_MARGIN)
          {
-           gb->width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+           gb->width = glyph_width (gb->glyph, window);
            used_in += gb->width;
            Dynarr_add (ib, *gb);
          }
@@ -2963,7 +3207,7 @@ create_left_glyph_block (struct window *w, struct display_line *dl,
        if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
            GL_INSIDE_MARGIN)
          {
-           int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+           int width = glyph_width (gb->glyph, window);
 
            if (used_out)
              {
@@ -3000,12 +3244,12 @@ create_left_glyph_block (struct window *w, struct display_line *dl,
       struct glyph_block *gb = Dynarr_atp (dl->left_glyphs, elt);
 
       if (NILP (gb->extent))
-       abort ();       /* these should have beeb handled in add_glyph_rune */
+       abort ();       /* these should have been handled in add_glyph_rune */
 
       if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
          GL_OUTSIDE_MARGIN)
        {
-         int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+         int width = glyph_width (gb->glyph, window);
 
          if (out_end + width <= in_out_start)
            {
@@ -3162,7 +3406,7 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
 
       if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_WHITESPACE)
        {
-         int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+         int width = glyph_width (gb->glyph, window);
 
          if (white_in_end + width <= dl->bounds.right_in)
            {
@@ -3190,7 +3434,7 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
      they should start.  The inside margin glyphs get whatever space
      is left after the whitespace glyphs have been displayed.  These
      are tricky to calculate since if we decide to use the overflow
-     area we basicaly have to start over.  So for these we build up a
+     area we basically have to start over.  So for these we build up a
      list of just the inside margin glyphs and manipulate it to
      determine the needed info. */
   {
@@ -3212,7 +3456,7 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
 
        if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
          {
-           gb->width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+           gb->width = glyph_width (gb->glyph, window);
            used_in += gb->width;
            Dynarr_add (ib, *gb);
          }
@@ -3276,7 +3520,7 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
 
        if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
          {
-           int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+           int width = glyph_width (gb->glyph, window);
 
            if (used_out)
              {
@@ -3313,11 +3557,11 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
       struct glyph_block *gb = Dynarr_atp (dl->right_glyphs, elt);
 
       if (NILP (gb->extent))
-       abort ();       /* these should have beeb handled in add_glyph_rune */
+       abort ();       /* these should have been handled in add_glyph_rune */
 
       if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_OUTSIDE_MARGIN)
        {
-         int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
+         int width = glyph_width (gb->glyph, window);
 
          if (out_start - width >= in_out_end)
            {
@@ -3433,103 +3677,8 @@ create_right_glyph_block (struct window *w, struct display_line *dl)
 /*                                                                        */
 /***************************************************************************/
 
-/* Ensure that the given display line DL accurately represents the
-   modeline for the given window. */
-
-static void
-generate_modeline (struct window *w, struct display_line *dl, int type)
-{
-  struct buffer *b = XBUFFER (w->buffer);
-  struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
-
-  /* Unlike display line and rune pointers, this one can't change underneath
-     our feet. */
-  struct display_block *db = get_display_block_from_line (dl, TEXT);
-  int max_pixpos, min_pixpos, ypos_adj;
-  Lisp_Object font_inst;
-
-  /* This will actually determine incorrect inside boundaries for the
-     modeline since it ignores the margins.  However being aware of this fact
-     we never use those values anywhere so it doesn't matter. */
-  dl->bounds = calculate_display_line_boundaries (w, 1);
-
-  /* We are generating a modeline. */
-  dl->modeline = 1;
-  dl->cursor_elt = -1;
-
-  /* Reset the runes on the modeline. */
-  Dynarr_reset (db->runes);
-
-  if (!WINDOW_HAS_MODELINE_P (w))
-    {
-      struct rune rb;
-
-      /* If there is a horizontal scrollbar, don't add anything. */
-      if (window_scrollbar_height (w))
-       return;
-
-      dl->ascent = DEVMETH (d, divider_height, ());
-      dl->descent = 0;
-      /* The modeline is at the bottom of the gutters. */
-      dl->ypos = WINDOW_BOTTOM (w);
-
-      rb.findex = MODELINE_INDEX;
-      rb.xpos = dl->bounds.left_out;
-      rb.width = dl->bounds.right_out - dl->bounds.left_out;
-      rb.bufpos = 0;
-      rb.endpos = 0;
-      rb.type = RUNE_HLINE;
-      rb.object.hline.thickness = 1;
-      rb.object.hline.yoffset = 0;
-      rb.cursor_type = NO_CURSOR;
-
-      if (!EQ (Qzero, w->modeline_shadow_thickness)
-         && FRAME_WIN_P (f))
-       {
-         int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
-
-         dl->ypos -= shadow_thickness;
-         rb.xpos += shadow_thickness;
-         rb.width -= 2 * shadow_thickness;
-       }
-
-      Dynarr_add (db->runes, rb);
-      return;
-    }
-
-  /* !!#### not right; needs to compute the max height of
-     all the charsets */
-  font_inst = WINDOW_FACE_CACHEL_FONT (w, MODELINE_INDEX, Vcharset_ascii);
-
-  dl->ascent = XFONT_INSTANCE (font_inst)->ascent;
-  dl->descent = XFONT_INSTANCE (font_inst)->descent;
-
-  min_pixpos = dl->bounds.left_out;
-  max_pixpos = dl->bounds.right_out;
-
-  if (!EQ (Qzero, w->modeline_shadow_thickness) && FRAME_WIN_P (f))
-    {
-      int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
-
-      ypos_adj = shadow_thickness;
-      min_pixpos += shadow_thickness;
-      max_pixpos -= shadow_thickness;
-    }
-  else
-    ypos_adj = 0;
-
-  generate_formatted_string_db (b->modeline_format,
-                               b->generated_modeline_string, w, dl, db,
-                               MODELINE_INDEX, min_pixpos, max_pixpos, type);
-
-  /* The modeline is at the bottom of the gutters.  We have to wait to
-     set this until we've generated teh modeline in order to account
-     for any embedded faces. */
-  dl->ypos = WINDOW_BOTTOM (w) - dl->descent - ypos_adj;
-}
-
-static void
+/* This function is also used in frame.c by `generate_title_string' */
+void
 generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
                               struct window *w, struct display_line *dl,
                               struct display_block *db, face_index findex,
@@ -3540,6 +3689,7 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
 
   pos_data data;
   int c_pixpos;
+  Charcount offset = 0;
 
   xzero (data);
   data.d = d;
@@ -3553,15 +3703,21 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
   data.last_findex = DEFAULT_INDEX;
   data.result_str = result_str;
   data.is_modeline = 1;
+  data.string = Qnil;
   XSETWINDOW (data.window, w);
 
   Dynarr_reset (formatted_string_extent_dynarr);
   Dynarr_reset (formatted_string_extent_start_dynarr);
   Dynarr_reset (formatted_string_extent_end_dynarr);
 
-  /* This recursively builds up the modeline. */
+  /* result_str is nil when we're building a frame or icon title. Otherwise,
+     we're building a modeline, so the offset starts at the modeline
+     horizontal scrolling amount */
+  if (! NILP (result_str))
+    offset = w->modeline_hscroll;
   generate_fstring_runes (w, &data, 0, 0, -1, format_str, 0,
-                          max_pixpos - min_pixpos, findex, type);
+                          max_pixpos - min_pixpos, findex, type, &offset,
+                         Qnil);
 
   if (Dynarr_length (db->runes))
     {
@@ -3593,6 +3749,8 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
       Bufbyte *strdata;
       struct buffer *buf = XBUFFER (WINDOW_BUFFER (w));
 
+      in_modeline_generation = 1;
+
       detach_all_extents (result_str);
       resize_string (XSTRING (result_str), -1,
                      data.bytepos - XSTRING_LENGTH (result_str));
@@ -3604,8 +3762,9 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
           if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
             {
               len += (set_charptr_emchar
-                      (strdata + len, Dynarr_atp (db->runes,
-                                                  elt)->object.chr.ch));
+                     (strdata + len,
+                      CHARC_TO_CHAR (Dynarr_atp (db->runes,
+                                                 elt)->object.cglyph)));
             }
         }
 
@@ -3629,16 +3788,113 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
              Dynarr_at (formatted_string_extent_end_dynarr, elt),
              result_str);
         }
+
+      in_modeline_generation = 0;
+    }
+}
+
+/* Ensure that the given display line DL accurately represents the
+   modeline for the given window. */
+static void
+generate_modeline (struct window *w, struct display_line *dl, int type)
+{
+  struct buffer *b = XBUFFER (w->buffer);
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+
+  /* Unlike display line and rune pointers, this one can't change underneath
+     our feet. */
+  struct display_block *db = get_display_block_from_line (dl, TEXT);
+  int max_pixpos, min_pixpos, ypos_adj;
+  Lisp_Object font_inst;
+
+  /* This will actually determine incorrect inside boundaries for the
+     modeline since it ignores the margins.  However being aware of this fact
+     we never use those values anywhere so it doesn't matter. */
+  dl->bounds = calculate_display_line_boundaries (w, 1);
+
+  /* We are generating a modeline. */
+  dl->modeline = 1;
+  dl->cursor_elt = -1;
+
+  /* Reset the runes on the modeline. */
+  Dynarr_reset (db->runes);
+
+  if (!WINDOW_HAS_MODELINE_P (w))
+    {
+      struct rune rb;
+
+      /* If there is a horizontal scrollbar, don't add anything. */
+      if (window_scrollbar_height (w))
+       return;
+
+      dl->ascent = DEVMETH (d, divider_height, ());
+      dl->descent = 0;
+      /* The modeline is at the bottom of the gutters. */
+      dl->ypos = WINDOW_BOTTOM (w);
+
+      rb.findex = MODELINE_INDEX;
+      rb.xpos = dl->bounds.left_out;
+      rb.width = dl->bounds.right_out - dl->bounds.left_out;
+      rb.bufpos = 0;
+      rb.endpos = 0;
+      rb.type = RUNE_HLINE;
+      rb.object.hline.thickness = 1;
+      rb.object.hline.yoffset = 0;
+      rb.cursor_type = NO_CURSOR;
+
+      if (!EQ (Qzero, w->modeline_shadow_thickness)
+         && FRAME_WIN_P (f))
+       {
+         int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
+
+         dl->ypos -= shadow_thickness;
+         rb.xpos += shadow_thickness;
+         rb.width -= 2 * shadow_thickness;
+       }
+
+      Dynarr_add (db->runes, rb);
+      return;
+    }
+
+  /* !!#### not right; needs to compute the max height of
+     all the charsets */
+  font_inst = WINDOW_FACE_CACHEL_FONT (w, MODELINE_INDEX, Vcharset_ascii);
+
+  dl->ascent = XFONT_INSTANCE (font_inst)->ascent;
+  dl->descent = XFONT_INSTANCE (font_inst)->descent;
+
+  min_pixpos = dl->bounds.left_out;
+  max_pixpos = dl->bounds.right_out;
+
+  if (!EQ (Qzero, w->modeline_shadow_thickness) && FRAME_WIN_P (f))
+    {
+      int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
+
+      ypos_adj = shadow_thickness;
+      min_pixpos += shadow_thickness;
+      max_pixpos -= shadow_thickness;
     }
+  else
+    ypos_adj = 0;
+
+  generate_formatted_string_db (b->modeline_format,
+                               b->generated_modeline_string, w, dl, db,
+                               MODELINE_INDEX, min_pixpos, max_pixpos, type);
+
+  /* The modeline is at the bottom of the gutters.  We have to wait to
+     set this until we've generated the modeline in order to account
+     for any embedded faces. */
+  dl->ypos = WINDOW_BOTTOM (w) - dl->descent - ypos_adj;
 }
 
 static Charcount
-add_string_to_fstring_db_runes (pos_data *data, CONST Bufbyte *str,
+add_string_to_fstring_db_runes (pos_data *data, const Bufbyte *str,
                                 Charcount pos, Charcount min_pos, Charcount max_pos)
 {
   /* This function has been Mule-ized. */
   Charcount end;
-  CONST Bufbyte *cur_pos = str;
+  const Bufbyte *cur_pos = str;
   struct display_block *db = data->db;
 
   data->blank_width = space_width (XWINDOW (data->window));
@@ -3646,13 +3902,13 @@ add_string_to_fstring_db_runes (pos_data *data, CONST Bufbyte *str,
     add_blank_rune (data, NULL, 0);
 
   end = (Dynarr_length (db->runes) +
-         bytecount_to_charcount (str, strlen ((CONST char *) str)));
+         bytecount_to_charcount (str, strlen ((const char *) str)));
   if (max_pos != -1)
     end = min (max_pos, end);
 
   while (pos < end && *cur_pos)
     {
-      CONST Bufbyte *old_cur_pos = cur_pos;
+      const Bufbyte *old_cur_pos = cur_pos;
       int succeeded;
 
       data->ch = charptr_emchar (cur_pos);
@@ -3677,7 +3933,8 @@ add_string_to_fstring_db_runes (pos_data *data, CONST Bufbyte *str,
    modeline extents. */
 static Charcount
 add_glyph_to_fstring_db_runes (pos_data *data, Lisp_Object glyph,
-                               Charcount pos, Charcount min_pos, Charcount max_pos)
+                               Charcount pos, Charcount min_pos,
+                              Charcount max_pos, Lisp_Object extent)
 {
   /* This function has been Mule-ized. */
   Charcount end;
@@ -3693,7 +3950,7 @@ add_glyph_to_fstring_db_runes (pos_data *data, Lisp_Object glyph,
     end = min (max_pos, end);
 
   gb.glyph = glyph;
-  gb.extent = Qnil;
+  gb.extent = extent;
   add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0, 0);
   pos++;
 
@@ -3720,7 +3977,8 @@ static Charcount
 generate_fstring_runes (struct window *w, pos_data *data, Charcount pos,
                         Charcount min_pos, Charcount max_pos,
                         Lisp_Object elt, int depth, int max_pixsize,
-                        face_index findex, int type)
+                        face_index findex, int type, Charcount *offset,
+                       Lisp_Object cur_ext)
 {
   /* This function has been Mule-ized. */
   /* #### The other losing things in this function are:
@@ -3752,13 +4010,22 @@ tail_recurse:
 
           if (this != last)
             {
-              /* The string is just a string. */
+              /* No %-construct */
               Charcount size =
-                bytecount_to_charcount (last, this - last) + pos;
-              Charcount tmp_max = (max_pos == -1 ? size : min (size, max_pos));
+               bytecount_to_charcount (last, this - last);
+
+             if (size <= *offset)
+               *offset -= size;
+             else
+               {
+                 Charcount tmp_max = (max_pos == -1 ? pos + size - *offset :
+                                      min (pos + size - *offset, max_pos));
+                 const Bufbyte *tmp_last = charptr_n_addr (last, *offset);
 
-              pos = add_string_to_fstring_db_runes (data, last, pos, pos,
-                                                    tmp_max);
+                 pos = add_string_to_fstring_db_runes (data, tmp_last,
+                                                       pos, pos, tmp_max);
+                 *offset = 0;
+               }
             }
           else /* *this == '%' */
             {
@@ -3783,7 +4050,7 @@ tail_recurse:
                   pos = generate_fstring_runes (w, data, pos, spec_width,
                                                 max_pos, Vglobal_mode_string,
                                                 depth, max_pixsize, findex,
-                                                type);
+                                                type, offset, cur_ext);
                 }
               else if (*this == '-')
                 {
@@ -3810,17 +4077,35 @@ tail_recurse:
 
                   while (num_to_add--)
                     pos = add_string_to_fstring_db_runes
-                      (data, (CONST Bufbyte *) "-", pos, pos, max_pos);
+                      (data, (const Bufbyte *) "-", pos, pos, max_pos);
                 }
               else if (*this != 0)
                 {
-                  Bufbyte *str;
                   Emchar ch = charptr_emchar (this);
+                  Bufbyte *str;
+                 Charcount size;
+
                   decode_mode_spec (w, ch, type);
 
                   str = Dynarr_atp (mode_spec_bufbyte_string, 0);
-                  pos = add_string_to_fstring_db_runes (data,str, pos, pos,
-                                                        max_pos);
+                 size = bytecount_to_charcount
+                   /* Skip the null character added by `decode_mode_spec' */
+                   (str, Dynarr_length (mode_spec_bufbyte_string)) - 1;
+
+                 if (size <= *offset)
+                   *offset -= size;
+                 else
+                   {
+                     const Bufbyte *tmp_str = charptr_n_addr (str, *offset);
+
+                     /* #### NOTE: I don't understand why a tmp_max is not
+                        computed and used here as in the plain string case
+                        above. -- dv */
+                     pos = add_string_to_fstring_db_runes (data, tmp_str,
+                                                           pos, pos,
+                                                           max_pos);
+                     *offset = 0;
+                   }
                 }
 
               /* NOT this++.  There could be any sort of character at
@@ -3846,13 +4131,26 @@ tail_recurse:
 
       if (!UNBOUNDP (tem))
         {
-          /* If value is a string, output that string literally:
+         /* If value is a string, output that string literally:
              don't check for % within it.  */
           if (STRINGP (tem))
             {
-              pos =
-                add_string_to_fstring_db_runes
-                (data, XSTRING_DATA (tem), pos, min_pos, max_pos);
+             Bufbyte *str = XSTRING_DATA (tem);
+             Charcount size = XSTRING_CHAR_LENGTH (tem);
+
+             if (size <= *offset)
+               *offset -= size;
+             else
+               {
+                 const Bufbyte *tmp_str = charptr_n_addr (str, *offset);
+
+                 /* #### NOTE: I don't understand why a tmp_max is not
+                    computed and used here as in the plain string case
+                    above. -- dv */
+                 pos = add_string_to_fstring_db_runes (data, tmp_str, pos,
+                                                       min_pos, max_pos);
+                 *offset = 0;
+               }
             }
           /* Give up right away for nil or t.  */
           else if (!EQ (tem, elt))
@@ -3877,50 +4175,53 @@ tail_recurse:
   else if (CONSP (elt))
     {
       /* A cons cell: four distinct cases.
-       * If first element is a string or a cons, process all the elements
-       * and effectively concatenate them.
-       * If first element is a negative number, truncate displaying cdr to
-       * at most that many characters.  If positive, pad (with spaces)
-       * to at least that many characters.
-       * If first element is a symbol, process the cadr or caddr recursively
-       * according to whether the symbol's value is non-nil or nil.
-       * If first element is a face, process the cdr recursively
-       * without altering the depth.
+       * - If first element is a string or a cons, process all the elements
+       *   and effectively concatenate them.
+       * - If first element is a negative number, truncate displaying cdr to
+       *   at most that many characters.  If positive, pad (with spaces)
+       *   to at least that many characters.
+       * - If first element is another symbol, process the cadr or caddr
+       *   recursively according to whether the symbol's value is non-nil or
+       *   nil.
+       * - If first element is an extent, process the cdr recursively
+       *   and handle the extent's face.
        */
+
       Lisp_Object car, tem;
 
       car = XCAR (elt);
       if (SYMBOLP (car))
-        {
-          elt = XCDR (elt);
-          if (!CONSP (elt))
-            goto invalid;
-          tem = symbol_value_in_buffer (car, w->buffer);
-          /* elt is now the cdr, and we know it is a cons cell.
-             Use its car if CAR has a non-nil value.  */
-          if (!UNBOUNDP (tem))
-            {
-              if (!NILP (tem))
-                {
-                  elt = XCAR (elt);
-                  goto tail_recurse;
-                }
-            }
-          /* Symbol's value is nil (or symbol is unbound)
-           * Get the cddr of the original list
-           * and if possible find the caddr and use that.
-           */
-          elt = XCDR (elt);
-          if (NILP (elt))
-            ;
-          else if (!CONSP (elt))
-            goto invalid;
-          else
-            {
-              elt = XCAR (elt);
-              goto tail_recurse;
-            }
-        }
+       {
+         elt = XCDR (elt);
+         if (!CONSP (elt))
+           goto invalid;
+
+         tem = symbol_value_in_buffer (car, w->buffer);
+         /* elt is now the cdr, and we know it is a cons cell.
+            Use its car if CAR has a non-nil value.  */
+         if (!UNBOUNDP (tem))
+           {
+             if (!NILP (tem))
+               {
+                 elt = XCAR (elt);
+                 goto tail_recurse;
+               }
+           }
+         /* Symbol's value is nil (or symbol is unbound)
+          * Get the cddr of the original list
+          * and if possible find the caddr and use that.
+          */
+         elt = XCDR (elt);
+         if (NILP (elt))
+           ;
+         else if (!CONSP (elt))
+           goto invalid;
+         else
+           {
+             elt = XCAR (elt);
+             goto tail_recurse;
+           }
+       }
       else if (INTP (car))
         {
           Charcount lim = XINT (car);
@@ -3959,13 +4260,14 @@ tail_recurse:
       else if (STRINGP (car) || CONSP (car))
         {
           int limit = 50;
+
           /* LIMIT is to protect against circular lists.  */
           while (CONSP (elt) && --limit > 0
                  && (pos < max_pos || max_pos == -1))
             {
               pos = generate_fstring_runes (w, data, pos, pos, max_pos,
-                                            XCAR (elt), depth,
-                                            max_pixsize, findex, type);
+                                            XCAR (elt), depth, max_pixsize,
+                                           findex, type, offset, cur_ext);
               elt = XCDR (elt);
             }
         }
@@ -4004,7 +4306,8 @@ tail_recurse:
               data->findex = new_findex;
               pos = generate_fstring_runes (w, data, pos, pos, max_pos,
                                             XCDR (elt), depth - 1,
-                                            max_pixsize, new_findex, type);
+                                           max_pixsize, new_findex, type,
+                                           offset, car);
               data->findex = old_findex;
               Dynarr_add (formatted_string_extent_dynarr, ext);
               Dynarr_add (formatted_string_extent_start_dynarr, start);
@@ -4014,57 +4317,46 @@ tail_recurse:
     }
   else if (GLYPHP (elt))
     {
-      pos = add_glyph_to_fstring_db_runes (data, elt, pos, pos, max_pos);
+      /* Glyphs are considered as one character with respect to the modeline
+        horizontal scrolling facility. -- dv */
+      if (*offset > 0)
+       *offset -= 1;
+      else
+       pos = add_glyph_to_fstring_db_runes (data, elt, pos, pos, max_pos,
+                                            cur_ext);
     }
   else
     {
     invalid:
-      pos =
-        add_string_to_fstring_db_runes
-          (data, (CONST Bufbyte *) GETTEXT ("*invalid*"), pos, min_pos,
-           max_pos);
+      {
+       char *str = GETTEXT ("*invalid*");
+       Charcount size = (Charcount) strlen (str); /* is this ok ?? -- dv */
+
+       if (size <= *offset)
+         *offset -= size;
+       else
+         {
+           const Bufbyte *tmp_str =
+             charptr_n_addr ((const Bufbyte *) str, *offset);
+
+           /* #### NOTE: I don't understand why a tmp_max is not computed and
+              used here as in the plain string case above. -- dv */
+           pos = add_string_to_fstring_db_runes (data, tmp_str, pos,
+                                                 min_pos, max_pos);
+           *offset = 0;
+         }
+      }
     }
 
   if (min_pos > pos)
     {
-      add_string_to_fstring_db_runes (data, (CONST Bufbyte *) "", pos, min_pos,
-                                      -1);
+      add_string_to_fstring_db_runes (data, (const Bufbyte *) "", pos,
+                                     min_pos, -1);
     }
 
   return pos;
 }
 
-/* The caller is responsible for freeing the returned string. */
-Bufbyte *
-generate_formatted_string (struct window *w, Lisp_Object format_str,
-                          Lisp_Object result_str, face_index findex, int type)
-{
-  struct display_line *dl;
-  struct display_block *db;
-  int elt = 0;
-
-  dl = &formatted_string_display_line;
-  db = get_display_block_from_line (dl, TEXT);
-  Dynarr_reset (db->runes);
-
-  generate_formatted_string_db (format_str, result_str, w, dl, db, findex, 0,
-                                -1, type);
-
-  Dynarr_reset (formatted_string_emchar_dynarr);
-  while (elt < Dynarr_length (db->runes))
-    {
-      if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
-       Dynarr_add (formatted_string_emchar_dynarr,
-                   Dynarr_atp (db->runes, elt)->object.chr.ch);
-      elt++;
-    }
-
-  return
-    convert_emchar_string_into_malloced_string
-    ( Dynarr_atp (formatted_string_emchar_dynarr, 0),
-      Dynarr_length (formatted_string_emchar_dynarr), 0);
-}
-
 /* Update just the modeline.  Assumes the desired display structs.  If
    they do not have a modeline block, it does nothing. */
 static void
@@ -4087,74 +4379,893 @@ regenerate_modeline (struct window *w)
 static int
 ensure_modeline_generated (struct window *w, int type)
 {
-  int need_modeline;
+  int need_modeline;
+
+  /* minibuffer windows don't have modelines */
+  if (MINI_WINDOW_P (w))
+    need_modeline = 0;
+  /* windows which haven't had it turned off do */
+  else if (WINDOW_HAS_MODELINE_P (w))
+    need_modeline = 1;
+  /* windows which have it turned off don't have a divider if there is
+     a horizontal scrollbar */
+  else if (window_scrollbar_height (w))
+    need_modeline = 0;
+  /* and in this case there is none */
+  else
+    need_modeline = 1;
+
+  if (need_modeline)
+    {
+      display_line_dynarr *dla;
+
+      dla = window_display_lines (w, type);
+
+      /* We don't care if there is a display line which is not
+         currently a modeline because it is definitely going to become
+         one if we have gotten to this point. */
+      if (Dynarr_length (dla) == 0)
+       {
+         if (Dynarr_largest (dla) > 0)
+           {
+             struct display_line *mlp = Dynarr_atp (dla, 0);
+             Dynarr_add (dla, *mlp);
+           }
+         else
+           {
+             struct display_line modeline;
+             xzero (modeline);
+             Dynarr_add (dla, modeline);
+           }
+       }
+
+      /* If we're adding a new place marker go ahead and generate the
+         modeline so that it is available for use by
+         window_modeline_height. */
+      generate_modeline (w, Dynarr_atp (dla, 0), type);
+    }
+
+  return need_modeline;
+}
+
+/* #### Kludge or not a kludge.  I tend towards the former. */
+int
+real_current_modeline_height (struct window *w)
+{
+  Fset_marker (w->start[CMOTION_DISP],  w->start[CURRENT_DISP],  w->buffer);
+  Fset_marker (w->pointm[CMOTION_DISP], w->pointm[CURRENT_DISP], w->buffer);
+
+  if (ensure_modeline_generated (w, CMOTION_DISP))
+    {
+      display_line_dynarr *dla = window_display_lines (w, CMOTION_DISP);
+
+      if (Dynarr_length (dla))
+       {
+         if (Dynarr_atp (dla, 0)->modeline)
+           return (Dynarr_atp (dla, 0)->ascent +
+                   Dynarr_atp (dla, 0)->descent);
+       }
+    }
+  return 0;
+}
+
+\f
+/***************************************************************************/
+/*                                                                         */
+/*                            displayable string routines                  */
+/*                                                                         */
+/***************************************************************************/
+
+/* Given a position for a string in a window, ensure that the given
+   display line DL accurately represents the text on a line starting
+   at the given position.
+
+   Yes, this is duplicating the code of create_text_block, but it
+   looked just too hard to change create_text_block to handle strings
+   *and* buffers. We already make a distinction between the two
+   elsewhere in the code so I think unifying them would require a
+   complete MULE rewrite. Besides, the other distinction is that these
+   functions cover text that the user *cannot edit* so we can remove
+   everything to do with cursors, minibuffers etc. Eventually the
+   modeline routines should be modified to use this code as it copes
+   with many more types of display situation. */
+
+static Bufpos
+create_string_text_block (struct window *w, Lisp_Object disp_string,
+                         struct display_line *dl,
+                         Bufpos start_pos,
+                         prop_block_dynarr **prop,
+                         face_index default_face)
+{
+  struct frame *f = XFRAME (w->frame);
+  /* Note that a lot of the buffer controlled stuff has been left in
+     because you might well want to make use of it (selective display
+     etc), its just the buffer text that we do not use. However, it
+     seems to be possible for buffer to be nil sometimes so protect
+     against this case. */
+  struct buffer *b = BUFFERP (w->buffer) ? XBUFFER (w->buffer) : 0;
+  struct device *d = XDEVICE (f->device);
+  Lisp_String* s = XSTRING (disp_string);
+
+  /* we're working with these a lot so precalculate them */
+  Bytecount slen = XSTRING_LENGTH (disp_string);
+  Bytecount bi_string_zv = slen;
+  Bytind bi_start_pos = charcount_to_bytecount (string_data (s), start_pos);
+
+  pos_data data;
+
+  int truncate_win = b ? window_truncation_on (w) : 0;
+
+  /* We're going to ditch selective display for static text, it's an
+     FSF thing and invisible extents are the way to go here.
+     Implementing it also relies on a number of buffer-specific
+     functions that we don't have the luxury of being able to use
+     here. */
+
+  /* The variable ctl-arrow allows the user to specify what characters
+     can actually be displayed and which octal should be used for.
+     #### This variable should probably have some rethought done to
+     it.
+
+     #### It would also be really nice if you could specify that
+     the characters come out in hex instead of in octal.  Mule
+     does that by adding a ctl-hexa variable similar to ctl-arrow,
+     but that's bogus -- we need a more general solution.  I
+     think you need to extend the concept of display tables
+     into a more general conversion mechanism.  Ideally you
+     could specify a Lisp function that converts characters,
+     but this violates the Second Golden Rule and besides would
+     make things way way way way slow.
+
+     So instead, we extend the display-table concept, which was
+     historically limited to 256-byte vectors, to one of the
+     following:
+
+     a) A 256-entry vector, for backward compatibility;
+     b) char-table, mapping characters to values;
+     c) range-table, mapping ranges of characters to values;
+     d) a list of the above.
+
+     The (d) option allows you to specify multiple display tables
+     instead of just one.  Each display table can specify conversions
+     for some characters and leave others unchanged.  The way the
+     character gets displayed is determined by the first display table
+     with a binding for that character.  This way, you could call a
+     function `enable-hex-display' that adds a hex display-table to
+     the list of display tables for the current buffer.
+
+     #### ...not yet implemented...  Also, we extend the concept of
+     "mapping" to include a printf-like spec.  Thus you can make all
+     extended characters show up as hex with a display table like
+     this:
+
+         #s(range-table data ((256 524288) (format "%x")))
+
+     Since more than one display table is possible, you have
+     great flexibility in mapping ranges of characters.  */
+  Emchar printable_min = b ? (CHAR_OR_CHAR_INTP (b->ctl_arrow)
+                         ? XCHAR_OR_CHAR_INT (b->ctl_arrow)
+                         : ((EQ (b->ctl_arrow, Qt) || EQ (b->ctl_arrow, Qnil))
+                            ? 255 : 160)) : 255;
+
+  Lisp_Object face_dt, window_dt;
+
+  /* The text display block for this display line. */
+  struct display_block *db = get_display_block_from_line (dl, TEXT);
+
+  /* The first time through the main loop we need to force the glyph
+     data to be updated. */
+  int initial = 1;
+
+  /* Apparently the new extent_fragment_update returns an end position
+     equal to the position passed in if there are no more runs to be
+     displayed. */
+  int no_more_frags = 0;
+
+  dl->used_prop_data = 0;
+  dl->num_chars = 0;
+  dl->line_continuation = 0;
+
+  /* set up faces to use for clearing areas, used by
+     output_display_line */
+  dl->default_findex = default_face;
+  if (default_face)
+    {
+      dl->left_margin_findex = default_face;
+      dl->right_margin_findex = default_face;
+    }
+  else
+    {
+      dl->left_margin_findex =
+       get_builtin_face_cache_index (w, Vleft_margin_face);
+      dl->right_margin_findex =
+       get_builtin_face_cache_index (w, Vright_margin_face);
+    }
+
+  xzero (data);
+  data.ef = extent_fragment_new (disp_string, f);
+
+  /* These values are used by all of the rune addition routines.  We add
+     them to this structure for ease of passing. */
+  data.d = d;
+  XSETWINDOW (data.window, w);
+  data.db = db;
+  data.dl = dl;
+
+  data.bi_bufpos = bi_start_pos;
+  data.pixpos = dl->bounds.left_in;
+  data.last_charset = Qunbound;
+  data.last_findex = default_face;
+  data.result_str = Qnil;
+  data.string = disp_string;
+
+  /* Set the right boundary adjusting it to take into account any end
+     glyph.  Save the width of the end glyph for later use. */
+  data.max_pixpos = dl->bounds.right_in;
+  data.max_pixpos -= data.end_glyph_width;
+
+  data.cursor_type = NO_CURSOR;
+  data.cursor_x = -1;
+
+  data.start_col = 0;
+  /* I don't think we want this, string areas should not scroll with
+     the window
+  data.start_col = w->hscroll;
+  data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0);
+  */
+  data.bi_start_col_enabled = 0;
+  data.hscroll_glyph_width_adjust = 0;
+
+  /* We regenerate the line from the very beginning. */
+  Dynarr_reset (db->runes);
+
+  /* Why is this less than or equal and not just less than?  If the
+     starting position is already equal to the maximum we can't add
+     anything else, right?  Wrong.  We might still have a newline to
+     add.  A newline can use the room allocated for an end glyph since
+     if we add it we know we aren't going to be adding any end
+     glyph. */
+
+  /* #### Chuck -- I think this condition should be while (1).
+     Otherwise if (e.g.) there is one begin-glyph and one end-glyph
+     and the begin-glyph ends exactly at the end of the window, the
+     end-glyph and text might not be displayed.  while (1) ensures
+     that the loop terminates only when either (a) there is
+     propagation data or (b) the end-of-line or end-of-buffer is hit.
+
+     #### Also I think you need to ensure that the operation
+     "add begin glyphs; add end glyphs; add text" is atomic and
+     can't get interrupted in the middle.  If you run off the end
+     of the line during that operation, then you keep accumulating
+     propagation data until you're done.  Otherwise, if the (e.g.)
+     there's a begin glyph at a particular position and attempting
+     to display that glyph results in window-end being hit and
+     propagation data being generated, then the character at that
+     position won't be displayed.
+
+     #### See also the comment after the end of this loop, below.
+     */
+  while (data.pixpos <= data.max_pixpos)
+    {
+      /* #### This check probably should not be necessary. */
+      if (data.bi_bufpos > bi_string_zv)
+       {
+         /* #### urk!  More of this lossage! */
+         data.bi_bufpos--;
+         goto done;
+       }
+
+      /* Check for face changes. */
+      if (initial || (!no_more_frags && data.bi_bufpos == data.ef->end))
+       {
+         Lisp_Object last_glyph = Qnil;
+         /* Deal with clipped glyphs that we have already displayed. */
+         if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH) 
+           {
+             last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+             Dynarr_free (*prop);
+             *prop = 0;
+           }
+         /* Now compute the face and begin/end-glyph information. */
+         data.findex =
+           /* Remember that the extent-fragment routines deal in Bytind's. */
+           extent_fragment_update (w, data.ef, data.bi_bufpos, last_glyph);
+         /* This is somewhat cheesy but the alternative is to
+             propagate default_face into extent_fragment_update. */
+         if (data.findex == DEFAULT_INDEX)
+           data.findex = default_face;
+
+         get_display_tables (w, data.findex, &face_dt, &window_dt);
+
+         if (data.bi_bufpos == data.ef->end)
+           no_more_frags = 1;
+       }
+      initial = 0;
+
+      /* Determine what is next to be displayed.  We first handle any
+         glyphs returned by glyphs_at_bufpos.  If there are no glyphs to
+         display then we determine what to do based on the character at the
+         current buffer position. */
+
+      /* If the current position is covered by an invisible extent, do
+         nothing (except maybe add some ellipses).
+
+        #### The behavior of begin and end-glyphs at the edge of an
+        invisible extent should be investigated further.  This is
+        fairly low priority though. */
+      if (data.ef->invisible)
+       {
+         /* #### Chuck, perhaps you could look at this code?  I don't
+            really know what I'm doing. */
+         if (*prop)
+           {
+             Dynarr_free (*prop);
+             *prop = 0;
+           }
+
+         /* The extent fragment code only sets this when we should
+            really display the ellipses.  It makes sure the ellipses
+            don't get displayed more than once in a row. */
+         if (data.ef->invisible_ellipses)
+           {
+             struct glyph_block gb;
+
+             data.ef->invisible_ellipses_already_displayed = 1;
+             data.ef->invisible_ellipses = 0;
+             gb.extent = Qnil;
+             gb.glyph = Vinvisible_text_glyph;
+             *prop = add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0,
+                                     GLYPH_CACHEL (w, INVIS_GLYPH_INDEX));
+             /* Perhaps they shouldn't propagate if the very next thing
+                is to display a newline (for compatibility with
+                selective-display-ellipses)?  Maybe that's too
+                abstruse. */
+             if (*prop)
+               goto done;
+           }
+
+         /* #### What if we're dealing with a display table? */
+         if (data.start_col)
+           data.start_col--;
+
+         if (data.bi_bufpos == bi_string_zv)
+           goto done;
+         else
+           INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+       }
+
+      /* If there is propagation data, then it represents the current
+         buffer position being displayed.  Add them and advance the
+         position counter.  This might also add the minibuffer
+         prompt. */
+      else if (*prop)
+       {
+         dl->used_prop_data = 1;
+         *prop = add_propagation_runes (prop, &data);
+
+         if (*prop)
+           goto done;  /* gee, a really narrow window */
+         else if (data.bi_bufpos == bi_string_zv)
+           goto done;
+         else if (data.bi_bufpos < 0)
+           /* #### urk urk urk! Aborts are not very fun! Fix this please! */
+           data.bi_bufpos = 0;
+         else
+           INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+       }
+
+      /* If there are end glyphs, add them to the line.  These are
+        the end glyphs for the previous run of text.  We add them
+        here rather than doing them at the end of handling the
+        previous run so that glyphs at the beginning and end of
+        a line are handled correctly. */
+      else if (Dynarr_length (data.ef->end_glyphs) > 0)
+       {
+         data.ch = string_char (s, data.bi_bufpos);
+         *prop = add_glyph_runes (&data, END_GLYPHS);
+
+         if (*prop) {
+           goto done;
+         }
+       }
+
+      /* If there are begin glyphs, add them to the line. */
+      else if (Dynarr_length (data.ef->begin_glyphs) > 0)
+       {
+         data.ch = string_char (s, data.bi_bufpos);
+         *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
+
+         if (*prop) {
+           goto done;
+         }
+       }
+
+      /* If at end-of-buffer, we've already processed begin and
+        end-glyphs at this point and there's no text to process,
+        so we're done. */
+      else if (data.bi_bufpos == bi_string_zv)
+       goto done;
+
+      else
+       {
+         Lisp_Object entry = Qnil;
+         /* Get the character at the current buffer position. */
+         data.ch = string_char (s, data.bi_bufpos);
+         if (!NILP (face_dt) || !NILP (window_dt))
+           entry = display_table_entry (data.ch, face_dt, window_dt);
+
+         /* If there is a display table entry for it, hand it off to
+             add_disp_table_entry_runes and let it worry about it. */
+         if (!NILP (entry) && !EQ (entry, make_char (data.ch)))
+           {
+             *prop = add_disp_table_entry_runes (&data, entry);
+
+             if (*prop)
+               goto done;
+           }
+
+         /* Check if we have hit a newline character.  If so, add a marker
+             to the line and end this loop. */
+         else if (data.ch == '\n')
+           {
+             /* We aren't going to be adding an end glyph so give its
+                 space back in order to make sure that the cursor can
+                 fit. */
+             data.max_pixpos += data.end_glyph_width;
+             goto done;
+           }
+
+         /* If the current character is considered to be printable, then
+             just add it. */
+         else if (data.ch >= printable_min)
+           {
+             *prop = add_emchar_rune (&data);
+             if (*prop)
+               goto done;
+           }
+
+         /* If the current character is a tab, determine the next tab
+             starting position and add a blank rune which extends from the
+             current pixel position to that starting position. */
+         else if (data.ch == '\t')
+           {
+             int tab_start_pixpos = data.pixpos;
+             int next_tab_start;
+             int char_tab_width;
+             int prop_width = 0;
+
+             if (data.start_col > 1)
+               tab_start_pixpos -= (space_width (w) * (data.start_col - 1));
+
+             next_tab_start =
+               next_tab_position (w, tab_start_pixpos,
+                                  dl->bounds.left_in +
+                                  data.hscroll_glyph_width_adjust);
+             if (next_tab_start > data.max_pixpos)
+               {
+                 prop_width = next_tab_start - data.max_pixpos;
+                 next_tab_start = data.max_pixpos;
+               }
+             data.blank_width = next_tab_start - data.pixpos;
+             char_tab_width =
+               (next_tab_start - tab_start_pixpos) / space_width (w);
+
+             *prop = add_blank_rune (&data, w, char_tab_width);
+
+             /* add_blank_rune is only supposed to be called with
+                sizes guaranteed to fit in the available space. */
+             assert (!(*prop));
+
+             if (prop_width)
+               {
+                 struct prop_block pb;
+                 *prop = Dynarr_new (prop_block);
+
+                 pb.type = PROP_BLANK;
+                 pb.data.p_blank.width = prop_width;
+                 pb.data.p_blank.findex = data.findex;
+                 Dynarr_add (*prop, pb);
+
+                 goto done;
+               }
+           }
+
+         /* If character is a control character, pass it off to
+             add_control_char_runes.
+
+            The is_*() routines have undefined results on
+            arguments outside of the range [-1, 255].  (This
+            often bites people who carelessly use `char' instead
+            of `unsigned char'.)
+            */
+         else if (data.ch < 0x100 && iscntrl ((Bufbyte) data.ch))
+           {
+             *prop = add_control_char_runes (&data, b);
+
+             if (*prop)
+               goto done;
+           }
+
+         /* If the character is above the ASCII range and we have not
+             already handled it, then print it as an octal number. */
+         else if (data.ch >= 0200)
+           {
+             *prop = add_octal_runes (&data);
+
+             if (*prop)
+               goto done;
+           }
+
+         /* Assume the current character is considered to be printable,
+             then just add it. */
+         else
+           {
+             *prop = add_emchar_rune (&data);
+             if (*prop)
+               goto done;
+           }
+
+         INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+       }
+    }
+
+ done:
+
+  /* Determine the starting point of the next line if we did not hit the
+     end of the buffer. */
+  if (data.bi_bufpos < bi_string_zv)
+    {
+      /* #### This check is not correct.  If the line terminated
+        due to a begin-glyph or end-glyph hitting window-end, then
+        data.ch will not point to the character at data.bi_bufpos.  If
+        you make the two changes mentioned at the top of this loop,
+        you should be able to say '(if (*prop))'.  That should also
+        make it possible to eliminate the data.bi_bufpos < BI_BUF_ZV (b)
+        check. */
+
+      /* The common case is that the line ended because we hit a newline.
+         In that case, the next character is just the next buffer
+         position. */
+      if (data.ch == '\n')
+       {
+         INC_CHARBYTIND (string_data (s), data.bi_bufpos);
+       }
+
+      /* Otherwise we have a buffer line which cannot fit on one display
+         line. */
+      else
+       {
+         struct glyph_block gb;
+         struct glyph_cachel *cachel;
+
+         /* If the line is to be truncated then we actually have to look
+             for the next newline.  We also add the end-of-line glyph which
+             we know will fit because we adjusted the right border before
+             we starting laying out the line. */
+         data.max_pixpos += data.end_glyph_width;
+         data.findex = default_face;
+         gb.extent = Qnil;
+
+         if (truncate_win)
+           {
+             Bytind bi_pos;
+
+             /* Now find the start of the next line. */
+             bi_pos = bi_find_next_emchar_in_string (s, '\n', data.bi_bufpos, 1);
+
+             data.cursor_type = NO_CURSOR;
+             data.bi_bufpos = bi_pos;
+             gb.glyph = Vtruncation_glyph;
+             cachel = GLYPH_CACHEL (w, TRUN_GLYPH_INDEX);
+           }
+         else
+           {
+             /* The cursor can never be on the continuation glyph. */
+             data.cursor_type = NO_CURSOR;
+
+             /* data.bi_bufpos is already at the start of the next line. */
+
+             dl->line_continuation = 1;
+             gb.glyph = Vcontinuation_glyph;
+             cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX);
+           }
+
+         if (data.end_glyph_width)
+           add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, cachel);
+
+         if (truncate_win && data.bi_bufpos == bi_string_zv)
+           {
+             const Bufbyte* endb = charptr_n_addr (string_data (s), bi_string_zv);
+             DEC_CHARPTR (endb);
+             if (charptr_emchar (endb) != '\n')
+               {
+                 /* #### Damn this losing shit. */
+                 data.bi_bufpos++;
+               }
+           }
+       }
+    }
+  else if (data.bi_bufpos == bi_string_zv)
+    {
+      /* create_text_block () adds a bogus \n marker here which screws
+        up subwindow display. Since we never have a cursor in the
+        gutter we can safely ignore it. */
+    }
+  /* Calculate left whitespace boundary. */
+  {
+    int elt = 0;
+
+    /* Whitespace past a newline is considered right whitespace. */
+    while (elt < Dynarr_length (db->runes))
+      {
+       struct rune *rb = Dynarr_atp (db->runes, elt);
+
+       if ((rb->type == RUNE_CHAR && CHARC_ASCII_EQ (rb->object.cglyph, ' '))
+           || rb->type == RUNE_BLANK)
+         {
+           dl->bounds.left_white += rb->width;
+           elt++;
+         }
+       else
+         elt = Dynarr_length (db->runes);
+      }
+  }
+
+  /* Calculate right whitespace boundary. */
+  {
+    int elt = Dynarr_length (db->runes) - 1;
+    int done = 0;
+
+    while (!done && elt >= 0)
+      {
+       struct rune *rb = Dynarr_atp (db->runes, elt);
+
+       if (!(rb->type == RUNE_CHAR && CHARC_IS_SPACE (rb->object.cglyph))
+           && !rb->type == RUNE_BLANK)
+         {
+           dl->bounds.right_white = rb->xpos + rb->width;
+           done = 1;
+         }
+
+       elt--;
+
+      }
+
+    /* The line is blank so everything is considered to be right
+       whitespace. */
+    if (!done)
+      dl->bounds.right_white = dl->bounds.left_in;
+  }
+
+  /* Set the display blocks bounds. */
+  db->start_pos = dl->bounds.left_in;
+  if (Dynarr_length (db->runes))
+    {
+      struct rune *rb = Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
+
+      db->end_pos = rb->xpos + rb->width;
+    }
+  else
+    db->end_pos = dl->bounds.right_white;
+
+  calculate_baseline (&data);
+
+  dl->ascent = data.new_ascent;
+  dl->descent = data.new_descent;
+
+  {
+    unsigned short ascent = (unsigned short) XINT (w->minimum_line_ascent);
+
+    if (dl->ascent < ascent)
+      dl->ascent = ascent;
+  }
+  {
+    unsigned short descent = (unsigned short) XINT (w->minimum_line_descent);
+
+    if (dl->descent < descent)
+      dl->descent = descent;
+  }
+
+  calculate_yoffset (dl, db);
+
+  dl->cursor_elt = data.cursor_x;
+  /* #### lossage lossage lossage! Fix this shit! */
+  if (data.bi_bufpos > bi_string_zv)
+    dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, bi_string_zv);
+  else
+    dl->end_bufpos = buffer_or_string_bytind_to_bufpos (disp_string, data.bi_bufpos) - 1;
+  if (truncate_win)
+    data.dl->num_chars =
+      string_column_at_point (s, dl->end_bufpos, b ? XINT (b->tab_width) : 8);
+  else
+    /* This doesn't correctly take into account tabs and control
+       characters but if the window isn't being truncated then this
+       value isn't going to end up being used anyhow. */
+    data.dl->num_chars = dl->end_bufpos - dl->bufpos;
+
+  /* #### handle horizontally scrolled line with text none of which
+     was actually laid out. */
+
+  /* #### handle any remainder of overlay arrow */
+
+  if (*prop == ADD_FAILED)
+    *prop = NULL;
+
+  if (truncate_win && *prop)
+    {
+      Dynarr_free (*prop);
+      *prop = NULL;
+    }
+
+  extent_fragment_delete (data.ef);
+
+  /* #### If we started at EOB, then make sure we return a value past
+     it so that regenerate_window will exit properly.  This is bogus.
+     The main loop should get fixed so that it isn't necessary to call
+     this function if we are already at EOB. */
+
+  if (data.bi_bufpos == bi_string_zv && bi_start_pos == bi_string_zv)
+    return bytecount_to_charcount (string_data (s), data.bi_bufpos) + 1; /* Yuck! */
+  else
+    return bytecount_to_charcount (string_data (s), data.bi_bufpos);
+}
+
+/* Given a display line and a starting position, ensure that the
+   contents of the display line accurately represent the visual
+   representation of the buffer contents starting from the given
+   position when displayed in the given window.  The display line ends
+   when the contents of the line reach the right boundary of the given
+   window.
+
+   This is very similar to generate_display_line but with the same
+   limitations as create_string_text_block. I have taken the liberty
+   of fixing the bytind stuff though.*/
+
+static Bufpos
+generate_string_display_line (struct window *w, Lisp_Object disp_string,
+                             struct display_line *dl,
+                             Bufpos start_pos,
+                             prop_block_dynarr **prop,
+                             face_index default_face)
+{
+  Bufpos ret_bufpos;
+
+  /* you must set bounds before calling this. */
+
+  /* Reset what this line is using. */
+  if (dl->display_blocks)
+    Dynarr_reset (dl->display_blocks);
+  if (dl->left_glyphs)
+    {
+      Dynarr_free (dl->left_glyphs);
+      dl->left_glyphs = 0;
+    }
+  if (dl->right_glyphs)
+    {
+      Dynarr_free (dl->right_glyphs);
+      dl->right_glyphs = 0;
+    }
+
+  /* We aren't generating a modeline at the moment. */
+  dl->modeline = 0;
+
+  /* Create a display block for the text region of the line. */
+  ret_bufpos = create_string_text_block (w, disp_string, dl, start_pos,
+                                        prop, default_face);
+  dl->bufpos = start_pos;
+  if (dl->end_bufpos < dl->bufpos)
+    dl->end_bufpos = dl->bufpos;
+
+  /* If there are left glyphs associated with any character in the
+     text block, then create a display block to handle them. */
+  if (dl->left_glyphs != NULL && Dynarr_length (dl->left_glyphs))
+    create_left_glyph_block (w, dl, 0);
+
+  /* If there are right glyphs associated with any character in the
+     text block, then create a display block to handle them. */
+  if (dl->right_glyphs != NULL && Dynarr_length (dl->right_glyphs))
+    create_right_glyph_block (w, dl);
+
+  return ret_bufpos;
+}
+
+/* This is ripped off from regenerate_window. All we want to do is
+   loop through elements in the string creating display lines until we
+   have covered the provided area. Simple really.  */
+void
+generate_displayable_area (struct window *w, Lisp_Object disp_string,
+                          int xpos, int ypos, int width, int height,
+                          display_line_dynarr* dla,
+                          Bufpos start_pos,
+                          face_index default_face)
+{
+  int yend = ypos + height;
+  Charcount s_zv;
 
-  /* minibuffer windows don't have modelines */
-  if (MINI_WINDOW_P (w))
-    need_modeline = 0;
-  /* windows which haven't had it turned off do */
-  else if (WINDOW_HAS_MODELINE_P (w))
-    need_modeline = 1;
-  /* windows which have it turned off don't have a divider if there is
-     a horizontal scrollbar */
-  else if (window_scrollbar_height (w))
-    need_modeline = 0;
-  /* and in this case there is none */
-  else
-    need_modeline = 1;
+  prop_block_dynarr *prop = 0;
+  layout_bounds bounds;
+  assert (dla);
 
-  if (need_modeline)
-    {
-      display_line_dynarr *dla;
+  Dynarr_reset (dla);
+  /* if there's nothing to do then do nothing. code after this assumes
+     there is something to do. */
+  if (NILP (disp_string))
+    return;
 
-      dla = window_display_lines (w, type);
+  s_zv = XSTRING_CHAR_LENGTH (disp_string);
 
-      /* We don't care if there is a display line which is not
-         currently a modeline because it is definitely going to become
-         one if we have gotten to this point. */
-      if (Dynarr_length (dla) == 0)
+  bounds.left_out = xpos;
+  bounds.right_out = xpos + width;
+  /* The inner boundaries mark where the glyph margins are located. */
+  bounds.left_in = bounds.left_out + window_left_margin_width (w);
+  bounds.right_in = bounds.right_out - window_right_margin_width (w);
+  /* We cannot fully calculate the whitespace boundaries as they
+     depend on the contents of the line being displayed. */
+  bounds.left_white = bounds.left_in;
+  bounds.right_white = bounds.right_in;
+
+  while (ypos < yend)
+    {
+      struct display_line dl;
+      struct display_line *dlp;
+      Bufpos next_pos;
+      int local;
+
+      if (Dynarr_length (dla) < Dynarr_largest (dla))
        {
-         if (Dynarr_largest (dla) > 0)
-           {
-             struct display_line *mlp = Dynarr_atp (dla, 0);
-             Dynarr_add (dla, *mlp);
-           }
-         else
-           {
-             struct display_line modeline;
-             xzero (modeline);
-             Dynarr_add (dla, modeline);
-           }
+         dlp = Dynarr_atp (dla, Dynarr_length (dla));
+         local = 0;
        }
+      else
+       {
 
-      /* If we're adding a new place marker go ahead and generate the
-         modeline so that it is available for use by
-         window_modeline_height. */
-      generate_modeline (w, Dynarr_atp (dla, 0), type);
-    }
-
-  return need_modeline;
-}
+         xzero (dl);
+         dlp = &dl;
+         local = 1;
+       }
 
-/* #### Kludge or not a kludge.  I tend towards the former. */
-int
-real_current_modeline_height (struct window *w)
-{
-  Fset_marker (w->start[CMOTION_DISP],  w->start[CURRENT_DISP],  w->buffer);
-  Fset_marker (w->pointm[CMOTION_DISP], w->pointm[CURRENT_DISP], w->buffer);
+      dlp->bounds = bounds;
+      dlp->offset = 0;
+      next_pos = generate_string_display_line (w, disp_string, dlp, start_pos,
+                                              &prop, default_face);
+      /* we need to make sure that we continue along the line if there
+         is more left to display otherwise we just end up redisplaying
+         the same chunk over and over again. */
+      if (next_pos == start_pos && next_pos < s_zv)
+       start_pos++;
+      else
+       start_pos = next_pos;
 
-  if (ensure_modeline_generated (w, CMOTION_DISP))
-    {
-      display_line_dynarr *dla = window_display_lines (w, CMOTION_DISP);
+      dlp->ypos = ypos + dlp->ascent;
+      ypos = dlp->ypos + dlp->descent;
 
-      if (Dynarr_length (dla))
+      if (ypos > yend)
        {
-         if (Dynarr_atp (dla, 0)->modeline)
-           return (Dynarr_atp (dla, 0)->ascent +
-                   Dynarr_atp (dla, 0)->descent);
+         int visible_height = dlp->ascent + dlp->descent;
+
+         dlp->clip = (ypos - yend);
+         visible_height -= dlp->clip;
+
+         if (visible_height < VERTICAL_CLIP (w, 1))
+           {
+             if (local)
+               free_display_line (dlp);
+             break;
+           }
        }
+      else
+       dlp->clip = 0;
+
+      Dynarr_add (dla, *dlp);
+
+      /* #### This type of check needs to be done down in the
+        generate_display_line call. */
+      if (start_pos >= s_zv)
+       break;
     }
-  return 0;
+
+  if (prop)
+    Dynarr_free (prop);
 }
 
 \f
@@ -4177,6 +5288,8 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
   struct buffer *b = XBUFFER (w->buffer);
   int ypos = WINDOW_TEXT_TOP (w);
   int yend;    /* set farther down */
+  int yclip = WINDOW_TEXT_TOP_CLIP (w);
+  int force;
 
   prop_block_dynarr *prop;
   layout_bounds bounds;
@@ -4230,7 +5343,14 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
   else
     prop = 0;
 
-  while (ypos < yend)
+  /* When we are computing things for scrolling purposes, make
+     sure at least one line is always generated */
+  force = (type == CMOTION_DISP);
+
+  /* Make sure this is set always */
+  /* Note the conversion at end */
+  w->window_end_pos[type] = start_pos;
+  while (ypos < yend || force)
     {
       struct display_line dl;
       struct display_line *dlp;
@@ -4243,6 +5363,7 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
        }
       else
        {
+
          xzero (dl);
          dlp = &dl;
          local = 1;
@@ -4250,19 +5371,39 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
 
       dlp->bounds = bounds;
       dlp->offset = 0;
-      start_pos = generate_display_line (w, dlp, 1, start_pos,
-                                        w->hscroll, &prop, type);
-      dlp->ypos = ypos + dlp->ascent;
+      start_pos = generate_display_line (w, dlp, 1, start_pos, &prop, type);
+
+      if (yclip > dlp->ascent)
+       {
+         /* this should never happen, but if it does just display the
+            whole line */
+         yclip = 0;
+       }
+
+      dlp->ypos = (ypos + dlp->ascent) - yclip;
       ypos = dlp->ypos + dlp->descent;
 
+      /* See if we've been asked to start midway through a line, for
+         partial display line scrolling. */
+      if (yclip)
+       {
+         dlp->top_clip = yclip;
+         yclip = 0;
+       }
+      else
+       dlp->top_clip = 0;
+
       if (ypos > yend)
        {
          int visible_height = dlp->ascent + dlp->descent;
 
          dlp->clip = (ypos - yend);
-         visible_height -= dlp->clip;
+         /* Although this seems strange we could have a single very
+            tall line visible for which we need to account for both
+            the top clip and the bottom clip. */
+         visible_height -= (dlp->clip + dlp->top_clip);
 
-         if (visible_height < VERTICAL_CLIP (w, 1))
+         if (visible_height < VERTICAL_CLIP (w, 1) && !force)
            {
              if (local)
                free_display_line (dlp);
@@ -4306,16 +5447,22 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
         generate_display_line call. */
       if (start_pos > BUF_ZV (b))
        break;
+
+      force = 0;
     }
 
   if (prop)
-      Dynarr_free (prop);
+    Dynarr_free (prop);
 
   /* #### More not quite right, but close enough. */
-  /* #### Ben sez: apparently window_end_pos[] is measured
+  /* Ben sez: apparently window_end_pos[] is measured
      as the number of characters between the window end and the
      end of the buffer?  This seems rather weirdo.  What's
-     the justification for this? */
+     the justification for this?
+
+     JV sez: Because BUF_Z (b) would be a good initial value, however
+     that can change. This representation allows initalizing with 0.
+  */
   w->window_end_pos[type] = BUF_Z (b) - w->window_end_pos[type];
 
   if (need_modeline)
@@ -4503,7 +5650,7 @@ regenerate_window_extents_only_changed (struct window *w, Bufpos startp,
        return 0;
 
       new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
-                                        w->hscroll, &prop, DESIRED_DISP);
+                                        &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* #### If there is propagated stuff the fail.  We could
@@ -4522,6 +5669,7 @@ regenerate_window_extents_only_changed (struct window *w, Bufpos startp,
       if (cdl->ypos != ddl->ypos
          || cdl->ascent != ddl->ascent
          || cdl->descent != ddl->descent
+         || cdl->top_clip != ddl->top_clip
          || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
          || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1)
          || old_start != ddl->bufpos
@@ -4624,11 +5772,9 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
   /* If the changes are below the visible area then if point hasn't
      moved return success otherwise fail in order to be safe. */
   if (line > dla_end)
-    {
-      return regenerate_window_extents_only_changed (w, startp, pointm,
-                                                    extent_beg_unchanged,
-                                                    extent_end_unchanged);
-    }
+    return regenerate_window_extents_only_changed (w, startp, pointm,
+                                                  extent_beg_unchanged,
+                                                  extent_end_unchanged);
   else
     /* At this point we know what line the changes first affect.  We
        now redraw that line.  If the changes are contained within it
@@ -4647,27 +5793,16 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
       assert (cdl->end_bufpos == ddl->end_bufpos);
       assert (cdl->offset == ddl->offset);
 
-      /* If the last rune is already a continuation glyph, fail.
-         #### We should be able to handle this better. */
-      {
-       struct display_block *db = get_display_block_from_line (ddl, TEXT);
-       if (Dynarr_length (db->runes))
-         {
-           struct rune *rb =
-             Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
-
-           if (rb->type == RUNE_DGLYPH
-               && EQ (rb->object.dglyph.glyph, Vcontinuation_glyph))
-             return 0;
-         }
-      }
+      /* If the line continues to next display line, fail. */
+      if (ddl->line_continuation)
+       return 0;
 
       /* If the line was generated using propagation data, fail. */
       if (ddl->used_prop_data)
        return 0;
 
       new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
-                                        w->hscroll, &prop, DESIRED_DISP);
+                                        &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* If there is propagated stuff then it is pretty much a
@@ -4678,25 +5813,16 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
          return 0;
        }
 
-      /* If the last rune is now a continuation glyph, fail. */
-      {
-       struct display_block *db = get_display_block_from_line (ddl, TEXT);
-       if (Dynarr_length (db->runes))
-         {
-           struct rune *rb =
-             Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
-
-           if (rb->type == RUNE_DGLYPH
-               && EQ (rb->object.dglyph.glyph, Vcontinuation_glyph))
-             return 0;
-         }
-      }
+      /* If the line continues to next display line, fail. */
+      if (ddl->line_continuation)
+       return 0;
 
       /* If any line position parameters have changed or a
          cursor has disappeared or disappeared, fail. */
       if (cdl->ypos != ddl->ypos
          || cdl->ascent != ddl->ascent
          || cdl->descent != ddl->descent
+         || cdl->top_clip != ddl->top_clip
          || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
          || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1))
        {
@@ -4752,12 +5878,9 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
              && extent_end_unchanged != -1
              && ((extent_beg_unchanged < ddl->bufpos)
                  || (extent_end_unchanged > ddl->end_bufpos)))
-           {
-             return
-               regenerate_window_extents_only_changed (w, startp, pointm,
-                                                       extent_beg_unchanged,
-                                                       extent_end_unchanged);
-           }
+           return regenerate_window_extents_only_changed (w, startp, pointm,
+                                                          extent_beg_unchanged,
+                                                          extent_end_unchanged);
          else
            return 1;
        }
@@ -4983,7 +6106,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
     }
   Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm), the_buffer);
 
-  /* If the buffer has changed we have to invalid all of our face
+  /* If the buffer has changed we have to invalidate all of our face
      cache elements. */
   if ((!echo_active && b != window_display_buffer (w))
       || !Dynarr_length (w->face_cachels)
@@ -4992,10 +6115,11 @@ redisplay_window (Lisp_Object window, int skip_selected)
   else
     mark_face_cachels_as_not_updated (w);
 
-  /* Ditto the glyph cache elements. */
+  /* Ditto the glyph cache elements, although we do *not* invalidate
+     the cache purely because glyphs have changed - this is now
+     handled by the dirty flag.*/
   if ((!echo_active && b != window_display_buffer (w))
-      || !Dynarr_length (w->glyph_cachels)
-      || f->glyphs_changed)
+      || !Dynarr_length (w->glyph_cachels) || f->faces_changed)
     reset_glyph_cachels (w);
   else
     mark_glyph_cachels_as_not_updated (w);
@@ -5049,7 +6173,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
          Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm),
                       the_buffer);
 
-         /* #### BUFU amounts of overkil just to get the cursor
+         /* #### BUFU amounts of overkill just to get the cursor
              location marked properly.  FIX ME FIX ME FIX ME */
          regenerate_window (w, startp, pointm, DESIRED_DISP);
        }
@@ -5066,7 +6190,10 @@ redisplay_window (Lisp_Object window, int skip_selected)
          temporary change to the echo area. */
       && !(MINI_WINDOW_P (w) && f->buffers_changed)
       && !f->frame_changed
-      && !truncation_changed)
+      && !truncation_changed
+      /* check whether start is really at the beginning of a line  GE */
+      && (!w->start_at_line_beg || beginning_of_line_p (b, startp))
+      )
     {
       /* Check if the cursor has actually moved. */
       if (EQ (Fmarker_buffer (w->last_point[CURRENT_DISP]), w->buffer)
@@ -5077,6 +6204,8 @@ redisplay_window (Lisp_Object window, int skip_selected)
          && !f->extents_changed
          && !f->faces_changed
          && !f->glyphs_changed
+         && !f->subwindows_changed
+         /*      && !f->subwindows_state_changed*/
          && !f->point_changed
          && !f->windows_structure_changed)
        {
@@ -5097,6 +6226,8 @@ redisplay_window (Lisp_Object window, int skip_selected)
              && !f->extents_changed
              && !f->faces_changed
              && !f->glyphs_changed
+             && !f->subwindows_changed
+             /*              && !f->subwindows_state_changed*/
              && !f->windows_structure_changed)
            {
              if (point_visible (w, pointm, CURRENT_DISP)
@@ -5154,6 +6285,8 @@ redisplay_window (Lisp_Object window, int skip_selected)
           && !f->clip_changed
           && !f->faces_changed
           && !f->glyphs_changed
+          && !f->subwindows_changed
+          /*      && !f->subwindows_state_changed*/
           && !f->windows_structure_changed
           && !f->frame_changed
           && !truncation_changed
@@ -5233,12 +6366,13 @@ regeneration_done:
       Bufpos end = (w->window_end_pos[DESIRED_DISP] == -1
                    ? BUF_ZV (b)
                    : BUF_Z (b) - w->window_end_pos[DESIRED_DISP] - 1);
-
-      update_line_start_cache (w, start, end, pointm, 1);
+      /* Don't pollute the cache if not sure if we are correct */
+      if (w->start_at_line_beg)
+       update_line_start_cache (w, start, end, pointm, 1);
       redisplay_output_window (w);
       /*
        * If we just displayed the echo area, the line start cache is
-       * no longer valid, because the minibuffer window is assocaited
+       * no longer valid, because the minibuffer window is associated
        * with the window now.
        */
       if (echo_active)
@@ -5249,6 +6383,12 @@ regeneration_done:
      somewhere else once tty updates occur on a per-frame basis. */
   mark_face_cachels_as_clean (w);
 
+  /* The glyph cachels only get dirty if someone changed something.
+   Since redisplay has now effectively ended we can reset the dirty
+   flag since everything must be up-to-date. */
+  if (glyphs_changed)
+    mark_glyph_cachels_as_clean (w);
+
   w->windows_changed = 0;
 }
 
@@ -5328,12 +6468,13 @@ call_redisplay_end_triggers (struct window *w, void *closure)
 
 /* Ensure that all windows on the given frame are correctly displayed. */
 
-static int
+int
 redisplay_frame (struct frame *f, int preemption_check)
 {
   struct device *d = XDEVICE (f->device);
 
-  if (preemption_check)
+  if (preemption_check
+      && !DEVICE_IMPL_FLAG (d, XDEVIMPF_DONT_PREEMPT_REDISPLAY))
     {
       /* The preemption check itself takes a lot of time,
         so normally don't do it here.  We do it if called
@@ -5345,19 +6486,29 @@ redisplay_frame (struct frame *f, int preemption_check)
        return 1;
     }
 
+  if (!internal_equal (f->old_buffer_alist, f->buffer_alist, 0))
+    {
+      Lisp_Object frame;
+
+      f->old_buffer_alist = Freplace_list (f->old_buffer_alist,
+                                          f->buffer_alist);
+      XSETFRAME (frame, f);
+      va_run_hook_with_args (Qbuffer_list_changed_hook, 1, frame);
+    }
+
   /* Before we put a hold on frame size changes, attempt to process
      any which are already pending. */
   if (f->size_change_pending)
     change_frame_size (f, f->new_height, f->new_width, 0);
 
   /* If frame size might need to be changed, due to changed size
-     of toolbars, scroolabrs etc, change it now */
+     of toolbars, scrollbars etc, change it now */
   if (f->size_slipped)
     {
       adjust_frame_size (f);
       assert (!f->size_slipped);
     }
-  
+
   /* The menubar, toolbar, and icon updates must be done before
      hold_frame_size_changes is called and we are officially
      'in_display'.  They may eval lisp code which may call Fsignal.
@@ -5370,11 +6521,38 @@ redisplay_frame (struct frame *f, int preemption_check)
      being handled. */
   update_frame_menubars (f);
 #endif /* HAVE_MENUBARS */
-
 #ifdef HAVE_TOOLBARS
-  /* Update the toolbars. */
-  update_frame_toolbars (f);
+  /* Update the toolbars geometry. We don't update the toolbars
+     themselves at this point since the space they are trying to
+     occupy may currently by occupied by gutter elements. Instead we
+     update the geometry, then update the gutter geometry, then update
+     the gutters - which will cause mapped windows to be repositioned
+     - and finally update the toolbars. */
+  update_frame_toolbars_geometry (f);
 #endif /* HAVE_TOOLBARS */
+  /* Gutter update proper has to be done inside display when no frame
+     size changes can occur, thus we separately update the gutter
+     geometry here if it needs it. */
+  update_frame_gutter_geometry (f);
+
+  /* If we clear the frame we have to force its contents to be redrawn. */
+  if (f->clear)
+    f->frame_changed = 1;
+
+  /* Invalidate the subwindow caches. We use subwindows_changed here
+     to cause subwindows to get instantiated. This is because
+     subwindows_state_changed is less strict - dealing with things
+     like the clicked state of button. We have to do this before
+     redisplaying the gutters as subwindows get unmapped in the
+     process.*/
+  if (f->frame_changed)
+    reset_frame_subwindow_instance_cache (f);
+
+  if (f->frame_changed || f->subwindows_changed)
+    {
+      /* we have to do this so the gutter gets regenerated. */
+      reset_gutter_display_lines (f);
+    }
 
   hold_frame_size_changes ();
 
@@ -5402,13 +6580,23 @@ redisplay_frame (struct frame *f, int preemption_check)
      #### If a frame-size change does occur we should probably
      actually be preempting redisplay. */
 
-  /* If we clear the frame we have to force its contents to be redrawn. */
-  if (f->clear)
-    f->frame_changed = 1;
+  MAYBE_DEVMETH (d, frame_output_begin, (f));
+
+  /* We can now update the gutters, safe in the knowledge that our
+     efforts won't get undone. */
+
+  /* This can call lisp, but redisplay is protected by binding
+     inhibit_quit.  More importantly the code involving display lines
+     *assumes* that GC will not happen and so does not GCPRO
+     anything. Since we use this code the whole time with the gutters
+     we cannot allow GC to happen when manipulating the gutters. */
+  update_frame_gutters (f);
 
   /* Erase the frame before outputting its contents. */
   if (f->clear)
-    DEVMETH (d, clear_frame, (f));
+    {
+      MAYBE_DEVMETH (d, clear_frame, (f));
+    }
 
   /* Do the selected window first. */
   redisplay_window (FRAME_SELECTED_WINDOW (f), 0);
@@ -5416,30 +6604,21 @@ redisplay_frame (struct frame *f, int preemption_check)
   /* Then do the rest. */
   redisplay_windows (f->root_window, 1);
 
-  /* We now call the output_end routine for tty frames.  We delay
-     doing so in order to avoid cursor flicker.  So much for 100%
-     encapsulation. */
-  if (FRAME_TTY_P (f))
-    DEVMETH (d, output_end, (d));
+  MAYBE_DEVMETH (d, frame_output_end, (f));
 
   update_frame_title (f);
 
-  f->buffers_changed  = 0;
-  f->clip_changed     = 0;
-  f->extents_changed  = 0;
-  f->faces_changed    = 0;
-  f->frame_changed    = 0;
-  f->glyphs_changed   = 0;
-  f->icon_changed     = 0;
-  f->menubar_changed  = 0;
-  f->modeline_changed = 0;
-  f->point_changed    = 0;
-  f->toolbar_changed  = 0;
-  f->windows_changed  = 0;
-  f->windows_structure_changed = 0;
+#ifdef HAVE_TOOLBARS
+  /* Finally update the toolbars. It seems its possible to get in a
+     cycle between updating the gutter and the toolbars. Basically we
+     want to end up with both being up-to-date and this doesn't seem
+     possible in a single pass. */
+  update_frame_toolbars (f);
+#endif /* HAVE_TOOLBARS */
+
+  CLASS_RESET_CHANGED_FLAGS (f);
   f->window_face_cache_reset = 0;
   f->echo_area_garbaged = 0;
-
   f->clear = 0;
 
   if (!f->size_change_pending)
@@ -5456,28 +6635,41 @@ redisplay_frame (struct frame *f, int preemption_check)
   return 0;
 }
 
-/* Ensure that all frames on the given device are correctly displayed. */
+/* Ensure that all frames on the given device are correctly displayed.
+   If AUTOMATIC is non-zero, and the device implementation indicates
+   no automatic redisplay, as printers do, then the device is not
+   redisplayed. AUTOMATIC is set to zero when called from lisp
+   functions (redraw-device) and (redisplay-device), and to non-zero
+   when called from "lazy" redisplay();
+*/
 
 static int
-redisplay_device (struct device *d)
+redisplay_device (struct device *d, int automatic)
 {
   Lisp_Object frame, frmcons;
-  int preempted = 0;
   int size_change_failed = 0;
   struct frame *f;
 
+  if (automatic && DEVICE_IMPL_FLAG (d, XDEVIMPF_NO_AUTO_REDISPLAY))
+    return 0;
+
   if (DEVICE_STREAM_P (d)) /* nothing to do */
     return 0;
 
   /* It is possible that redisplay has been called before the
-     device is fully initialized.  If so then continue with the
-     next device. */
+     device is fully initialized, or that the console implementation
+     allows frameless devices.  If so then continue with the next
+     device. */
   if (NILP (DEVICE_SELECTED_FRAME (d)))
     return 0;
 
-  REDISPLAY_PREEMPTION_CHECK;
-  if (preempted)
-    return 1;
+  if (!DEVICE_IMPL_FLAG (d, XDEVIMPF_DONT_PREEMPT_REDISPLAY))
+    {
+      int preempted;
+      REDISPLAY_PREEMPTION_CHECK;
+      if (preempted)
+       return 1;
+    }
 
   /* Always do the selected frame first. */
   frame = DEVICE_SELECTED_FRAME (d);
@@ -5489,18 +6681,13 @@ redisplay_device (struct device *d)
 
   if (FRAME_REPAINT_P (f))
     {
-      if (f->buffers_changed  || f->clip_changed  || f->extents_changed ||
-         f->faces_changed    || f->frame_changed || f->menubar_changed ||
-         f->modeline_changed || f->point_changed || f->size_changed    ||
-         f->toolbar_changed  || f->windows_changed || f->size_slipped  ||
-         f->windows_structure_changed || f->glyphs_changed)
+      if (CLASS_REDISPLAY_FLAGS_CHANGEDP(f))
        {
-         preempted = redisplay_frame (f, 0);
+         int preempted = redisplay_frame (f, 0);
+         if (preempted)
+           return 1;
        }
 
-      if (preempted)
-       return 1;
-
       /* If the frame redisplay did not get preempted, then this flag
          should have gotten set to 0.  It might be possible for that
          not to happen if a size change event were to occur at an odd
@@ -5523,19 +6710,13 @@ redisplay_device (struct device *d)
 
       if (FRAME_REPAINT_P (f))
        {
-         if (f->buffers_changed  || f->clip_changed  || f->extents_changed ||
-             f->faces_changed    || f->frame_changed || f->menubar_changed ||
-             f->modeline_changed || f->point_changed || f->size_changed    ||
-             f->toolbar_changed  || f->windows_changed ||
-             f->windows_structure_changed ||
-             f->glyphs_changed)
+         if (CLASS_REDISPLAY_FLAGS_CHANGEDP (f))
            {
-             preempted = redisplay_frame (f, 0);
+             int preempted = redisplay_frame (f, 0);
+             if (preempted)
+               return 1;
            }
 
-         if (preempted)
-           return 1;
-
          if (f->size_change_pending)
            size_change_failed = 1;
        }
@@ -5543,19 +6724,7 @@ redisplay_device (struct device *d)
 
   /* If we get here then we redisplayed all of our frames without
      getting preempted so mark ourselves as clean. */
-  d->buffers_changed  = 0;
-  d->clip_changed     = 0;
-  d->extents_changed  = 0;
-  d->faces_changed    = 0;
-  d->frame_changed    = 0;
-  d->glyphs_changed   = 0;
-  d->icon_changed     = 0;
-  d->menubar_changed  = 0;
-  d->modeline_changed = 0;
-  d->point_changed    = 0;
-  d->toolbar_changed  = 0;
-  d->windows_changed  = 0;
-  d->windows_structure_changed = 0;
+  CLASS_RESET_CHANGED_FLAGS (d);
 
   if (!size_change_failed)
     d->size_changed = 0;
@@ -5590,13 +6759,8 @@ redisplay_without_hooks (void)
   if (asynch_device_change_pending)
     handle_asynch_device_change ();
 
-  if (!buffers_changed && !clip_changed     && !extents_changed &&
-      !faces_changed   && !frame_changed    && !icon_changed    &&
-      !menubar_changed && !modeline_changed && !point_changed   &&
-      !size_changed    && !toolbar_changed  && !windows_changed &&
-      !glyphs_changed  &&
-      !windows_structure_changed && !disable_preemption &&
-      preemption_count < max_preempts)
+  if (!GLOBAL_REDISPLAY_FLAGS_CHANGEDP &&
+      !disable_preemption && preemption_count < max_preempts)
     goto done;
 
   DEVICE_LOOP_NO_BREAK (devcons, concons)
@@ -5604,14 +6768,9 @@ redisplay_without_hooks (void)
       struct device *d = XDEVICE (XCAR (devcons));
       int preempted;
 
-      if (d->buffers_changed  || d->clip_changed     || d->extents_changed ||
-         d->faces_changed    || d->frame_changed    || d->icon_changed    ||
-         d->menubar_changed  || d->modeline_changed || d->point_changed   ||
-         d->size_changed     || d->toolbar_changed  || d->windows_changed ||
-         d->windows_structure_changed ||
-         d->glyphs_changed)
+      if (CLASS_REDISPLAY_FLAGS_CHANGEDP (d))
        {
-         preempted = redisplay_device (d);
+         preempted = redisplay_device (d, 1);
 
          if (preempted)
            {
@@ -5628,18 +6787,7 @@ redisplay_without_hooks (void)
   preemption_count = 0;
 
   /* Mark redisplay as accurate */
-  buffers_changed  = 0;
-  clip_changed     = 0;
-  extents_changed  = 0;
-  frame_changed    = 0;
-  glyphs_changed   = 0;
-  icon_changed     = 0;
-  menubar_changed  = 0;
-  modeline_changed = 0;
-  point_changed    = 0;
-  toolbar_changed  = 0;
-  windows_changed  = 0;
-  windows_structure_changed = 0;
+  GLOBAL_RESET_CHANGED_FLAGS;
   RESET_CHANGED_SET_FLAGS;
 
   if (faces_changed)
@@ -5700,9 +6848,9 @@ window_line_number (struct window *w, int type)
 {
   struct device *d = XDEVICE (XFRAME (w->frame)->device);
   struct buffer *b = XBUFFER (w->buffer);
-  /* Be careful in the order of these tests. The first clasue will
+  /* Be careful in the order of these tests. The first clause will
      fail if DEVICE_SELECTED_FRAME == Qnil (since w->frame cannot be).
-     This can occur when the frame title is computed really early */ 
+     This can occur when the frame title is computed really early */
   Bufpos pos =
     ((EQ(DEVICE_SELECTED_FRAME(d), w->frame) &&
        (w == XWINDOW (FRAME_SELECTED_WINDOW (device_selected_frame(d)))) &&
@@ -5736,7 +6884,7 @@ static void
 decode_mode_spec (struct window *w, Emchar spec, int type)
 {
   Lisp_Object obj = Qnil;
-  CONST char *str = NULL;
+  const char *str = NULL;
   struct buffer *b = XBUFFER (w->buffer);
 
   Dynarr_reset (mode_spec_bufbyte_string);
@@ -5765,7 +6913,7 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
        long_to_string (buf, col);
 
        Dynarr_add_many (mode_spec_bufbyte_string,
-                        (CONST Bufbyte *) buf, strlen (buf));
+                        (const Bufbyte *) buf, strlen (buf));
 
        goto decode_mode_spec_done;
       }
@@ -5870,7 +7018,6 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
     case 'p':
     {
       Bufpos pos = marker_position (w->start[type]);
-      Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
       /* This had better be while the desired lines are being done. */
       if (w->window_end_pos[type] <= BUF_Z (b) - BUF_ZV (b))
@@ -5887,15 +7034,20 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
          /* This hard limit is ok since the string it will hold has a
              fixed maximum length of 3.  But just to be safe... */
          char buf[10];
+         Charcount chars = pos - BUF_BEGV (b);
+         Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
-         total = ((pos - BUF_BEGV (b)) * 100 + total - 1) / total;
+         /* Avoid overflow on big buffers */
+         int percent = total > LONG_MAX/200 ?
+           (chars + total/200) / (total / 100) :
+           (chars * 100 + total/2) / 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 (percent == 100)
+           percent = 99;
 
-         sprintf (buf, "%2d%%", total);
+         sprintf (buf, "%d%%", percent);
          Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
                           strlen (buf));
 
@@ -5910,7 +7062,6 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
     {
       Bufpos toppos = marker_position (w->start[type]);
       Bufpos botpos = BUF_Z (b) - w->window_end_pos[type];
-      Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
       /* botpos is only accurate as of the last redisplay, so we can
          only treat it as a hint.  In particular, after erase-buffer,
@@ -5930,18 +7081,23 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
          /* This hard limit is ok since the string it will hold has a
              fixed maximum length of around 6.  But just to be safe... */
          char buf[10];
+         Charcount chars = botpos - BUF_BEGV (b);
+         Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
-         total = ((botpos - BUF_BEGV (b)) * 100 + total - 1) / total;
+         /* Avoid overflow on big buffers */
+         int percent = total > LONG_MAX/200 ?
+           (chars + total/200) / (total / 100) :
+           (chars * 100 + total/2) / max (total, 1);
 
          /* 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 (percent == 100)
+           percent = 99;
 
          if (toppos <= BUF_BEGV (b))
-           sprintf (buf, "Top%2d%%", total);
+           sprintf (buf, "Top%d%%", percent);
          else
-           sprintf (buf, "%2d%%", total);
+           sprintf (buf, "%d%%", percent);
 
          Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
                           strlen (buf));
@@ -6044,7 +7200,7 @@ free_display_line (struct display_line *dl)
 /* Given an array of display lines, free them and all data structures
    contained within them. */
 
-static void
+void
 free_display_lines (display_line_dynarr *dla)
 {
   int line;
@@ -6077,7 +7233,7 @@ free_display_structs (struct window_mirror *mir)
 \f
 
 static void
-mark_glyph_block_dynarr (glyph_block_dynarr *gba, void (*markobj) (Lisp_Object))
+mark_glyph_block_dynarr (glyph_block_dynarr *gba)
 {
   if (gba)
     {
@@ -6086,14 +7242,18 @@ mark_glyph_block_dynarr (glyph_block_dynarr *gba, void (*markobj) (Lisp_Object))
 
       for (; gb < gb_last; gb++)
        {
-         if (!NILP (gb->glyph))  ((markobj) (gb->glyph));
-         if (!NILP (gb->extent)) ((markobj) (gb->extent));
+         if (!NILP (gb->glyph))
+           mark_object (gb->glyph);
+         if (!NILP (gb->extent))
+           mark_object (gb->extent);
        }
     }
 }
 
-static void
-mark_redisplay_structs (display_line_dynarr *dla, void (*markobj) (Lisp_Object))
+/* See the comment in image_instantiate_cache_result as to why marking
+   the glyph will also mark the image_instance. */
+void
+mark_redisplay_structs (display_line_dynarr *dla)
 {
   display_line *dl = Dynarr_atp (dla, 0);
   display_line *dl_last = Dynarr_atp (dla, Dynarr_length (dla));
@@ -6115,43 +7275,43 @@ mark_redisplay_structs (display_line_dynarr *dla, void (*markobj) (Lisp_Object))
              if (r->type == RUNE_DGLYPH)
                {
                  if (!NILP (r->object.dglyph.glyph))
-                   ((markobj) (r->object.dglyph.glyph));
+                   mark_object (r->object.dglyph.glyph);
                  if (!NILP (r->object.dglyph.extent))
-                   ((markobj) (r->object.dglyph.extent));
+                   mark_object (r->object.dglyph.extent);
                }
            }
        }
 
-      mark_glyph_block_dynarr (dl->left_glyphs,  markobj);
-      mark_glyph_block_dynarr (dl->right_glyphs, markobj);
+      mark_glyph_block_dynarr (dl->left_glyphs);
+      mark_glyph_block_dynarr (dl->right_glyphs);
     }
 }
 
 static void
-mark_window_mirror (struct window_mirror *mir, void (*markobj)(Lisp_Object))
+mark_window_mirror (struct window_mirror *mir)
 {
-  mark_redisplay_structs (mir->current_display_lines, markobj);
-  mark_redisplay_structs (mir->desired_display_lines, markobj);
+  mark_redisplay_structs (mir->current_display_lines);
+  mark_redisplay_structs (mir->desired_display_lines);
 
   if (mir->next)
-    mark_window_mirror (mir->next, markobj);
+    mark_window_mirror (mir->next);
 
   if (mir->hchild)
-    mark_window_mirror (mir->hchild, markobj);
+    mark_window_mirror (mir->hchild);
   else if (mir->vchild)
-    mark_window_mirror (mir->vchild, markobj);
+    mark_window_mirror (mir->vchild);
 }
 
 void
-mark_redisplay (void (*markobj)(Lisp_Object))
+mark_redisplay (void)
 {
   Lisp_Object frmcons, devcons, concons;
 
   FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
     {
       struct frame *f = XFRAME (XCAR (frmcons));
-      update_frame_window_mirror (f);
-      mark_window_mirror (f->root_mirror, markobj);
+      mark_window_mirror (f->root_mirror);
+      mark_gutters (f);
     }
 }
 \f
@@ -6215,7 +7375,7 @@ mark_redisplay (void (*markobj)(Lisp_Object))
 
 /* This will get used quite a bit so we don't want to be constantly
    allocating and freeing it. */
-line_start_cache_dynarr *internal_cache;
+static line_start_cache_dynarr *internal_cache;
 
 /* Makes internal_cache represent the TYPE display structs and only
    the TYPE display structs. */
@@ -6261,7 +7421,7 @@ validate_line_start_cache (struct window *w)
          size changes can cause text shifting.  However, the extent
          covering the region is constantly having its face set and
          priority altered by the mouse code.  This means that the line
-         start cache is constanty being invalidated.  This is bad
+         start cache is constantly being invalidated.  This is bad
          since the mouse code also triggers heavy usage of the cache.
          Since it is an unlikely that f->extents being changed
          indicates that the cache really needs to be updated and if it
@@ -6321,7 +7481,8 @@ point_in_line_start_cache (struct window *w, Bufpos point, int min_past)
 {
   struct buffer *b = XBUFFER (w->buffer);
   line_start_cache_dynarr *cache = w->line_start_cache;
-  unsigned int top, bottom, pos;
+  unsigned int top, bottom;
+  EMACS_INT pos;
 
   validate_line_start_cache (w);
   w->line_cache_validation_override++;
@@ -6449,7 +7610,7 @@ find_point_loop:
 
   while (1)
     {
-      unsigned int new_pos;
+      EMACS_INT new_pos;
       Bufpos start, end;
 
       pos = (bottom + top + 1) >> 1;
@@ -6509,7 +7670,7 @@ int
 point_would_be_visible (struct window *w, Bufpos startp, Bufpos point)
 {
   struct buffer *b = XBUFFER (w->buffer);
-  int pixpos = 0;
+  int pixpos = -WINDOW_TEXT_TOP_CLIP(w);
   int bottom = WINDOW_TEXT_HEIGHT (w);
   int start_elt;
 
@@ -6588,11 +7749,18 @@ point_would_be_visible (struct window *w, Bufpos startp, Bufpos point)
    displayed.  The end of the last line is also know as the window end
    position.
 
+   WARNING: It is possible that redisplay failed to layout any lines for the
+   windows. Under normal circumstances this is rare. However it seems that it
+   does occur in the following situation: A mouse event has come in and we
+   need to compute its location in a window. That code (in
+   pixel_to_glyph_translation) already can handle 0 as an error return value.
+
    #### With a little work this could probably be reworked as just a
    call to start_with_line_at_pixpos. */
 
 static Bufpos
-start_end_of_last_line (struct window *w, Bufpos startp, int end)
+start_end_of_last_line (struct window *w, Bufpos startp, int end,
+                        int may_error)
 {
   struct buffer *b = XBUFFER (w->buffer);
   line_start_cache_dynarr *cache = w->line_start_cache;
@@ -6612,7 +7780,7 @@ start_end_of_last_line (struct window *w, Bufpos startp, int end)
 
   start_elt = point_in_line_start_cache (w, cur_start, 0);
   if (start_elt == -1)
-    abort ();  /* this had better never happen */
+      return may_error ? 0 : startp;
 
   while (1)
     {
@@ -6676,7 +7844,7 @@ start_end_of_last_line (struct window *w, Bufpos startp, int end)
 Bufpos
 start_of_last_line (struct window *w, Bufpos startp)
 {
-  return start_end_of_last_line (w, startp, 0);
+  return start_end_of_last_line (w, startp, 0 , 0);
 }
 
 /* For the given window W, if display starts at STARTP, what will be
@@ -6686,9 +7854,16 @@ start_of_last_line (struct window *w, Bufpos startp)
 Bufpos
 end_of_last_line (struct window *w, Bufpos startp)
 {
-  return start_end_of_last_line (w, startp, 1);
+  return start_end_of_last_line (w, startp, 1, 0);
+}
+
+static Bufpos
+end_of_last_line_may_error (struct window *w, Bufpos startp)
+{
+  return start_end_of_last_line (w, startp, 1, 1);
 }
 
+
 /* For window W, what does the starting position have to be so that
    the line containing POINT will cover pixel position PIXPOS. */
 
@@ -6734,7 +7909,7 @@ start_with_line_at_pixpos (struct window *w, Bufpos point, int pixpos)
        }
 
       cur_elt--;
-      if (cur_elt < 0)
+      while (cur_elt < 0)
        {
          Bufpos from, to;
          int win_char_height;
@@ -6754,7 +7929,20 @@ start_with_line_at_pixpos (struct window *w, Bufpos point, int pixpos)
          update_line_start_cache (w, from, to, point, 0);
 
          cur_elt = point_in_line_start_cache (w, cur_pos, 2) - 1;
-         assert (cur_elt >= 0);
+         assert (cur_elt >= -1);
+         /* This used to be cur_elt>=0 under the assumption that if
+            point is in the top line and not at BUF_BEGV, then
+            setting the window_start to a newline before the start of
+            the first line will always cause scrolling.
+
+            However in my (jv) opinion this is wrong.  That new line
+            can be hidden in various ways: invisible extents, an
+            explicit window-start not at a newline character etc.
+            The existence of those are indeed known to create crashes
+            on that assert.  So we have no option but to continue the
+            search if we found point at the top of the line_start_cache
+            again. */
+         cur_pos = Dynarr_atp (w->line_start_cache,0)->start;
        }
       prev_pos = cur_pos;
     }
@@ -6859,7 +8047,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
 
   validate_line_start_cache (w);
   w->line_cache_validation_override++;
-  updating_line_start_cache = 1;
 
   if (from < BUF_BEGV (b))
     from = BUF_BEGV (b);
@@ -6868,7 +8055,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
 
   if (from > to)
     {
-      updating_line_start_cache = 0;
       w->line_cache_validation_override--;
       return;
     }
@@ -6881,7 +8067,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
       /* Check to see if the desired range is already in the cache. */
       if (from >= low_bound && to <= high_bound)
        {
-         updating_line_start_cache = 0;
          w->line_cache_validation_override--;
          return;
        }
@@ -6910,7 +8095,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
       update_internal_cache_list (w, DESIRED_DISP);
       if (!Dynarr_length (internal_cache))
        {
-         updating_line_start_cache = 0;
          w->line_cache_validation_override--;
          return;
        }
@@ -6938,7 +8122,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
        {
          Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
                           Dynarr_length (internal_cache));
-         updating_line_start_cache = 0;
          w->line_cache_validation_override--;
          return;
        }
@@ -6947,7 +8130,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
          the bounds of the DESIRED structs in the first place. */
       if (start >= low_bound && end <= high_bound)
        {
-         updating_line_start_cache = 0;
          w->line_cache_validation_override--;
          return;
        }
@@ -6970,7 +8152,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
              Dynarr_reset (cache);
              Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
                               Dynarr_length (internal_cache));
-             updating_line_start_cache = 0;
              w->line_cache_validation_override--;
              return;
            }
@@ -6996,7 +8177,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
              Dynarr_reset (cache);
              Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
                               Dynarr_length (internal_cache));
-             updating_line_start_cache = 0;
              w->line_cache_validation_override--;
              return;
            }
@@ -7005,7 +8185,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
                           Dynarr_length (internal_cache) - ic_elt);
        }
 
-      updating_line_start_cache = 0;
       w->line_cache_validation_override--;
       return;
     }
@@ -7025,23 +8204,9 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
          update_internal_cache_list (w, CMOTION_DISP);
 
          /* If this assert is triggered then regenerate_window failed
-             to layout a single line.  That is not supposed to be
-             possible because we impose a minimum height on the buffer
-             and override vertical clip when we are in here. */
-         /* #### Ah, but it is because the window may temporarily
-             exist but not have any lines at all if the minibuffer is
-             real big.  Look into that situation better. */
-         if (!Dynarr_length (internal_cache))
-           {
-             if (old_lb == -1 && low_bound == -1)
-               {
-                 updating_line_start_cache = 0;
-                 w->line_cache_validation_override--;
-                 return;
-               }
-
-             assert (Dynarr_length (internal_cache));
-           }
+             to layout a single line. This is not possible since we
+            force at least a single line to be layout for CMOTION_DISP */
+         assert (Dynarr_length (internal_cache));
          assert (startp == Dynarr_atp (internal_cache, 0)->start);
 
          ic_elt = Dynarr_length (internal_cache) - 1;
@@ -7061,7 +8226,7 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
 
           /*
            * Handle invisible text properly:
-           * If the last line we're inserting has the same end as the 
+           * If the last line we're inserting has the same end as the
            * line before which it will be added, merge the two lines.
            */
           if (Dynarr_length (cache)  &&
@@ -7087,7 +8252,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
          startp = new_startp;
          if (startp > BUF_ZV (b))
            {
-             updating_line_start_cache = 0;
              w->line_cache_validation_override--;
              return;
            }
@@ -7121,7 +8285,6 @@ update_line_start_cache (struct window *w, Bufpos from, Bufpos to,
       while (to > high_bound);
     }
 
-  updating_line_start_cache = 0;
   w->line_cache_validation_override--;
   assert (to <= high_bound);
 }
@@ -7297,7 +8460,7 @@ get_position_object (struct display_line *dl, Lisp_Object *obj1,
     d->pixel_to_glyph_cache.obj1 = *obj1;                              \
     d->pixel_to_glyph_cache.obj2 = *obj2;                              \
     d->pixel_to_glyph_cache.retval = position;                         \
-    RETURN__ position;                                                 \
+    RETURN_SANS_WARNINGS position;                                     \
   } while (0)
 
 /* Given x and y coordinates in pixels relative to a frame, return
@@ -7758,7 +8921,7 @@ pixel_to_glyph_translation (struct frame *f, int x_coord, int y_coord,
                    }
                  else if (past_end
                           || (rb->type == RUNE_CHAR
-                              && rb->object.chr.ch == '\n'))
+                              && CHARC_ASCII_EQ (rb->object.cglyph, '\n')))
                    {
                      (*row)--;
                      /* At this point we may have glyphs in the right
@@ -7829,7 +8992,7 @@ pixel_to_glyph_translation (struct frame *f, int x_coord, int y_coord,
   if (!MARKERP ((*w)->start[CURRENT_DISP]))
     *closest = 0;
   else
-    *closest = end_of_last_line (*w,
+    *closest = end_of_last_line_may_error (*w,
                                 marker_position ((*w)->start[CURRENT_DISP]));
   *col = 0;
   UPDATE_CACHE_RETURN;
@@ -7862,6 +9025,9 @@ Ensure that all minibuffers are correctly showing the echo area.
          if (FRAME_REPAINT_P (f) && FRAME_HAS_MINIBUF_P (f))
            {
              Lisp_Object window = FRAME_MINIBUF_WINDOW (f);
+
+             MAYBE_DEVMETH (d, frame_output_begin, (f));
+
              /*
               * If the frame size has changed, there may be random
               * chud on the screen left from previous messages
@@ -7870,19 +9036,15 @@ Ensure that all minibuffers are correctly showing the echo area.
               */
              if (f->echo_area_garbaged)
                {
-                 DEVMETH (d, clear_frame, (f));
+                 MAYBE_DEVMETH (d, clear_frame, (f));
                  f->echo_area_garbaged = 0;
                }
              redisplay_window (window, 0);
+             MAYBE_DEVMETH (d, frame_output_end, (f));
+
              call_redisplay_end_triggers (XWINDOW (window), 0);
            }
        }
-
-      /* We now call the output_end routine for tty frames.  We delay
-        doing so in order to avoid cursor flicker.  So much for 100%
-        encapsulation. */
-      if (DEVICE_TTY_P (d))
-       DEVMETH (d, output_end, (d));
     }
 
   return Qnil;
@@ -7917,6 +9079,9 @@ input and is guaranteed to proceed to completion.
   f->clear = 1;
   redisplay_frame (f, 1);
 
+  /* See the comment in Fredisplay_frame. */
+  RESET_CHANGED_SET_FLAGS;
+
   return unbind_to (count, Qnil);
 }
 
@@ -7944,6 +9109,15 @@ input and is guaranteed to proceed to completion.
 
   redisplay_frame (f, 1);
 
+  /* If we don't reset the global redisplay flags here, subsequent
+     changes to the display will not get registered by redisplay
+     because it thinks it already has registered changes. If you
+     really knew what you were doing you could confuse redisplay by
+     calling Fredisplay_frame while updating another frame. We assume
+     that if you know what you are doing you will not be that
+     stupid. */
+  RESET_CHANGED_SET_FLAGS;
+
   return unbind_to (count, Qnil);
 }
 
@@ -7971,7 +9145,10 @@ input and is guaranteed to proceed to completion.
     {
       XFRAME (XCAR (frmcons))->clear = 1;
     }
-  redisplay_device (d);
+  redisplay_device (d, 0);
+
+  /* See the comment in Fredisplay_frame. */
+  RESET_CHANGED_SET_FLAGS;
 
   return unbind_to (count, Qnil);
 }
@@ -7998,7 +9175,10 @@ input and is guaranteed to proceed to completion.
       disable_preemption++;
     }
 
-  redisplay_device (d);
+  redisplay_device (d, 0);
+
+  /* See the comment in Fredisplay_frame. */
+  RESET_CHANGED_SET_FLAGS;
 
   return unbind_to (count, Qnil);
 }
@@ -8050,46 +9230,37 @@ redisplay_variable_changed (Lisp_Object sym, Lisp_Object *val,
   return 0;
 }
 
+/* This is called if the built-in glyphs have their properties
+   changed. */
 void
 redisplay_glyph_changed (Lisp_Object glyph, Lisp_Object property,
                         Lisp_Object locale)
 {
   if (WINDOWP (locale))
     {
-      struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (locale)));
-      MARK_FRAME_GLYPHS_CHANGED (f);
+      MARK_FRAME_GLYPHS_CHANGED (XFRAME (WINDOW_FRAME (XWINDOW (locale))));
     }
   else if (FRAMEP (locale))
     {
-      struct frame *f = XFRAME (locale);
-      MARK_FRAME_GLYPHS_CHANGED (f);
+      MARK_FRAME_GLYPHS_CHANGED (XFRAME (locale));
     }
   else if (DEVICEP (locale))
     {
       Lisp_Object frmcons;
       DEVICE_FRAME_LOOP (frmcons, XDEVICE (locale))
-       {
-         struct frame *f = XFRAME (XCAR (frmcons));
-         MARK_FRAME_GLYPHS_CHANGED (f);
-       }
+       MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
     }
   else if (CONSOLEP (locale))
     {
       Lisp_Object frmcons, devcons;
       CONSOLE_FRAME_LOOP_NO_BREAK (frmcons, devcons, XCONSOLE (locale))
-       {
-         struct frame *f = XFRAME (XCAR (frmcons));
-         MARK_FRAME_GLYPHS_CHANGED (f);
-       }
+       MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
     }
   else /* global or buffer */
     {
       Lisp_Object frmcons, devcons, concons;
       FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
-       {
-         struct frame *f = XFRAME (XCAR (frmcons));
-         MARK_FRAME_GLYPHS_CHANGED (f);
-       }
+       MARK_FRAME_GLYPHS_CHANGED (XFRAME (XCAR (frmcons)));
     }
 }
 
@@ -8183,16 +9354,22 @@ init_redisplay (void)
   preemption_count = 0;
   max_preempts = INIT_MAX_PREEMPTS;
 
+#ifndef PDUMP
   if (!initialized)
+#endif
     {
-      cmotion_display_lines = Dynarr_new (display_line);
-      mode_spec_bufbyte_string = Dynarr_new (Bufbyte);
-      formatted_string_emchar_dynarr = Dynarr_new (Emchar);
-      formatted_string_extent_dynarr = Dynarr_new (EXTENT);
-      formatted_string_extent_start_dynarr = Dynarr_new (Bytecount);
-      formatted_string_extent_end_dynarr = Dynarr_new (Bytecount);
-      internal_cache = Dynarr_new (line_start_cache);
-      xzero (formatted_string_display_line);
+      if (!cmotion_display_lines)
+       cmotion_display_lines = Dynarr_new (display_line);
+      if (!mode_spec_bufbyte_string)
+       mode_spec_bufbyte_string = Dynarr_new (Bufbyte);
+      if (!formatted_string_extent_dynarr)
+       formatted_string_extent_dynarr = Dynarr_new (EXTENT);
+      if (!formatted_string_extent_start_dynarr)
+       formatted_string_extent_start_dynarr = Dynarr_new (Bytecount);
+      if (!formatted_string_extent_end_dynarr)
+       formatted_string_extent_end_dynarr = Dynarr_new (Bytecount);
+      if (!internal_cache)
+       internal_cache = Dynarr_new (line_start_cache);
     }
 
   /* window system is nil when in -batch mode */
@@ -8217,6 +9394,15 @@ init_redisplay (void)
     }
 #endif /* HAVE_X_WINDOWS */
 
+#ifdef HAVE_GTK
+  if (!strcmp (display_use, "gtk"))
+    {
+      Vwindow_system = Qgtk;
+      Vinitial_window_system = Qgtk;
+      return;
+    }
+#endif
+
 #ifdef HAVE_MS_WINDOWS
   if (!strcmp (display_use, "mswindows"))
     {
@@ -8263,9 +9449,10 @@ syms_of_redisplay (void)
 #endif /* INHIBIT_REDISPLAY_HOOKS */
   defsymbol (&Qdisplay_warning_buffer, "display-warning-buffer");
   defsymbol (&Qbar_cursor, "bar-cursor");
-  defsymbol (&Qwindow_scroll_functions, "window-scroll-functions");
   defsymbol (&Qredisplay_end_trigger_functions,
             "redisplay-end-trigger-functions");
+  defsymbol (&Qtop_bottom, "top-bottom");
+  defsymbol (&Qbuffer_list_changed_hook, "buffer-list-changed-hook");
 
   DEFSUBR (Fredisplay_echo_area);
   DEFSUBR (Fredraw_frame);
@@ -8279,6 +9466,7 @@ syms_of_redisplay (void)
 void
 vars_of_redisplay (void)
 {
+
 #if 0
   staticpro (&last_arrow_position);
   staticpro (&last_arrow_string);
@@ -8286,8 +9474,6 @@ vars_of_redisplay (void)
   last_arrow_string = Qnil;
 #endif /* 0 */
 
-  updating_line_start_cache = 0;
-
   /* #### Probably temporary */
   DEFVAR_INT ("redisplay-cache-adjustment", &cache_adjustment /*
 \(Temporary) Setting this will impact the performance of the internal
@@ -8299,7 +9485,7 @@ line start cache.
 Minimum pixel height for clipped bottom display line.
 A clipped line shorter than this won't be displayed.
 */ ,
-                  redisplay_variable_changed);
+                   redisplay_variable_changed);
   vertical_clip = 5;
 
   DEFVAR_INT_MAGIC ("pixel-horizontal-clip-threshold", &horizontal_clip /*
@@ -8307,7 +9493,7 @@ Minimum visible area for clipped glyphs at right boundary.
 Clipped glyphs shorter than this won't be displayed.
 Only pixmap glyph instances are currently allowed to be clipped.
 */ ,
-             redisplay_variable_changed);
+                   redisplay_variable_changed);
   horizontal_clip = 5;
 
   DEFVAR_LISP ("global-mode-string", &Vglobal_mode_string /*
@@ -8319,13 +9505,16 @@ String displayed by modeline-format's "%m" specification.
 Marker for where to display an arrow on top of the buffer text.
 This must be the beginning of a line in order to work.
 See also `overlay-arrow-string'.
-*/ , redisplay_variable_changed);
+*/ ,
+                    redisplay_variable_changed);
   Voverlay_arrow_position = Qnil;
 
   DEFVAR_LISP_MAGIC ("overlay-arrow-string", &Voverlay_arrow_string /*
-String to display as an arrow.  See also `overlay-arrow-position'.
+String or glyph to display as an arrow.  See also `overlay-arrow-position'.
+\(Note that despite the name of this variable, it can be set to a glyph as
+well as a string.)
 */ ,
-                   redisplay_variable_changed);
+                    redisplay_variable_changed);
   Voverlay_arrow_string = Qnil;
 
   DEFVAR_INT ("scroll-step", &scroll_step /*
@@ -8344,13 +9533,22 @@ If this is zero, point is always centered after it moves off screen.
                     &truncate_partial_width_windows /*
 *Non-nil means truncate lines in all windows less than full frame wide.
 */ ,
-                   redisplay_variable_changed);
+                    redisplay_variable_changed);
   truncate_partial_width_windows = 1;
 
-  DEFVAR_BOOL ("visible-bell", &visible_bell /*
-*Non-nil means try to flash the frame to represent a bell.
+  DEFVAR_LISP ("visible-bell", &Vvisible_bell /*
+*Non-nil substitutes a visual signal for the audible bell.
+
+Default behavior is to flash the whole screen.  On some platforms,
+special effects are available using the following values:
+
+'display       Flash the whole screen (ie, the default behavior).
+'top-bottom    Flash only the top and bottom lines of the selected frame.
+
+When effects are unavailable on a platform, the visual bell is the
+default, whole screen.  (Currently only X supports any special effects.)
 */ );
-  visible_bell = 0;
+  Vvisible_bell = Qnil;
 
   DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter /*
 *Non-nil means no need to redraw entire frame after suspending.
@@ -8371,7 +9569,7 @@ instead.
   Vwindow_system = Qnil;
 
   /* #### Temporary shit until window-system is eliminated. */
-  DEFVAR_LISP ("initial-window-system", &Vinitial_window_system /*
+  DEFVAR_CONST_LISP ("initial-window-system", &Vinitial_window_system /*
 DON'T TOUCH
 */ );
   Vinitial_window_system = Qnil;
@@ -8394,24 +9592,29 @@ Non-nil means put cursor in minibuffer, at end of any message there.
      maybe a caret cursor, etc. */
 
   DEFVAR_LISP ("bar-cursor", &Vbar_cursor /*
-Use vertical bar cursor if non-nil.  If t width is 1 pixel, otherwise 2.
+*Use vertical bar cursor if non-nil.  If t width is 1 pixel, otherwise 2.
 */ );
   Vbar_cursor = Qnil;
 
 #ifndef INHIBIT_REDISPLAY_HOOKS
   xxDEFVAR_LISP ("pre-redisplay-hook", &Vpre_redisplay_hook /*
 Function or functions to run before every redisplay.
-Functions on this hook must be careful to avoid signalling errors!
 */ );
   Vpre_redisplay_hook = Qnil;
 
   xxDEFVAR_LISP ("post-redisplay-hook", &Vpost_redisplay_hook /*
 Function or functions to run after every redisplay.
-Functions on this hook must be careful to avoid signalling errors!
 */ );
   Vpost_redisplay_hook = Qnil;
 #endif /* INHIBIT_REDISPLAY_HOOKS */
 
+  DEFVAR_LISP ("buffer-list-changed-hook", &Vbuffer_list_changed_hook /*
+Function or functions to call when a frame's buffer list has changed.
+This is called during redisplay, before redisplaying each frame.
+Functions on this hook are called with one argument, the frame.
+*/ );
+  Vbuffer_list_changed_hook = Qnil;
+
   DEFVAR_INT ("display-warning-tick", &display_warning_tick /*
 Bump this to tell the C code to call `display-warning-buffer'
 at next redisplay.  You should not normally change this; the function
@@ -8470,10 +9673,10 @@ This is a specifier; use `set-specifier' to change it.
   Vleft_margin_width = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vleft_margin_width, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vleft_margin_width,
-                        slot_offset (struct window, left_margin_width),
+                        offsetof (struct window, left_margin_width),
                         some_window_value_changed,
-                        slot_offset (struct frame, left_margin_width),
-                        margin_width_changed_in_frame);
+                        offsetof (struct frame, left_margin_width),
+                        margin_width_changed_in_frame, 0);
 
   DEFVAR_SPECIFIER ("right-margin-width", &Vright_margin_width /*
 *Width of right margin.
@@ -8482,10 +9685,10 @@ This is a specifier; use `set-specifier' to change it.
   Vright_margin_width = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vright_margin_width, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vright_margin_width,
-                        slot_offset (struct window, right_margin_width),
+                        offsetof (struct window, right_margin_width),
                         some_window_value_changed,
-                        slot_offset (struct frame, right_margin_width),
-                        margin_width_changed_in_frame);
+                        offsetof (struct frame, right_margin_width),
+                        margin_width_changed_in_frame, 0);
 
   DEFVAR_SPECIFIER ("minimum-line-ascent", &Vminimum_line_ascent /*
 *Minimum ascent height of lines.
@@ -8494,9 +9697,9 @@ This is a specifier; use `set-specifier' to change it.
   Vminimum_line_ascent = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vminimum_line_ascent, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vminimum_line_ascent,
-                        slot_offset (struct window, minimum_line_ascent),
+                        offsetof (struct window, minimum_line_ascent),
                         some_window_value_changed,
-                        0, 0);
+                        0, 0, 0);
 
   DEFVAR_SPECIFIER ("minimum-line-descent", &Vminimum_line_descent /*
 *Minimum descent height of lines.
@@ -8505,9 +9708,9 @@ This is a specifier; use `set-specifier' to change it.
   Vminimum_line_descent = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vminimum_line_descent, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vminimum_line_descent,
-                        slot_offset (struct window, minimum_line_descent),
+                        offsetof (struct window, minimum_line_descent),
                         some_window_value_changed,
-                        0, 0);
+                        0, 0, 0);
 
   DEFVAR_SPECIFIER ("use-left-overflow", &Vuse_left_overflow /*
 *Non-nil means use the left outside margin as extra whitespace when
@@ -8517,9 +9720,9 @@ This is a specifier; use `set-specifier' to change it.
   Vuse_left_overflow = Fmake_specifier (Qboolean);
   set_specifier_fallback (Vuse_left_overflow, list1 (Fcons (Qnil, Qnil)));
   set_specifier_caching (Vuse_left_overflow,
-                        slot_offset (struct window, use_left_overflow),
+                        offsetof (struct window, use_left_overflow),
                         some_window_value_changed,
-                        0, 0);
+                        0, 0, 0);
 
   DEFVAR_SPECIFIER ("use-right-overflow", &Vuse_right_overflow /*
 *Non-nil means use the right outside margin as extra whitespace when
@@ -8529,9 +9732,9 @@ This is a specifier; use `set-specifier' to change it.
   Vuse_right_overflow = Fmake_specifier (Qboolean);
   set_specifier_fallback (Vuse_right_overflow, list1 (Fcons (Qnil, Qnil)));
   set_specifier_caching (Vuse_right_overflow,
-                        slot_offset (struct window, use_right_overflow),
+                        offsetof (struct window, use_right_overflow),
                         some_window_value_changed,
-                        0, 0);
+                        0, 0, 0);
 
   DEFVAR_SPECIFIER ("text-cursor-visible-p", &Vtext_cursor_visible_p /*
 *Non-nil means the text cursor is visible (this is usually the case).
@@ -8540,8 +9743,8 @@ This is a specifier; use `set-specifier' to change it.
   Vtext_cursor_visible_p = Fmake_specifier (Qboolean);
   set_specifier_fallback (Vtext_cursor_visible_p, list1 (Fcons (Qnil, Qt)));
   set_specifier_caching (Vtext_cursor_visible_p,
-                        slot_offset (struct window, text_cursor_visible_p),
+                        offsetof (struct window, text_cursor_visible_p),
                         text_cursor_visible_p_changed,
-                        0, 0);
+                        0, 0, 0);
 
 }