diff src/redisplay.c @ 412:697ef44129c6 r21-2-14

Import from CVS: tag r21-2-14
author cvs
date Mon, 13 Aug 2007 11:20:41 +0200
parents de805c49cfc1
children 95016f13131a
line wrap: on
line diff
--- a/src/redisplay.c	Mon Aug 13 11:19:22 2007 +0200
+++ b/src/redisplay.c	Mon Aug 13 11:20:41 2007 +0200
@@ -40,6 +40,7 @@
 
 #include <config.h>
 #include "lisp.h"
+#include <limits.h>
 
 #include "buffer.h"
 #include "commands.h"
@@ -50,7 +51,6 @@
 #include "faces.h"
 #include "frame.h"
 #include "glyphs.h"
-#include "gutter.h"
 #include "insdel.h"
 #include "menubar.h"
 #include "objects.h"
@@ -63,10 +63,11 @@
 #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,10 +86,16 @@
 #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)					\
-    ((WINDOW_TTY_P (w) | (!display && scroll_on_clipped_lines))	        \
+  (updating_line_start_cache						\
+   ? 0									\
+   : ((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. */
@@ -100,9 +107,6 @@
   /* 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;
@@ -146,10 +150,6 @@
 			   (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).
@@ -238,19 +238,25 @@
 } 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,
-					 Charcount *offset,
-					 Lisp_Object cur_ext);
+					 face_index findex, int type);
 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, prop_block_dynarr **prop,
+				 Bytind bi_start_pos, int start_col,
+				 prop_block_dynarr **prop,
 				 int type);
 static int create_overlay_glyph_block (struct window *w,
 				       struct display_line *dl);
@@ -287,6 +293,10 @@
    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
@@ -294,9 +304,9 @@
    (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. */
-static EXTENT_dynarr *formatted_string_extent_dynarr;
-static Bytecount_dynarr *formatted_string_extent_start_dynarr;
-static Bytecount_dynarr *formatted_string_extent_end_dynarr;
+EXTENT_dynarr *formatted_string_extent_dynarr;
+Bytecount_dynarr *formatted_string_extent_start_dynarr;
+Bytecount_dynarr *formatted_string_extent_end_dynarr;
 
 
 /* #### probably temporary */
@@ -320,6 +330,9 @@
 /* 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;
+
 /* Nonzero means reading single-character input with prompt
    so put cursor on minibuffer after the prompt.  */
 int cursor_in_echo_area;
@@ -353,15 +366,11 @@
 int glyphs_changed;
 int glyphs_changed_set;
 
-/* non-zero if any subwindow has been deleted. */
+/* non-zero if any displayed subwindow is in need of updating
+   somewhere. */
 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;
@@ -392,10 +401,6 @@
 int toolbar_changed;
 int toolbar_changed_set;
 
-/* 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;
 
@@ -407,9 +412,10 @@
 Lisp_Object Vbar_cursor;
 Lisp_Object Qbar_cursor;
 
-Lisp_Object Vvisible_bell;	/* If true and the terminal will support it
-				   then the frame will flash instead of
-				   beeping when an error occurs */
+
+int visible_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,
@@ -436,12 +442,9 @@
 Lisp_Object Voverlay_arrow_string;
 
 Lisp_Object Vwindow_size_change_functions;
-Lisp_Object Vwindow_scroll_functions;
+Lisp_Object Qwindow_scroll_functions, 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
@@ -451,7 +454,7 @@
 Lisp_Object Qpre_redisplay_hook, Qpost_redisplay_hook;
 #endif /* INHIBIT_REDISPLAY_HOOKS */
 
-static int last_display_warning_tick, display_warning_tick;
+int last_display_warning_tick, display_warning_tick;
 Lisp_Object Qdisplay_warning_buffer;
 int inhibit_warning_display;
 
@@ -461,12 +464,6 @@
 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)
-
 
 /***************************************************************************/
 /*									   */
@@ -636,8 +633,8 @@
   int pix_tab_width = tab_pix_width (w);
 
   /* Adjust n_pos for any hscrolling which has happened. */
-  if (WINDOW_SCROLLED (w))
-    n_pos -= space_width (w) * (w->hscroll - 1) + w->left_xoffset;
+  if (w->hscroll > 1)
+    n_pos -= space_width (w) * (w->hscroll - 1);
 
   while (n_pos <= start_pixpos)
     n_pos += pix_tab_width;
@@ -689,7 +686,8 @@
 
 static Bufpos
 generate_display_line (struct window *w, struct display_line *dl, int bounds,
-		       Bufpos start_pos, prop_block_dynarr **prop,
+		       Bufpos start_pos, int start_col,
+		       prop_block_dynarr **prop,
 		       int type)
 {
   Bufpos ret_bufpos;
@@ -722,7 +720,7 @@
     /* #### urk urk urk!!! Chuck fix this shit! */
     Bytind hacked_up_bytind =
       create_text_block (w, dl, bufpos_to_bytind (b, start_pos),
-			 prop, type);
+			 start_col, prop, type);
     if (hacked_up_bytind > BI_BUF_ZV (b))
       ret_bufpos = BUF_ZV (b) + 1;
     else
@@ -796,7 +794,7 @@
   gb.glyph = Vhscroll_glyph;
   {
     int oldpixpos = data->pixpos;
-    retval = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0,
+    retval = add_glyph_rune (data, &gb, BEGIN_GLYPHS, 1,
 			     GLYPH_CACHEL (XWINDOW (data->window),
 					   HSCROLL_GLYPH_INDEX));
     data->hscroll_glyph_width_adjust =
@@ -852,7 +850,7 @@
 	  Lisp_Object font_instance =
 	    ensure_face_cachel_contains_charset (cachel, data->window,
 						 charset);
-	  Lisp_Font_Instance *fi;
+	  struct Lisp_Font_Instance *fi;
 
 	  if (EQ (font_instance, Vthe_null_font_instance))
 	    {
@@ -904,19 +902,15 @@
   crb->xpos = data->pixpos;
   crb->width = width;
   if (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);
-    }
+    crb->bufpos =
+      bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
+			data->bi_bufpos);
   else if (data->is_modeline)
     crb->bufpos = data->modeline_charpos;
   else
-    /* Text but not in buffer */
+    /* fuckme if this shouldn't be an abort. */
+    /* abort (); fuckme harder, this abort gets tripped quite often,
+		 in propagation and whatnot.  #### fixme */
     crb->bufpos = 0;
   crb->type = RUNE_CHAR;
   crb->object.chr.ch = data->font_is_bogus ? '~' : data->ch;
@@ -1311,7 +1305,6 @@
 		    case '%':
 		      dst += set_charptr_emchar (dst, '%');
 		      break;
-		      /* #### unimplemented */
 		    }
 		}
 	    }
