Merge r21-4-11-chise-0_20-=ucs.
[chise/xemacs-chise.git.1] / src / redisplay-output.c
index 6266fd5..fc0b5c3 100644 (file)
@@ -2,6 +2,7 @@
    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
    Copyright (C) 1995, 1996 Ben Wing.
    Copyright (C) 1996 Chuck Thompson.
    Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
    Copyright (C) 1995, 1996 Ben Wing.
    Copyright (C) 1996 Chuck Thompson.
+   Copyright (C) 1999, 2002 Andy Piper.
 
 This file is part of XEmacs.
 
 
 This file is part of XEmacs.
 
@@ -26,9 +27,11 @@ Boston, MA 02111-1307, USA.  */
 
 /* Author: Chuck Thompson */
 
 
 /* Author: Chuck Thompson */
 
+/* Heavily hacked for modularity, gutter and subwindow support by Andy
+   Piper. */
+
 #include <config.h>
 #include "lisp.h"
 #include <config.h>
 #include "lisp.h"
-#include "debug.h"
 
 #include "buffer.h"
 #include "window.h"
 
 #include "buffer.h"
 #include "window.h"
@@ -37,13 +40,25 @@ Boston, MA 02111-1307, USA.  */
 #include "glyphs.h"
 #include "redisplay.h"
 #include "faces.h"
 #include "glyphs.h"
 #include "redisplay.h"
 #include "faces.h"
-
-#include "sysdep.h"
+#include "gutter.h"
 
 static int compare_runes (struct window *w, struct rune *crb,
                          struct rune *drb);
 static void redraw_cursor_in_window (struct window *w,
                                     int run_end_begin_glyphs);
 
 static int compare_runes (struct window *w, struct rune *crb,
                          struct rune *drb);
 static void redraw_cursor_in_window (struct window *w,
                                     int run_end_begin_glyphs);
+static void redisplay_output_display_block (struct window *w, struct display_line *dl,
+                                           int block, int start, int end, int start_pixpos,
+                                           int cursor_start, int cursor_width,
+                                           int cursor_height);
+static void redisplay_normalize_display_box (struct display_box* dest,
+                                            struct display_glyph_area* src);
+static int redisplay_display_boxes_in_window_p (struct window* w,
+                                               struct display_box* db,
+                                               struct display_glyph_area* dga);
+static void redisplay_clear_clipped_region (Lisp_Object locale, face_index findex,
+                                           struct display_box* dest,
+                                           struct display_glyph_area* glyphsrc,
+                                           int fullheight_p, Lisp_Object);
 
 /*****************************************************************************
  sync_rune_structs
 
 /*****************************************************************************
  sync_rune_structs
@@ -87,7 +102,7 @@ sync_rune_structs (struct window *w, rune_dynarr *cra, rune_dynarr *dra)
  For the given LINE in window W, make the current display line equal
  the desired display line.
  ****************************************************************************/
  For the given LINE in window W, make the current display line equal
  the desired display line.
  ****************************************************************************/
-static void
+void
 sync_display_line_structs (struct window *w, int line, int do_blocks,
                           display_line_dynarr *cdla,
                           display_line_dynarr *ddla)
 sync_display_line_structs (struct window *w, int line, int do_blocks,
                           display_line_dynarr *cdla,
                           display_line_dynarr *ddla)
