XEmacs 21.2.25 "Hephaestus".
[chise/xemacs-chise.git.1] / src / redisplay.c
index 73a5a15..7e29d59 100644 (file)
@@ -51,6 +51,7 @@ Boston, MA 02111-1307, USA.  */
 #include "faces.h"
 #include "frame.h"
 #include "glyphs.h"
+#include "gutter.h"
 #include "insdel.h"
 #include "menubar.h"
 #include "objects.h"
@@ -107,6 +108,9 @@ typedef struct position_redisplay_data_type
   /* This information is normally filled in by the create_*_block
      routines and is used by the add_*_rune routines. */
   Lisp_Object window;
+  /* if we are working with strings rather than buffers we need a
+     handle to the string */
+  Lisp_Object string;
   struct device *d;
   struct display_block *db;
   struct display_line *dl;
@@ -150,6 +154,10 @@ typedef struct position_redisplay_data_type
                           (those off the left side of the screen) need
                           to be skipped before anything is displayed. */
   Bytind bi_start_col_enabled;
+  int start_col_xoffset;       /* Number of pixels that still need to
+                          be skipped.  This is used for
+                          horizontal scrolling of glyphs, where we want
+                          to be able to scroll over part of the glyph. */
 
   int hscroll_glyph_width_adjust;  /* how much the width of the hscroll
                                      glyph differs from space_width (w).
@@ -255,8 +263,7 @@ static prop_block_dynarr *add_glyph_rune (pos_data *data,
                                          int pos_type, int allow_cursor,
                                          struct glyph_cachel *cachel);
 static Bytind create_text_block (struct window *w, struct display_line *dl,
-                                Bytind bi_start_pos, int start_col,
-                                prop_block_dynarr **prop,
+                                Bytind bi_start_pos, prop_block_dynarr **prop,
                                 int type);
 static int create_overlay_glyph_block (struct window *w,
                                       struct display_line *dl);
@@ -295,8 +302,8 @@ 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;
+static Emchar_dynarr *formatted_string_emchar_dynarr;
+static 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
@@ -304,9 +311,9 @@ struct display_line formatted_string_display_line;
    (to avoid having to resize the string multiple times), and we don't
    want to go around adding extents to a string when the extents might
    stretch off the end of the string. */
-EXTENT_dynarr *formatted_string_extent_dynarr;
-Bytecount_dynarr *formatted_string_extent_start_dynarr;
-Bytecount_dynarr *formatted_string_extent_end_dynarr;
+static EXTENT_dynarr *formatted_string_extent_dynarr;
+static Bytecount_dynarr *formatted_string_extent_start_dynarr;
+static Bytecount_dynarr *formatted_string_extent_end_dynarr;
 
 
 /* #### probably temporary */
@@ -331,7 +338,7 @@ int vertical_clip;
 int horizontal_clip;
 
 /* Set if currently inside update_line_start_cache. */
-int updating_line_start_cache;
+static int updating_line_start_cache;
 
 /* Nonzero means reading single-character input with prompt
    so put cursor on minibuffer after the prompt.  */
@@ -371,6 +378,11 @@ int glyphs_changed_set;
 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;
@@ -401,6 +413,10 @@ int asynch_device_change_pending;
 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;
 
@@ -442,7 +458,7 @@ Lisp_Object Voverlay_arrow_position;
 Lisp_Object Voverlay_arrow_string;
 
 Lisp_Object Vwindow_size_change_functions;
-Lisp_Object Qwindow_scroll_functions, Vwindow_scroll_functions;
+Lisp_Object Vwindow_scroll_functions;
 Lisp_Object Qredisplay_end_trigger_functions, Vredisplay_end_trigger_functions;
 
 #define INHIBIT_REDISPLAY_HOOKS  /* #### Until we've thought about
@@ -454,7 +470,7 @@ Lisp_Object Vpre_redisplay_hook, Vpost_redisplay_hook;
 Lisp_Object Qpre_redisplay_hook, Qpost_redisplay_hook;
 #endif /* INHIBIT_REDISPLAY_HOOKS */
 
-int last_display_warning_tick, display_warning_tick;
+static int last_display_warning_tick, display_warning_tick;
 Lisp_Object Qdisplay_warning_buffer;
 int inhibit_warning_display;
 
@@ -464,6 +480,10 @@ Lisp_Object Vuse_left_overflow, Vuse_right_overflow;
 Lisp_Object Vtext_cursor_visible_p;
 
 int column_number_start_at_one;
+
+#define WINDOW_SCROLLED(w) \
+(w->hscroll > 0 || w->left_xoffset)
+
 \f
 /***************************************************************************/
 /*                                                                        */
@@ -633,8 +653,8 @@ next_tab_position (struct window *w, int start_pixpos, int left_pixpos)
   int pix_tab_width = tab_pix_width (w);
 
   /* Adjust n_pos for any hscrolling which has happened. */
-  if (w->hscroll > 1)
-    n_pos -= space_width (w) * (w->hscroll - 1);
+  if (WINDOW_SCROLLED (w))
+    n_pos -= space_width (w) * (w->hscroll - 1) + w->left_xoffset;
 
   while (n_pos <= start_pixpos)
     n_pos += pix_tab_width;