@@ -1333,7 +1326,7 @@
   prop_block_dynarr *prop = NULL;
   if (VECTORP (entry))
     {
-      Lisp_Vector *de = XVECTOR (entry);
+      struct Lisp_Vector *de = XVECTOR (entry);
       EMACS_INT len = vector_length (de);
       int elt;
 
@@ -1516,25 +1509,12 @@
 {
   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)
-      || pos_type == LEFT_GLYPHS || pos_type == RIGHT_GLYPHS)
+	  extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_TEXT))
     {
       struct rune rb;
       int width;
@@ -1542,18 +1522,16 @@
       int ascent, descent;
       Lisp_Object baseline;
       Lisp_Object face;
-      Lisp_Object instance;
-      face_index findex;
 
       if (cachel)
 	width = cachel->width;
       else
-	width = glyph_width (gb->glyph, data->window);
+	width = glyph_width (gb->glyph, Qnil, data->findex, data->window);
 
       if (!width)
 	return NULL;
 
-      if (data->start_col || data->start_col_xoffset)
+      if (data->start_col)
 	{
 	  prop_block_dynarr *retval;
 	  int glyph_char_width = width / space_width (w);
@@ -1611,8 +1589,9 @@
 	}
       else
 	{
-	  ascent = glyph_ascent (gb->glyph, data->window);
-	  descent = glyph_descent (gb->glyph, data->window);
+	  ascent = glyph_ascent (gb->glyph, Qnil, data->findex, data->window);
+	  descent = glyph_descent (gb->glyph, Qnil, data->findex,
+				   data->window);
 	}
 
       baseline = glyph_baseline (gb->glyph, data->window);
@@ -1655,32 +1634,10 @@
 
       face = glyph_face (gb->glyph, data->window);
       if (NILP (face))
-	findex = data->findex;
+	rb.findex = data->findex;
       else
-	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);
-	  data->findex = orig_findex;
-	  data->bi_bufpos = orig_bufpos;
-	  data->bi_start_col_enabled = orig_start_col_enabled;
-	  return NULL;
-	}
-
-      rb.findex = findex;
+	rb.findex = get_builtin_face_cache_index (w, face);
+
       rb.xpos = data->pixpos;
       rb.width = width;
       rb.bufpos = 0;			/* glyphs are never "at" anywhere */
@@ -1691,6 +1648,13 @@
       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;
@@ -1801,7 +1765,8 @@
 
 static Bytind
 create_text_block (struct window *w, struct display_line *dl,
-		   Bytind bi_start_pos, prop_block_dynarr **prop,
+		   Bytind bi_start_pos, int start_col,
+		   prop_block_dynarr **prop,
 		   int type)
 {
   struct frame *f = XFRAME (w->frame);
@@ -1826,7 +1791,7 @@
      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.
@@ -1893,7 +1858,6 @@
 
   dl->used_prop_data = 0;
   dl->num_chars = 0;
-  dl->line_continuation = 0;
 
   xzero (data);
   data.ef = extent_fragment_new (w->buffer, f);
@@ -1902,7 +1866,6 @@
      them to this structure for ease of passing. */
   data.d = d;
   XSETWINDOW (data.window, w);
-  data.string = Qnil;
   data.db = db;
   data.dl = dl;
 
@@ -1946,7 +1909,6 @@
   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;
 
@@ -2266,7 +2228,6 @@
 		  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);
@@ -2300,8 +2261,7 @@
 	      int prop_width = 0;
 
 	      if (data.start_col > 1)