@@ -167,7 +182,7 @@ sync_display_line_structs (struct window *w, int line, int do_blocks,
 /*****************************************************************************
  compare_runes
 
 /*****************************************************************************
  compare_runes
 
- Compare to runes to see if each of their fields is equal.  If so,
+ Compare two runes to see if each of their fields is equal.  If so,
  return true otherwise return false.
  ****************************************************************************/
 static int
  return true otherwise return false.
  ****************************************************************************/
 static int
@@ -176,10 +191,28 @@ compare_runes (struct window *w, struct rune *crb, struct rune *drb)
   /* Do not compare the values of bufpos and endpos.  They do not
      affect the display characteristics. */
 
   /* Do not compare the values of bufpos and endpos.  They do not
      affect the display characteristics. */
 
-  if ((crb->findex != drb->findex) ||
-      (WINDOW_FACE_CACHEL_DIRTY (w, drb->findex)))
-    return 0;
-  else if (crb->xpos != drb->xpos)
+  /* Note: (hanoi 6) spends 95% of its time in redisplay, and about
+     30% here. Not using bitfields for rune.type alone gives a redisplay
+     speed up of 10%.
+
+     #### In profile arcs run of a normal Gnus session this function
+     is run 6.76 million times, only to return 1 in 6.73 million of
+     those.
+
+     In addition a quick look GCC sparc assembly shows that GCC is not
+     doing a good job here.
+     1. The function is not inlined (too complicated?)
+     2. It seems to be reloading the crb and drb variables all the
+     time.
+     3. It doesn't seem to notice that the second half of these if's
+     are really a switch statement.
+
+     So I (JV) conjecture
+
+     #### It would really be worth it to arrange for this function to
+     be (almost) a single call to memcmp. */
+
+  if (crb->xpos != drb->xpos)
     return 0;
   else if (crb->width != drb->width)
     return 0;
     return 0;
   else if (crb->width != drb->width)
     return 0;
@@ -188,16 +221,83 @@ compare_runes (struct window *w, struct rune *crb, struct rune *drb)
   else if (crb->type != drb->type)
     return 0;
   else if (crb->type == RUNE_CHAR &&
   else if (crb->type != drb->type)
     return 0;
   else if (crb->type == RUNE_CHAR &&
-          (crb->object.chr.ch != drb->object.chr.ch))
+          !CHARC_EQ (crb->object.cglyph, drb->object.cglyph))
+    return 0;
+  else if (crb->type == RUNE_HLINE &&
+          (crb->object.hline.thickness != drb->object.hline.thickness ||
+           crb->object.hline.yoffset != drb->object.hline.yoffset))
     return 0;
   else if (crb->type == RUNE_DGLYPH &&
           (!EQ (crb->object.dglyph.glyph, drb->object.dglyph.glyph) ||
            !EQ (crb->object.dglyph.extent, drb->object.dglyph.extent) ||
     return 0;
   else if (crb->type == RUNE_DGLYPH &&
           (!EQ (crb->object.dglyph.glyph, drb->object.dglyph.glyph) ||
            !EQ (crb->object.dglyph.extent, drb->object.dglyph.extent) ||
-           crb->object.dglyph.xoffset != drb->object.dglyph.xoffset))
+           crb->object.dglyph.xoffset != drb->object.dglyph.xoffset ||
+           crb->object.dglyph.yoffset != drb->object.dglyph.yoffset ||
+            crb->object.dglyph.ascent != drb->object.dglyph.ascent ||
+            crb->object.dglyph.descent != drb->object.dglyph.descent))
     return 0;
     return 0;
-  else if (crb->type == RUNE_HLINE &&
-          (crb->object.hline.thickness != drb->object.hline.thickness ||
-           crb->object.hline.yoffset != drb->object.hline.yoffset))
+  /* Only check dirtiness if we know something has changed. */
+  else if (crb->type == RUNE_DGLYPH &&
+          (XGLYPH_DIRTYP (crb->object.dglyph.glyph) ||
+           crb->findex != drb->findex))
+    {
+      /* We need some way of telling redisplay_output_layout () that the
+         only reason we are outputting it is because something has
+         changed internally. That way we can optimize whether we need
+         to clear the layout first and also only output the components
+         that have changed. The image_instance dirty flag and
+         display_hash are no good to us because these will invariably
+         have been set anyway if the layout has changed. So it looks
+         like we need yet another change flag that we can set here and
+         then clear in redisplay_output_layout (). */
+      Lisp_Object window, image;
+      Lisp_Image_Instance* ii;
+      XSETWINDOW (window, w);
+      image = glyph_image_instance (crb->object.dglyph.glyph,
+                                   window, ERROR_ME_NOT, 1);
+
+      if (!IMAGE_INSTANCEP (image))
+       return 0;
+      ii = XIMAGE_INSTANCE (image);
+
+      if (TEXT_IMAGE_INSTANCEP (image) &&
+         (crb->findex != drb->findex ||
+          WINDOW_FACE_CACHEL_DIRTY (w, drb->findex)))
+       return 0;
+
+      /* It is quite common for the two glyphs to be EQ since in many
+        cases they will actually be the same object. This does not
+        mean, however, that nothing has changed. We therefore need to
+        check the current hash of the glyph against the last recorded
+        display hash and the pending display items. See
+        update_subwindow (). */
+      if (image_instance_changed (image) ||
+         crb->findex != drb->findex ||
+         WINDOW_FACE_CACHEL_DIRTY (w, drb->findex))
+       {
+         /* Now we are going to re-output the glyph, but since
+            this is for some internal reason not related to geometry
+            changes, send a hint to the output routines that they can
+            take some short cuts. This is most useful for
+            layouts. This flag should get reset by the output
+            routines.
+
+            #### It is possible for us to get here when the
+            face_cachel is dirty. I do not know what the implications
+            of this are.*/
+         IMAGE_INSTANCE_OPTIMIZE_OUTPUT (ii) = 1;
+         return 0;
+       }
+      else
+       return 1;
+    }
+  /* We now do this last so that glyph checks can do their own thing
+     for face changes. Face changes quite often happen when we are
+     trying to output something in the gutter, this would normally
+     lead to a lot of flashing. The indices can quite often be
+     different and yet the faces are the same, we do not want to
+     re-output in this instance. */
+  else  if (crb->findex != drb->findex ||
+           WINDOW_FACE_CACHEL_DIRTY (w, drb->findex))
     return 0;
   else
     return 1;
     return 0;
   else
     return 1;
@@ -305,8 +405,6 @@ compare_display_blocks (struct window *w, struct display_line *cdl,
                        int cursor_height)
 {
   struct frame *f = XFRAME (w->frame);
                        int cursor_height)
 {
   struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
-
   struct display_block *cdb, *ddb;
   int start_pos;
   int stop_pos;
   struct display_block *cdb, *ddb;
   int start_pos;
   int stop_pos;
@@ -352,8 +450,14 @@ compare_display_blocks (struct window *w, struct display_line *cdl,
     force = 1;
 
   if (f->windows_structure_changed ||
     force = 1;
 
   if (f->windows_structure_changed ||
+      /* #### Why is this so? We have face cachels so that we don't
+         have to recalculate all the display blocks when faces
+         change. I have fixed this for glyphs and am inclined to think
+         that faces should "Just Work", but I'm not feeling brave
+         today. Maybe its because the face cachels represent merged
+         faces rather than simply instantiations in a particular
+         domain. */
       f->faces_changed ||
       f->faces_changed ||
-      f->glyphs_changed ||
       cdl->ypos != ddl->ypos ||
       cdl->ascent != ddl->ascent ||
       cdl->descent != ddl->descent ||
       cdl->ypos != ddl->ypos ||
       cdl->ascent != ddl->ascent ||
       cdl->descent != ddl->descent ||
@@ -416,10 +520,10 @@ compare_display_blocks (struct window *w, struct display_line *cdl,
          stop_pos = elt + 1;
        }
 
          stop_pos = elt + 1;
        }
 
-      DEVMETH (d, output_display_block, (w, ddl, d_block, start_pos,
-                                        stop_pos, start_pixpos,
-                                        cursor_start, cursor_width,
-                                        cursor_height));
+      redisplay_output_display_block (w, ddl, d_block, start_pos,
+                                     stop_pos, start_pixpos,
+                                     cursor_start, cursor_width,
+                                     cursor_height);
       return 1;
     }
 
       return 1;
     }
 
@@ -435,13 +539,12 @@ static void
 clear_left_border (struct window *w, int y, int height)
 {
   struct frame *f = XFRAME (w->frame);
 clear_left_border (struct window *w, int y, int height)
 {
   struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
   Lisp_Object window;
 
   XSETWINDOW (window, w);
   Lisp_Object window;
 
   XSETWINDOW (window, w);
-  DEVMETH (d, clear_region, (window, DEFAULT_INDEX,
-                            FRAME_LEFT_BORDER_START (f), y,
-                            FRAME_BORDER_WIDTH (f), height));
+  redisplay_clear_region (window, DEFAULT_INDEX,
+               FRAME_LEFT_BORDER_START (f), y,
+               FRAME_BORDER_WIDTH (f), height);
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -453,13 +556,12 @@ static void
 clear_right_border (struct window *w, int y, int height)
 {
   struct frame *f = XFRAME (w->frame);
 clear_right_border (struct window *w, int y, int height)
 {
   struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
   Lisp_Object window;
 
   XSETWINDOW (window, w);
   Lisp_Object window;
 
   XSETWINDOW (window, w);
-  DEVMETH (d, clear_region, (window, DEFAULT_INDEX,
-                            FRAME_RIGHT_BORDER_START (f),
-                            y, FRAME_BORDER_WIDTH (f), height));
+  redisplay_clear_region (window, DEFAULT_INDEX,
+               FRAME_RIGHT_BORDER_START (f),
+               y, FRAME_BORDER_WIDTH (f), height);
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -476,7 +578,6 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
 
 {
   struct frame *f = XFRAME (w->frame);
 
 {
   struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
   struct buffer *b = XBUFFER (w->buffer);
   struct buffer *old_b = window_display_buffer (w);
   struct display_line *cdl, *ddl;
   struct buffer *b = XBUFFER (w->buffer);
   struct buffer *old_b = window_display_buffer (w);
   struct display_line *cdl, *ddl;
@@ -538,7 +639,7 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
      a TEXT block. */
   if (ddl->modeline)
     {
      a TEXT block. */
   if (ddl->modeline)
     {
-      /* The shadow thickness check is necesssary if only the sign of
+      /* The shadow thickness check is necessary if only the sign of
          the size changed. */
       if (cdba && !w->shadow_thickness_changed)
        {
          the size changed. */
       if (cdba && !w->shadow_thickness_changed)
        {
@@ -547,8 +648,8 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
        }
       else
        {
        }
       else
        {
-         DEVMETH (d, output_display_block, (w, ddl, 0, 0, -1, start_pixpos,
-                                            0, 0, 0));
+         redisplay_output_display_block (w, ddl, 0, 0, -1, start_pixpos,
+                                         0, 0, 0);
          must_sync = 1;
        }
 
          must_sync = 1;
        }
 
@@ -593,37 +694,46 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
                  (cdl && (cdl->ypos != ddl->ypos ||
                           cdl->ascent != ddl->ascent ||
                           cdl->descent != ddl->descent ||
                  (cdl && (cdl->ypos != ddl->ypos ||
                           cdl->ascent != ddl->ascent ||
                           cdl->descent != ddl->descent ||
+                          cdl->top_clip != ddl->top_clip ||
                           cdl->clip != ddl->clip)))
                {
                  int x, y, width, height;
                           cdl->clip != ddl->clip)))
                {
                  int x, y, width, height;
-                 Lisp_Object face;
+                 face_index findex;
 
                  must_sync = 1;
                  x = start_pixpos;
 
                  must_sync = 1;
                  x = start_pixpos;
-                 y = ddl->ypos - ddl->ascent;
+                 y = DISPLAY_LINE_YPOS (ddl);
                  width = min (next_start_pixpos, block_end) - x;
                  width = min (next_start_pixpos, block_end) - x;
-                 height = ddl->ascent + ddl->descent - ddl->clip;
+                 height = DISPLAY_LINE_HEIGHT (ddl);
 
                  if (x < ddl->bounds.left_in)
 
                  if (x < ddl->bounds.left_in)
-                   face = Vleft_margin_face;
+                   {
+                     findex = ddl->left_margin_findex ?
+                       ddl->left_margin_findex
+                       : get_builtin_face_cache_index (w, Vleft_margin_face);
+                   }
                  else if (x < ddl->bounds.right_in)
                  else if (x < ddl->bounds.right_in)
-                   face = Vdefault_face;
+                   {
+                     /* no check here because DEFAULT_INDEX == 0 anyway */
+                     findex = ddl->default_findex;
+                   }
                  else if (x < ddl->bounds.right_out)
                  else if (x < ddl->bounds.right_out)
-                   face = Vright_margin_face;
+                   {
+                     findex = ddl->right_margin_findex ?
+                       ddl->right_margin_findex
+                       : get_builtin_face_cache_index (w, Vright_margin_face);
+                   }
                  else
                  else
-                   face = Qnil;
+                   findex = (face_index) -1;
 
 
-                 if (!NILP (face))
+                 if (findex != (face_index) -1)
                    {
                      Lisp_Object window;
 
                      XSETWINDOW (window, w);
 
                      /* Clear the empty area. */
                    {
                      Lisp_Object window;
 
                      XSETWINDOW (window, w);
 
                      /* Clear the empty area. */
-                     DEVMETH (d, clear_region,
-                              (window, get_builtin_face_cache_index (w,
-                                                                     face),
-                               x, y, width, height));
+                     redisplay_clear_region (window, findex, x, y, width, height);
 
                      /* Mark that we should clear the border.  This is
                         necessary because italic fonts may leave
 
                      /* Mark that we should clear the border.  This is
                         necessary because italic fonts may leave
@@ -654,7 +764,11 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
             region or if it was a block of a different type, then
             output the entire ddb.  Otherwise, compare cdb and
             ddb and output only the changed region. */
             region or if it was a block of a different type, then
             output the entire ddb.  Otherwise, compare cdb and
             ddb and output only the changed region. */
-         if (!force && cdb && ddb->type == cdb->type && b == old_b)
+         if (!force && cdb && ddb->type == cdb->type
+             /* If there was no buffer being display before the
+                 compare anyway as we might be outputting a gutter. */
+             &&
+             (b == old_b || !old_b))
            {
              must_sync |= compare_display_blocks (w, cdl, ddl, old_block,
                                                   block, start_pixpos,
            {
              must_sync |= compare_display_blocks (w, cdl, ddl, old_block,
                                                   block, start_pixpos,
@@ -686,11 +800,11 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
                }
 
              must_sync = 1;
                }
 
              must_sync = 1;
-             DEVMETH (d, output_display_block, (w, ddl, block, first_elt,
-                                                last_elt,
-                                                start_pixpos,
-                                                cursor_start, cursor_width,
-                                                cursor_height));
+             redisplay_output_display_block (w, ddl, block, first_elt,
+                                             last_elt,
+                                             start_pixpos,
+                                             cursor_start, cursor_width,
+                                             cursor_height);
            }
 
          start_pixpos = next_start_pixpos;
            }
 
          start_pixpos = next_start_pixpos;
@@ -705,19 +819,23 @@ output_display_line (struct window *w, display_line_dynarr *cdla,
   if (f->windows_structure_changed || f->faces_changed || clear_border
       || f->clear)
     {
   if (f->windows_structure_changed || f->faces_changed || clear_border
       || f->clear)
     {
-      int y = ddl->ypos - ddl->ascent;
-      int height = ddl->ascent + ddl->descent - ddl->clip;
+      int y = DISPLAY_LINE_YPOS (ddl);
+      int height = DISPLAY_LINE_HEIGHT (ddl);
 
 
-      if (ddl->modeline)
+      /* If we are in the gutter then we musn't clear the borders. */
+      if (y >= WINDOW_TEXT_TOP (w) && (y + height) <= WINDOW_TEXT_BOTTOM (w))
        {
        {
-         y -= MODELINE_SHADOW_THICKNESS (w);
-         height += (2 * MODELINE_SHADOW_THICKNESS (w));
-       }
+         if (ddl->modeline)
+           {
+             y -= MODELINE_SHADOW_THICKNESS (w);
+             height += (2 * MODELINE_SHADOW_THICKNESS (w));
+           }
 
 
-      if (window_is_leftmost (w))
-       clear_left_border (w, y, height);
-      if (window_is_rightmost (w))
-       clear_right_border (w, y, height);
+         if (window_is_leftmost (w))
+           clear_left_border (w, y, height);
+         if (window_is_rightmost (w))
+           clear_right_border (w, y, height);
+       }
     }
 
   if (cdla)
     }
 
   if (cdla)
@@ -784,16 +902,11 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
     }
   else
     {
     }
   else
     {
-      DEVMETH (d, output_begin, (d));
-
-      /* #### This is a gross kludge.  Cursor handling is such a royal
-         pain in the ass. */
-      if (rb->type == RUNE_DGLYPH &&
-         (EQ (rb->object.dglyph.glyph, Vtruncation_glyph) ||
-          EQ (rb->object.dglyph.glyph, Vcontinuation_glyph)))
-       rb->cursor_type = NO_CURSOR;
-      else
-       rb->cursor_type = CURSOR_OFF;
+      {
+       MAYBE_DEVMETH (d, frame_output_begin, (f));
+       MAYBE_DEVMETH (d, window_output_begin, (w));
+      }
+      rb->cursor_type = CURSOR_OFF;
       dl->cursor_elt = -1;
       output_display_line (w, 0, cla, y, rb->xpos, rb->xpos + rb->width);
     }
       dl->cursor_elt = -1;
       output_display_line (w, 0, cla, y, rb->xpos, rb->xpos + rb->width);
     }
@@ -807,7 +920,10 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
   if (w != XWINDOW (FRAME_SELECTED_WINDOW (device_selected_frame (d))))
     {
       if (!no_output_end)
   if (w != XWINDOW (FRAME_SELECTED_WINDOW (device_selected_frame (d))))
     {
       if (!no_output_end)
-       DEVMETH (d, output_end, (d));
+       {
+         MAYBE_DEVMETH (d, window_output_end, (w));
+         MAYBE_DEVMETH (d, frame_output_end, (f));
+       }
 
       return 1;
     }
 
       return 1;
     }
@@ -826,7 +942,10 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
       output_display_line (w, 0, cla, y, rb->xpos, rb->xpos + rb->width);
 
       if (!no_output_end)
       output_display_line (w, 0, cla, y, rb->xpos, rb->xpos + rb->width);
 
       if (!no_output_end)
-       DEVMETH (d, output_end, (d));
+       {
+         MAYBE_DEVMETH (d, window_output_end, (w));
+         MAYBE_DEVMETH (d, frame_output_end, (f));
+       }
       return 1;
     }
   else
       return 1;
     }
   else
@@ -858,7 +977,7 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
            }
        }
 
            }
        }
 
-      while ((up ? (cur_dl < Dynarr_length (cla)) : (cur_dl >= 0)))
+      while (up ? (cur_dl < Dynarr_length (cla)) : (cur_dl >= 0))
        {
          dl = Dynarr_atp (cla, cur_dl);
          db = get_display_block_from_line (dl, TEXT);
        {
          dl = Dynarr_atp (cla, cur_dl);
          db = get_display_block_from_line (dl, TEXT);
@@ -890,7 +1009,10 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
                               make_int (ADJ_BUFPOS), w->buffer);
 
                  if (!no_output_end)
                               make_int (ADJ_BUFPOS), w->buffer);
 
                  if (!no_output_end)
-                   DEVMETH (d, output_end, (d));
+                   {
+                     MAYBE_DEVMETH (d, window_output_end, (w));
+                     MAYBE_DEVMETH (d, frame_output_end, (f));
+                   }
                  return 1;
                }
 
                  return 1;
                }
 
@@ -903,7 +1025,10 @@ redisplay_move_cursor (struct window *w, Bufpos new_point, int no_output_end)
     }
 
   if (!no_output_end)
     }
 
   if (!no_output_end)
-    DEVMETH (d, output_end, (d));
+    {
+      MAYBE_DEVMETH (d, window_output_end, (w));
+      MAYBE_DEVMETH (d, frame_output_end, (f));
+    }
   return 0;
 }
 #undef ADJ_BUFPOS
   return 0;
 }
 #undef ADJ_BUFPOS
