diff src/redisplay-msw.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redisplay-msw.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,1392 @@
+/* mswindows output and frame manipulation routines.
+   Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
+   Copyright (C) 1994 Lucid, Inc.
+   Copyright (C) 1995 Sun Microsystems, Inc.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with:  Not in FSF. */
+
+/* Authorship:
+
+   Chuck Thompson
+   Lots of work done by Ben Wing for Mule
+   Partially rewritten for mswindows by Jonathan Harris, November 1997 for 21.0.
+ */
+
+#include <config.h>
+#include "lisp.h"
+
+#include "console-msw.h"
+#include "objects-msw.h"
+
+#include "buffer.h"
+#include "debug.h"
+#include "events.h"
+#include "faces.h"
+#include "frame.h"
+#include "glyphs-msw.h"
+#include "gutter.h"
+#include "redisplay.h"
+#include "sysdep.h"
+#include "window.h"
+
+#include "windows.h"
+#ifdef MULE
+#include "mule-ccl.h"
+#include "mule-charset.h"
+#endif
+
+#define MSWINDOWS_EOL_CURSOR_WIDTH	5
+
+/*
+ * Random forward declarations
+ */
+static void mswindows_update_dc (HDC hdc, Lisp_Object font, Lisp_Object fg,
+				 Lisp_Object bg, Lisp_Object bg_pmap);
+static void mswindows_output_vertical_divider (struct window *w, int clear);
+static void mswindows_redraw_exposed_windows (Lisp_Object window, int x,
+					int y, int width, int height);
+static void mswindows_output_dibitmap (struct frame *f, 
+				       struct Lisp_Image_Instance *p,
+				       struct display_box* db,
+				       struct display_glyph_area* dga);
+
+typedef struct textual_run
+{
+  Lisp_Object charset;
+  unsigned char *ptr;
+  int len;
+  int dimension;
+} textual_run;
+
+/* Separate out the text in DYN into a series of textual runs of a
+   particular charset.  Also convert the characters as necessary into
+   the format needed by XDrawImageString(), XDrawImageString16(), et
+   al.  (This means converting to one or two byte format, possibly
+   tweaking the high bits, and possibly running a CCL program.) You
+   must pre-allocate the space used and pass it in. (This is done so
+   you can alloca() the space.)  You need to allocate (2 * len) bytes
+   of TEXT_STORAGE and (len * sizeof (textual_run)) bytes of
+   RUN_STORAGE, where LEN is the length of the dynarr.
+
+   Returns the number of runs actually used. */
+
+static int
+separate_textual_runs (unsigned char *text_storage,
+		       textual_run *run_storage,
+		       CONST Emchar *str, Charcount len)
+{
+  Lisp_Object prev_charset = Qunbound; /* not Qnil because that is a
+					  possible valid charset when
+					  MULE is not defined */
+  int runs_so_far = 0;
+  int i;
+#ifdef MULE
+  struct ccl_program char_converter;
+  int need_ccl_conversion = 0;
+#endif
+
+  for (i = 0; i < len; i++)
+    {
+      Emchar ch = str[i];
+      Lisp_Object charset;
+      int byte1, byte2;
+      int dimension;
+      int graphic;
+
+      BREAKUP_CHAR (ch, charset, byte1, byte2);
+      dimension = XCHARSET_DIMENSION (charset);
+      graphic   = XCHARSET_GRAPHIC   (charset);
+
+      if (!EQ (charset, prev_charset))
+	{
+	  run_storage[runs_so_far].ptr       = text_storage;
+	  run_storage[runs_so_far].charset   = charset;
+	  run_storage[runs_so_far].dimension = dimension;
+
+	  if (runs_so_far)
+	    {
+	      run_storage[runs_so_far - 1].len =
+		text_storage - run_storage[runs_so_far - 1].ptr;
+	      if (run_storage[runs_so_far - 1].dimension == 2)
+		run_storage[runs_so_far - 1].len >>= 1;
+	    }
+	  runs_so_far++;
+	  prev_charset = charset;
+#ifdef MULE
+	  {
+	    Lisp_Object ccl_prog = XCHARSET_CCL_PROGRAM (charset);
+	    need_ccl_conversion = !NILP (ccl_prog);
+	    if (need_ccl_conversion)
+	      setup_ccl_program (&char_converter, ccl_prog);
+	  }
+#endif
+	}
+
+      if (graphic == 0)
+	{
+	  byte1 &= 0x7F;
+	  byte2 &= 0x7F;
+	}
+      else if (graphic == 1)
+	{
+	  byte1 |= 0x80;
+	  byte2 |= 0x80;
+	}
+#ifdef MULE
+      if (need_ccl_conversion)
+	{
+	  char_converter.reg[0] = XCHARSET_ID (charset);
+	  char_converter.reg[1] = byte1;
+	  char_converter.reg[2] = byte2;
+	  char_converter.ic = 0; /* start at beginning each time */
+	  ccl_driver (&char_converter, 0, 0, 0, 0, CCL_MODE_ENCODING);
+	  byte1 = char_converter.reg[1];
+	  byte2 = char_converter.reg[2];
+	}
+#endif
+      *text_storage++ = (unsigned char) byte1;
+      if (dimension == 2)
+	*text_storage++ = (unsigned char) byte2;
+    }
+
+  if (runs_so_far)
+    {
+      run_storage[runs_so_far - 1].len =
+	text_storage - run_storage[runs_so_far - 1].ptr;
+      if (run_storage[runs_so_far - 1].dimension == 2)
+	run_storage[runs_so_far - 1].len >>= 1;
+    }
+
+  return runs_so_far;
+}
+
+
+static int
+mswindows_text_width_single_run (HDC hdc, struct face_cachel *cachel,
+				 textual_run *run)
+{
+  Lisp_Object font_inst = FACE_CACHEL_FONT (cachel, run->charset);
+  struct Lisp_Font_Instance *fi = XFONT_INSTANCE (font_inst);
+  SIZE size;
+
+  if (!fi->proportional_p || !hdc)
+    return (fi->width * run->len);
+  else
+    {
+      assert(run->dimension == 1);	/* #### FIXME! */
+      mswindows_update_dc (hdc, font_inst, Qnil, Qnil, Qnil);
+      GetTextExtentPoint32 (hdc, run->ptr, run->len, &size);
+      return(size.cx);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_update_dc
+
+ Given a number of parameters munge the DC so it has those properties.
+ ****************************************************************************/
+static void
+mswindows_update_dc (HDC hdc, Lisp_Object font, Lisp_Object fg,
+		     Lisp_Object bg, Lisp_Object bg_pmap)
+{
+  if (!NILP (font))
+    SelectObject(hdc, FONT_INSTANCE_MSWINDOWS_HFONT (XFONT_INSTANCE (font)));
+
+
+  if (!NILP (fg))
+    {
+      SetTextColor (hdc, COLOR_INSTANCE_MSWINDOWS_COLOR 
+		    (XCOLOR_INSTANCE (fg)));
+    }
+  if (!NILP (bg))
+    { 
+      SetBkMode (hdc, OPAQUE);
+      SetBkColor (hdc, COLOR_INSTANCE_MSWINDOWS_COLOR (XCOLOR_INSTANCE (bg)));
+    }
+  else 
+    {
+      SetBkMode (hdc, TRANSPARENT);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_apply_face_effects
+
+ Draw underline and strikeout as if this was X.
+ #### On mswindows this really should be done as part of drawing the font.
+ The line width used is chosen arbitrarily from the font height.
+ ****************************************************************************/
+static void
+mswindows_apply_face_effects (HDC hdc, struct display_line *dl, int xpos,
+			      int width, struct Lisp_Font_Instance *fi,
+			      struct face_cachel *cachel,
+			      struct face_cachel *color_cachel)
+{
+  int yclip;
+  HBRUSH brush, oldbrush;
+  RECT rect;
+
+  brush = CreateSolidBrush (COLOR_INSTANCE_MSWINDOWS_COLOR (
+			    XCOLOR_INSTANCE (color_cachel->foreground)));
+  if (brush)
+    {
+      yclip = dl->ypos + dl->descent - dl->clip;
+      rect.left = xpos;
+      rect.right = xpos + width;
+      oldbrush = SelectObject (hdc, brush);
+
+      if (cachel->underline)
+	{
+	  rect.top = dl->ypos + dl->descent/2;
+	  rect.bottom = rect.top + (fi->height >= 0x20 ? 2 : 1);
+	  if (rect.bottom <= yclip)
+	    FillRect (hdc, &rect, brush);
+	}
+      if (cachel->strikethru)
+	{
+	  rect.top = dl->ypos + dl->descent - (dl->ascent + dl->descent)/2;
+	  rect.bottom = rect.top + (fi->height >= 0x20 ? 2 : 1);
+	  if (rect.bottom <= yclip)
+	    FillRect (hdc, &rect, brush);
+	}
+
+      SelectObject (hdc, oldbrush);
+      DeleteObject (brush);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_output_hline
+
+ Output a horizontal line in the foreground of its face.
+ ****************************************************************************/
+static void
+mswindows_output_hline (struct window *w, struct display_line *dl, struct rune *rb)
+{ /* XXX Implement me */
+}
+
+
+/*****************************************************************************
+ mswindows_output_blank
+
+ Output a blank by clearing the area it covers in the background color
+ of its face.
+ ****************************************************************************/
+static void
+mswindows_output_blank (struct window *w, struct display_line *dl, 
+			struct rune *rb, int start_pixpos)
+{
+  struct frame *f = XFRAME (w->frame);
+  RECT rect = { rb->xpos, DISPLAY_LINE_YPOS (dl),
+		rb->xpos+rb->width, 
+		DISPLAY_LINE_YEND (dl) };
+  struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, rb->findex);
+
+  Lisp_Object bg_pmap = WINDOW_FACE_CACHEL_BACKGROUND_PIXMAP (w, rb->findex);
+
+  /* Unmap all subwindows in the area we are going to blank. */
+  redisplay_unmap_subwindows_maybe (f, rb->xpos, DISPLAY_LINE_YPOS (dl),
+				    rb->width, DISPLAY_LINE_HEIGHT (dl));
+
+  if (!IMAGE_INSTANCEP (bg_pmap)
+      || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap)))
+    bg_pmap = Qnil;
+
+  if (!NILP(bg_pmap))
+    {
+      struct display_box db;
+      struct display_glyph_area dga;
+      redisplay_calculate_display_boxes (dl, rb->xpos, 
+					 /*rb->object.dglyph.xoffset*/ 0,
+					 start_pixpos, rb->width,
+					 &db, &dga);
+      /* blank the background in the appropriate color */
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, cachel->foreground,
+			   cachel->background, Qnil);
+      redisplay_output_pixmap (w, bg_pmap, &db, &dga, rb->findex,
+			       0, 0, 0, TRUE);
+    }
+  else 
+    {
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil,
+			   cachel->background, Qnil);
+      
+      ExtTextOut (FRAME_MSWINDOWS_DC (f), 0, 0, ETO_OPAQUE, 
+		  &rect, NULL, 0, NULL);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_output_cursor
+
+ Draw a normal or end-of-line cursor. The end-of-line cursor is
+ narrower than the normal cursor.
+ ****************************************************************************/
+static void
+mswindows_output_cursor (struct window *w, struct display_line *dl, int xpos,
+			 int width, face_index findex, Emchar ch, int image_p)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  struct face_cachel *cachel=0;
+  Lisp_Object font = Qnil;
+  int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d));
+  HDC hdc = FRAME_MSWINDOWS_DC (f);
+  unsigned int face_index=0;
+  char *p_char = NULL;
+  int n_char = 0;
+  RECT rect = { xpos,
+		DISPLAY_LINE_YPOS (dl),
+		xpos + width,
+		DISPLAY_LINE_YEND (dl) };
+  Lisp_Object bar = symbol_value_in_buffer (Qbar_cursor,
+					    WINDOW_BUFFER (w));
+  int bar_p = image_p || !NILP (bar);
+  int cursor_p = !NILP (w->text_cursor_visible_p);
+  int real_char_p = ch != 0;
+
+  /* Unmap all subwindows in the area we are going to blank. */
+  redisplay_unmap_subwindows_maybe (f, xpos, DISPLAY_LINE_YPOS (dl),
+				    width, DISPLAY_LINE_HEIGHT (dl));
+
+  if (real_char_p)
+    {
+      /* Use the font from the underlying character */
+      cachel = WINDOW_FACE_CACHEL (w, findex);
+
+      /* #### MULE: Need to know the charset! */
+      font = FACE_CACHEL_FONT (cachel, Vcharset_ascii);
+    }
+
+  if ((focus || bar_p) && real_char_p)
+    {
+      p_char = (char*) &ch;
+      n_char = 1;
+    }
+
+  if (!image_p)
+    {
+      struct face_cachel *color_cachel;
+
+      /* Use cursor fg/bg for block cursor, or character fg/bg for the bar
+	 or when we need to erase the cursor. Output nothing at eol if bar
+	 cursor */
+      face_index = get_builtin_face_cache_index (w, Vtext_cursor_face);
+      color_cachel = WINDOW_FACE_CACHEL (w, ((!cursor_p || bar_p) ?
+					     findex : face_index));
+      mswindows_update_dc (hdc, font, color_cachel->foreground,
+			   color_cachel->background, Qnil);
+      ExtTextOut (hdc, xpos, dl->ypos, ETO_OPAQUE|ETO_CLIPPED, &rect, p_char, n_char, NULL);
+      if (real_char_p && (cachel->underline || cachel->strikethru))
+        mswindows_apply_face_effects (hdc, dl, xpos, width,
+				      XFONT_INSTANCE (font),
+				      cachel, color_cachel);
+    }
+
+  if (!cursor_p)
+    return;
+
+  if (focus && bar_p)
+    {
+      rect.right = rect.left + (EQ (bar, Qt) ? 1 : min (2, width));
+      face_index = get_builtin_face_cache_index (w, Vtext_cursor_face);
+      cachel = WINDOW_FACE_CACHEL (w, face_index);
+      mswindows_update_dc (hdc, Qnil, Qnil, cachel->background, Qnil);
+      ExtTextOut (hdc, xpos, dl->ypos, ETO_OPAQUE, &rect, NULL, 0, NULL);
+    }
+  else if (!focus)
+    {
+      /* Now have real character drawn in its own color. We deflate
+	 the rectangle so character cell will be bounded by the
+	 previously drawn cursor shape */
+      InflateRect (&rect, -1, -1);
+
+      if (real_char_p)
+	{
+	  p_char = (char*) &ch;
+	  n_char = 1;
+	}
+
+      face_index = get_builtin_face_cache_index (w, Vdefault_face);
+      cachel = WINDOW_FACE_CACHEL (w, (real_char_p ? findex : face_index));
+      mswindows_update_dc (hdc, Qnil, cachel->foreground,
+			   cachel->background, Qnil);
+      ExtTextOut (hdc, xpos, dl->ypos, ETO_OPAQUE | ETO_CLIPPED,
+		  &rect, p_char, n_char, NULL);
+      if (cachel->underline || cachel->strikethru)
+        mswindows_apply_face_effects (hdc, dl, xpos+1, width-2,
+				      XFONT_INSTANCE (font),
+				      cachel, cachel);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_output_string
+
+ Given a string and a starting position, output that string in the
+ given face.
+ Correctly handles multiple charsets in the string.
+
+ The meaning of the parameters is something like this:
+
+ W		Window that the text is to be displayed in.
+ DL		Display line that this text is on.  The values in the
+ 		structure are used to determine the vertical position and
+		clipping range of the text.
+ BUF		Dynamic array of Emchars specifying what is actually to be
+		drawn.
+ XPOS		X position in pixels where the text should start being drawn.
+ XOFFSET	Number of pixels to be chopped off the left side of the
+ 		text.  The effect is as if the text were shifted to the
+		left this many pixels and clipped at XPOS.
+ CLIP_START	Clip everything left of this X position.
+ WIDTH		Clip everything right of XPOS + WIDTH.
+ FINDEX		Index for the face cache element describing how to display
+ 		the text.
+ ****************************************************************************/
+void
+mswindows_output_string (struct window *w, struct display_line *dl,
+			 Emchar_dynarr *buf, int xpos, int xoffset, int clip_start,
+			 int width, face_index findex,
+			 int cursor, int cursor_start, int cursor_width,
+			 int cursor_height)
+{
+  struct frame *f = XFRAME (w->frame);
+  /* struct device *d = XDEVICE (f->device);*/
+  Lisp_Object window;
+  HDC hdc = FRAME_MSWINDOWS_DC (f);
+  int clip_end;
+  Lisp_Object bg_pmap;
+  int len = Dynarr_length (buf);
+  unsigned char *text_storage = (unsigned char *) alloca (2 * len);
+  textual_run *runs = alloca_array (textual_run, len);
+  int nruns;
+  int i, height;
+  RECT rect;
+  struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, findex);
+
+  XSETWINDOW (window, w);
+
+#if 0	/* #### FIXME? */
+  /* We can't work out the width before we've set the font in the DC */
+  if (width < 0)
+    width = mswindows_text_width (cachel, Dynarr_atp (buf, 0), Dynarr_length (buf));
+#else
+  assert(width>=0);
+#endif
+
+  /* Regularize the variables passed in. */
+  if (clip_start < xpos)
+    clip_start = xpos;
+  clip_end = xpos + width;
+  if (clip_start >= clip_end)
+    /* It's all clipped out. */
+    return;
+
+  xpos -= xoffset;
+
+  /* sort out the destination rectangle */
+  height = DISPLAY_LINE_HEIGHT (dl);
+  rect.left = clip_start;
+  rect.top  = DISPLAY_LINE_YPOS (dl);
+  rect.right = clip_end;
+  rect.bottom = rect.top + height;
+
+  /* make sure the area we are about to display is subwindow free. */
+  redisplay_unmap_subwindows_maybe (f, clip_start, DISPLAY_LINE_YPOS (dl),
+				    clip_end - clip_start, DISPLAY_LINE_HEIGHT (dl));
+
+  /* output the background pixmap if there is one */
+  bg_pmap = cachel->background_pixmap;
+  if (!IMAGE_INSTANCEP (bg_pmap)
+      || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap)))
+    bg_pmap = Qnil;
+
+  if (!NILP(bg_pmap))
+    {
+      struct display_box db;
+      struct display_glyph_area dga;
+      redisplay_calculate_display_boxes (dl, xpos + xoffset, 0,
+					 clip_start, width, &db, &dga);
+      /* blank the background in the appropriate color */
+      mswindows_update_dc (hdc, Qnil, cachel->foreground,
+			   cachel->background, Qnil);
+      redisplay_output_pixmap (w, bg_pmap, &db, &dga, findex,
+			       0, 0, 0, TRUE);
+      /* output pixmap calls this so we have to recall to get correct
+         references */
+      cachel = WINDOW_FACE_CACHEL (w, findex);
+    }
+
+  nruns = separate_textual_runs (text_storage, runs, Dynarr_atp (buf, 0),
+				 Dynarr_length (buf));
+
+  for (i = 0; i < nruns; i++)
+    {
+      Lisp_Object font = FACE_CACHEL_FONT (cachel, runs[i].charset);
+      struct Lisp_Font_Instance *fi = XFONT_INSTANCE (font);
+      int this_width;
+
+      if (EQ (font, Vthe_null_font_instance))
+	continue;
+
+      mswindows_update_dc (hdc, font, cachel->foreground,
+			   NILP(bg_pmap) ? cachel->background : Qnil, Qnil);
+
+      this_width = mswindows_text_width_single_run (hdc, cachel, runs + i);
+      
+      /* cope with fonts taller than lines */
+      if ((int) fi->height < (int) (height + dl->clip + dl->top_clip))
+	{
+	  int clear_start = max (xpos, clip_start);
+	  int clear_end = min (xpos + this_width, clip_end);
+	  
+	  {
+	    redisplay_clear_region (window, findex, clear_start,
+				    DISPLAY_LINE_YPOS (dl), 
+				    clear_end - clear_start,
+				    height);
+	    /* output pixmap calls this so we have to recall to get correct
+	       references */
+	    cachel = WINDOW_FACE_CACHEL (w, findex);
+	  }
+	}
+
+      assert (runs[i].dimension == 1);	/* #### FIXME: Broken when Mule? */
+      ExtTextOut (hdc, xpos, dl->ypos,
+		  NILP(bg_pmap) ? ETO_CLIPPED | ETO_OPAQUE : ETO_CLIPPED,
+		  &rect, (char *) runs[i].ptr, runs[i].len, NULL); 
+
+      /* #### X does underline/strikethrough here so we do the same.
+	 On mswindows, underline/strikethrough really belongs to the font */
+      if (cachel->underline || cachel->strikethru)
+        mswindows_apply_face_effects (hdc, dl, xpos, this_width, fi,
+				      cachel, cachel);
+      xpos += this_width;
+    }
+}
+
+static void
+mswindows_output_dibitmap (struct frame *f, struct Lisp_Image_Instance *p,
+			   struct display_box* db,
+			   struct display_glyph_area* dga)
+{
+  HDC hdc = FRAME_MSWINDOWS_DC (f);
+  HGDIOBJ old=NULL;
+  COLORREF bgcolor = GetBkColor (hdc);
+
+  /* first blt the mask */
+  if (IMAGE_INSTANCE_MSWINDOWS_MASK (p))
+    {
+      RGBQUAD col;
+      col.rgbBlue = GetBValue (bgcolor);
+      col.rgbRed = GetRValue (bgcolor);
+      col.rgbGreen = GetGValue (bgcolor);
+      col.rgbReserved = 0;
+
+      old = SelectObject (FRAME_MSWINDOWS_CDC (f),
+			  IMAGE_INSTANCE_MSWINDOWS_MASK (p));
+      
+      SetDIBColorTable (FRAME_MSWINDOWS_CDC (f), 1, 1, &col);
+
+      BitBlt (hdc, 
+	      db->xpos, db->ypos,
+	      dga->width, dga->height, 
+	      FRAME_MSWINDOWS_CDC (f),
+	      dga->xoffset, dga->yoffset, 
+	      SRCCOPY);                  
+
+      SelectObject (FRAME_MSWINDOWS_CDC (f), old);
+    }
+  
+  /* Now blt the bitmap itself, or one of its slices. */
+  old = SelectObject (FRAME_MSWINDOWS_CDC (f),
+		      IMAGE_INSTANCE_MSWINDOWS_BITMAP_SLICE 
+		      (p, IMAGE_INSTANCE_PIXMAP_SLICE (p)));
+
+  BitBlt (hdc, 
+	  db->xpos, db->ypos,
+	  dga->width, dga->height,
+	  FRAME_MSWINDOWS_CDC (f),
+	  dga->xoffset, dga->yoffset, 
+	  IMAGE_INSTANCE_MSWINDOWS_MASK (p) ? SRCINVERT : SRCCOPY);
+
+  SelectObject (FRAME_MSWINDOWS_CDC (f),old);
+}
+
+/* X gc's have this nice property that setting the bg pixmap will
+ * output it offset relative to the window. Windows doesn't have this
+ * feature so we have to emulate this by outputting multiple pixmaps.
+ * This is only used for background pixmaps. Normal pixmaps are
+ * outputted once and are scrollable */
+static void
+mswindows_output_dibitmap_region (struct frame *f, 
+				  struct Lisp_Image_Instance *p,
+				  struct display_box *db,
+				  struct display_glyph_area *dga)
+{
+  struct display_box xdb = { db->xpos, db->ypos, db->width, db->height };
+  struct display_glyph_area xdga
+    = { 0, 0, IMAGE_INSTANCE_PIXMAP_WIDTH (p),
+	IMAGE_INSTANCE_PIXMAP_HEIGHT (p) };
+  int pxoffset = 0, pyoffset = 0;
+
+  if (dga)
+    {	
+      xdga.width = dga->width;
+      xdga.height = dga->height;
+    }
+  else if (!redisplay_normalize_glyph_area (&xdb, &xdga))
+    return;
+
+  /* when doing a bg pixmap do a partial pixmap first so that we
+     blt whole pixmaps thereafter */
+  xdga.height = min (xdga.height, IMAGE_INSTANCE_PIXMAP_HEIGHT (p) -
+		      db->ypos % IMAGE_INSTANCE_PIXMAP_HEIGHT (p));
+
+  while (xdga.height > 0)
+    {
+      xdga.width = min (min (db->width, IMAGE_INSTANCE_PIXMAP_WIDTH (p)),
+			IMAGE_INSTANCE_PIXMAP_WIDTH (p) -
+			db->xpos % IMAGE_INSTANCE_PIXMAP_WIDTH (p));
+      pxoffset = 0;
+      while (xdga.width > 0)
+	{
+	  xdb.xpos = db->xpos + pxoffset;
+	  xdb.ypos = db->ypos + pyoffset;
+	    /* do we need to offset the pixmap vertically? this is necessary
+	       for background pixmaps. */
+	  xdga.yoffset = xdb.ypos % IMAGE_INSTANCE_PIXMAP_HEIGHT (p);
+	  xdga.xoffset = xdb.xpos % IMAGE_INSTANCE_PIXMAP_WIDTH (p);
+	  /* the width is handled by mswindows_output_pixmap_region */
+	  mswindows_output_dibitmap (f, p, &xdb, &xdga);
+	  pxoffset += xdga.width;
+	  xdga.width = min ((db->width - pxoffset),
+			    IMAGE_INSTANCE_PIXMAP_WIDTH (p));
+	}
+      pyoffset += xdga.height;
+      xdga.height = min ((db->height - pyoffset), 
+			 IMAGE_INSTANCE_PIXMAP_HEIGHT (p));
+    }
+}
+
+/* Output a pixmap at the desired location. 
+   DB		normalized display_box.
+   DGA		normalized display_glyph_area. */
+static void
+mswindows_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 bg_pixmap)
+{
+  struct frame *f = XFRAME (w->frame);
+  HDC hdc = FRAME_MSWINDOWS_DC (f);
+
+  struct Lisp_Image_Instance *p = XIMAGE_INSTANCE (image_instance);
+  Lisp_Object window;
+
+  XSETWINDOW (window, w);
+
+  /* Output the pixmap. Have to do this as many times as is required
+   to fill the given area */
+  mswindows_update_dc (hdc, Qnil,
+		       WINDOW_FACE_CACHEL_FOREGROUND (w, findex),
+		       WINDOW_FACE_CACHEL_BACKGROUND (w, findex), Qnil);
+
+  if (bg_pixmap)
+    mswindows_output_dibitmap_region (f, p, db, dga);
+  else
+    mswindows_output_dibitmap (f, p, db, dga);
+}
+
+#ifdef HAVE_SCROLLBARS
+/*
+ * This function paints window's deadbox, a rectangle between window
+ * borders and two short edges of both scrollbars.
+ *
+ * Function checks whether deadbox intersects with the rectangle pointed
+ * to by PRC, and paints only the intersection
+ */
+static void
+mswindows_redisplay_deadbox_maybe (struct window *w, CONST RECT* prc)
+{
+  int sbh = window_scrollbar_height (w);
+  int sbw = window_scrollbar_width (w);
+  RECT rect_dead, rect_paint;
+  if (sbh == 0 || sbw == 0)
+    return;
+
+  if (!NILP (w->scrollbar_on_left_p))
+    rect_dead.left = WINDOW_LEFT (w);
+  else
+    rect_dead.left = WINDOW_TEXT_RIGHT (w);
+  rect_dead.right = rect_dead.left + sbw;
+
+  if (!NILP (w->scrollbar_on_top_p))
+    rect_dead.top = WINDOW_TOP (w);
+  else
+    rect_dead.top = WINDOW_TEXT_BOTTOM (w);
+  rect_dead.bottom = rect_dead.top + sbh;
+      
+  if (IntersectRect (&rect_paint, &rect_dead, prc))
+    {
+      struct frame *f = XFRAME (WINDOW_FRAME (w));
+      FillRect (FRAME_MSWINDOWS_DC (f), &rect_paint,
+		(HBRUSH) (COLOR_BTNFACE+1));
+    }
+}
+
+#endif /* HAVE_SCROLLBARS */
+
+/*****************************************************************************
+ mswindows_redraw_exposed_window
+
+ Given a bounding box for an area that needs to be redrawn, determine
+ what parts of what lines are contained within and re-output their
+ contents.
+ Copied from redisplay-x.c
+ ****************************************************************************/
+static void
+mswindows_redraw_exposed_window (struct window *w, int x, int y, int width,
+			   int height)
+{
+  struct frame *f = XFRAME (w->frame);
+  int line;
+  int orig_windows_structure_changed;
+  RECT rect_window = { WINDOW_LEFT (w), WINDOW_TOP (w),
+		       WINDOW_RIGHT (w), WINDOW_BOTTOM (w) };
+  RECT rect_expose = { x, y, x + width, y + height };
+  RECT rect_draw;
+
+  display_line_dynarr *cdla = window_display_lines (w, CURRENT_DISP);
+
+  if (!NILP (w->vchild))
+    {
+      mswindows_redraw_exposed_windows (w->vchild, x, y, width, height);
+      return;
+    }
+  else if (!NILP (w->hchild))
+    {
+      mswindows_redraw_exposed_windows (w->hchild, x, y, width, height);
+      return;
+    }
+
+  /* If the window doesn't intersect the exposed region, we're done here. */
+  if (!IntersectRect (&rect_draw, &rect_window, &rect_expose))
+      return;
+
+  /* We do this to make sure that the 3D modelines get redrawn if
+     they are in the exposed region. */
+  orig_windows_structure_changed = f->windows_structure_changed;
+  f->windows_structure_changed = 1;
+
+  if (window_needs_vertical_divider (w))
+    {
+      mswindows_output_vertical_divider (w, 0);
+    }
+
+  for (line = 0; line < Dynarr_length (cdla); line++)
+    {
+      struct display_line *cdl = Dynarr_atp (cdla, line);
+
+      if (DISPLAY_LINE_YPOS (cdl) + DISPLAY_LINE_HEIGHT (cdl)
+	  >= rect_draw.top)
+	{
+	  if (DISPLAY_LINE_YPOS (cdl) > rect_draw.bottom)
+	    {
+	      if (line == 0)
+		continue;
+	      else
+		break;
+	    }
+	  else
+	    {
+	      output_display_line (w, 0, cdla, line,
+				   rect_draw.left, rect_draw.right);
+	    }
+	}
+    }
+
+  f->windows_structure_changed = orig_windows_structure_changed;
+
+  /* If there have never been any face cache_elements created, then this
+     expose event doesn't actually have anything to do. */
+  if (Dynarr_largest (w->face_cachels))
+    redisplay_clear_bottom_of_window (w, cdla, rect_draw.top, rect_draw.bottom);
+
+#ifdef HAVE_SCROLLBARS
+  mswindows_redisplay_deadbox_maybe (w, &rect_expose);
+#endif
+}
+
+/*****************************************************************************
+ mswindows_redraw_exposed_windows
+
+ For each window beneath the given window in the window hierarchy,
+ ensure that it is redrawn if necessary after an Expose event.
+ ****************************************************************************/
+static void
+mswindows_redraw_exposed_windows (Lisp_Object window, int x, int y, int width,
+			    int height)
+{
+  for (; !NILP (window); window = XWINDOW (window)->next)
+    mswindows_redraw_exposed_window (XWINDOW (window), x, y, width, height);
+}
+
+/*****************************************************************************
+ mswindows_redraw_exposed_area
+
+ For each window on the given frame, ensure that any area in the
+ Exposed area is redrawn.
+ ****************************************************************************/
+void
+mswindows_redraw_exposed_area (struct frame *f, int x, int y, int width, int height)
+{
+  /* If any window on the frame has had its face cache reset then the
+     redisplay structures are effectively invalid.  If we attempt to
+     use them we'll blow up.  We mark the frame as changed to ensure
+     that redisplay will do a full update.  This probably isn't
+     necessary but it can't hurt. */
+#ifdef HAVE_TOOLBARS
+  /* #### We would rather put these off as well but there is currently
+     no combination of flags which will force an unchanged toolbar to
+     redraw anyhow. */
+  MAYBE_FRAMEMETH (f, redraw_exposed_toolbars, (f, x, y, width, height));
+#endif
+  redraw_exposed_gutters (f, x, y, width, height);
+
+  if (!f->window_face_cache_reset)
+	{
+	  mswindows_redraw_exposed_windows (f->root_window, x, y, width, height);
+	  GdiFlush();
+	}
+  else
+    MARK_FRAME_CHANGED (f);
+}
+
+
+/*****************************************************************************
+ mswindows_bevel_area
+
+ Draw a 3d border around the specified area on window W.
+ ****************************************************************************/
+static void
+mswindows_bevel_area (struct window *w, face_index findex, int x, int y, 
+		      int width, int height, int thickness,
+		      int edges, enum edge_style style)
+{
+  struct frame *f = XFRAME (w->frame);
+  UINT edge;
+  UINT border = 0;
+
+  if (style == EDGE_ETCHED_IN)
+    edge = EDGE_ETCHED;
+  else if (style == EDGE_ETCHED_OUT)
+    edge = EDGE_BUMP;
+  else if (style == EDGE_BEVEL_IN)
+    {
+      if (thickness == 1)
+	edge = BDR_SUNKENINNER;
+      else
+	edge = EDGE_SUNKEN;
+    }
+  else				/* EDGE_BEVEL_OUT */
+    {
+      if (thickness == 1)
+	edge = BDR_RAISEDINNER;
+      else
+	edge = EDGE_RAISED;
+    }
+
+  if (edges & EDGE_TOP)
+    border |= BF_TOP;
+  if (edges & EDGE_LEFT)
+    border |= BF_LEFT;
+  if (edges & EDGE_BOTTOM)
+    border |= BF_BOTTOM;
+  if (edges & EDGE_RIGHT)
+    border |= BF_RIGHT;
+
+  {
+    RECT rect = { x, y, x + width, y + height };
+    Lisp_Object color = WINDOW_FACE_CACHEL_BACKGROUND (w, findex);
+    mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil, color, Qnil);
+
+    DrawEdge (FRAME_MSWINDOWS_DC (f), &rect, edge, border);
+  }
+}
+
+
+/*****************************************************************************
+ Display methods
+*****************************************************************************/
+
+/*****************************************************************************
+ mswindows_divider_height
+
+ Return the height of the horizontal divider.
+ ****************************************************************************/
+static int
+mswindows_divider_height (void)
+{
+  return 1;   /* XXX Copied from redisplay-X.c. What is this? */
+}
+
+/*****************************************************************************
+ mswindows_eol_cursor_width
+
+ Return the width of the end-of-line cursor.
+ ****************************************************************************/
+static int
+mswindows_eol_cursor_width (void)
+{
+  return MSWINDOWS_EOL_CURSOR_WIDTH;
+}
+
+/*****************************************************************************
+ mswindows_output_begin
+
+ Perform any necessary initialization prior to an update.
+ ****************************************************************************/
+static void
+mswindows_output_begin (struct device *d)
+{
+}
+
+/*****************************************************************************
+ mswindows_output_end
+
+ Perform any necessary flushing of queues when an update has completed.
+ ****************************************************************************/
+static void
+mswindows_output_end (struct device *d)
+{
+  GdiFlush();
+}
+
+static int
+mswindows_flash (struct device *d)
+{
+  struct frame *f = device_selected_frame (d);
+  RECT rc;
+
+  GetClientRect (FRAME_MSWINDOWS_HANDLE (f), &rc);
+  InvertRect (FRAME_MSWINDOWS_DC (f), &rc);
+  GdiFlush ();
+  Sleep (25);
+  InvertRect (FRAME_MSWINDOWS_DC (f), &rc);
+
+  return 1;
+}
+
+static void
+mswindows_ring_bell (struct device *d, int volume, int pitch, int duration)
+{
+  /* Beep does not work at all, anyways! -kkm */
+  MessageBeep (MB_OK);
+}
+
+/*****************************************************************************
+ mswindows_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.
+ Ripped off with minimal thought from the corresponding X routine.
+ ****************************************************************************/
+static void
+mswindows_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);
+  Emchar_dynarr *buf = Dynarr_new (Emchar);
+  Lisp_Object window;
+
+  struct display_block *db = Dynarr_atp (dl->display_blocks, block);
+  rune_dynarr *rba = db->runes;
+  struct rune *rb;
+
+  int elt = start;
+  face_index findex;
+  int xpos, width;
+  Lisp_Object charset = Qunbound; /* Qnil is a valid charset when
+				     MULE is not defined */
+  XSETWINDOW (window, w);
+  rb = Dynarr_atp (rba, start);
+
+  if (!rb)
+      /* Nothing to do so don't do anything. */
+      return;
+
+  findex = rb->findex;
+  xpos = rb->xpos;
+  width = 0;
+  if (rb->type == RUNE_CHAR)
+    charset = CHAR_CHARSET (rb->object.chr.ch);
+
+  if (end < 0)
+    end = Dynarr_length (rba);
+  Dynarr_reset (buf);
+
+  while (elt < end)
+    {
+      rb = Dynarr_atp (rba, elt);
+
+      if (rb->findex == findex && rb->type == RUNE_CHAR
+	  && rb->object.chr.ch != '\n' && rb->cursor_type != CURSOR_ON
+	  && EQ (charset, CHAR_CHARSET (rb->object.chr.ch)))
+	{
+	  Dynarr_add (buf, rb->object.chr.ch);
+	  width += rb->width;
+	  elt++;
+	}
+      else
+	{
+	  if (Dynarr_length (buf))
+	    {
+	      mswindows_output_string (w, dl, buf, xpos, 0, start_pixpos, width,
+				 findex, 0, 0, 0, 0);
+	      xpos = rb->xpos;
+	      width = 0;
+	    }
+	  Dynarr_reset (buf);
+	  width = 0;
+
+	  if (rb->type == RUNE_CHAR)
+	    {
+	      findex = rb->findex;
+	      xpos = rb->xpos;
+	      charset = CHAR_CHARSET (rb->object.chr.ch);
+
+	      if (rb->cursor_type == CURSOR_ON)
+		{
+		  if (rb->object.chr.ch == '\n')
+		    {
+		      mswindows_output_cursor (w, dl, xpos, cursor_width,
+					       findex, 0, 0);
+		    }
+		  else
+		    {
+		      Dynarr_add (buf, rb->object.chr.ch);
+		      mswindows_output_cursor (w, dl, xpos, cursor_width,
+					       findex, rb->object.chr.ch, 0);
+		      Dynarr_reset (buf);
+		    }
+
+		  xpos += rb->width;
+		  elt++;
+		}
+	      else if (rb->object.chr.ch == '\n')
+		{
+		  /* Clear in case a cursor was formerly here. */
+		  redisplay_clear_region (window, findex, xpos, 
+					  DISPLAY_LINE_YPOS (dl),
+					  rb->width, DISPLAY_LINE_HEIGHT (dl));
+		  elt++;
+		}
+	    }
+	  else if (rb->type == RUNE_BLANK || rb->type == RUNE_HLINE)
+	    {
+	      if (rb->type == RUNE_BLANK)
+		mswindows_output_blank (w, dl, rb, start_pixpos);
+	      else
+		{
+		  /* #### Our flagging of when we need to redraw the
+                     modeline shadows sucks.  Since RUNE_HLINE is only used
+                     by the modeline at the moment it is a good bet
+                     that if it gets redrawn then we should also
+                     redraw the shadows.  This won't be true forever.
+                     We borrow the shadow_thickness_changed flag for
+                     now. */
+		  w->shadow_thickness_changed = 1;
+		  mswindows_output_hline (w, dl, rb);
+		}
+
+	      if (rb->cursor_type == CURSOR_ON)
+		mswindows_output_cursor (w, dl, xpos, cursor_width, rb->findex, 0, 0);
+
+	      elt++;
+	      if (elt < end)
+		{
+		  rb = Dynarr_atp (rba, elt);
+
+		  findex = rb->findex;
+		  xpos = rb->xpos;
+		}
+	    }
+	  else if (rb->type == RUNE_DGLYPH)
+	    {
+	      Lisp_Object instance;
+	      struct display_box db;
+	      struct display_glyph_area dga;
+	      redisplay_calculate_display_boxes (dl, rb->xpos, rb->object.dglyph.xoffset,
+						 start_pixpos, rb->width,
+						 &db, &dga);
+
+	      XSETWINDOW (window, w);
+	      instance = glyph_image_instance (rb->object.dglyph.glyph,
+					       window, ERROR_ME_NOT, 1);
+	      findex = rb->findex;
+
+	      if (IMAGE_INSTANCEP (instance))
+		switch (XIMAGE_INSTANCE_TYPE (instance))
+		  {
+		  case IMAGE_TEXT:
+		    {
+		      /* #### This is way losing.  See the comment in
+			 add_glyph_rune(). */
+		      Lisp_Object string =
+			XIMAGE_INSTANCE_TEXT_STRING (instance);
+		      convert_bufbyte_string_into_emchar_dynarr
+			(XSTRING_DATA (string), XSTRING_LENGTH (string), buf);
+
+		      if (rb->cursor_type == CURSOR_ON)
+			mswindows_output_cursor (w, dl, xpos, cursor_width,
+						 findex, Dynarr_at (buf, 0), 0);
+		      else /* #### redisplay-x passes -1 as the width: why ? */
+			mswindows_output_string (w, dl, buf, xpos,
+					   rb->object.dglyph.xoffset,
+					   start_pixpos, rb->width, findex,
+						 0, 0, 0, 0);
+		      Dynarr_reset (buf);
+		    }
+		    break;
+
+		  case IMAGE_MONO_PIXMAP:
+		  case IMAGE_COLOR_PIXMAP:
+		    redisplay_output_pixmap (w, instance, &db, &dga, findex,
+					     cursor_start, cursor_width,
+					     cursor_height, 0);
+		    if (rb->cursor_type == CURSOR_ON)
+		      mswindows_output_cursor (w, dl, xpos, cursor_width,
+					       findex, 0, 1);
+		    break;
+
+		  case IMAGE_POINTER:
+		    abort ();
+
+		  case IMAGE_SUBWINDOW:
+		  case IMAGE_WIDGET:
+		    redisplay_output_subwindow (w, instance, &db, &dga, findex,
+						cursor_start, cursor_width,
+						cursor_height);
+		    if (rb->cursor_type == CURSOR_ON)
+		      mswindows_output_cursor (w, dl, xpos, cursor_width,
+					       findex, 0, 1);
+		    break;
+
+		  case IMAGE_LAYOUT:
+		    redisplay_output_layout (w, instance, &db, &dga, findex,
+					     cursor_start, cursor_width,
+					     cursor_height);
+		    if (rb->cursor_type == CURSOR_ON)
+		      mswindows_output_cursor (w, dl, xpos, cursor_width,
+					       findex, 0, 1);
+		    break;
+
+		  case IMAGE_NOTHING:
+		    /* nothing is as nothing does */
+		    break;
+
+		  default:
+		    abort ();
+		  }
+
+	      xpos += rb->width;
+	      elt++;
+	    }
+	  else
+	    abort ();
+	}
+    }
+
+  if (Dynarr_length (buf))
+    mswindows_output_string (w, dl, buf, xpos, 0, start_pixpos, width, findex,
+			     0, 0, 0, 0);
+
+  if (dl->modeline
+      && !EQ (Qzero, w->modeline_shadow_thickness)
+      && (f->clear
+	  || f->windows_structure_changed
+	  || w->shadow_thickness_changed))
+    bevel_modeline (w, dl);
+
+  Dynarr_free (buf);
+}
+
+
+/*****************************************************************************
+ mswindows_output_vertical_divider
+
+ Draw a vertical divider down the right side of the given window.
+ ****************************************************************************/
+static void
+mswindows_output_vertical_divider (struct window *w, int clear_unused)
+{
+  struct frame *f = XFRAME (w->frame);
+  RECT rect;
+  int spacing = XINT (w->vertical_divider_spacing);
+  int shadow = XINT (w->vertical_divider_shadow_thickness);
+  int abs_shadow = abs (shadow);
+  int line_width = XINT (w->vertical_divider_line_width);
+  int div_left = WINDOW_RIGHT (w) - window_divider_width (w);
+  int y1 = WINDOW_TOP (w) + FRAME_TOP_GUTTER_BOUNDS (f);
+  int y2 = WINDOW_BOTTOM (w) + FRAME_BOTTOM_GUTTER_BOUNDS (f);
+
+  /* Clear left and right spacing areas */
+  if (spacing)
+    {
+      rect.top = y1;
+      rect.bottom = y2;
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil,
+		   WINDOW_FACE_CACHEL_BACKGROUND (w, DEFAULT_INDEX), Qnil);
+      rect.right = WINDOW_RIGHT (w);
+      rect.left = rect.right - spacing;
+      ExtTextOut (FRAME_MSWINDOWS_DC (f), 0, 0, ETO_OPAQUE, 
+		  &rect, NULL, 0, NULL);
+      rect.left = div_left;
+      rect.right = div_left + spacing;
+      ExtTextOut (FRAME_MSWINDOWS_DC (f), 0, 0, ETO_OPAQUE, 
+		  &rect, NULL, 0, NULL);
+    }
+  
+  /* Clear divider face */
+  rect.top = y1 + abs_shadow;
+  rect.bottom = y2 - abs_shadow;
+  rect.left = div_left + spacing + abs_shadow;
+  rect.right = rect.left + line_width;
+  if (rect.left < rect.right)
+    {
+      face_index div_face
+	= get_builtin_face_cache_index (w, Vvertical_divider_face);
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil,
+		   WINDOW_FACE_CACHEL_BACKGROUND (w, div_face), Qnil);
+      ExtTextOut (FRAME_MSWINDOWS_DC (f), 0, 0, ETO_OPAQUE, 
+		  &rect, NULL, 0, NULL);
+    }
+
+  /* Draw a shadow around the divider */
+  if (shadow != 0)
+    {
+      /* #### This will be fixed to support arbitrary thickness */
+      InflateRect (&rect, abs_shadow, abs_shadow);
+      DrawEdge (FRAME_MSWINDOWS_DC (f), &rect,
+		shadow > 0 ? EDGE_RAISED : EDGE_SUNKEN, BF_RECT);
+    }
+}
+
+/****************************************************************************
+ mswindows_text_width
+
+ Given a string and a face, return the string's length in pixels when
+ displayed in the font associated with the face.
+ ****************************************************************************/
+static int
+mswindows_text_width (struct frame *f, struct face_cachel *cachel,
+		      CONST Emchar *str, Charcount len)
+{
+  int width_so_far = 0;
+  unsigned char *text_storage = (unsigned char *) alloca (2 * len);
+  textual_run *runs = alloca_array (textual_run, len);
+  int nruns;
+  int i;
+
+  nruns = separate_textual_runs (text_storage, runs, str, len);
+
+  for (i = 0; i < nruns; i++)
+    width_so_far += mswindows_text_width_single_run (FRAME_MSWINDOWS_DC (f),
+						     cachel, runs + i);
+
+  return width_so_far;
+}
+
+
+/****************************************************************************
+ mswindows_clear_region
+
+ Clear the area in the box defined by the given parameters using the
+ given face.
+ ****************************************************************************/
+static void
+mswindows_clear_region (Lisp_Object locale, struct device* d, struct frame* f, 
+			face_index findex, int x, int y,
+			int width, int height, Lisp_Object fcolor, Lisp_Object bcolor,
+			Lisp_Object background_pixmap)
+{
+  RECT rect = { x, y, x+width, y+height };
+
+  if (!NILP (background_pixmap))
+    {
+      struct display_box db = { x, y, width, height };
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f),
+			   Qnil, fcolor, bcolor, background_pixmap);
+      mswindows_output_dibitmap_region 
+	( f, XIMAGE_INSTANCE (background_pixmap), &db, 0);
+    }
+  else
+    {
+      mswindows_update_dc (FRAME_MSWINDOWS_DC (f), Qnil, Qnil, fcolor, Qnil);
+      ExtTextOut (FRAME_MSWINDOWS_DC (f), 0, 0, ETO_OPAQUE, 
+		  &rect, NULL, 0, NULL);
+    }
+
+#ifdef HAVE_SCROLLBARS
+  if (WINDOWP (locale))
+    mswindows_redisplay_deadbox_maybe (XWINDOW (locale), &rect);
+#endif
+}
+
+/* XXX Implement me! */
+static void
+mswindows_clear_frame (struct frame *f)
+{
+  GdiFlush();
+}
+
+
+
+/************************************************************************/
+/*                            initialization                            */
+/************************************************************************/
+
+void
+console_type_create_redisplay_mswindows (void)
+{
+  /* redisplay methods */
+  CONSOLE_HAS_METHOD (mswindows, text_width);
+  CONSOLE_HAS_METHOD (mswindows, output_display_block);
+  CONSOLE_HAS_METHOD (mswindows, divider_height);
+  CONSOLE_HAS_METHOD (mswindows, eol_cursor_width);
+  CONSOLE_HAS_METHOD (mswindows, output_vertical_divider);
+  CONSOLE_HAS_METHOD (mswindows, clear_region);
+  CONSOLE_HAS_METHOD (mswindows, clear_frame);
+  CONSOLE_HAS_METHOD (mswindows, output_begin);
+  CONSOLE_HAS_METHOD (mswindows, output_end);
+  CONSOLE_HAS_METHOD (mswindows, flash);
+  CONSOLE_HAS_METHOD (mswindows, ring_bell);
+  CONSOLE_HAS_METHOD (mswindows, bevel_area);
+  CONSOLE_HAS_METHOD (mswindows, output_string);
+  CONSOLE_HAS_METHOD (mswindows, output_pixmap);
+}