-		tab_start_pixpos -= (space_width (w) * (data.start_col - 1))
-		  + data.start_col_xoffset;
+		tab_start_pixpos -= (space_width (w) * (data.start_col - 1));
 
 	      next_tab_start =
 		next_tab_position (w, tab_start_pixpos,
@@ -2484,12 +2444,11 @@
 
 	      /* 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, 0, cachel);
+	  add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 1, cachel);
 
 	  if (truncate_win && data.bi_bufpos == BI_BUF_ZV (b)
 	      && BI_BUF_FETCH_CHAR (b, prev_bytind (b, BI_BUF_ZV (b))) != '\n')
@@ -2509,7 +2468,6 @@
       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;
@@ -2685,7 +2643,6 @@
   data.last_charset = Qunbound;
   data.last_findex = DEFAULT_INDEX;
   data.result_str = Qnil;
-  data.string = Qnil;
 
   Dynarr_reset (data.db->runes);
 
@@ -2737,26 +2694,8 @@
 			     ? 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))
@@ -2785,31 +2724,79 @@
 	   || (side == RIGHT_GLYPHS &&
 	       extent_end_glyph_layout (XEXTENT (gb->extent)) == layout)))
 	{
-	  data.findex = gb->findex;
-	  data.max_pixpos = data.pixpos + gb->width;
-	  add_glyph_rune (&data, gb, side, 0, NULL);
+	  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;
 	  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;
+
+		  pix_descent = (int) (gheight * dl->descent) / line_height;
+		  pix_ascent = gheight - pix_descent;
+
+		  dl->ascent = max ((int) dl->ascent, pix_ascent);
+		  dl->descent = max ((int) dl->descent, pix_descent);
+		}
+
+	      /* A string so determine contribution normally. */
+	      else if (EQ (baseline, Qt))
+		{
+		  dl->ascent = max (dl->ascent, ascent);
+		  dl->descent = max (dl->descent, descent);
+		}
+
+	      /* 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;
+
+		  pix_ascent = height * XINT (baseline) / 100;
+		  pix_descent = height - pix_ascent;
+
+		  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++);
     }
 
-  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);
-    }
-
-  dl->ascent = data.new_ascent;
-  dl->descent = data.new_descent;
-
-  return data.pixpos;
+  return xpos;
 }
 
 /* Add a blank to a margin display block. */
@@ -2875,7 +2862,7 @@
 	{
 	  int width;
 
-	  width = glyph_width (gb->glyph, window);
+	  width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	  if (white_in_start - width >= left_in_end)
 	    {
@@ -2926,7 +2913,7 @@
 	if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
 	    GL_INSIDE_MARGIN)
 	  {
-	    gb->width = glyph_width (gb->glyph, window);
+	    gb->width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 	    used_in += gb->width;
 	    Dynarr_add (ib, *gb);
 	  }
@@ -2995,7 +2982,7 @@
 	if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
 	    GL_INSIDE_MARGIN)
 	  {
-	    int width = glyph_width (gb->glyph, window);
+	    int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	    if (used_out)
 	      {
@@ -3037,7 +3024,7 @@
       if (extent_begin_glyph_layout (XEXTENT (gb->extent)) ==
 	  GL_OUTSIDE_MARGIN)
 	{
-	  int width = glyph_width (gb->glyph, window);
+	  int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	  if (out_end + width <= in_out_start)
 	    {
@@ -3194,7 +3181,7 @@
 
       if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_WHITESPACE)
 	{
-	  int width = glyph_width (gb->glyph, window);
+	  int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	  if (white_in_end + width <= dl->bounds.right_in)
 	    {
@@ -3244,7 +3231,7 @@
 
 	if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
 	  {
-	    gb->width = glyph_width (gb->glyph, window);
+	    gb->width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 	    used_in += gb->width;
 	    Dynarr_add (ib, *gb);
 	  }
@@ -3308,7 +3295,7 @@
 
 	if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_INSIDE_MARGIN)
 	  {
-	    int width = glyph_width (gb->glyph, window);
+	    int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	    if (used_out)
 	      {
@@ -3349,7 +3336,7 @@
 
       if (extent_end_glyph_layout (XEXTENT (gb->extent)) == GL_OUTSIDE_MARGIN)
 	{
-	  int width = glyph_width (gb->glyph, window);
+	  int width = glyph_width (gb->glyph, Qnil, gb->findex, window);
 
 	  if (out_start - width >= in_out_end)
 	    {
@@ -3465,123 +3452,9 @@
 /*									   */
 /***************************************************************************/
 
-/* 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,
-                              int min_pixpos, int max_pixpos, int type)
-{
-  struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
-
-  pos_data data;
-  int c_pixpos;
-  Charcount offset = 0;
-
-  xzero (data);
-  data.d = d;
-  data.db = db;
-  data.dl = dl;
-  data.findex = findex;
-  data.pixpos = min_pixpos;
-  data.max_pixpos = max_pixpos;
-  data.cursor_type = NO_CURSOR;
-  data.last_charset = Qunbound;
-  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);
-
-  /* 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 ammount */
-  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, &offset,
-			  Qnil);
-
-  if (Dynarr_length (db->runes))
-    {
-      struct rune *rb =
-        Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
-      c_pixpos = rb->xpos + rb->width;
-    }
-  else
-    c_pixpos = min_pixpos;
-
-  /* If we don't reach the right side of the window, add a blank rune
-     to make up the difference.  This usually only occurs if the
-     modeline face is using a proportional width font or a fixed width
-     font of a different size from the default face font. */
-
-  if (c_pixpos < max_pixpos)
-    {
-      data.pixpos = c_pixpos;
-      data.blank_width = max_pixpos - data.pixpos;
-
-      add_blank_rune (&data, NULL, 0);
-    }
-
-  /* Now create the result string and frob the extents into it. */
-  if (!NILP (result_str))
-    {
-      int elt;
-      Bytecount len;
-      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));
-
-      strdata = XSTRING_DATA (result_str);
-
-      for (elt = 0, len = 0; elt < Dynarr_length (db->runes); elt++)
-        {
-          if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
-            {
-              len += (set_charptr_emchar
-                      (strdata + len, Dynarr_atp (db->runes,
-                                                  elt)->object.chr.ch));
-            }
-        }
-
-      for (elt = 0; elt < Dynarr_length (formatted_string_extent_dynarr);
-           elt++)
-        {
-          Lisp_Object extent = Qnil;
-          Lisp_Object child;
-
-          XSETEXTENT (extent, Dynarr_at (formatted_string_extent_dynarr, elt));
-          child = Fgethash (extent, buf->modeline_extent_table, Qnil);
-          if (NILP (child))
-            {
-              child = Fmake_extent (Qnil, Qnil, result_str);
-              Fputhash (extent, child, buf->modeline_extent_table);
-            }
-          Fset_extent_parent (child, extent);
-          set_extent_endpoints
-            (XEXTENT (child),
-             Dynarr_at (formatted_string_extent_start_dynarr, elt),
-             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)
 {
@@ -3675,13 +3548,116 @@
   dl->ypos = WINDOW_BOTTOM (w) - dl->descent - ypos_adj;
 }
 
+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)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+
+  pos_data data;
+  int c_pixpos;
+
+  xzero (data);
+  data.d = d;
+  data.db = db;
+  data.dl = dl;
+  data.findex = findex;
+  data.pixpos = min_pixpos;
+  data.max_pixpos = max_pixpos;
+  data.cursor_type = NO_CURSOR;
+  data.last_charset = Qunbound;
+  data.last_findex = DEFAULT_INDEX;
+  data.result_str = result_str;
+  data.is_modeline = 1;
+  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. */
+  generate_fstring_runes (w, &data, 0, 0, -1, format_str, 0,
+                          max_pixpos - min_pixpos, findex, type);
+
+  if (Dynarr_length (db->runes))
+    {
+      struct rune *rb =
+        Dynarr_atp (db->runes, Dynarr_length (db->runes) - 1);
+      c_pixpos = rb->xpos + rb->width;
+    }
+  else
+    c_pixpos = min_pixpos;
+
+  /* If we don't reach the right side of the window, add a blank rune
+     to make up the difference.  This usually only occurs if the
+     modeline face is using a proportional width font or a fixed width
+     font of a different size from the default face font. */
+
+  if (c_pixpos < max_pixpos)
+    {
+      data.pixpos = c_pixpos;
+      data.blank_width = max_pixpos - data.pixpos;
+
+      add_blank_rune (&data, NULL, 0);
+    }
+
+  /* Now create the result string and frob the extents into it. */
+  if (!NILP (result_str))
+    {
+      int elt;
+      Bytecount len;
+      Bufbyte *strdata;
+      struct buffer *buf = XBUFFER (WINDOW_BUFFER (w));
+
+      detach_all_extents (result_str);
+      resize_string (XSTRING (result_str), -1,
+                     data.bytepos - XSTRING_LENGTH (result_str));
+
+      strdata = XSTRING_DATA (result_str);
+
+      for (elt = 0, len = 0; elt < Dynarr_length (db->runes); elt++)
+        {
+          if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
+            {
+              len += (set_charptr_emchar
+                      (strdata + len, Dynarr_atp (db->runes,
+                                                  elt)->object.chr.ch));
+            }
+        }
+
+      for (elt = 0; elt < Dynarr_length (formatted_string_extent_dynarr);
+           elt++)
+        {
+          Lisp_Object extent = Qnil;
+          Lisp_Object child;
+
+          XSETEXTENT (extent, Dynarr_at (formatted_string_extent_dynarr, elt));
+          child = Fgethash (extent, buf->modeline_extent_table, Qnil);
+          if (NILP (child))
+            {
+              child = Fmake_extent (Qnil, Qnil, result_str);
+              Fputhash (extent, child, buf->modeline_extent_table);
+            }
+          Fset_extent_parent (child, extent);
+          set_extent_endpoints
+            (XEXTENT (child),
+             Dynarr_at (formatted_string_extent_start_dynarr, elt),
+             Dynarr_at (formatted_string_extent_end_dynarr, elt),
+             result_str);
+        }
+    }
+}
+
 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));
@@ -3689,13 +3665,13 @@
     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);
@@ -3720,8 +3696,7 @@
    modeline extents. */
 static Charcount
 add_glyph_to_fstring_db_runes (pos_data *data, Lisp_Object glyph,
-                               Charcount pos, Charcount min_pos,
-			       Charcount max_pos, Lisp_Object extent)
+                               Charcount pos, Charcount min_pos, Charcount max_pos)
 {
   /* This function has been Mule-ized. */
   Charcount end;
@@ -3737,7 +3712,7 @@
     end = min (max_pos, end);
 
   gb.glyph = glyph;
-  gb.extent = extent;
+  gb.extent = Qnil;
   add_glyph_rune (data, &gb, BEGIN_GLYPHS, 0, 0);
   pos++;
 
@@ -3764,8 +3739,7 @@
 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, Charcount *offset,
-			Lisp_Object cur_ext)
+                        face_index findex, int type)
 {
   /* This function has been Mule-ized. */
   /* #### The other losing things in this function are:
@@ -3797,22 +3771,13 @@
 
           if (this != last)
             {
-              /* No %-construct */
+              /* The string is just a string. */
               Charcount size =
-		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, tmp_last,
-							pos, pos, tmp_max);
-		  *offset = 0;
-		}
+                bytecount_to_charcount (last, this - last) + pos;
+              Charcount tmp_max = (max_pos == -1 ? size : min (size, max_pos));
+
+              pos = add_string_to_fstring_db_runes (data, last, pos, pos,
+                                                    tmp_max);
             }
           else /* *this == '%' */
             {
@@ -3837,7 +3802,7 @@
                   pos = generate_fstring_runes (w, data, pos, spec_width,
                                                 max_pos, Vglobal_mode_string,
                                                 depth, max_pixsize, findex,
-                                                type, offset, cur_ext);
+                                                type);
                 }
               else if (*this == '-')
                 {
@@ -3864,35 +3829,17 @@
 
                   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);