@@ -958,12 +1083,18 @@ redraw_cursor_in_window (struct window *w, int run_end_begin_meths)
                     (f, dl->ypos - 1, rb->xpos));
 
       if (run_end_begin_meths)
                     (f, dl->ypos - 1, rb->xpos));
 
       if (run_end_begin_meths)
-       DEVMETH (d, output_begin, (d));
+       {
+         MAYBE_DEVMETH (d, frame_output_begin, (f));
+         MAYBE_DEVMETH (d, window_output_begin, (w));
+       }
 
       output_display_line (w, 0, dla, y, rb->xpos, rb->xpos + rb->width);
 
       if (run_end_begin_meths)
 
       output_display_line (w, 0, dla, y, rb->xpos, rb->xpos + rb->width);
 
       if (run_end_begin_meths)
-       DEVMETH (d, output_end, (d));
+       {
+         MAYBE_DEVMETH (d, window_output_end, (w));
+         MAYBE_DEVMETH (d, frame_output_end, (f));
+       }
     }
 }
 
     }
 }
 
@@ -988,12 +1119,870 @@ redisplay_redraw_cursor (struct frame *f, int run_end_begin_meths)
   redraw_cursor_in_window (XWINDOW (window), run_end_begin_meths);
 }
 
   redraw_cursor_in_window (XWINDOW (window), run_end_begin_meths);
 }
 
