diff src/redisplay-msw.c @ 213:78f53ef88e17 r20-4b5

Import from CVS: tag r20-4b5
author cvs
date Mon, 13 Aug 2007 10:06:47 +0200
parents
children 1f0dabaa0855
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/redisplay-msw.c	Mon Aug 13 10:06:47 2007 +0200
@@ -0,0 +1,1187 @@
+/* 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 20.4.
+ */
+
+#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.h"	/* XXX FIXME: Should be glyphs-mswindows when we make one */
+#include "redisplay.h"
+#include "sysdep.h"
+#include "window.h"
+
+#include "windows.h"
+
+/* MSWINDOWS_DIVIDER_LINE_WIDTH is the width of the line drawn in the gutter.
+   MSWINDOWS_DIVIDER_SPACING is the amount of blank space on each side of the line.
+   MSWINDOWS_DIVIDER_WIDTH = MSWINDOWS_DIVIDER_LINE_WIDTH + 2*MSWINDOWS_DIVIDER_SPACING
+*/
+#define MSWINDOWS_DIVIDER_LINE_WIDTH	7
+#define MSWINDOWS_DIVIDER_SPACING	0
+#define MSWINDOWS_DIVIDER_WIDTH	(MSWINDOWS_DIVIDER_LINE_WIDTH + 2 * MSWINDOWS_DIVIDER_SPACING)
+
+#define MSWINDOWS_EOL_CURSOR_WIDTH	5
+
+/*
+ * Random forward delarations
+ */
+static void mswindows_clear_region (Lisp_Object locale, face_index findex,
+			      int x, int y, int width, int height);
+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);
+
+
+
+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);
+	  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 0	/* XXX HACK: mswindows_text_width is broken and will pass in a NULL hdc */
+  if (!fi->proportional_p)
+#else
+  if (!fi->proportional_p || !hdc)
+#endif
+    return (fi->width * run->len);
+  else
+    {
+      assert(run->dimension == 1);	/* XXX FIXME! */
+      GetTextExtentPoint32(hdc, run->ptr, run->len, &size);
+      return(size.cx);
+    }
+}
+
+
+/*****************************************************************************
+ mswindows_update_gc
+
+ Given a number of parameters munge the GC so it has those properties.
+ ****************************************************************************/
+static void
+mswindows_update_gc (HDC hdc, Lisp_Object font, Lisp_Object fg, Lisp_Object bg,
+	       Lisp_Object bg_pmap, Lisp_Object lwidth)
+{
+  if (!NILP (font))
+    SelectObject(hdc, (XFONT_INSTANCE (font))->data);
+
+  /* evil kludge! - XXX do we need this? */
+  if (!NILP (fg) && !COLOR_INSTANCEP (fg))
+    {
+      fprintf (stderr, "Help! mswindows_update_gc got a bogus fg value! fg = ");
+      debug_print (fg);
+      fg = Qnil;
+    }
+
+  if (!NILP (fg))
+    SetTextColor (hdc, COLOR_INSTANCE_MSWINDOWS_COLOR (XCOLOR_INSTANCE (fg)));
+
+  if (!NILP (bg))
+    SetBkColor (hdc, COLOR_INSTANCE_MSWINDOWS_COLOR (XCOLOR_INSTANCE (bg)));
+
+#if 0	/* XXX Implement me */
+  /* I expect that the Lisp_Image_Instance's data will point to a brush */
+  if (IMAGE_INSTANCEP (bg_pmap)
+      && IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap)))
+    {
+      if (XIMAGE_INSTANCE_PIXMAP_DEPTH (bg_pmap) == 0)
+	{
+	  gcv.fill_style = FillOpaqueStippled;
+	  gcv.stipple = XIMAGE_INSTANCE_X_PIXMAP (bg_pmap);
+	  mask |= (GCStipple | GCFillStyle);
+	}
+      else
+	{
+	  gcv.fill_style = FillTiled;
+	  gcv.tile = XIMAGE_INSTANCE_X_PIXMAP (bg_pmap);
+	  mask |= (GCTile | GCFillStyle);
+	}
+    }
+#endif
+
+#if 0	/* XXX FIXME */
+  if (!NILP (lwidth))
+    {
+      gcv.line_width = XINT (lwidth);
+      mask |= GCLineWidth;
+    }
+#endif
+}
+
+
+/*****************************************************************************
+ 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)
+{
+  struct frame *f = XFRAME (w->frame);
+  RECT rect = { rb->xpos, dl->ypos-dl->ascent,
+		rb->xpos+rb->width, dl->ypos+dl->descent-dl->clip };
+  struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, rb->findex);
+
+  Lisp_Object bg_pmap = WINDOW_FACE_CACHEL_BACKGROUND_PIXMAP (w, rb->findex);
+  
+  if (!IMAGE_INSTANCEP (bg_pmap)
+      || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap)))
+    bg_pmap = Qnil;
+
+  FillRect (FRAME_MSWINDOWS_DC (f), &rect,
+	    COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (cachel->background)));
+}
+
+
+/*****************************************************************************
+ 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, struct rune *rb)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  struct face_cachel *cachel;
+  Lisp_Object font;
+  int focus = EQ (w->frame, DEVICE_FRAME_WITH_FOCUS_REAL (d));
+  HBRUSH brush;
+  HDC hdc = FRAME_MSWINDOWS_DC (f);
+  int real_char_p = (rb->type == RUNE_CHAR && rb->object.chr.ch != '\n');
+  RECT rect = { xpos,
+		dl->ypos - dl->ascent,
+		xpos + width,
+		dl->ypos + dl->descent - dl->clip};
+
+#if 0	/* XXX FIXME: Whar about the bar_cursor? */
+  Lisp_Object bar_cursor_value = symbol_value_in_buffer (Qbar_cursor,
+							 WINDOW_BUFFER (w));
+#endif
+
+  if (real_char_p)
+    {
+      /* Use the font from the underlying character */
+      cachel = WINDOW_FACE_CACHEL (w, rb->findex);
+
+      /* XXX MULE: Need to know the charset! */
+      font = FACE_CACHEL_FONT (cachel, Vcharset_ascii);
+    }
+
+  /* Clear the area */
+  if (focus)
+    cachel = WINDOW_FACE_CACHEL (w,
+		get_builtin_face_cache_index (w, Vtext_cursor_face));
+  else if (!real_char_p)
+    cachel = WINDOW_FACE_CACHEL (w, rb->findex);
+
+  brush = COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (cachel->background));
+  FillRect (hdc, &rect, brush);
+
+  if (real_char_p)
+    {
+      /* XXX FIXME: Need to clip if dl->clip!=0. How rare is this case? */
+      /* Output the underlying character */
+      mswindows_update_gc (hdc, font, cachel->foreground,
+		     cachel->background, Qnil, Qnil);
+      TextOut(hdc, xpos, dl->ypos, (char*) &rb->object.chr.ch, 1);
+    }
+
+  if (!focus)
+    {
+      /* Draw hollow rectangle in cursor's background color */
+      cachel = WINDOW_FACE_CACHEL (w,
+		get_builtin_face_cache_index (w, Vtext_cursor_face));
+      brush = COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (cachel->background));
+      FrameRect (hdc, &rect, brush);
+    }
+}
+
+
+/*****************************************************************************
+ 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)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct device *d = XDEVICE (f->device);
+  Lisp_Object window = Qnil;
+  HDC hdc;
+  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;
+  struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, findex);
+
+  XSETWINDOW (window, w);
+  hdc = FRAME_MSWINDOWS_DC(f);
+
+#if 0	/* XXX: 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;
+
+  nruns = separate_textual_runs (text_storage, runs, Dynarr_atp (buf, 0),
+				 Dynarr_length (buf));
+
+  bg_pmap = cachel->background_pixmap;
+  if (!IMAGE_INSTANCEP (bg_pmap)
+      || !IMAGE_INSTANCE_PIXMAP_TYPE_P (XIMAGE_INSTANCE (bg_pmap)))
+    bg_pmap = Qnil;
+
+  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;
+      int need_clipping;
+      RECT rect = { clip_start, dl->ypos - dl->ascent,
+		    clip_end, dl->ypos + dl->descent - dl->clip };
+      HRGN region;
+
+      if (EQ (font, Vthe_null_font_instance))
+	continue;
+
+      mswindows_update_gc (hdc, font, cachel->foreground,
+		     cachel->background, Qnil, Qnil);
+
+      this_width = mswindows_text_width_single_run (hdc, cachel, runs + i);
+      need_clipping = (dl->clip || clip_start > xpos ||
+		       clip_end < xpos + this_width);
+
+      if (need_clipping)
+	{
+	  region = CreateRectRgn (rect.left, rect.top,
+				  rect.right, rect.bottom);
+	  SelectClipRgn (hdc, region);
+	}
+
+      /* TextOut only clears the area equal to the height of
+	 the given font.  It is possible that a font is being displayed
+	 on a line taller than it is, so this would cause us to fail to
+	 clear some areas. */
+      if (fi->ascent < dl->ascent || fi->descent < dl->descent-dl->clip)
+	FillRect (hdc, &rect,
+		  COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (cachel->background)));
+
+      assert (runs[i].dimension == 1);	/* XXX FIXME */
+      TextOut(hdc, xpos, dl->ypos, (char *) runs[i].ptr, runs[i].len);
+
+      /* XXX FIXME? X does underline/strikethrough here
+	 we will do it as part of face's font */
+
+      if (need_clipping)
+	{
+	  SelectClipRgn (hdc, NULL);
+	  DeleteObject (region);
+	}
+
+      xpos += this_width;
+    }
+}
+
+/*****************************************************************************
+ 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 start_x, start_y, end_x, end_y;
+  int orig_windows_structure_changed;
+
+  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 (x >= WINDOW_RIGHT (w) || (x + width) <= WINDOW_LEFT (w)
+      || y >= WINDOW_BOTTOM (w) || (y + height) <= WINDOW_TOP (w))
+    {
+      return;
+    }
+  else
+    {
+      start_x = max (WINDOW_LEFT (w), x);
+      end_x = min (WINDOW_RIGHT (w), (x + width));
+      start_y = max (WINDOW_TOP (w), y);
+      end_y = min (WINDOW_BOTTOM (w), y + height);
+
+      /* 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);
+      int top_y = cdl->ypos - cdl->ascent;
+      int bottom_y = cdl->ypos + cdl->descent;
+
+      if (bottom_y >= start_y)
+	{
+	  if (top_y > end_y)
+	    {
+	      if (line == 0)
+		continue;
+	      else
+		break;
+	    }
+	  else
+	    {
+	      output_display_line (w, 0, cdla, line, start_x, end_x);
+	    }
+	}
+    }
+
+  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, start_y, end_y);
+}
+
+/*****************************************************************************
+ 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. */
+
+  if (!f->window_face_cache_reset)
+    mswindows_redraw_exposed_windows (f->root_window, x, y, width, height);
+  else
+    MARK_FRAME_CHANGED (f);
+}
+
+
+/*****************************************************************************
+ mswindows_bevel_modeline
+
+ Draw a 3d border around the modeline on window W.
+ ****************************************************************************/
+static void
+mswindows_bevel_modeline (struct window *w, struct display_line *dl)
+{
+  struct frame *f = XFRAME (w->frame);
+  Lisp_Object color;
+  int shadow_width = MODELINE_SHADOW_THICKNESS (w);
+  RECT rect = {	WINDOW_MODELINE_LEFT (w), 
+		dl->ypos - dl->ascent - shadow_width,
+		WINDOW_MODELINE_RIGHT (w),
+		dl->ypos + dl->descent + shadow_width};
+
+
+  color = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX);
+  mswindows_update_gc(FRAME_MSWINDOWS_DC(f), Qnil, Qnil, color, Qnil, Qnil);
+
+#if 0	/* XXX Eh? */
+  if (shadow_width < 0)
+    {
+      GC temp;
+
+      temp = top_shadow_gc;
+      top_shadow_gc = bottom_shadow_gc;
+      bottom_shadow_gc = temp;
+    }
+#endif
+
+  DrawEdge (FRAME_MSWINDOWS_DC(f), &rect, shadow_width==1 ? BDR_RAISEDINNER :
+					  EDGE_RAISED, BF_RECT);
+}
+
+
+/*****************************************************************************
+ #### Display methods
+/*****************************************************************************
+
+/*****************************************************************************
+ mswindows_divider_width
+
+ Return the width of the vertical divider.
+ ****************************************************************************/
+static int
+mswindows_divider_width (void)
+{
+  return MSWINDOWS_DIVIDER_WIDTH;
+}
+
+/*****************************************************************************
+ 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)
+{
+}
+
+static int
+mswindows_flash (struct device *d)
+{
+  struct frame *f = device_selected_frame (d);
+
+  /* XXX FIXME: Do something more visible here, maybe involving a timer */
+  FlashWindow (FRAME_MSWINDOWS_HANDLE (f), TRUE);
+  FlashWindow (FRAME_MSWINDOWS_HANDLE (f), FALSE);
+}
+
+static void
+mswindows_ring_bell (struct device *d, int volume, int pitch, int duration)
+{
+  /* XXX FIXME: I'm guessing pitch=Hz and duration is milliseconds */
+
+  if ((pitch|duration) == -1)	/* Pitch and/or duration may be bogus */
+    MessageBeep(-1);		/* Default system sound via speaker */
+  else
+    Beep(pitch, duration);
+}
+
+
+/*****************************************************************************
+ 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 mininmal 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;
+    }
+  else
+    {
+      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);
+	      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, rb);
+		    }
+		  else
+		    {
+		      Dynarr_add (buf, rb->object.chr.ch);
+#if 0
+		      mswindows_output_string (w, dl, buf, xpos, 0, start_pixpos,
+					 rb->width, findex, 1,
+					 cursor_start, cursor_width,
+				         cursor_height);
+#else
+		      mswindows_output_cursor (w, dl, xpos, cursor_width, rb);
+#endif
+		      Dynarr_reset (buf);
+		    }
+
+		  xpos += rb->width;
+		  elt++;
+		}
+	      else if (rb->object.chr.ch == '\n')
+		{
+		  /* Clear in case a cursor was formerly here. */
+		  int height = dl->ascent + dl->descent - dl->clip;
+
+		  mswindows_clear_region (window, findex, xpos, dl->ypos - dl->ascent,
+				    rb->width, height);
+		  elt++;
+		}
+	    }
+	  else if (rb->type == RUNE_BLANK || rb->type == RUNE_HLINE)
+	    {
+	      if (rb->type == RUNE_BLANK)
+		mswindows_output_blank (w, dl, rb);
+	      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);
+
+	      elt++;
+	      if (elt < end)
+		{
+		  rb = Dynarr_atp (rba, elt);
+
+		  findex = rb->findex;
+		  xpos = rb->xpos;
+		}
+	    }
+	  else if (rb->type == RUNE_DGLYPH)
+	    {
+	      Lisp_Object instance;
+
+	      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, rb);
+		      else
+			mswindows_output_string (w, dl, buf, xpos,
+					   rb->object.dglyph.xoffset,
+					   start_pixpos, -1, findex);
+		      Dynarr_reset (buf);
+		    }
+		    break;
+
+		  case IMAGE_MONO_PIXMAP:
+		  case IMAGE_COLOR_PIXMAP:
+#if 0
+		    mswindows_output_pixmap (w, dl, instance, xpos,
+				     rb->object.dglyph.xoffset, start_pixpos,
+				     rb->width, findex, cursor_start,
+				     cursor_width, cursor_height);
+#endif
+		    break;
+
+		  case IMAGE_POINTER:
+		    abort ();
+
+		  case IMAGE_SUBWINDOW:
+		    /* #### implement me */
+		    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);
+
+  if (dl->modeline
+      && !EQ (Qzero, w->modeline_shadow_thickness)
+      && (f->clear
+	  || f->windows_structure_changed
+	  || w->shadow_thickness_changed))
+    mswindows_bevel_modeline (w, dl);
+
+  Dynarr_free (buf);
+  
+}
+
+
+/*****************************************************************************
+ mswindows_output_vertical_divider
+
+ Draw a vertical divider down the left side of the given window.
+ ****************************************************************************/
+static void
+mswindows_output_vertical_divider (struct window *w, int clear)
+{
+  struct frame *f = XFRAME (w->frame);
+  Lisp_Object color;
+  RECT rect;
+  HBRUSH brush;
+  int shadow_width = MODELINE_SHADOW_THICKNESS (w);
+
+  /* We don't use the normal gutter measurements here because the
+     horizontal scrollbars and toolbars do not stretch completely over
+     to the right edge of the window.  Only the modeline does. */
+  int modeline_height = window_modeline_height (w);
+
+  assert(!MSWINDOWS_DIVIDER_SPACING);		/* This code doesn't handle this */
+
+  /* XXX Not sure about this */
+#ifdef HAVE_SCROLLBARS
+  if (f->scrollbar_on_left)
+    rect.left = WINDOW_LEFT (w);
+  else
+    rect.left = WINDOW_RIGHT (w) - MSWINDOWS_DIVIDER_WIDTH;
+#else
+  rect.left = WINDOW_LEFT (w);
+#endif
+  rect.right = rect.left + MSWINDOWS_DIVIDER_WIDTH;
+
+#ifdef HAVE_SCROLLBARS
+  if (f->scrollbar_on_top)
+    rect.top = WINDOW_TOP (w);
+  else
+#endif
+    rect.top = WINDOW_TEXT_TOP (w);
+  rect.bottom = WINDOW_BOTTOM (w) - modeline_height;
+
+  /* Draw the divider line */
+  color = WINDOW_FACE_CACHEL_BACKGROUND (w, MODELINE_INDEX);
+  mswindows_update_gc(FRAME_MSWINDOWS_DC(f), Qnil, Qnil, color, Qnil, Qnil);
+  brush = COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (color));
+  FillRect (FRAME_MSWINDOWS_DC(f), &rect, brush);
+  if (shadow_width)
+    DrawEdge (FRAME_MSWINDOWS_DC(f), &rect,
+	      shadow_width==1 ? BDR_RAISEDINNER : EDGE_RAISED,
+	      BF_TOP|BF_RIGHT|BF_LEFT);
+}
+
+
+/****************************************************************************
+ 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.
+ XXX FIXME: get redisplay_text_width_emchar_string() etc to pass in the
+ window so we can get hold of the window's frame's gc
+ ****************************************************************************/
+static int
+mswindows_text_width (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;
+  HDC hdc=NULL;	/* XXXXX FIXME! only works for non-proportional fonts! */
+
+  nruns = separate_textual_runs (text_storage, runs, str, len);
+
+  for (i = 0; i < nruns; i++)
+    width_so_far += mswindows_text_width_single_run (hdc, 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, face_index findex, int x, int y,
+		  int width, int height)
+{
+  struct window *w;
+  struct frame *f;
+  Lisp_Object background_pixmap = Qunbound;
+  Lisp_Object temp;
+  RECT rect = { x, y, x+width, y+height };
+  HBRUSH brush;
+
+  if (!(width && height))   /* We often seem to get called with width==0 */
+    return;
+
+  if (WINDOWP (locale))
+    {
+      w = XWINDOW (locale);
+      f = XFRAME (w->frame);
+    }
+  else if (FRAMEP (locale))
+    {
+      w = NULL;
+      f = XFRAME (locale);
+    }
+  else
+    abort ();
+  
+  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))
+    {
+      if (XIMAGE_INSTANCE_PIXMAP_DEPTH (background_pixmap) == 0)
+	{
+	  Lisp_Object fcolor, bcolor;
+
+	  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);
+	    }
+
+	  mswindows_update_gc (FRAME_MSWINDOWS_DC(f), Qnil, fcolor, bcolor, background_pixmap, Qnil);
+      }
+
+      /* XX FIXME: Get brush from background_pixmap here */
+      assert(0);
+    }
+  else
+    {
+      Lisp_Object color = (w ? WINDOW_FACE_CACHEL_BACKGROUND (w, findex) :
+			   FACE_BACKGROUND (Vdefault_face, locale));
+      brush = COLOR_INSTANCE_MSWINDOWS_BRUSH (XCOLOR_INSTANCE (color));
+    }
+
+  FillRect (FRAME_MSWINDOWS_DC(f), &rect, brush);
+}
+
+
+/*****************************************************************************
+ mswindows_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.
+ ****************************************************************************/
+static void
+mswindows_clear_to_window_end (struct window *w, int ypos1, int ypos2)
+{
+  int height = ypos2 - ypos1;
+
+  if (height)
+    {
+      struct frame *f = XFRAME (w->frame);
+      Lisp_Object window;
+      int bflag = (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))
+	mswindows_clear_region (window, DEFAULT_INDEX, FRAME_LEFT_BORDER_START (f),
+			  ypos1, FRAME_BORDER_WIDTH (f), height);
+
+      if (bounds.left_in - bounds.left_out > 0)
+	mswindows_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)
+	mswindows_clear_region (window, DEFAULT_INDEX, bounds.left_in, ypos1,
+			  bounds.right_in - bounds.left_in, height);
+
+      if (bounds.right_out - bounds.right_in > 0)
+	mswindows_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))
+	mswindows_clear_region (window, DEFAULT_INDEX, FRAME_RIGHT_BORDER_START (f),
+			  ypos1, FRAME_BORDER_WIDTH (f), height);
+    }
+
+}
+
+
+static void
+mswindows_clear_frame (struct frame *f)
+{
+}
+
+
+
+
+/************************************************************************/
+/*                            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_width);
+  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_to_window_end);
+  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);
+}