-		  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;
-		    }
+                  pos = add_string_to_fstring_db_runes (data,str, pos, pos,
+                                                        max_pos);
                 }
 
               /* NOT this++.  There could be any sort of character at
@@ -3918,26 +3865,13 @@
 
       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))
             {
-	      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;
-		}
+              pos =
+                add_string_to_fstring_db_runes
+                (data, XSTRING_DATA (tem), pos, min_pos, max_pos);
             }
           /* Give up right away for nil or t.  */
           else if (!EQ (tem, elt))
@@ -3962,53 +3896,50 @@
   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 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.
+       * 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.
        */
-
       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);
@@ -4047,14 +3978,13 @@
       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, offset, cur_ext);
+                                            XCAR (elt), depth,
+                                            max_pixsize, findex, type);
               elt = XCDR (elt);
             }
         }
@@ -4093,8 +4023,7 @@
               data->findex = new_findex;
               pos = generate_fstring_runes (w, data, pos, pos, max_pos,
                                             XCDR (elt), depth - 1,
-					    max_pixsize, new_findex, type,
-					    offset, car);
+                                            max_pixsize, new_findex, type);
               data->findex = old_findex;
               Dynarr_add (formatted_string_extent_dynarr, ext);
               Dynarr_add (formatted_string_extent_start_dynarr, start);
@@ -4104,46 +4033,57 @@
     }
   else if (GLYPHP (elt))
     {
-      /* 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);
+      pos = add_glyph_to_fstring_db_runes (data, elt, pos, pos, max_pos);
     }
   else
     {
     invalid:
-      {
-	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;
-	  }
-      }
+      pos =
+        add_string_to_fstring_db_runes
+          (data, (CONST Bufbyte *) GETTEXT ("*invalid*"), pos, min_pos,
+           max_pos);
     }
 
   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
@@ -4238,836 +4178,6 @@
 
 
 /***************************************************************************/
-/*								*/
-/*                            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;
-  int end_glyph_width = 0;
-
-  /* we're going to ditch selective display for static text, its an
-     FSF thing and invisble 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;
-#if 0
-  if (truncate_win)
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
-  else
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
-#endif
-  data.max_pixpos -= 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))
-	{
-	  /* 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);
-	  /* 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 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)
-	{
-	  *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)
-	{
-	  *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 += 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 += 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 (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 && rb->object.chr.ch == ' ')
-	    || 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 && rb->object.chr.ch < 0x100
-	    && isspace (rb->object.chr.ch))
-	    && !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;
-
-  /* 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);
-    }
-
-  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;
-  }
-
-  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;
-
-  prop_block_dynarr *prop = 0;
-  layout_bounds bounds;
-  assert (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;
-
-  s_zv = XSTRING_CHAR_LENGTH (disp_string);
-
-  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))
-	{
-	  dlp = Dynarr_atp (dla, Dynarr_length (dla));
-	  local = 0;
-	}
-      else
-	{
-
-	  xzero (dl);
-	  dlp = &dl;
-	  local = 1;
-	}
-
-      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;
-
-      dlp->ypos = ypos + dlp->ascent;
-      ypos = dlp->ypos + dlp->descent;
-
-      if (ypos > yend)
-	{
-	  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;
-    }
-
-  if (prop)
-    Dynarr_free (prop);
-}
-
-
-/***************************************************************************/
 /*									   */
 /*                        window-regeneration routines                     */
 /*									   */
@@ -5086,8 +4196,6 @@
   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;
@@ -5141,14 +4249,7 @@
   else
     prop = 0;
 
-  /* 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)
+  while (ypos < yend)
     {
       struct display_line dl;
       struct display_line *dlp;
@@ -5161,7 +4262,6 @@
 	}
       else
 	{
-
 	  xzero (dl);
 	  dlp = &dl;
 	  local = 1;
@@ -5169,39 +4269,19 @@
 
       dlp->bounds = bounds;
       dlp->offset = 0;
-      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;
+      start_pos = generate_display_line (w, dlp, 1, start_pos,
+					 w->hscroll, &prop, type);
+      dlp->ypos = ypos + dlp->ascent;
       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);
-	  /* 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) && !force)
+	  visible_height -= dlp->clip;
+
+	  if (visible_height < VERTICAL_CLIP (w, 1))
 	    {
 	      if (local)
 		free_display_line (dlp);
@@ -5245,22 +4325,16 @@
 	 generate_display_line call. */
       if (start_pos > BUF_ZV (b))
 	break;
-
-      force = 0;
     }
 
   if (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?
-
-     JV sez: Because BUF_Z (b) would be a good initial value, however
-     that can change. This representation allows initalizing with 0.
-  */
+     the justification for this? */
   w->window_end_pos[type] = BUF_Z (b) - w->window_end_pos[type];
 
   if (need_modeline)
@@ -5448,7 +4522,7 @@
 	return 0;
 
       new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
-					 &prop, DESIRED_DISP);
+					 w->hscroll, &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* #### If there is propagated stuff the fail.  We could
@@ -5467,7 +4541,6 @@
       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