+/****************************************************************************
+ redisplay_output_display_block
+
+ Given a display line, a block number for that start line, output all
+ runes between start and end in the specified display block.
+ ****************************************************************************/
+static void
+redisplay_output_display_block (struct window *w, struct display_line *dl, int block,
+                               int start, int end, int start_pixpos, int cursor_start,
+                               int cursor_width, int cursor_height)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  /* Temporarily disabled until generalization is done. */
+#if 0
+  struct display_block *db = Dynarr_atp (dl->display_blocks, block);
+  rune_dynarr *rba = db->runes;
+  struct rune *rb;
+  int xpos, width;
+  rb = Dynarr_atp (rba, start);
+
+  if (!rb)
+      /* Nothing to do so don't do anything. */
+      return;
+
+  xpos = max (start_pixpos, rb->xpos);
+
+  if (end < 0)
+    end = Dynarr_length (rba);
+
+  rb  = Dynarr_atp (rba, end - 1);
+  width = rb->xpos + rb->width - xpos;
+#endif
+  /* now actually output the block. */
+  DEVMETH (d, output_display_block, (w, dl, block, start,
+                                    end, start_pixpos,
+                                    cursor_start, cursor_width,
+                                    cursor_height));
+}
+
+/****************************************************************************
+ redisplay_unmap_subwindows
+
+ Remove subwindows from the area in the box defined by the given
+ parameters.
+ ****************************************************************************/
+static void
+redisplay_unmap_subwindows (struct frame* f, int x, int y, int width, int height,
+                           Lisp_Object ignored_window)
+{
+  Lisp_Object rest;
+
+  LIST_LOOP (rest, XWEAK_LIST_LIST (FRAME_SUBWINDOW_CACHE (f)))
+    {
+      Lisp_Image_Instance *ii = XIMAGE_INSTANCE (XCAR (rest));
+      if (IMAGE_INSTANCE_SUBWINDOW_DISPLAYEDP (ii)
+         &&
+         IMAGE_INSTANCE_DISPLAY_X (ii)
+         + IMAGE_INSTANCE_DISPLAY_WIDTH (ii) > (unsigned) x
+         &&
+         IMAGE_INSTANCE_DISPLAY_X (ii) < (unsigned) (x + width)
+         &&
+         IMAGE_INSTANCE_DISPLAY_Y (ii)
+         + IMAGE_INSTANCE_DISPLAY_HEIGHT (ii) > (unsigned) y
+         &&
+         IMAGE_INSTANCE_DISPLAY_Y (ii) < (unsigned) (y + height)
+         &&
+         !EQ (XCAR (rest), ignored_window))
+       {
+         unmap_subwindow (XCAR (rest));
+       }
+    }
+}
+
+/****************************************************************************
+ redisplay_unmap_subwindows_maybe
+
+ Potentially subwindows from the area in the box defined by the given
+ parameters.
+ ****************************************************************************/
+void redisplay_unmap_subwindows_maybe (struct frame* f, int x, int y, int width, int height)
+{
+  if (!NILP (XWEAK_LIST_LIST (FRAME_SUBWINDOW_CACHE (f))))
+    {
+      redisplay_unmap_subwindows (f, x, y, width, height, Qnil);
+    }
+}
+
+static void redisplay_unmap_subwindows_except_us (struct frame* f, int x, int y, int width,
+                                                 int height, Lisp_Object subwindow)
+{
+  if (!NILP (XWEAK_LIST_LIST (FRAME_SUBWINDOW_CACHE (f))))
+    {
+      redisplay_unmap_subwindows (f, x, y, width, height, subwindow);
+    }
+}
+
+/****************************************************************************
+ redisplay_output_subwindow
+
+ output a subwindow.  This code borrows heavily from the pixmap stuff,
+ although is much simpler not needing to account for partial
+ pixmaps, backgrounds etc.
+ ****************************************************************************/
+void
+redisplay_output_subwindow (struct window *w,
+                           Lisp_Object image_instance,
+                           struct display_box* db, struct display_glyph_area* dga,
+                           face_index findex, int cursor_start, int cursor_width,
+                           int cursor_height)
+{
+  Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object window;
+  struct display_glyph_area sdga;
+
+  dga->height = IMAGE_INSTANCE_HEIGHT (p);
+  dga->width = IMAGE_INSTANCE_WIDTH (p);
+
+  /* The first thing we are going to do is update the display
+     characteristics of the subwindow. This also clears the dirty
+     flags as a side effect. */
+  redisplay_subwindow (image_instance);
+
+  /* This makes the glyph area fit into the display area. */
+  if (!redisplay_normalize_glyph_area (db, dga))
+    return;
+
+  XSETWINDOW (window, w);
+
+  /* Clear the area the subwindow is going into. */
+  redisplay_clear_clipped_region (window, findex,
+                                 db, dga, 0, image_instance);
+
+  /* This shrinks the display box to exactly enclose the glyph
+     area. */
+  redisplay_normalize_display_box (db, dga);
+
+  /* if we can't view the whole window we can't view any of it. We
+     have to be careful here since we may be being asked to display
+     part of a subwindow, the rest of which is on-screen as well. We
+     need to allow this case and map the entire subwindow. We also
+     need to be careful since the subwindow could be outside the
+     window in the gutter or modeline - we also need to allow these
+     cases.*/
+  sdga.xoffset = -dga->xoffset;
+  sdga.yoffset = -dga->yoffset;
+  sdga.height = IMAGE_INSTANCE_HEIGHT (p);
+  sdga.width = IMAGE_INSTANCE_WIDTH (p);
+
+  if (redisplay_display_boxes_in_window_p (w, db, &sdga) == 0
+      ||
+      /* We only want to do full subwindow display for windows that
+        are completely in the gutter, otherwise we must clip to be
+        safe. */
+      display_boxes_in_gutter_p (XFRAME (w->frame), db, &sdga) <= 0)
+    {
+      map_subwindow (image_instance, db->xpos, db->ypos, dga);
+    }
+  else
+    {
+      sdga.xoffset = sdga.yoffset = 0;
+      map_subwindow (image_instance, db->xpos - dga->xoffset,
+                    db->ypos - dga->yoffset, &sdga);
+    }
+}
+
+/****************************************************************************
+ redisplay_output_layout
+
+ Output a widget hierarchy. This can safely call itself recursively.
+
+ The complexity of outputting layouts is deciding whether to do it or
+ not. Consider a layout enclosing some text, the text changes and is
+ marked as dirty, but the enclosing layout has not been marked as
+ dirty so no updates occur and the text will potentially be truncated.
+ Alternatively we hold a back pointer in the image instance to the
+ parent and mark the parent as dirty. But the layout code assumes that
+ if the layout is dirty then the whole layout should be redisplayed,
+ so we then get lots of flashing even though only the text has changed
+ size. Of course if the text shrinks in size then we do actually need
+ to redisplay the layout to repaint the exposed area. So what happens
+ if we make a non-structural change like changing color? Either we
+ redisplay everything, or we redisplay nothing. These are exactly the
+ issues lwlib has to grapple with. We really need to know what has
+ actually changed and make a layout decision based on that. We also
+ really need to know what has changed so that we can only make the
+ necessary changes in update_subwindow.  This has all now been
+ implemented, Viva la revolution!
+ ****************************************************************************/
+void
+redisplay_output_layout (Lisp_Object domain,
+                        Lisp_Object image_instance,
+                        struct display_box* db, struct display_glyph_area* dga,
+                        face_index findex, int cursor_start, int cursor_width,
+                        int cursor_height)
+{
+  Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object rest, window = DOMAIN_WINDOW (domain);
+  Charc_dynarr *buf = Dynarr_new (Charc);
+  struct window *w = XWINDOW (window);
+  struct device *d = DOMAIN_XDEVICE (domain);
+  int layout_height, layout_width;
+
+  layout_height = glyph_height (image_instance, domain);
+  layout_width = glyph_width (image_instance, domain);
+
+  dga->height = layout_height;
+  dga->width = layout_width;
+#ifdef DEBUG_WIDGET_OUTPUT
+  printf ("outputing layout glyph %p\n", p);
+#endif
+  /* This makes the glyph area fit into the display area. */
+  if (!redisplay_normalize_glyph_area (db, dga))
+    return;
+
+  /* Highly dodgy optimization. We want to only output the whole
+     layout if we really have to. */
+  if (!IMAGE_INSTANCE_OPTIMIZE_OUTPUT (p)
+      || IMAGE_INSTANCE_LAYOUT_CHANGED (p)
+      || IMAGE_INSTANCE_WIDGET_FACE_CHANGED (p)
+      || IMAGE_INSTANCE_SIZE_CHANGED (p)
+      || IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED (p))
+    {
+      /* First clear the area we are drawing into. This is the easiest
+        thing to do since we have many gaps that we have to make sure are
+        filled in. */
+      redisplay_clear_clipped_region (window, findex, db, dga, 1, Qnil);
+
+      /* Output a border if required */
+      if (!NILP (IMAGE_INSTANCE_LAYOUT_BORDER (p)))
+       {
+         int edges = 0;
+         enum edge_style style;
+         int ypos = db->ypos;
+         int xpos = db->xpos;
+         int height = dga->height;
+         int width = dga->width;
+
+         /* The bevel_area routines always draw in from the specified
+            area so there is no need to adjust the displayed area to
+            make sure that the lines are visible. */
+         if (dga->xoffset >= 0) 
+           edges |= EDGE_LEFT;
+         if (dga->width - dga->xoffset == layout_width) 
+           edges |= EDGE_RIGHT;
+         if (dga->yoffset >= 0) 
+           edges |= EDGE_TOP;
+         if (dga->height - dga->yoffset == layout_height)
+           edges |= EDGE_BOTTOM;
+         
+         if (EQ (IMAGE_INSTANCE_LAYOUT_BORDER (p), Qetched_in))
+           style = EDGE_ETCHED_IN;
+         else if (EQ (IMAGE_INSTANCE_LAYOUT_BORDER (p), Qetched_out))
+           style = EDGE_ETCHED_OUT;
+         else if (EQ (IMAGE_INSTANCE_LAYOUT_BORDER (p), Qbevel_in))
+           style = EDGE_BEVEL_IN;
+         else if (INTP (IMAGE_INSTANCE_LAYOUT_BORDER (p)))
+           {
+             style = EDGE_ETCHED_IN;
+             if (edges & EDGE_TOP)
+               {
+                 ypos += XINT (IMAGE_INSTANCE_LAYOUT_BORDER (p));
+                 height -= XINT (IMAGE_INSTANCE_LAYOUT_BORDER (p));
+               }
+           }
+         else
+           style = EDGE_BEVEL_OUT;
+
+         MAYBE_DEVMETH (d, bevel_area,
+                        (w, findex, xpos, ypos, width, height,
+                         DEFAULT_WIDGET_SHADOW_WIDTH, edges, style));
+       }
+    }
+
+  /* This shrinks the display box to exactly enclose the glyph
+     area. */
+  redisplay_normalize_display_box (db, dga);
+
+  /* Flip through the widgets in the layout displaying as necessary */
+  LIST_LOOP (rest, IMAGE_INSTANCE_LAYOUT_CHILDREN (p))
+    {
+      Lisp_Object child = glyph_image_instance (XCAR (rest), image_instance,
+                                               ERROR_ME_NOT, 1);
+
+      struct display_box cdb;
+      /* For losing HP-UX */
+      cdb.xpos = db->xpos;
+      cdb.ypos = db->ypos;
+      cdb.width = db->width;
+      cdb.height = db->height;
+
+      /* First determine if the image is visible at all */
+      if (IMAGE_INSTANCEP (child))
+       {
+         Lisp_Image_Instance* childii = XIMAGE_INSTANCE (child);
+
+         /* The enclosing layout offsets are +ve at this point */
+         struct display_glyph_area cdga;
+         cdga.xoffset  = IMAGE_INSTANCE_XOFFSET (childii) - dga->xoffset;
+         cdga.yoffset = IMAGE_INSTANCE_YOFFSET (childii) - dga->yoffset;
+         cdga.width = glyph_width (child, image_instance);
+         cdga.height = glyph_height (child, image_instance);
+
+         IMAGE_INSTANCE_OPTIMIZE_OUTPUT (childii) =
+           IMAGE_INSTANCE_OPTIMIZE_OUTPUT (p);
+
+         /* Although normalization is done by the output routines
+            we have to do it here so that they don't try and
+            clear all of db. This is true below also. */
+         if (redisplay_normalize_glyph_area (&cdb, &cdga))
+           {
+             redisplay_normalize_display_box (&cdb, &cdga);
+             /* Since the display boxes will now be totally in the
+                window if they are visible at all we can now check this easily. */
+             if (cdb.xpos < db->xpos || cdb.ypos < db->ypos
+                 || cdb.xpos + cdb.width > db->xpos + db->width
+                 || cdb.ypos + cdb.height > db->ypos + db->height)
+               continue;
+             /* We have to invert the offset here as normalization
+                will have made them positive which the output
+                routines will treat as a truly +ve offset. */
+             cdga.xoffset = -cdga.xoffset;
+             cdga.yoffset = -cdga.yoffset;
+
+             switch (IMAGE_INSTANCE_TYPE (childii))
+               {
+               case IMAGE_TEXT:
+                 {
+                   /* #### This is well hacked and could use some
+                      generalisation.*/
+                   if (redisplay_normalize_glyph_area (&cdb, &cdga)
+                       &&
+                       (!IMAGE_INSTANCE_OPTIMIZE_OUTPUT (childii) ||
+                        IMAGE_INSTANCE_DIRTYP (childii)))
+                     {
+                       struct display_line dl; /* this is fake */
+                       Lisp_Object string =
+                         IMAGE_INSTANCE_TEXT_STRING (childii);
+                       Charset_ID charsets[NUM_LEADING_BYTES];
+                       struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, findex);
+
+                       find_charsets_in_bufbyte_string (charsets,
+                                                        XSTRING_DATA (string),
+                                                        XSTRING_LENGTH (string));
+                       ensure_face_cachel_complete (cachel, window, charsets);
+
+                       convert_bufbyte_string_into_charc_dynarr
+                         (XSTRING_DATA (string), XSTRING_LENGTH (string),
+                          buf);
+
+                       redisplay_normalize_display_box (&cdb, &cdga);
+                       /* Offsets are now +ve again so be careful
+                          when fixing up the display line. */
+                       xzero (dl);
+                       /* Munge boxes into display lines. */
+                       dl.ypos = (cdb.ypos - cdga.yoffset)
+                         + glyph_ascent (child, image_instance);
+                       dl.ascent = glyph_ascent (child, image_instance);
+                       dl.descent = glyph_descent (child, image_instance);
+                       dl.top_clip = cdga.yoffset;
+                       dl.clip = (dl.ypos + dl.descent) - (cdb.ypos + cdb.height);
+                       /* output_string doesn't understand offsets in
+                          the same way as other routines - we have to
+                          add the offset to the width so that we
+                          output the full string. */
+                       MAYBE_DEVMETH (d, output_string, (w, &dl, buf, cdb.xpos,
+                                                         cdga.xoffset, cdb.xpos,
+                                                         cdga.width + cdga.xoffset,
+                                                         findex, 0, 0, 0, 0));
+                       Dynarr_reset (buf);
+                     }
+                 }
+                 break;
+
+               case IMAGE_MONO_PIXMAP:
+               case IMAGE_COLOR_PIXMAP:
+                 if (!IMAGE_INSTANCE_OPTIMIZE_OUTPUT (childii)
+                     || IMAGE_INSTANCE_DIRTYP (childii))
+                   redisplay_output_pixmap (w, child, &cdb, &cdga, findex,
+                                            0, 0, 0, 0);
+                 break;
+
+               case IMAGE_WIDGET:
+                 if (EQ (IMAGE_INSTANCE_WIDGET_TYPE (childii), Qlayout))
+                   {
+                     redisplay_output_layout (image_instance, child, &cdb, &cdga, findex,
+                                              0, 0, 0);
+                     break;
+                   }
+               case IMAGE_SUBWINDOW:
+                 if (!IMAGE_INSTANCE_OPTIMIZE_OUTPUT (childii) ||
+                     IMAGE_INSTANCE_DIRTYP (childii))
+                   redisplay_output_subwindow (w, child, &cdb, &cdga, findex,
+                                               0, 0, 0);
+                 break;
+
+               case IMAGE_NOTHING:
+                 /* nothing is as nothing does */
+                 break;
+
+               case IMAGE_POINTER:
+               default:
+                 abort ();
+               }
+           }
+         IMAGE_INSTANCE_OPTIMIZE_OUTPUT (childii) = 0;
+       }
+    }
+
+  /* Update any display properties. I'm not sure whether this actually
+     does anything for layouts except clear the changed flags. */
+  redisplay_subwindow (image_instance);
+
+  Dynarr_free (buf);
+}
+
+/****************************************************************************
+ redisplay_output_pixmap
+
+
+ output a pixmap.
+ ****************************************************************************/
+void
+redisplay_output_pixmap (struct window *w,
+                        Lisp_Object image_instance,
+                        struct display_box* db, struct display_glyph_area* dga,
+                        face_index findex, int cursor_start, int cursor_width,
+                        int cursor_height, int offset_bitmap)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object window;
+  XSETWINDOW (window, w);
+
+  dga->height = IMAGE_INSTANCE_PIXMAP_HEIGHT (p);
+  dga->width = IMAGE_INSTANCE_PIXMAP_WIDTH (p);
+
+#ifdef DEBUG_REDISPLAY
+  printf ("redisplay_output_pixmap(request) \
+[%dx%d@%d+%d] in [%dx%d@%d+%d]\n", 
+         db->width, db->height, db->xpos, db->ypos,
+         dga->width, dga->height, dga->xoffset, dga->yoffset);
+#endif
+
+  /* This makes the glyph area fit into the display area. */
+  if (!redisplay_normalize_glyph_area (db, dga))
+    return;
+
+#ifdef DEBUG_REDISPLAY
+  printf ("redisplay_output_pixmap(normalized) \
+[%dx%d@%d+%d] in [%dx%d@%d+%d]\n",
+         db->width, db->height, db->xpos, db->ypos,
+         dga->width, dga->height, dga->xoffset, dga->yoffset);
+#endif
+
+  /* Clear the area the pixmap is going into.  The pixmap itself will
+     always take care of the full width.  We don't want to clear where
+     it is going to go in order to avoid flicker.  So, all we have to
+     take care of is any area above or below the pixmap. If the pixmap
+     has a mask in which case we have to clear the whole damn thing
+     since we can't yet clear just the area not included in the
+     mask. */
+  if (!offset_bitmap)
+    {
+      redisplay_clear_clipped_region (window, findex,
+                                     db, dga,
+                                     (IMAGE_INSTANCE_PIXMAP_MASK (p) != 0),
+                                     Qnil);
+
+      /* This shrinks the display box to exactly enclose the glyph
+        area. */
+      redisplay_normalize_display_box (db, dga);
+    }
+  assert (db->xpos >= 0 && db->ypos >= 0);
+
+  MAYBE_DEVMETH (d, output_pixmap, (w, image_instance,
+                                   db, dga,
+                                   findex, cursor_start,
+                                   cursor_width, cursor_height,
+                                   offset_bitmap));
+}
+
+/****************************************************************************
+ redisplay_clear_region
+
+ Clear the area in the box defined by the given parameters using the
+ given face. This has been generalised so that subwindows can be
+ coped with effectively.
+ ****************************************************************************/
+void
+redisplay_clear_region (Lisp_Object locale, face_index findex, int x, int y,
+                       int width, int height)
+{
+  struct window *w = NULL;
+  struct frame *f = NULL;
+  struct device *d;
+  Lisp_Object background_pixmap = Qunbound;
+  Lisp_Object fcolor = Qnil, bcolor = Qnil;
+
+  if (!width || !height)
+     return;
+
+  if (WINDOWP (locale))
+    {
+      w = XWINDOW (locale);
+      f = XFRAME (w->frame);
+    }
+  else if (FRAMEP (locale))
+    {
+      w = NULL;
+      f = XFRAME (locale);
+    }
+  else
+    abort ();
+
+  d = XDEVICE (f->device);
+
+  /* if we have subwindows in the region we have to unmap them */
+  redisplay_unmap_subwindows_maybe (f, x, y, width, height);
+
+  /* #### This isn't quite right for when this function is called
+     from the toolbar code. */
+
+  /* Don't use a backing pixmap in the border area */
+  if (x >= FRAME_LEFT_BORDER_END (f)
+      && x < FRAME_RIGHT_BORDER_START (f)
+      && y >= FRAME_TOP_BORDER_END (f)
+      && y < FRAME_BOTTOM_BORDER_START (f))
+    {
+      Lisp_Object temp;
+
+      if (w)
+       {
+         temp = WINDOW_FACE_CACHEL_BACKGROUND_PIXMAP (w, findex);
+
+         if (IMAGE_INSTANCEP (temp)
+             && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (temp)))
+           {
+             /* #### maybe we could implement such that a string
+                can be a background pixmap? */
+             background_pixmap = temp;
+           }
+       }
+      else
+       {
+         temp = FACE_BACKGROUND_PIXMAP (Vdefault_face, locale);
+
+         if (IMAGE_INSTANCEP (temp)
+             && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (temp)))
+           {
+             background_pixmap = temp;
+           }
+       }
+    }
+
+  if (!UNBOUNDP (background_pixmap) &&
+      XIMAGE_INSTANCE_PIXMAP_DEPTH (background_pixmap) == 0)
+    {
+      if (w)
+       {
+         fcolor = WINDOW_FACE_CACHEL_FOREGROUND (w, findex);
+         bcolor = WINDOW_FACE_CACHEL_BACKGROUND (w, findex);
+       }
+      else
+       {
+         fcolor = FACE_FOREGROUND (Vdefault_face, locale);
+         bcolor = FACE_BACKGROUND (Vdefault_face, locale);
+       }
+    }
+  else
+    {
+      fcolor = (w ?
+               WINDOW_FACE_CACHEL_BACKGROUND (w, findex) :
+               FACE_BACKGROUND (Vdefault_face, locale));
+
+    }
+
+  if (UNBOUNDP (background_pixmap))
+    background_pixmap = Qnil;
+
+  DEVMETH (d, clear_region,
+          (locale, d, f, findex, x, y, width, height, fcolor, bcolor, background_pixmap));
+}
+
+/****************************************************************************
+ redisplay_clear_clipped_region
+
+ Clear the area in the dest display_box not covered by the src
+ display_glyph_area using the given face. This is a common occurrence
+ for images shorter than the display line. Clipping can be played
+ around with by altering these. glyphsrc should be normalized.
+ ****************************************************************************/
+static void
+redisplay_clear_clipped_region (Lisp_Object window, face_index findex,
+       struct display_box* dest, struct display_glyph_area* glyphsrc,
+       int fullheight_p, Lisp_Object ignored_subwindow)
+{
+  /* assume dest->xpos >= 0 */
+  int clear_x;
+  struct frame* f = XFRAME (XWINDOW (window)->frame);
+
+  if (glyphsrc->xoffset > 0)
+    {
+      clear_x = dest->xpos + glyphsrc->xoffset;
+    }
+  else
+    {
+      clear_x = dest->xpos;
+    }
+
+  /* If we need the whole height cleared then just do it. */
+  if (fullheight_p)
+    {
+      redisplay_clear_region (window, findex, clear_x, dest->ypos,
+                             glyphsrc->width, dest->height);
+    }
+  else
+    {
+      int yoffset = (glyphsrc->yoffset > 0 ? glyphsrc->yoffset : 0);
+
+      /* We need to make sure that subwindows are unmapped from the
+         whole area. */
+      redisplay_unmap_subwindows_except_us (f, clear_x, dest->ypos,
+                                           glyphsrc->width, dest->height,
+                                           ignored_subwindow);
+      /* first the top box */
+      if (yoffset > 0)
+       {
+         redisplay_clear_region (window, findex, clear_x, dest->ypos,
+                                 glyphsrc->width, yoffset);
+
+       }
+      /* Then the bottom box */
+      if (yoffset + glyphsrc->height < dest->height)
+       {
+         redisplay_clear_region (window, findex, clear_x,
+                                 dest->ypos + yoffset + glyphsrc->height,
+                                 glyphsrc->width,
+                                 dest->height - (yoffset + glyphsrc->height));
+
+       }
+    }
+}
+
+/*****************************************************************************
+ redisplay_normalize_glyph_area
+ redisplay_normalize_display_box
+
+ Calculate the visible box for displaying glyphsrc in dest.
+
+ display_box and display_glyph_area are used to represent an area to
+ displayed and where to display it. Using these two structures all
+ combinations of clipping and position can be accommodated.
+
+ dest - display_box
+
+       xpos - absolute horizontal position of area.
+
+       ypos - absolute vertical position of area.
+
+  glyphsrc - display_glyph_area
+
+       xoffset - horizontal offset of the glyph, +ve means display
+       the glyph with the x position offset by xoffset, -ve means
+       display starting xoffset into the glyph.
+
+       yoffset - vertical offset of the glyph, +ve means display the
+       glyph with y position offset by yoffset, -ve means display
+       starting xoffset into the glyph.
+
+ ****************************************************************************/
+int
+redisplay_normalize_glyph_area (struct display_box* dest,
+                               struct display_glyph_area* glyphsrc)
+{
+  if (dest->xpos + glyphsrc->xoffset > dest->xpos + dest->width
+      ||
+      dest->ypos + glyphsrc->yoffset > dest->ypos + dest->height
+      ||
+      -glyphsrc->xoffset >= glyphsrc->width
+      ||
+      -glyphsrc->yoffset >= glyphsrc->height
+      ||
+      /* #### Not sure why this wasn't coped with before but normalizing
+        to zero width or height is definitely wrong. */
+      (dest->xpos + glyphsrc->xoffset + glyphsrc->width > dest->xpos + dest->width
+       &&
+       dest->width - glyphsrc->xoffset <= 0)
+      ||
+      (dest->ypos + glyphsrc->yoffset + glyphsrc->height > dest->ypos + dest->height
+       &&
+       dest->height - glyphsrc->yoffset <= 0))
+    {
+      /* It's all clipped out */
+      return 0;
+    }
+
+  /* Horizontal offsets. This works because xoffset can be -ve as well
+     as +ve.  When we enter this function the glyphsrc width and
+     height are set to the actual glyph width and height irrespective
+     of how much can be displayed. We are trying to clip both the
+     offset into the image and the rightmost bounding box. Its
+     possible for the glyph width to be much larger than the area we
+     are displaying into (e.g. a large glyph in a small frame). */
+  if (dest->xpos + glyphsrc->xoffset + glyphsrc->width > dest->xpos + dest->width)
+    {
+      /* glyphsrc offset is +ve we are trying to display offset from the
+        origin (the bounding box contains some space and then the
+        glyph). At most the width we want to display is dest->width -
+        glyphsrc->xoffset. */
+      if (glyphsrc->xoffset > 0)
+       glyphsrc->width = dest->width - glyphsrc->xoffset;
+      /* glyphsrc offset is -ve we are trying to display hard up
+        against the dest corner inset into the glyphsrc by
+        xoffset.*/
+      else if (glyphsrc->xoffset < 0) 
+       {
+         glyphsrc->width += glyphsrc->xoffset;
+         glyphsrc->width = min (glyphsrc->width, dest->width);
+       }
+      else
+       glyphsrc->width = dest->width;
+    }
+
+  else if (glyphsrc->xoffset < 0) 
+    glyphsrc->width += glyphsrc->xoffset;
+
+  /* Vertical offsets. This works because yoffset can be -ve as well as +ve */
+  if (dest->ypos + glyphsrc->yoffset + glyphsrc->height > dest->ypos + dest->height)
+    {
+      if ((glyphsrc->yoffset > 0) && (dest->height > glyphsrc->yoffset))
+       glyphsrc->height = dest->height - glyphsrc->yoffset;
+      else if (glyphsrc->yoffset < 0) 
+       {
+         glyphsrc->height += glyphsrc->yoffset;
+         glyphsrc->height = min (glyphsrc->height, dest->height);
+       }
+      else
+       glyphsrc->height = dest->height;
+    }
+
+  else if (glyphsrc->yoffset < 0)
+    glyphsrc->height += glyphsrc->yoffset;
+
+  return 1;
+}
+
+static void
+redisplay_normalize_display_box (struct display_box* dest,
+                                struct display_glyph_area* glyphsrc)
+{
+  /* Adjust the destination area. At the end of this the destination
+   area will exactly enclose the glyph area. The only remaining
+   adjustment will be offsets into the glyph area. */
+
+  /* Horizontal adjustment. */
+  if (glyphsrc->xoffset > 0)
+    {
+      dest->xpos += glyphsrc->xoffset;
+      dest->width -= glyphsrc->xoffset;
+      glyphsrc->xoffset = 0;
+    }
+  else
+    glyphsrc->xoffset = -glyphsrc->xoffset;
+
+  if (glyphsrc->width < dest->width)
+    dest->width = glyphsrc->width;
+
+  /* Vertical adjustment. */
+  if (glyphsrc->yoffset > 0)
+    {
+      dest->ypos += glyphsrc->yoffset;
+      dest->height -= glyphsrc->yoffset;
+      glyphsrc->yoffset = 0;
+    }
+  else
+    glyphsrc->yoffset = -glyphsrc->yoffset;
+
+  if (glyphsrc->height < dest->height)
+    dest->height = glyphsrc->height;
+}
+
+/*****************************************************************************
+ redisplay_display_boxes_in_window_p
+
+ Determine whether the required display_glyph_area is completely inside
+ the window. -1 means the display_box is not in the window. 1 means the
+ display_box and the display_glyph_area are in the window. 0 means
+ the display_box is in the window but the display_glyph_area is not.
+ ****************************************************************************/
+static int
+redisplay_display_boxes_in_window_p (struct window* w,
+                                    struct display_box* db,
+                                    struct display_glyph_area* dga)
+{
+  int left = WINDOW_TEXT_LEFT (w);
+  int right = WINDOW_TEXT_RIGHT (w);
+  int top = WINDOW_TEXT_TOP (w);
+  int bottom = WINDOW_TEXT_BOTTOM (w);
+
+  if (db->xpos < left || db->ypos < top
+      || db->xpos + db->width > right
+      || db->ypos + db->height > bottom)
+      /* We are not displaying in a window at all */
+      return -1;
+
+  if (db->xpos + dga->xoffset >= left
+      &&
+      db->ypos + dga->yoffset >= top
+      &&
+      db->xpos + dga->xoffset + dga->width <= right
+      &&
+      db->ypos + dga->yoffset + dga->height <= bottom)
+    return 1;
+
+  return 0;
+}
+
+/*****************************************************************************
+ redisplay_calculate_display_boxes
+
+ Convert from rune/display_line co-ordinates to display_box
+ co-ordinates.
+ ****************************************************************************/
+int
+redisplay_calculate_display_boxes (struct display_line *dl, int xpos,
+                                  int xoffset, int yoffset, int start_pixpos,
+                                   int width, struct display_box* dest,
+                                  struct display_glyph_area* src)
+{
+  dest->xpos = xpos;
+  dest->ypos = DISPLAY_LINE_YPOS (dl);
+  dest->width = width;
+  dest->height = DISPLAY_LINE_HEIGHT (dl);
+
+  src->xoffset = -xoffset;
+  src->width = 0;
+  src->height = 0;
+
+  src->yoffset = -dl->top_clip + yoffset;
+
+  if (start_pixpos >=0 && start_pixpos > xpos)
+    {
+      /* Oops, we're asking for a start outside of the displayable
+         area. */
+      if (start_pixpos > xpos + width)
+       return 0;
+      dest->xpos = start_pixpos;
+      dest->width -= (start_pixpos - xpos);
+      /* Offsets are -ve when we want to clip pixels off the displayed
+         glyph. */
+      src->xoffset -= (start_pixpos - xpos);
+    }
+
+  return 1;
+}
+
 /*****************************************************************************
  redisplay_clear_top_of_window
 
  If window is topmost, clear the internal border above it.
  ****************************************************************************/
 /*****************************************************************************
  redisplay_clear_top_of_window
 
  If window is topmost, clear the internal border above it.
  ****************************************************************************/