@@ -686,8 +706,7 @@ calculate_display_line_boundaries (struct window *w, int modeline)
 
 static Bufpos
 generate_display_line (struct window *w, struct display_line *dl, int bounds,
-                      Bufpos start_pos, int start_col,
-                      prop_block_dynarr **prop,
+                      Bufpos start_pos, prop_block_dynarr **prop,
                       int type)
 {
   Bufpos ret_bufpos;
@@ -720,7 +739,7 @@ generate_display_line (struct window *w, struct display_line *dl, int bounds,
     /* #### urk urk urk!!! Chuck fix this shit! */
     Bytind hacked_up_bytind =
       create_text_block (w, dl, bufpos_to_bytind (b, start_pos),
-                        start_col, prop, type);
+                        prop, type);
     if (hacked_up_bytind > BI_BUF_ZV (b))
       ret_bufpos = BUF_ZV (b) + 1;
     else
@@ -902,9 +921,15 @@ add_emchar_rune (pos_data *data)
   crb->xpos = data->pixpos;
   crb->width = width;
   if (data->bi_bufpos)
-    crb->bufpos =
-      bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
-                       data->bi_bufpos);
+    {
+      if (NILP (data->string))
+       crb->bufpos =
+         bytind_to_bufpos (XBUFFER (WINDOW_BUFFER (XWINDOW (data->window))),
+                           data->bi_bufpos);
+      else
+       crb->bufpos =
+         bytecount_to_charcount (XSTRING_DATA (data->string), data->bi_bufpos);
+    }
   else if (data->is_modeline)
     crb->bufpos = data->modeline_charpos;
   else