@@ -5591,16 +4664,27 @@
       assert (cdl->end_bufpos == ddl->end_bufpos);
       assert (cdl->offset == ddl->offset);
 
-      /* If the line continues to next display line, fail. */
-      if (ddl->line_continuation)
-	return 0;
+      /* 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 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,
-					 &prop, DESIRED_DISP);
+					 w->hscroll, &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* If there is propagated stuff then it is pretty much a
@@ -5611,16 +4695,25 @@
 	  return 0;
 	}
 
-      /* If the line continues to next display line, fail. */
-      if (ddl->line_continuation)
-	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 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))
 	{
@@ -5904,7 +4997,7 @@
     }
   Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm), the_buffer);
 
-  /* If the buffer has changed we have to invalidate all of our face
+  /* If the buffer has changed we have to invalid all of our face
      cache elements. */
   if ((!echo_active && b != window_display_buffer (w))
       || !Dynarr_length (w->face_cachels)
@@ -5913,11 +5006,10 @@
   else
     mark_face_cachels_as_not_updated (w);
 
-  /* 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.*/
+  /* Ditto the glyph cache elements. */
   if ((!echo_active && b != window_display_buffer (w))
-      || !Dynarr_length (w->glyph_cachels) || f->faces_changed)
+      || !Dynarr_length (w->glyph_cachels)
+      || f->glyphs_changed)
     reset_glyph_cachels (w);
   else
     mark_glyph_cachels_as_not_updated (w);
@@ -6003,7 +5095,6 @@
 	  && !f->faces_changed
 	  && !f->glyphs_changed
 	  && !f->subwindows_changed
-	  /*	  && !f->subwindows_state_changed*/
 	  && !f->point_changed
 	  && !f->windows_structure_changed)
 	{
@@ -6025,7 +5116,6 @@
 	      && !f->faces_changed
 	      && !f->glyphs_changed
 	      && !f->subwindows_changed
-	      /*	      && !f->subwindows_state_changed*/
 	      && !f->windows_structure_changed)
 	    {
 	      if (point_visible (w, pointm, CURRENT_DISP)
@@ -6084,7 +5174,6 @@
 	   && !f->faces_changed
 	   && !f->glyphs_changed
 	   && !f->subwindows_changed
-	   /*	   && !f->subwindows_state_changed*/
 	   && !f->windows_structure_changed
 	   && !f->frame_changed
 	   && !truncation_changed
@@ -6181,12 +5270,6 @@
      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;
 }
 
@@ -6266,7 +5349,7 @@
 
 /* Ensure that all windows on the given frame are correctly displayed. */
 
-int
+static int
 redisplay_frame (struct frame *f, int preemption_check)
 {
   struct device *d = XDEVICE (f->device);
@@ -6283,16 +5366,6 @@
 	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)
@@ -6318,30 +5391,15 @@
      being handled. */
   update_frame_menubars (f);
 #endif /* HAVE_MENUBARS */
+  /* widgets are similar to menus in that they can call lisp to
+     determine activation etc. Therefore update them before we get
+     into redisplay. This is primarily for connected widgets such as
+     radio buttons. */
+  update_frame_subwindows (f);
 #ifdef HAVE_TOOLBARS
   /* Update the toolbars. */
   update_frame_toolbars (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 cache. 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 || f->subwindows_changed)
-    {
-      /* we have to do this so the gutter gets regenerated. */
-      reset_gutter_display_lines (f);
-    }
 
   hold_frame_size_changes ();
 
@@ -6369,23 +5427,26 @@
      #### If a frame-size change does occur we should probably
      actually be preempting redisplay. */
 
-  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);
+  /* If we clear the frame we have to force its contents to be redrawn. */
+  if (f->clear)
+    f->frame_changed = 1;
 
   /* Erase the frame before outputting its contents. */
   if (f->clear)
     {
-      MAYBE_DEVMETH (d, clear_frame, (f));
-    }
+      DEVMETH (d, clear_frame, (f));
+    }
+
+  /* invalidate the subwindow cache. we are going to reuse the glyphs
+     flag here to cause subwindows to get instantiated. This is
+     because subwindows changed is less strict - dealing with things
+     like the clicked state of button. */
+  if (!Dynarr_length (f->subwindow_cachels)
+      || f->glyphs_changed
+      || f->frame_changed)
+    reset_subwindow_cachels (f);
+  else
+    mark_subwindow_cachels_as_not_updated (f);
 
   /* Do the selected window first. */
   redisplay_window (FRAME_SELECTED_WINDOW (f), 0);
@@ -6393,13 +5454,31 @@
   /* Then do the rest. */
   redisplay_windows (f->root_window, 1);
 
-  MAYBE_DEVMETH (d, frame_output_end, (f));
+  /* 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));
 
   update_frame_title (f);
 
-  CLASS_RESET_CHANGED_FLAGS (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->subwindows_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;
   f->window_face_cache_reset = 0;
   f->echo_area_garbaged = 0;
+
   f->clear = 0;
 
   if (!f->size_change_pending)
@@ -6416,27 +5495,16 @@
   return 0;
 }
 
-/* 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();
-*/
+/* Ensure that all frames on the given device are correctly displayed. */
 
 static int