-static void
+void
 redisplay_clear_top_of_window (struct window *w)
 {
   Lisp_Object window;
 redisplay_clear_top_of_window (struct window *w)
 {
   Lisp_Object window;
@@ -1002,7 +1991,6 @@ redisplay_clear_top_of_window (struct window *w)
   if (!NILP (Fwindow_highest_p (window)))
     {
       struct frame *f = XFRAME (w->frame);
   if (!NILP (Fwindow_highest_p (window)))
     {
       struct frame *f = XFRAME (w->frame);
-      struct device *d = XDEVICE (f->device);
       int x, y, width, height;
 
       x = w->pixel_left;
       int x, y, width, height;
 
       x = w->pixel_left;
@@ -1019,7 +2007,64 @@ redisplay_clear_top_of_window (struct window *w)
       y = FRAME_TOP_BORDER_START (f) - 1;
       height = FRAME_BORDER_HEIGHT (f) + 1;
 
       y = FRAME_TOP_BORDER_START (f) - 1;
       height = FRAME_BORDER_HEIGHT (f) + 1;
 
-      DEVMETH (d, clear_region, (window, DEFAULT_INDEX, x, y, width, height));
+      redisplay_clear_region (window, DEFAULT_INDEX, x, y, width, height);
+    }
+}
+
+/*****************************************************************************
+ redisplay_clear_to_window_end
+
+ Clear the area between ypos1 and ypos2.  Each margin area and the
+ text area is handled separately since they may each have their own
+ background color.
+ ****************************************************************************/
+void
+redisplay_clear_to_window_end (struct window *w, int ypos1, int ypos2)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+
+  if (HAS_DEVMETH_P (d, clear_to_window_end))
+    DEVMETH (d, clear_to_window_end, (w, ypos1, ypos2));
+  else
+    {
+      int height = ypos2 - ypos1;
+
+      if (height)
+       {
+         Lisp_Object window;
+         int bflag = 0 ; /* (window_needs_vertical_divider (w) ? 0 : 1);*/
+         layout_bounds bounds;
+
+         bounds = calculate_display_line_boundaries (w, bflag);
+         XSETWINDOW (window, w);
+
+         if (window_is_leftmost (w))
+           redisplay_clear_region (window, DEFAULT_INDEX, FRAME_LEFT_BORDER_START (f),
+                                   ypos1, FRAME_BORDER_WIDTH (f), height);
+
+         if (bounds.left_in - bounds.left_out > 0)
+           redisplay_clear_region (window,
+                                   get_builtin_face_cache_index (w, Vleft_margin_face),
+                                   bounds.left_out, ypos1,
+                                   bounds.left_in - bounds.left_out, height);
+
+         if (bounds.right_in - bounds.left_in > 0)
+           redisplay_clear_region (window,
+                                   DEFAULT_INDEX,
+                                   bounds.left_in, ypos1,
+                                   bounds.right_in - bounds.left_in, height);
+
+         if (bounds.right_out - bounds.right_in > 0)
+           redisplay_clear_region (window,
+                                   get_builtin_face_cache_index (w, Vright_margin_face),
+                                   bounds.right_in, ypos1,
+                                   bounds.right_out - bounds.right_in, height);
+
+         if (window_is_rightmost (w))
+           redisplay_clear_region (window, DEFAULT_INDEX, FRAME_RIGHT_BORDER_START (f),
+                                   ypos1, FRAME_BORDER_WIDTH (f), height);
+       }
     }
 }
 
     }
 }
 