@@ -1531,7 +1556,7 @@ add_glyph_rune (pos_data *data, struct glyph_block *gb, int pos_type,
       if (!width)
        return NULL;
 
-      if (data->start_col)
+      if (data->start_col || data->start_col_xoffset)
        {
          prop_block_dynarr *retval;
          int glyph_char_width = width / space_width (w);
@@ -1765,8 +1790,7 @@ add_glyph_runes (pos_data *data, int pos_type)
 
 static Bytind
 create_text_block (struct window *w, struct display_line *dl,
-                  Bytind bi_start_pos, int start_col,
-                  prop_block_dynarr **prop,
+                  Bytind bi_start_pos, prop_block_dynarr **prop,
                   int type)
 {
   struct frame *f = XFRAME (w->frame);
@@ -1791,7 +1815,7 @@ create_text_block (struct window *w, struct display_line *dl,
      after a ^M is invisible. */
   int selective = (INTP (b->selective_display)
                   ? XINT (b->selective_display)
-                  : ((!NILP (b->selective_display) ? -1 : 0)));
+                  : (!NILP (b->selective_display) ? -1 : 0));
 
   /* The variable ctl-arrow allows the user to specify what characters
      can actually be displayed and which octal should be used for.
@@ -1866,6 +1890,7 @@ create_text_block (struct window *w, struct display_line *dl,
      them to this structure for ease of passing. */
   data.d = d;
   XSETWINDOW (data.window, w);
+  data.string = Qnil;
   data.db = db;
   data.dl = dl;
 
@@ -1909,6 +1934,7 @@ create_text_block (struct window *w, struct display_line *dl,
   data.cursor_x = -1;
 
   data.start_col = w->hscroll;
+  data.start_col_xoffset = w->left_xoffset;
   data.bi_start_col_enabled = (w->hscroll ? bi_start_pos : 0);
   data.hscroll_glyph_width_adjust = 0;
 
@@ -2228,6 +2254,7 @@ create_text_block (struct window *w, struct display_line *dl,
                  data.blank_width = DEVMETH (d, eol_cursor_width, ());
                  data.findex = DEFAULT_INDEX;
                  data.start_col = 0;
+                 data.start_col_xoffset = 0;
                  data.bi_start_col_enabled = 0;
 
                  add_emchar_rune (&data);
@@ -2261,7 +2288,8 @@ create_text_block (struct window *w, struct display_line *dl,
              int prop_width = 0;
 
              if (data.start_col > 1)
-               tab_start_pixpos -= (space_width (w) * (data.start_col - 1));
+               tab_start_pixpos -= (space_width (w) * (data.start_col - 1))
+                 + data.start_col_xoffset;
 
              next_tab_start =
                next_tab_position (w, tab_start_pixpos,
@@ -2468,6 +2496,7 @@ done:
       data.blank_width = DEVMETH (d, eol_cursor_width, ());
       data.findex = DEFAULT_INDEX;
       data.start_col = 0;
+      data.start_col_xoffset = 0;
       data.bi_start_col_enabled = 0;
 
       data.max_pixpos += data.blank_width;
@@ -2643,6 +2672,7 @@ create_overlay_glyph_block (struct window *w, struct display_line *dl)
   data.last_charset = Qunbound;
   data.last_findex = DEFAULT_INDEX;
   data.result_str = Qnil;
+  data.string = Qnil;
 
   Dynarr_reset (data.db->runes);
 
@@ -3493,6 +3523,10 @@ generate_modeline (struct window *w, struct display_line *dl, int type)
       /* The modeline is at the bottom of the gutters. */
       dl->ypos = WINDOW_BOTTOM (w);
 
+      /* adjust for the bottom gutter */
+      if (window_is_lowest (w))
+       dl->ypos -= FRAME_BOTTOM_GUTTER_BOUNDS (f);
+
       rb.findex = MODELINE_INDEX;
       rb.xpos = dl->bounds.left_out;
       rb.width = dl->bounds.right_out - dl->bounds.left_out;
@@ -3546,6 +3580,9 @@ generate_modeline (struct window *w, struct display_line *dl, int type)
      set this until we've generated the modeline in order to account
      for any embedded faces. */
   dl->ypos = WINDOW_BOTTOM (w) - dl->descent - ypos_adj;
+  /* adjust for the bottom gutter */
+  if (window_is_lowest (w))
+    dl->ypos -= FRAME_BOTTOM_GUTTER_BOUNDS (f);
 }
 
 static void
@@ -3572,6 +3609,7 @@ generate_formatted_string_db (Lisp_Object format_str, Lisp_Object result_str,
   data.last_findex = DEFAULT_INDEX;
   data.result_str = result_str;
   data.is_modeline = 1;
+  data.string = Qnil;
   XSETWINDOW (data.window, w);
 
   Dynarr_reset (formatted_string_extent_dynarr);
@@ -4044,136 +4082,964 @@ tail_recurse:
            max_pos);
     }
 
-  if (min_pos > pos)
+  if (min_pos > pos)
+    {
+      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
+regenerate_modeline (struct window *w)
+{
+  display_line_dynarr *dla = window_display_lines (w, DESIRED_DISP);
+
+  if (!Dynarr_length (dla) || !Dynarr_atp (dla, 0)->modeline)
+    return;
+  else
+    {
+      generate_modeline (w, Dynarr_atp (dla, 0), DESIRED_DISP);
+      redisplay_update_line (w, 0, 0, 0);
+    }
+}
+
+/* Make sure that modeline display line is present in the given
+   display structs if the window has a modeline and update that
+   line.  Returns true if a modeline was needed. */
+static int
+ensure_modeline_generated (struct window *w, int type)
+{
+  int need_modeline;
+
+  /* minibuffer windows don't have modelines */
+  if (MINI_WINDOW_P (w))
+    need_modeline = 0;
+  /* windows which haven't had it turned off do */
+  else if (WINDOW_HAS_MODELINE_P (w))
+    need_modeline = 1;
+  /* windows which have it turned off don't have a divider if there is
+     a horizontal scrollbar */
+  else if (window_scrollbar_height (w))
+    need_modeline = 0;
+  /* and in this case there is none */
+  else
+    need_modeline = 1;
+
+  if (need_modeline)
+    {
+      display_line_dynarr *dla;
+
+      dla = window_display_lines (w, type);
+
+      /* We don't care if there is a display line which is not
+         currently a modeline because it is definitely going to become
+         one if we have gotten to this point. */
+      if (Dynarr_length (dla) == 0)
+       {
+         if (Dynarr_largest (dla) > 0)
+           {
+             struct display_line *mlp = Dynarr_atp (dla, 0);
+             Dynarr_add (dla, *mlp);
+           }
+         else
+           {
+             struct display_line modeline;
+             xzero (modeline);
+             Dynarr_add (dla, modeline);
+           }
+       }
+
+      /* If we're adding a new place marker go ahead and generate the
+         modeline so that it is available for use by
+         window_modeline_height. */
+      generate_modeline (w, Dynarr_atp (dla, 0), type);
+    }
+
+  return need_modeline;
+}
+
+/* #### Kludge or not a kludge.  I tend towards the former. */
+int
+real_current_modeline_height (struct window *w)
+{
+  Fset_marker (w->start[CMOTION_DISP],  w->start[CURRENT_DISP],  w->buffer);
+  Fset_marker (w->pointm[CMOTION_DISP], w->pointm[CURRENT_DISP], w->buffer);
+
+  if (ensure_modeline_generated (w, CMOTION_DISP))
+    {
+      display_line_dynarr *dla = window_display_lines (w, CMOTION_DISP);
+
+      if (Dynarr_length (dla))
+       {
+         if (Dynarr_atp (dla, 0)->modeline)
+           return (Dynarr_atp (dla, 0)->ascent +
+                   Dynarr_atp (dla, 0)->descent);
+       }
+    }
+  return 0;
+}
+
+\f
+/***************************************************************************/
+/*                                                             */
+/*                            displayable string routines                      */
+/*                                                             */
+/***************************************************************************/
+
+/* Given a position for a string in a window, ensure that the given
+   display line DL accurately represents the text on a line starting
+   at the given position.
+
+   Yes, this is duplicating the code of create_text_block, but it
+   looked just too hard to change create_text_block to handle strings
+   *and* buffers. We already make a distinction between the two
+   elsewhere in the code so I think unifying them would require a
+   complete MULE rewrite. Besides, the other distinction is that these
+   functions cover text that the user *cannot edit* so we can remove
+   everything to do with cursors, minibuffers etc. Eventually the
+   modeline routines should be modified to use this code as it copes
+   with many more types of display situation. */
+
+static Bufpos
+create_string_text_block (struct window *w, Lisp_Object disp_string,
+                         struct display_line *dl,
+                         Bufpos start_pos,
+                         prop_block_dynarr **prop,
+                         face_index default_face)
+{
+  struct frame *f = XFRAME (w->frame);
+  /* Note that a lot of the buffer controlled stuff has been left in
+     because you might well want to make use of it (selective display
+     etc), its just the buffer text that we do not use. However, it
+     seems to be possible for buffer to be nil sometimes so protect
+     against this case. */
+  struct buffer *b = BUFFERP (w->buffer) ? XBUFFER (w->buffer) : 0;
+  struct device *d = XDEVICE (f->device);
+  struct 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;
+
+  /* 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. */
+
+             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)
     {
-      add_string_to_fstring_db_runes (data, (CONST Bufbyte *) "", pos, min_pos,
-                                      -1);
+      Dynarr_free (*prop);
+      *prop = NULL;
     }
 
-  return pos;
+  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);
 }
 
-/* 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;
+/* 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.
 
-  dl = &formatted_string_display_line;
-  db = get_display_block_from_line (dl, TEXT);
-  Dynarr_reset (db->runes);
+   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.*/
 
-  generate_formatted_string_db (format_str, result_str, w, dl, db, findex, 0,
-                                -1, type);
+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;
 
-  Dynarr_reset (formatted_string_emchar_dynarr);
-  while (elt < Dynarr_length (db->runes))
+  /* 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)
     {
-      if (Dynarr_atp (db->runes, elt)->type == RUNE_CHAR)
-       Dynarr_add (formatted_string_emchar_dynarr,
-                   Dynarr_atp (db->runes, elt)->object.chr.ch);
-      elt++;
+      Dynarr_free (dl->left_glyphs);
+      dl->left_glyphs = 0;
+    }
+  if (dl->right_glyphs)
+    {
+      Dynarr_free (dl->right_glyphs);
+      dl->right_glyphs = 0;
     }
 
-  return
-    convert_emchar_string_into_malloced_string
-    ( Dynarr_atp (formatted_string_emchar_dynarr, 0),
-      Dynarr_length (formatted_string_emchar_dynarr), 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;
 }
 
-/* Update just the modeline.  Assumes the desired display structs.  If
-   they do not have a modeline block, it does nothing. */
-static void
-regenerate_modeline (struct window *w)
+/* 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)
 {
-  display_line_dynarr *dla = window_display_lines (w, DESIRED_DISP);
+  int yend = ypos + height;
+  Charcount s_zv;
 
-  if (!Dynarr_length (dla) || !Dynarr_atp (dla, 0)->modeline)
+  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;
-  else
-    {
-      generate_modeline (w, Dynarr_atp (dla, 0), DESIRED_DISP);
-      redisplay_update_line (w, 0, 0, 0);
-    }
-}
 
-/* Make sure that modeline display line is present in the given
-   display structs if the window has a modeline and update that
-   line.  Returns true if a modeline was needed. */
-static int
-ensure_modeline_generated (struct window *w, int type)
-{
-  int need_modeline;
+  s_zv = XSTRING_CHAR_LENGTH (disp_string);
 
-  /* minibuffer windows don't have modelines */
-  if (MINI_WINDOW_P (w))
-    need_modeline = 0;
-  /* windows which haven't had it turned off do */
-  else if (WINDOW_HAS_MODELINE_P (w))
-    need_modeline = 1;
-  /* windows which have it turned off don't have a divider if there is
-     a horizontal scrollbar */
-  else if (window_scrollbar_height (w))
-    need_modeline = 0;
-  /* and in this case there is none */
-  else
-    need_modeline = 1;
+  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;
 
-  if (need_modeline)
+  while (ypos < yend)
     {
-      display_line_dynarr *dla;
-
-      dla = window_display_lines (w, type);
+      struct display_line dl;
+      struct display_line *dlp;
+      Bufpos next_pos;
+      int local;
 
-      /* We don't care if there is a display line which is not
-         currently a modeline because it is definitely going to become
-         one if we have gotten to this point. */
-      if (Dynarr_length (dla) == 0)
+      if (Dynarr_length (dla) < Dynarr_largest (dla))
        {
-         if (Dynarr_largest (dla) > 0)
-           {
-             struct display_line *mlp = Dynarr_atp (dla, 0);
-             Dynarr_add (dla, *mlp);
-           }
-         else
-           {
-             struct display_line modeline;
-             xzero (modeline);
-             Dynarr_add (dla, modeline);
-           }
+         dlp = Dynarr_atp (dla, Dynarr_length (dla));
+         local = 0;
        }
+      else
+       {
 
-      /* If we're adding a new place marker go ahead and generate the
-         modeline so that it is available for use by
-         window_modeline_height. */
-      generate_modeline (w, Dynarr_atp (dla, 0), type);
-    }
-
-  return need_modeline;
-}
+         xzero (dl);
+         dlp = &dl;
+         local = 1;
+       }
 
-/* #### Kludge or not a kludge.  I tend towards the former. */
-int
-real_current_modeline_height (struct window *w)
-{
-  Fset_marker (w->start[CMOTION_DISP],  w->start[CURRENT_DISP],  w->buffer);
-  Fset_marker (w->pointm[CMOTION_DISP], w->pointm[CURRENT_DISP], w->buffer);
+      dlp->bounds = bounds;
+      dlp->offset = 0;
+      next_pos = generate_string_display_line (w, disp_string, dlp, start_pos,
+                                              &prop, default_face);
+      /* we need to make sure that we continue along the line if there
+         is more left to display otherwise we just end up redisplaying
+         the same chunk over and over again. */
+      if (next_pos == start_pos && next_pos < s_zv)
+       start_pos++;
+      else
+       start_pos = next_pos;
 
-  if (ensure_modeline_generated (w, CMOTION_DISP))
-    {
-      display_line_dynarr *dla = window_display_lines (w, CMOTION_DISP);
+      dlp->ypos = ypos + dlp->ascent;
+      ypos = dlp->ypos + dlp->descent;
 
-      if (Dynarr_length (dla))
+      if (ypos > yend)
        {
-         if (Dynarr_atp (dla, 0)->modeline)
-           return (Dynarr_atp (dla, 0)->ascent +
-                   Dynarr_atp (dla, 0)->descent);
+         int visible_height = dlp->ascent + dlp->descent;
+
+         dlp->clip = (ypos - yend);
+         visible_height -= dlp->clip;
+
+         if (visible_height < VERTICAL_CLIP (w, 1))
+           {
+             if (local)
+               free_display_line (dlp);
+             break;
+           }
        }
+      else
+       dlp->clip = 0;
+
+      Dynarr_add (dla, *dlp);
+
+      /* #### This type of check needs to be done down in the
+        generate_display_line call. */
+      if (start_pos >= s_zv)
+       break;
     }
-  return 0;
+
+  if (prop)
+    Dynarr_free (prop);
 }
 
 \f
@@ -4196,6 +5062,7 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
   struct buffer *b = XBUFFER (w->buffer);
   int ypos = WINDOW_TEXT_TOP (w);
   int yend;    /* set farther down */
+  int yclip = WINDOW_TEXT_TOP_CLIP (w);
 
   prop_block_dynarr *prop;
   layout_bounds bounds;
@@ -4262,6 +5129,7 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
        }
       else
        {
+
          xzero (dl);
          dlp = &dl;
          local = 1;
@@ -4269,17 +5137,37 @@ regenerate_window (struct window *w, Bufpos start_pos, Bufpos point, int type)
 
       dlp->bounds = bounds;
       dlp->offset = 0;
-      start_pos = generate_display_line (w, dlp, 1, start_pos,
-                                        w->hscroll, &prop, type);
-      dlp->ypos = ypos + dlp->ascent;
+      start_pos = generate_display_line (w, dlp, 1, start_pos, &prop, type);
+
+      if (yclip > dlp->ascent)
+       {
+         /* this should never happen, but if it does just display the
+            whole line */
+         yclip = 0;
+       }
+
+      dlp->ypos = (ypos + dlp->ascent) - yclip;
       ypos = dlp->ypos + dlp->descent;
 
+      /* See if we've been asked to start midway through a line, for
+         partial display line scrolling. */
+      if (yclip)
+       {
+         dlp->top_clip = yclip;
+         yclip = 0;
+       }
+      else
+       dlp->top_clip = 0;
+
       if (ypos > yend)
        {
          int visible_height = dlp->ascent + dlp->descent;
 
          dlp->clip = (ypos - yend);
-         visible_height -= dlp->clip;
+         /* Although this seems strange we could have a single very
+            tall line visible for which we need to account for both
+            the top clip and the bottom clip. */
+         visible_height -= (dlp->clip + dlp->top_clip);
 
          if (visible_height < VERTICAL_CLIP (w, 1))
            {
@@ -4522,7 +5410,7 @@ regenerate_window_extents_only_changed (struct window *w, Bufpos startp,
        return 0;
 
       new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
-                                        w->hscroll, &prop, DESIRED_DISP);
+                                        &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* #### If there is propagated stuff the fail.  We could
@@ -4541,6 +5429,7 @@ regenerate_window_extents_only_changed (struct window *w, Bufpos startp,
       if (cdl->ypos != ddl->ypos
          || cdl->ascent != ddl->ascent
          || cdl->descent != ddl->descent
+         || cdl->top_clip != ddl->top_clip
          || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
          || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1)
          || old_start != ddl->bufpos
@@ -4684,7 +5573,7 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
        return 0;
 
       new_start = generate_display_line (w, ddl, 0, ddl->bufpos + ddl->offset,
-                                        w->hscroll, &prop, DESIRED_DISP);
+                                        &prop, DESIRED_DISP);
       ddl->offset = 0;
 
       /* If there is propagated stuff then it is pretty much a
@@ -4714,6 +5603,7 @@ regenerate_window_incrementally (struct window *w, Bufpos startp,
       if (cdl->ypos != ddl->ypos
          || cdl->ascent != ddl->ascent
          || cdl->descent != ddl->descent
+         || cdl->top_clip != ddl->top_clip
          || (cdl->cursor_elt != -1 && ddl->cursor_elt == -1)
          || (cdl->cursor_elt == -1 && ddl->cursor_elt != -1))
        {
@@ -4997,7 +5887,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
     }
   Fset_marker (w->pointm[DESIRED_DISP], make_int (pointm), the_buffer);
 
-  /* If the buffer has changed we have to invalid all of our face
+  /* If the buffer has changed we have to invalidate all of our face
      cache elements. */
   if ((!echo_active && b != window_display_buffer (w))
       || !Dynarr_length (w->face_cachels)
@@ -5006,10 +5896,11 @@ redisplay_window (Lisp_Object window, int skip_selected)
   else
     mark_face_cachels_as_not_updated (w);
 
-  /* Ditto the glyph cache elements. */
+  /* Ditto the glyph cache elements, although we do *not* invalidate
+     the cache purely because glyphs have changed - this is now
+     handled by the dirty flag.*/
   if ((!echo_active && b != window_display_buffer (w))
-      || !Dynarr_length (w->glyph_cachels)
-      || f->glyphs_changed)
+      || !Dynarr_length (w->glyph_cachels))
     reset_glyph_cachels (w);
   else
     mark_glyph_cachels_as_not_updated (w);
@@ -5095,6 +5986,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
          && !f->faces_changed
          && !f->glyphs_changed
          && !f->subwindows_changed
+         && !f->subwindows_state_changed
          && !f->point_changed
          && !f->windows_structure_changed)
        {
@@ -5116,6 +6008,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
              && !f->faces_changed
              && !f->glyphs_changed
              && !f->subwindows_changed
+             && !f->subwindows_state_changed
              && !f->windows_structure_changed)
            {
              if (point_visible (w, pointm, CURRENT_DISP)
@@ -5174,6 +6067,7 @@ redisplay_window (Lisp_Object window, int skip_selected)
           && !f->faces_changed
           && !f->glyphs_changed
           && !f->subwindows_changed
+          && !f->subwindows_state_changed
           && !f->windows_structure_changed
           && !f->frame_changed
           && !truncation_changed
@@ -5253,8 +6147,9 @@ regeneration_done:
       Bufpos end = (w->window_end_pos[DESIRED_DISP] == -1
                    ? BUF_ZV (b)
                    : BUF_Z (b) - w->window_end_pos[DESIRED_DISP] - 1);
-
-      update_line_start_cache (w, start, end, pointm, 1);
+      /* Don't pollute the cache if not sure if we are correct */
+      if (w->start_at_line_beg)
+       update_line_start_cache (w, start, end, pointm, 1);
       redisplay_output_window (w);
       /*
        * If we just displayed the echo area, the line start cache is
@@ -5269,6 +6164,10 @@ regeneration_done:
      somewhere else once tty updates occur on a per-frame basis. */
   mark_face_cachels_as_clean (w);
 
+  /* The glyph cachels only get dirty if someone changed something. */
+  if (glyphs_changed)
+    mark_glyph_cachels_as_clean (w);
+
   w->windows_changed = 0;
 }
 
@@ -5400,6 +6299,31 @@ redisplay_frame (struct frame *f, int preemption_check)
   update_frame_toolbars (f);
 #endif /* HAVE_TOOLBARS */
 
+  /* 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 (!Dynarr_length (f->subwindow_cachels)
+      || f->subwindows_changed
+      || f->faces_changed
+      || f->frame_changed)
+    {
+      reset_subwindow_cachels (f);
+      /* we have to do this so the gutter gets regenerated. */
+      reset_gutter_display_lines (f);
+    }
+  else
+    mark_subwindow_cachels_as_not_updated (f);
+  /* We can now update the gutters, safe in the knowledge that our
+     efforts won't get undone. */
+  update_frame_gutters (f);
+
   hold_frame_size_changes ();
 
   /* ----------------- BEGIN CRITICAL REDISPLAY SECTION ---------------- */
@@ -5426,27 +6350,12 @@ redisplay_frame (struct frame *f, int preemption_check)
      #### If a frame-size change does occur we should probably
      actually be preempting redisplay. */
 
-  /* If we clear the frame we have to force its contents to be redrawn. */
-  if (f->clear)
-    f->frame_changed = 1;
-
   /* Erase the frame before outputting its contents. */
   if (f->clear)
     {
       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);
 
@@ -5461,23 +6370,9 @@ redisplay_frame (struct frame *f, int preemption_check)
 
   update_frame_title (f);
 
-  f->buffers_changed  = 0;
-  f->clip_changed     = 0;
-  f->extents_changed  = 0;
-  f->faces_changed    = 0;
-  f->frame_changed    = 0;
-  f->glyphs_changed   = 0;
-  f->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;
+  CLASS_RESET_CHANGED_FLAGS (f);
   f->window_face_cache_reset = 0;
   f->echo_area_garbaged = 0;
-
   f->clear = 0;
 
   if (!f->size_change_pending)
@@ -5527,11 +6422,7 @@ redisplay_device (struct device *d)
 
   if (FRAME_REPAINT_P (f))
     {
-      if (f->buffers_changed  || f->clip_changed  || f->extents_changed ||
-         f->faces_changed    || f->frame_changed || f->menubar_changed ||
-         f->modeline_changed || f->point_changed || f->size_changed    ||
-         f->toolbar_changed  || f->windows_changed || f->size_slipped  ||
-         f->windows_structure_changed || f->glyphs_changed || f->subwindows_changed)
+      if (CLASS_REDISPLAY_FLAGS_CHANGEDP(f))
        {
          preempted = redisplay_frame (f, 0);
        }
@@ -5561,12 +6452,7 @@ redisplay_device (struct device *d)
 
       if (FRAME_REPAINT_P (f))
        {
-         if (f->buffers_changed  || f->clip_changed  || f->extents_changed ||
-             f->faces_changed    || f->frame_changed || f->menubar_changed ||
-             f->modeline_changed || f->point_changed || f->size_changed    ||
-             f->toolbar_changed  || f->windows_changed ||
-             f->windows_structure_changed ||
-             f->glyphs_changed || f->subwindows_changed)
+         if (CLASS_REDISPLAY_FLAGS_CHANGEDP (f))
            {
              preempted = redisplay_frame (f, 0);
            }
@@ -5581,20 +6467,7 @@ redisplay_device (struct device *d)
 
   /* If we get here then we redisplayed all of our frames without
      getting preempted so mark ourselves as clean. */
-  d->buffers_changed  = 0;
-  d->clip_changed     = 0;
-  d->extents_changed  = 0;
-  d->faces_changed    = 0;
-  d->frame_changed    = 0;
-  d->glyphs_changed   = 0;
-  d->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;
+  CLASS_RESET_CHANGED_FLAGS (d);
 
   if (!size_change_failed)
     d->size_changed = 0;
@@ -5629,13 +6502,8 @@ redisplay_without_hooks (void)
   if (asynch_device_change_pending)
     handle_asynch_device_change ();
 
-  if (!buffers_changed && !clip_changed     && !extents_changed &&
-      !faces_changed   && !frame_changed    && !icon_changed    &&
-      !menubar_changed && !modeline_changed && !point_changed   &&
-      !size_changed    && !toolbar_changed  && !windows_changed &&
-      !glyphs_changed  && !subwindows_changed &&
-      !windows_structure_changed && !disable_preemption &&
-      preemption_count < max_preempts)
+  if (!GLOBAL_REDISPLAY_FLAGS_CHANGEDP &&
+      !disable_preemption && preemption_count < max_preempts)
     goto done;
 
   DEVICE_LOOP_NO_BREAK (devcons, concons)
@@ -5643,12 +6511,7 @@ redisplay_without_hooks (void)
       struct device *d = XDEVICE (XCAR (devcons));
       int preempted;
 
-      if (d->buffers_changed  || d->clip_changed     || d->extents_changed ||
-         d->faces_changed    || d->frame_changed    || d->icon_changed    ||
-         d->menubar_changed  || d->modeline_changed || d->point_changed   ||
-         d->size_changed     || d->toolbar_changed  || d->windows_changed ||
-         d->windows_structure_changed ||
-         d->glyphs_changed || d->subwindows_changed)
+      if (CLASS_REDISPLAY_FLAGS_CHANGEDP (d))
        {
          preempted = redisplay_device (d);
 
@@ -5667,19 +6530,7 @@ redisplay_without_hooks (void)
   preemption_count = 0;
 
   /* Mark redisplay as accurate */
-  buffers_changed  = 0;
-  clip_changed     = 0;
-  extents_changed  = 0;
-  frame_changed    = 0;
-  glyphs_changed   = 0;
-  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;
+  GLOBAL_RESET_CHANGED_FLAGS;
   RESET_CHANGED_SET_FLAGS;
 
   if (faces_changed)
@@ -5910,7 +6761,6 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
     case 'p':
     {
       Bufpos pos = marker_position (w->start[type]);
-      Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
       /* This had better be while the desired lines are being done. */
       if (w->window_end_pos[type] <= BUF_Z (b) - BUF_ZV (b))
@@ -5927,15 +6777,20 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
          /* This hard limit is ok since the string it will hold has a
              fixed maximum length of 3.  But just to be safe... */
          char buf[10];
+         Charcount chars = pos - BUF_BEGV (b);
+         Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
-         total = ((pos - BUF_BEGV (b)) * 100 + total - 1) / total;
+         /* Avoid overflow on big buffers */
+         int percent = total > LONG_MAX/200 ?
+           (chars + total/200) / (total / 100) :
+           (chars * 100 + total/2) / total;
 
          /* We can't normally display a 3-digit number, so get us a
              2-digit number that is close. */
-         if (total == 100)
-           total = 99;
+         if (percent == 100)
+           percent = 99;
 
-         sprintf (buf, "%2d%%", total);
+         sprintf (buf, "%d%%", percent);
          Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
                           strlen (buf));
 
@@ -5950,7 +6805,6 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
     {
       Bufpos toppos = marker_position (w->start[type]);
       Bufpos botpos = BUF_Z (b) - w->window_end_pos[type];
-      Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
       /* botpos is only accurate as of the last redisplay, so we can
          only treat it as a hint.  In particular, after erase-buffer,
@@ -5970,18 +6824,23 @@ decode_mode_spec (struct window *w, Emchar spec, int type)
          /* This hard limit is ok since the string it will hold has a
              fixed maximum length of around 6.  But just to be safe... */
          char buf[10];
+         Charcount chars = botpos - BUF_BEGV (b);
+         Charcount total = BUF_ZV (b) - BUF_BEGV (b);
 
-         total = ((botpos - BUF_BEGV (b)) * 100 + total - 1) / total;
+         /* Avoid overflow on big buffers */
+         int percent = total > LONG_MAX/200 ?
+           (chars + total/200) / (total / 100) :
+           (chars * 100 + total/2) / max (total, 1);
 
          /* We can't normally display a 3-digit number, so get us a
              2-digit number that is close. */
-         if (total == 100)
-           total = 99;
+         if (percent == 100)
+           percent = 99;
 
          if (toppos <= BUF_BEGV (b))
-           sprintf (buf, "Top%2d%%", total);
+           sprintf (buf, "Top%d%%", percent);
          else
-           sprintf (buf, "%2d%%", total);
+           sprintf (buf, "%d%%", percent);
 
          Dynarr_add_many (mode_spec_bufbyte_string, (Bufbyte *) buf,
                           strlen (buf));
@@ -6084,7 +6943,7 @@ free_display_line (struct display_line *dl)
 /* Given an array of display lines, free them and all data structures
    contained within them. */
 
-static void
+void
 free_display_lines (display_line_dynarr *dla)
 {
   int line;
@@ -6117,7 +6976,7 @@ free_display_structs (struct window_mirror *mir)
 \f
 
 static void
-mark_glyph_block_dynarr (glyph_block_dynarr *gba, void (*markobj) (Lisp_Object))
+mark_glyph_block_dynarr (glyph_block_dynarr *gba)
 {
   if (gba)
     {
@@ -6127,15 +6986,15 @@ mark_glyph_block_dynarr (glyph_block_dynarr *gba, void (*markobj) (Lisp_Object))
       for (; gb < gb_last; gb++)
        {
          if (!NILP (gb->glyph))
-           markobj (gb->glyph);
+           mark_object (gb->glyph);
          if (!NILP (gb->extent))
-           markobj (gb->extent);
+           mark_object (gb->extent);
        }
     }
 }
 
 static void
-mark_redisplay_structs (display_line_dynarr *dla, void (*markobj) (Lisp_Object))
+mark_redisplay_structs (display_line_dynarr *dla)
 {
   display_line *dl = Dynarr_atp (dla, 0);
   display_line *dl_last = Dynarr_atp (dla, Dynarr_length (dla));
@@ -6157,35 +7016,35 @@ mark_redisplay_structs (display_line_dynarr *dla, void (*markobj) (Lisp_Object))
              if (r->type == RUNE_DGLYPH)
                {
                  if (!NILP (r->object.dglyph.glyph))
-                   markobj (r->object.dglyph.glyph);
+                   mark_object (r->object.dglyph.glyph);
                  if (!NILP (r->object.dglyph.extent))
-                   markobj (r->object.dglyph.extent);
+                   mark_object (r->object.dglyph.extent);
                }
            }
        }
 
-      mark_glyph_block_dynarr (dl->left_glyphs,  markobj);
-      mark_glyph_block_dynarr (dl->right_glyphs, markobj);
+      mark_glyph_block_dynarr (dl->left_glyphs);
+      mark_glyph_block_dynarr (dl->right_glyphs);
     }
 }
 
 static void
-mark_window_mirror (struct window_mirror *mir, void (*markobj)(Lisp_Object))
+mark_window_mirror (struct window_mirror *mir)
 {
-  mark_redisplay_structs (mir->current_display_lines, markobj);
-  mark_redisplay_structs (mir->desired_display_lines, markobj);
+  mark_redisplay_structs (mir->current_display_lines);
+  mark_redisplay_structs (mir->desired_display_lines);
 
   if (mir->next)
-    mark_window_mirror (mir->next, markobj);
+    mark_window_mirror (mir->next);
 
   if (mir->hchild)
-    mark_window_mirror (mir->hchild, markobj);
+    mark_window_mirror (mir->hchild);
   else if (mir->vchild)
-    mark_window_mirror (mir->vchild, markobj);
+    mark_window_mirror (mir->vchild);
 }
 
 void
-mark_redisplay (void (*markobj)(Lisp_Object))
+mark_redisplay (void)
 {
   Lisp_Object frmcons, devcons, concons;
 
@@ -6193,7 +7052,7 @@ mark_redisplay (void (*markobj)(Lisp_Object))
     {
       struct frame *f = XFRAME (XCAR (frmcons));
       update_frame_window_mirror (f);
-      mark_window_mirror (f->root_mirror, markobj);
+      mark_window_mirror (f->root_mirror);
     }
 }
 \f
@@ -6257,7 +7116,7 @@ mark_redisplay (void (*markobj)(Lisp_Object))
 
 /* This will get used quite a bit so we don't want to be constantly
    allocating and freeing it. */
-line_start_cache_dynarr *internal_cache;
+static line_start_cache_dynarr *internal_cache;
 
 /* Makes internal_cache represent the TYPE display structs and only
    the TYPE display structs. */
@@ -6551,7 +7410,7 @@ int
 point_would_be_visible (struct window *w, Bufpos startp, Bufpos point)
 {
   struct buffer *b = XBUFFER (w->buffer);
-  int pixpos = 0;
+  int pixpos = -WINDOW_TEXT_TOP_CLIP(w);
   int bottom = WINDOW_TEXT_HEIGHT (w);
   int start_elt;
 
@@ -6776,7 +7635,7 @@ start_with_line_at_pixpos (struct window *w, Bufpos point, int pixpos)
        }
 
       cur_elt--;
-      if (cur_elt < 0)
+      while (cur_elt < 0)
        {
          Bufpos from, to;
          int win_char_height;
@@ -6796,7 +7655,20 @@ start_with_line_at_pixpos (struct window *w, Bufpos point, int pixpos)
          update_line_start_cache (w, from, to, point, 0);
 
          cur_elt = point_in_line_start_cache (w, cur_pos, 2) - 1;
-         assert (cur_elt >= 0);
+         assert (cur_elt >= -1);
+         /* This used to be cur_elt>=0 under the assumption that if
+            point is in the top line and not at BUF_BEGV, then
+            setting the window_start to a newline before the start of
+            the first line will always cause scrolling.
+
+            However in my (jv) opinion this is wrong.  That new line
+            can be hidden in various ways: invisible extents, an
+            explicit window-start not at a newline character etc.
+            The existence of those are indeed known to create crashes
+            on that assert.  So we have no option but to continue the
+            search if we found point at the top of the line_start_cache
+            again. */
+         cur_pos = Dynarr_atp (w->line_start_cache,0)->start;
        }
       prev_pos = cur_pos;
     }
@@ -8092,6 +8964,8 @@ redisplay_variable_changed (Lisp_Object sym, Lisp_Object *val,
   return 0;
 }
 
+/* This is called if the built-in glyphs have their properties
+   changed. */
 void
 redisplay_glyph_changed (Lisp_Object glyph, Lisp_Object property,
                         Lisp_Object locale)
@@ -8214,7 +9088,9 @@ init_redisplay (void)
   preemption_count = 0;
   max_preempts = INIT_MAX_PREEMPTS;
 
+#ifndef PDUMP
   if (!initialized)
+#endif
     {
       cmotion_display_lines = Dynarr_new (display_line);
       mode_spec_bufbyte_string = Dynarr_new (Bufbyte);
@@ -8294,7 +9170,6 @@ syms_of_redisplay (void)
 #endif /* INHIBIT_REDISPLAY_HOOKS */
   defsymbol (&Qdisplay_warning_buffer, "display-warning-buffer");
   defsymbol (&Qbar_cursor, "bar-cursor");
-  defsymbol (&Qwindow_scroll_functions, "window-scroll-functions");
   defsymbol (&Qredisplay_end_trigger_functions,
             "redisplay-end-trigger-functions");
 
@@ -8308,8 +9183,16 @@ syms_of_redisplay (void)
 }
 
 void
+reinit_vars_of_redisplay (void)
+{
+  updating_line_start_cache = 0;
+}
+
+void
 vars_of_redisplay (void)
 {
+  reinit_vars_of_redisplay ();
+
 #if 0
   staticpro (&last_arrow_position);
   staticpro (&last_arrow_string);
@@ -8317,8 +9200,6 @@ vars_of_redisplay (void)
   last_arrow_string = Qnil;
 #endif /* 0 */
 
-  updating_line_start_cache = 0;
-
   /* #### Probably temporary */
   DEFVAR_INT ("redisplay-cache-adjustment", &cache_adjustment /*
 \(Temporary) Setting this will impact the performance of the internal
@@ -8403,7 +9284,7 @@ instead.
   Vwindow_system = Qnil;
 
   /* #### Temporary shit until window-system is eliminated. */
-  DEFVAR_LISP ("initial-window-system", &Vinitial_window_system /*
+  DEFVAR_CONST_LISP ("initial-window-system", &Vinitial_window_system /*
 DON'T TOUCH
 */ );
   Vinitial_window_system = Qnil;