-redisplay_device (struct device *d, int automatic)
+redisplay_device (struct device *d)
 {
   Lisp_Object frame, frmcons;
   int preempted = 0;
   int size_change_failed = 0;
   struct frame *f;
 
-  if (automatic
-      && (MAYBE_INT_DEVMETH (d, device_implementation_flags, ())
-	  & XDEVIMPF_NO_AUTO_REDISPLAY))
-    return 0;
-
   if (DEVICE_STREAM_P (d)) /* nothing to do */
     return 0;
 
@@ -6460,7 +5528,11 @@
 
   if (FRAME_REPAINT_P (f))
     {
-      if (CLASS_REDISPLAY_FLAGS_CHANGEDP(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 || f->subwindows_changed)
 	{
 	  preempted = redisplay_frame (f, 0);
 	}
@@ -6490,7 +5562,12 @@
 
       if (FRAME_REPAINT_P (f))
 	{
-	  if (CLASS_REDISPLAY_FLAGS_CHANGEDP (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 || f->subwindows_changed)
 	    {
 	      preempted = redisplay_frame (f, 0);
 	    }
@@ -6505,7 +5582,20 @@
 
   /* If we get here then we redisplayed all of our frames without
      getting preempted so mark ourselves as clean. */
-  CLASS_RESET_CHANGED_FLAGS (d);
+  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->subwindows_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;
 
   if (!size_change_failed)
     d->size_changed = 0;
@@ -6540,8 +5630,13 @@
   if (asynch_device_change_pending)
     handle_asynch_device_change ();
 
-  if (!GLOBAL_REDISPLAY_FLAGS_CHANGEDP &&
-      !disable_preemption && preemption_count < max_preempts)
+  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  && !subwindows_changed &&
+      !windows_structure_changed && !disable_preemption &&
+      preemption_count < max_preempts)
     goto done;
 
   DEVICE_LOOP_NO_BREAK (devcons, concons)
@@ -6549,9 +5644,14 @@
       struct device *d = XDEVICE (XCAR (devcons));
       int preempted;
 
-      if (CLASS_REDISPLAY_FLAGS_CHANGEDP (d))
-	{
-	  preempted = redisplay_device (d, 1);
+      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 || d->subwindows_changed)
+	{
+	  preempted = redisplay_device (d);
 
 	  if (preempted)
 	    {
@@ -6568,7 +5668,19 @@
   preemption_count = 0;
 
   /* Mark redisplay as accurate */
-  GLOBAL_RESET_CHANGED_FLAGS;
+  buffers_changed  = 0;
+  clip_changed     = 0;
+  extents_changed  = 0;
+  frame_changed    = 0;
+  glyphs_changed   = 0;
+  subwindows_changed   = 0;
+  icon_changed     = 0;
+  menubar_changed  = 0;
+  modeline_changed = 0;
+  point_changed    = 0;
+  toolbar_changed  = 0;
+  windows_changed  = 0;
+  windows_structure_changed = 0;
   RESET_CHANGED_SET_FLAGS;
 
   if (faces_changed)
@@ -6665,7 +5777,7 @@
 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);
@@ -6694,7 +5806,7 @@
 	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;
       }
@@ -6799,6 +5911,7 @@
     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))
@@ -6815,20 +5928,15 @@
 	  /* 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);
-
-	  /* Avoid overflow on big buffers */
-	  int percent = total > LONG_MAX/200 ?
-	    (chars + total/200) / (total / 100) :
-	    (chars * 100 + total/2) / total;
+
+	  total = ((pos - BUF_BEGV (b)) * 100 + total - 1) / total;
 
 	  /* We can't normally display a 3-digit number, so get us a
              2-digit number that is close. */
-	  if (percent == 100)
-	    percent = 99;
-
-	  sprintf (buf, "%d%%", percent);
+	  if (total == 100)
+	    total = 99;
+
+	  sprintf (buf, "%2d%%", total);
 	  Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
 			   strlen (buf));
 
@@ -6843,6 +5951,7 @@
     {
       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,
@@ -6862,23 +5971,18 @@
 	  /* 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);
-
-	  /* Avoid overflow on big buffers */
-	  int percent = total > LONG_MAX/200 ?
-	    (chars + total/200) / (total / 100) :
-	    (chars * 100 + total/2) / max (total, 1);
+
+	  total = ((botpos - BUF_BEGV (b)) * 100 + total - 1) / total;
 
 	  /* We can't normally display a 3-digit number, so get us a
              2-digit number that is close. */
-	  if (percent == 100)
-	    percent = 99;
+	  if (total == 100)
+	    total = 99;
 
 	  if (toppos <= BUF_BEGV (b))
-	    sprintf (buf, "Top%d%%", percent);
+	    sprintf (buf, "Top%2d%%", total);
 	  else
-	    sprintf (buf, "%d%%", percent);
+	    sprintf (buf, "%2d%%", total);
 
 	  Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
 			   strlen (buf));
@@ -6981,7 +6085,7 @@
 /* Given an array of display lines, free them and all data structures
    contained within them. */
 
-void
+static void
 free_display_lines (display_line_dynarr *dla)
 {
   int line;
@@ -7014,7 +6118,7 @@
 
 
 static void
-mark_glyph_block_dynarr (glyph_block_dynarr *gba)
+mark_glyph_block_dynarr (glyph_block_dynarr *gba, void (*markobj) (Lisp_Object))
 {
   if (gba)
     {
@@ -7024,17 +6128,15 @@
       for (; gb < gb_last; gb++)
 	{
 	  if (!NILP (gb->glyph))
-	    mark_object (gb->glyph);
+	    markobj (gb->glyph);
 	  if (!NILP (gb->extent))
-	    mark_object (gb->extent);
+	    markobj (gb->extent);
 	}
     }
 }
 
-/* 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)
+static void
+mark_redisplay_structs (display_line_dynarr *dla, void (*markobj) (Lisp_Object))
 {
   display_line *dl = Dynarr_atp (dla, 0);
   display_line *dl_last = Dynarr_atp (dla, Dynarr_length (dla));
@@ -7056,35 +6158,35 @@
 	      if (r->type == RUNE_DGLYPH)
 		{
 		  if (!NILP (r->object.dglyph.glyph))
-		    mark_object (r->object.dglyph.glyph);
+		    markobj (r->object.dglyph.glyph);
 		  if (!NILP (r->object.dglyph.extent))
-		    mark_object (r->object.dglyph.extent);
+		    markobj (r->object.dglyph.extent);
 		}
 	    }
 	}
 
-      mark_glyph_block_dynarr (dl->left_glyphs);
-      mark_glyph_block_dynarr (dl->right_glyphs);
+      mark_glyph_block_dynarr (dl->left_glyphs,  markobj);
+      mark_glyph_block_dynarr (dl->right_glyphs, markobj);
     }
 }
 
 static void
-mark_window_mirror (struct window_mirror *mir)
+mark_window_mirror (struct window_mirror *mir, void (*markobj)(Lisp_Object))
 {
-  mark_redisplay_structs (mir->current_display_lines);
-  mark_redisplay_structs (mir->desired_display_lines);
+  mark_redisplay_structs (mir->current_display_lines, markobj);
+  mark_redisplay_structs (mir->desired_display_lines, markobj);
 
   if (mir->next)
-    mark_window_mirror (mir->next);
+    mark_window_mirror (mir->next, markobj);
 
   if (mir->hchild)
-    mark_window_mirror (mir->hchild);
+    mark_window_mirror (mir->hchild, markobj);
   else if (mir->vchild)
-    mark_window_mirror (mir->vchild);
+    mark_window_mirror (mir->vchild, markobj);
 }
 
 void
-mark_redisplay (void)
+mark_redisplay (void (*markobj)(Lisp_Object))
 {
   Lisp_Object frmcons, devcons, concons;
 
@@ -7092,8 +6194,7 @@
     {
       struct frame *f = XFRAME (XCAR (frmcons));
       update_frame_window_mirror (f);
-      mark_window_mirror (f->root_mirror);
-      mark_gutters (f);
+      mark_window_mirror (f->root_mirror, markobj);
     }
 }
 
@@ -7157,7 +6258,7 @@
 
 /* This will get used quite a bit so we don't want to be constantly
    allocating and freeing it. */
-static line_start_cache_dynarr *internal_cache;
+line_start_cache_dynarr *internal_cache;
 
 /* Makes internal_cache represent the TYPE display structs and only
    the TYPE display structs. */
@@ -7178,7 +6279,7 @@
       else
 	{
 	  struct line_start_cache lsc;
-
+	  
 	  lsc.start = dl->bufpos;
 	  lsc.end = dl->end_bufpos;
 	  lsc.height = dl->ascent + dl->descent;
@@ -7451,7 +6552,7 @@
 point_would_be_visible (struct window *w, Bufpos startp, Bufpos point)
 {
   struct buffer *b = XBUFFER (w->buffer);
-  int pixpos = -WINDOW_TEXT_TOP_CLIP(w);
+  int pixpos = 0;
   int bottom = WINDOW_TEXT_HEIGHT (w);
   int start_elt;
 
@@ -7530,18 +6631,11 @@
    displayed.  The end of the last line is also know as the window end
    position.
 
-   WARNING: It is possible that rediplay 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,
-                        int may_error)
+start_end_of_last_line (struct window *w, Bufpos startp, int end)
 {
   struct buffer *b = XBUFFER (w->buffer);
   line_start_cache_dynarr *cache = w->line_start_cache;
@@ -7561,7 +6655,7 @@
 
   start_elt = point_in_line_start_cache (w, cur_start, 0);
   if (start_elt == -1)
-      return may_error ? 0 : startp;
+    abort ();	/* this had better never happen */
 
   while (1)
     {
@@ -7625,7 +6719,7 @@
 Bufpos
 start_of_last_line (struct window *w, Bufpos startp)
 {
-  return start_end_of_last_line (w, startp, 0 , 0);
+  return start_end_of_last_line (w, startp, 0);
 }
 
 /* For the given window W, if display starts at STARTP, what will be
@@ -7635,16 +6729,9 @@
 Bufpos
 end_of_last_line (struct window *w, Bufpos startp)
 {
-  return start_end_of_last_line (w, startp, 1, 0);
+  return start_end_of_last_line (w, startp, 1);
 }
 
-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. */
 
@@ -7690,7 +6777,7 @@
 	}
 
       cur_elt--;
-      while (cur_elt < 0)
+      if (cur_elt < 0)
 	{
 	  Bufpos from, to;
 	  int win_char_height;
@@ -7710,20 +6797,7 @@
 	  update_line_start_cache (w, from, to, point, 0);
 
 	  cur_elt = point_in_line_start_cache (w, cur_pos, 2) - 1;
-	  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;
+	  assert (cur_elt >= 0);
 	}
       prev_pos = cur_pos;
     }
@@ -7828,6 +6902,7 @@
 
   validate_line_start_cache (w);
   w->line_cache_validation_override++;
+  updating_line_start_cache = 1;
 
   if (from < BUF_BEGV (b))
     from = BUF_BEGV (b);
@@ -7836,6 +6911,7 @@
 
   if (from > to)
     {
+      updating_line_start_cache = 0;
       w->line_cache_validation_override--;
       return;
     }
@@ -7848,6 +6924,7 @@
       /* 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;
 	}
@@ -7876,6 +6953,7 @@
       update_internal_cache_list (w, DESIRED_DISP);
       if (!Dynarr_length (internal_cache))
 	{
+	  updating_line_start_cache = 0;
 	  w->line_cache_validation_override--;
 	  return;
 	}
@@ -7903,6 +6981,7 @@
 	{
 	  Dynarr_add_many (cache, Dynarr_atp (internal_cache, 0),
 			   Dynarr_length (internal_cache));
+	  updating_line_start_cache = 0;
 	  w->line_cache_validation_override--;
 	  return;
 	}
@@ -7911,6 +6990,7 @@
          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;
 	}
@@ -7933,6 +7013,7 @@
 	      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;
 	    }
@@ -7958,6 +7039,7 @@
 	      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;
 	    }
@@ -7966,6 +7048,7 @@
 			   Dynarr_length (internal_cache) - ic_elt);
 	}
 
+      updating_line_start_cache = 0;
       w->line_cache_validation_override--;
       return;
     }
@@ -7985,9 +7068,23 @@
 	  update_internal_cache_list (w, CMOTION_DISP);
 
 	  /* If this assert is triggered then regenerate_window failed
-             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));
+             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));
+	    }
 	  assert (startp == Dynarr_atp (internal_cache, 0)->start);
 
 	  ic_elt = Dynarr_length (internal_cache) - 1;
@@ -8033,6 +7130,7 @@
 	  startp = new_startp;
 	  if (startp > BUF_ZV (b))
 	    {
+	      updating_line_start_cache = 0;
 	      w->line_cache_validation_override--;
 	      return;
 	    }
@@ -8066,6 +7164,7 @@
       while (to > high_bound);
     }
 
+  updating_line_start_cache = 0;
   w->line_cache_validation_override--;
   assert (to <= high_bound);
 }
@@ -8773,7 +7872,7 @@
   if (!MARKERP ((*w)->start[CURRENT_DISP]))
     *closest = 0;
   else
-    *closest = end_of_last_line_may_error (*w,
+    *closest = end_of_last_line (*w,
 				 marker_position ((*w)->start[CURRENT_DISP]));
   *col = 0;
   UPDATE_CACHE_RETURN;
@@ -8806,9 +7905,6 @@
 	  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
@@ -8817,15 +7913,19 @@
 	       */
 	      if (f->echo_area_garbaged)
 		{
-		  MAYBE_DEVMETH (d, clear_frame, (f));
+		  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;
@@ -8860,9 +7960,6 @@
   f->clear = 1;
   redisplay_frame (f, 1);
 
-  /* See the comment in Fredisplay_frame. */
-  RESET_CHANGED_SET_FLAGS;
-
   return unbind_to (count, Qnil);
 }
 
@@ -8890,15 +7987,6 @@
 
   redisplay_frame (f, 1);
 
-  /* If we don't reset the global redisplay flafs 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);
 }
 
@@ -8926,10 +8014,7 @@
     {
       XFRAME (XCAR (frmcons))->clear = 1;
     }
-  redisplay_device (d, 0);
-
-  /* See the comment in Fredisplay_frame. */
-  RESET_CHANGED_SET_FLAGS;
+  redisplay_device (d);
 
   return unbind_to (count, Qnil);
 }
@@ -8956,10 +8041,7 @@
       disable_preemption++;
     }
 
-  redisplay_device (d, 0);
-
-  /* See the comment in Fredisplay_frame. */
-  RESET_CHANGED_SET_FLAGS;
+  redisplay_device (d);
 
   return unbind_to (count, Qnil);
 }