@@ -1035,7 +2080,6 @@ redisplay_clear_bottom_of_window (struct window *w, display_line_dynarr *ddla,
                                  int min_start, int max_end)
 {
   struct frame *f = XFRAME (w->frame);
                                  int min_start, int max_end)
 {
   struct frame *f = XFRAME (w->frame);
-  struct device *d = XDEVICE (f->device);
   int ypos1, ypos2;
   int ddla_len = Dynarr_length (ddla);
 
   int ypos1, ypos2;
   int ddla_len = Dynarr_length (ddla);
 
@@ -1079,7 +2123,7 @@ redisplay_clear_bottom_of_window (struct window *w, display_line_dynarr *ddla,
   if (ypos2 <= ypos1)
     return;
 
   if (ypos2 <= ypos1)
     return;
 
-  DEVMETH (d, clear_to_window_end, (w, ypos1, ypos2));
+  redisplay_clear_to_window_end (w, ypos1, ypos2);
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -1099,7 +2143,7 @@ redisplay_update_line (struct window *w, int first_line, int last_line,
   display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP);
   display_line_dynarr *ddla = window_display_lines (w, DESIRED_DISP);
 
   display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP);
   display_line_dynarr *ddla = window_display_lines (w, DESIRED_DISP);
 
-  DEVMETH (d, output_begin, (d));
+  MAYBE_DEVMETH (d, window_output_begin, (w));
 
   while (first_line <= last_line)
     {
 
   while (first_line <= last_line)
     {
@@ -1175,6 +2219,7 @@ redisplay_update_line (struct window *w, int first_line, int last_line,
      larger impact on their sizing. */
   /* #### See if we can get away with only calling this if
      max_line_len is greater than the window_char_width. */
      larger impact on their sizing. */
   /* #### See if we can get away with only calling this if
      max_line_len is greater than the window_char_width. */
+  /* #### BILL!!! Should we do this for GTK as well? */
 #if defined(HAVE_SCROLLBARS) && defined(HAVE_X_WINDOWS)
   {
     extern int stupid_vertical_scrollbar_drag_hack;
 #if defined(HAVE_SCROLLBARS) && defined(HAVE_X_WINDOWS)
   {
     extern int stupid_vertical_scrollbar_drag_hack;
@@ -1184,14 +2229,8 @@ redisplay_update_line (struct window *w, int first_line, int last_line,
   }
 #endif
 
   }
 #endif
 
-  /* This has to be done after we've updated the values.  We don't
-     call output_end for tty frames.  Redisplay will do this after all
-     tty windows have been updated.  This cuts down on cursor
-     flicker. */
-  if (FRAME_TTY_P (f))
-    redisplay_redraw_cursor (f, 0);
-  else
-    DEVMETH (d, output_end, (d));
+  redisplay_redraw_cursor (f, 0);
+  MAYBE_DEVMETH (d, window_output_end, (w));
 }
 
 /*****************************************************************************
 }
 
 /*****************************************************************************
@@ -1305,11 +2344,11 @@ redisplay_output_window (struct window *w)
     }
 
   /* Perform any output initialization. */
     }
 
   /* Perform any output initialization. */
-  DEVMETH (d, output_begin, (d));
+  MAYBE_DEVMETH (d, window_output_begin, (w));
 
   /* If the window's structure has changed clear the internal border
      above it if it is topmost (the function will check). */
 
   /* If the window's structure has changed clear the internal border
      above it if it is topmost (the function will check). */
-  if (f->windows_structure_changed)
+  if (f->windows_structure_changed || f->faces_changed)
     redisplay_clear_top_of_window (w);
 
   /* Output each line. */
     redisplay_clear_top_of_window (w);
 
   /* Output each line. */
@@ -1328,7 +2367,7 @@ redisplay_output_window (struct window *w)
   if (window_needs_vertical_divider (w)
       && (f->windows_structure_changed || f->clear))
     {
   if (window_needs_vertical_divider (w)
       && (f->windows_structure_changed || f->clear))
     {
-      DEVMETH (d, output_vertical_divider, (w, f->windows_structure_changed));
+      MAYBE_DEVMETH (d, output_vertical_divider, (w, f->windows_structure_changed));
     }
 
   /* Clear the rest of the window, if necessary. */
     }
 
   /* Clear the rest of the window, if necessary. */
@@ -1362,15 +2401,43 @@ redisplay_output_window (struct window *w)
      get invalidated when it should be. */
   INVALIDATE_DEVICE_PIXEL_TO_GLYPH_CACHE (d);
 
      get invalidated when it should be. */
   INVALIDATE_DEVICE_PIXEL_TO_GLYPH_CACHE (d);
 
-  /* We don't call output_end for tty frames.  Redisplay will do this
-     after all tty windows have been updated.  This cuts down on
-     cursor flicker. */
-  if (FRAME_TTY_P (f))
-    redisplay_redraw_cursor (f, 0);
-  else
-    DEVMETH (d, output_end, (d));
+  redisplay_redraw_cursor (f, 0);
+  MAYBE_DEVMETH (d, window_output_end, (w));
 
 #ifdef HAVE_SCROLLBARS
   update_window_scrollbars (w, NULL, !MINI_WINDOW_P (w), 0);
 #endif
 }
 
 #ifdef HAVE_SCROLLBARS
   update_window_scrollbars (w, NULL, !MINI_WINDOW_P (w), 0);
 #endif
 }
+
+/*****************************************************************************
+ bevel_modeline
+
+ Draw a 3d border around the modeline on window W.
+ ****************************************************************************/
+void
+bevel_modeline (struct window *w, struct display_line *dl)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  int x, y, width, height;
+  int shadow_thickness = MODELINE_SHADOW_THICKNESS (w);
+  enum edge_style style;
+
+  x = WINDOW_MODELINE_LEFT (w);
+  width = WINDOW_MODELINE_RIGHT (w) - x;
+  y = dl->ypos - dl->ascent - shadow_thickness;
+  height = dl->ascent + dl->descent + 2 * shadow_thickness;
+
+  if (XINT (w->modeline_shadow_thickness) < 0)
+    {
+      style = EDGE_BEVEL_IN;
+    }
+  else
+    {
+      style = EDGE_BEVEL_OUT;
+    }
+
+  MAYBE_DEVMETH (d, bevel_area,
+                (w, MODELINE_INDEX, x, y, width, height, shadow_thickness,
+                 EDGE_ALL, style));
+}