@@ -9011,8 +8093,6 @@
   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)
@@ -9135,22 +8215,16 @@
   preemption_count = 0;
   max_preempts = INIT_MAX_PREEMPTS;
 
-#ifndef PDUMP
   if (!initialized)
-#endif
-    {
-      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);
+    {
+      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);
     }
 
   /* window system is nil when in -batch mode */
@@ -9221,10 +8295,9 @@
 #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);
@@ -9238,7 +8311,6 @@
 void
 vars_of_redisplay (void)
 {
-
 #if 0
   staticpro (&last_arrow_position);
   staticpro (&last_arrow_string);
@@ -9246,6 +8318,8 @@
   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
@@ -9282,9 +8356,7 @@
   Voverlay_arrow_position = Qnil;
 
   DEFVAR_LISP_MAGIC ("overlay-arrow-string", &Voverlay_arrow_string /*
-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.)
+String to display as an arrow.  See also `overlay-arrow-position'.
 */ ,
 		     redisplay_variable_changed);
   Voverlay_arrow_string = Qnil;
@@ -9308,19 +8380,10 @@
 		     redisplay_variable_changed);
   truncate_partial_width_windows = 1;
 
-  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.)
+  DEFVAR_BOOL ("visible-bell", &visible_bell /*
+*Non-nil means try to flash the frame to represent a bell.
 */ );
-  Vvisible_bell = Qnil;
+  visible_bell = 0;
 
   DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter /*
 *Non-nil means no need to redraw entire frame after suspending.
@@ -9341,7 +8404,7 @@
   Vwindow_system = Qnil;
 
   /* #### Temporary shit until window-system is eliminated. */
-  DEFVAR_CONST_LISP ("initial-window-system", &Vinitial_window_system /*
+  DEFVAR_LISP ("initial-window-system", &Vinitial_window_system /*
 DON'T TOUCH
 */ );
   Vinitial_window_system = Qnil;
@@ -9371,22 +8434,17 @@
 #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
@@ -9445,9 +8503,9 @@
   Vleft_margin_width = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vleft_margin_width, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vleft_margin_width,
-			 offsetof (struct window, left_margin_width),
+			 slot_offset (struct window, left_margin_width),
 			 some_window_value_changed,
-			 offsetof (struct frame, left_margin_width),
+			 slot_offset (struct frame, left_margin_width),
 			 margin_width_changed_in_frame);
 
   DEFVAR_SPECIFIER ("right-margin-width", &Vright_margin_width /*
@@ -9457,9 +8515,9 @@
   Vright_margin_width = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vright_margin_width, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vright_margin_width,
-			 offsetof (struct window, right_margin_width),
+			 slot_offset (struct window, right_margin_width),
 			 some_window_value_changed,
-			 offsetof (struct frame, right_margin_width),
+			 slot_offset (struct frame, right_margin_width),
 			 margin_width_changed_in_frame);
 
   DEFVAR_SPECIFIER ("minimum-line-ascent", &Vminimum_line_ascent /*
@@ -9469,7 +8527,7 @@
   Vminimum_line_ascent = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vminimum_line_ascent, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vminimum_line_ascent,
-			 offsetof (struct window, minimum_line_ascent),
+			 slot_offset (struct window, minimum_line_ascent),
 			 some_window_value_changed,
 			 0, 0);
 
@@ -9480,7 +8538,7 @@
   Vminimum_line_descent = Fmake_specifier (Qnatnum);
   set_specifier_fallback (Vminimum_line_descent, list1 (Fcons (Qnil, Qzero)));
   set_specifier_caching (Vminimum_line_descent,
-			 offsetof (struct window, minimum_line_descent),
+			 slot_offset (struct window, minimum_line_descent),
 			 some_window_value_changed,
 			 0, 0);
 
@@ -9492,7 +8550,7 @@
   Vuse_left_overflow = Fmake_specifier (Qboolean);
   set_specifier_fallback (Vuse_left_overflow, list1 (Fcons (Qnil, Qnil)));
   set_specifier_caching (Vuse_left_overflow,
-			 offsetof (struct window, use_left_overflow),
+			 slot_offset (struct window, use_left_overflow),
 			 some_window_value_changed,
 			 0, 0);
 
@@ -9504,7 +8562,7 @@
   Vuse_right_overflow = Fmake_specifier (Qboolean);
   set_specifier_fallback (Vuse_right_overflow, list1 (Fcons (Qnil, Qnil)));
   set_specifier_caching (Vuse_right_overflow,
-			 offsetof (struct window, use_right_overflow),
+			 slot_offset (struct window, use_right_overflow),
 			 some_window_value_changed,
 			 0, 0);
 
@@ -9515,7 +8573,7 @@
   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,
-			 offsetof (struct window, text_cursor_visible_p),
+			 slot_offset (struct window, text_cursor_visible_p),
 			 text_cursor_visible_p_changed,
 			 0, 0);