diff src/objects-x.c @ 3094:ad2f4ae9895b

[xemacs-hg @ 2005-11-26 11:45:47 by stephent] Xft merge. <87k6ev4p8q.fsf@tleepslib.sk.tsukuba.ac.jp>
author stephent
date Sat, 26 Nov 2005 11:46:25 +0000
parents 491f8cf78a9c
children 859a2346db99
line wrap: on
line diff
--- a/src/objects-x.c	Fri Nov 25 22:51:38 2005 +0000
+++ b/src/objects-x.c	Sat Nov 26 11:46:25 2005 +0000
@@ -38,6 +38,10 @@
 #include "console-x-impl.h"
 #include "objects-x-impl.h"
 
+#ifdef USE_XFT
+#include "xft-fonts.h"
+#endif
+
 int x_handle_non_fully_specified_fonts;
 
 
@@ -45,185 +49,6 @@
 /*                          color instances                             */
 /************************************************************************/
 
-/* Replacement for XAllocColor() that tries to return the nearest
-   available color if the colormap is full.  Original was from FSFmacs,
-   but rewritten by Jareth Hein <jareth@camelot-soft.com> 97/11/25
-   Modified by Lee Kindness <lkindness@csl.co.uk> 31/08/99 to handle previous
-   total failure which was due to a read/write colorcell being the nearest
-   match - tries the next nearest...
-
-   Return value is 1 for normal success, 2 for nearest color success,
-   3 for Non-deallocable success. */
-int
-allocate_nearest_color (Display *display, Colormap colormap, Visual *visual,
-		        XColor *color_def)
-{
-  int status;
-
-  if (visual->X_CLASSFIELD == DirectColor || visual->X_CLASSFIELD == TrueColor)
-    {
-      if (XAllocColor (display, colormap, color_def) != 0)
-	{
-	  status = 1;
-	}
-      else
-	{
-	  /* We're dealing with a TrueColor/DirectColor visual, so play games
-	     with the RGB values in the XColor struct. */
-	  /* #### JH: I'm not sure how a call to XAllocColor can fail in a
-	     TrueColor or DirectColor visual, so I will just reformat the
-	     request to match the requirements of the visual, and re-issue
-	     the request.  If this fails for anybody, I wanna know about it
-	     so I can come up with a better plan */
-
-	  unsigned long rshift,gshift,bshift,rbits,gbits,bbits,junk;
-	  junk = visual->red_mask;
-	  rshift = 0;
-	  while ((junk & 0x1) == 0) {
-	    junk = junk >> 1;
-	    rshift ++;
-	  }
-	  rbits = 0;
-	  while (junk != 0) {
-	    junk = junk >> 1;
-	    rbits++;
-	  }
-	  junk = visual->green_mask;
-	  gshift = 0;
-	  while ((junk & 0x1) == 0) {
-	    junk = junk >> 1;
-	    gshift ++;
-	  }
-	  gbits = 0;
-	  while (junk != 0) {
-	    junk = junk >> 1;
-	    gbits++;
-	  }
-	  junk = visual->blue_mask;
-	  bshift = 0;
-	  while ((junk & 0x1) == 0) {
-	    junk = junk >> 1;
-	    bshift ++;
-	  }
-	  bbits = 0;
-	  while (junk != 0) {
-	    junk = junk >> 1;
-	    bbits++;
- 	  }
-
-	  color_def->red = color_def->red >> (16 - rbits);
-	  color_def->green = color_def->green >> (16 - gbits);
-	  color_def->blue = color_def->blue >> (16 - bbits);
-	  if (XAllocColor (display, colormap, color_def) != 0)
-	    status = 1;
-	  else
-  	    {
-  	      int rd, gr, bl;
-	      /* #### JH: I'm punting here, knowing that doing this will at
-		 least draw the color correctly.  However, unless we convert
-		 all of the functions that allocate colors (graphics
-		 libraries, etc) to use this function doing this is very
-		 likely to cause problems later... */
-
-	      if (rbits > 8)
-		rd = color_def->red << (rbits - 8);
-	      else
-		rd = color_def->red >> (8 - rbits);
-	      if (gbits > 8)
-		gr = color_def->green << (gbits - 8);
-	      else
-		gr = color_def->green >> (8 - gbits);
-	      if (bbits > 8)
-		bl = color_def->blue << (bbits - 8);
-	      else
-		bl = color_def->blue >> (8 - bbits);
-	      color_def->pixel = (rd << rshift) | (gr << gshift) | (bl <<
-								    bshift);
-	      status = 3;
-	    }
-	}
-    }
-  else
-    {
-      XColor *cells = NULL;
-      /* JH: I can't believe there's no way to go backwards from a
-	 colormap ID and get its visual and number of entries, but X
-	 apparently isn't built that way... */
-      int no_cells = visual->map_entries;
-      status = 0;
-
-      if (XAllocColor (display, colormap, color_def) != 0)
-	status = 1;
-      else while( status != 2 )
-	{
-	  /* If we got to this point, the colormap is full, so we're
-	     going to try and get the next closest color.  The algorithm used
-	     is a least-squares matching, which is what X uses for closest
-	     color matching with StaticColor visuals. */
-	  int nearest;
-	  long nearest_delta, trial_delta;
-	  int x;
-
-	  if( cells == NULL )
-	    {
-	      cells = alloca_array (XColor, no_cells);
-	      for (x = 0; x < no_cells; x++)
-		cells[x].pixel = x;
-
-	      /* read the current colormap */
-	      XQueryColors (display, colormap, cells, no_cells);
-	    }
-
-	  nearest = 0;
-	  /* I'm assuming CSE so I'm not going to condense this. */
-	  nearest_delta = ((((color_def->red >> 8) - (cells[0].red >> 8))
-			    * ((color_def->red >> 8) - (cells[0].red >> 8)))
-			   +
-			   (((color_def->green >> 8) - (cells[0].green >> 8))
-			    * ((color_def->green >> 8) - (cells[0].green >>
-							  8)))
-			   +
-			   (((color_def->blue >> 8) - (cells[0].blue >> 8))
-			    * ((color_def->blue >> 8) - (cells[0].blue >>
-							 8))));
-	  for (x = 1; x < no_cells; x++)
-	    {
-	      trial_delta = ((((color_def->red >> 8) - (cells[x].red >> 8))
-			      * ((color_def->red >> 8) - (cells[x].red >> 8)))
-			     +
-			     (((color_def->green >> 8) - (cells[x].green >> 8))
-			      * ((color_def->green >> 8) - (cells[x].green >>
-							    8)))
-			     +
-			     (((color_def->blue >> 8) - (cells[x].blue >> 8))
-			      * ((color_def->blue >> 8) - (cells[x].blue >>
-							   8))));
-
-	      /* less? Ignore cells marked as previously failing */
-	      if( (trial_delta < nearest_delta) &&
-		  (cells[x].pixel != ULONG_MAX) )
-		{
-		  nearest = x;
-		  nearest_delta = trial_delta;
-		}
-	    }
-	  color_def->red = cells[nearest].red;
-	  color_def->green = cells[nearest].green;
-	  color_def->blue = cells[nearest].blue;
-	  if (XAllocColor (display, colormap, color_def) != 0)
-	    status = 2;
-	  else
-	    /* LSK: Either the colour map has changed since
-	     * we read it, or the colour is allocated
-	     * read/write... Mark this cmap entry so it's
-	     * ignored in the next iteration.
-	     */
-	    cells[nearest].pixel = ULONG_MAX;
-	}
-    }
-  return status;
-}
-
 static int
 x_parse_nearest_color (struct device *d, XColor *color, Lisp_Object name,
 		       Error_Behavior errb)
@@ -246,7 +71,7 @@
 			  name, Qcolor, errb);
       return 0;
     }
-  result = allocate_nearest_color (dpy, cmap, visual, color);
+  result = x_allocate_nearest_color (dpy, cmap, visual, color);
   if (!result)
     {
       maybe_signal_error (Qgui_error, "Couldn't allocate color",
@@ -262,6 +87,9 @@
 			     Lisp_Object device, Error_Behavior errb)
 {
   XColor color;
+#ifdef USE_XFT
+  XftColor xftColor;
+#endif
   int result;
 
   result = x_parse_nearest_color (XDEVICE (device), &color, name, errb);
@@ -277,6 +105,17 @@
   else
     COLOR_INSTANCE_X_DEALLOC (c) = 1;
   COLOR_INSTANCE_X_COLOR (c) = color;
+
+#ifdef USE_XFT
+  xftColor.pixel = color.pixel;
+  xftColor.color.red = color.red;
+  xftColor.color.green = color.green;
+  xftColor.color.blue = color.blue;
+  xftColor.color.alpha = 0xffff;
+
+  COLOR_INSTANCE_X_XFTCOLOR (c) = xftColor;
+#endif
+
   return 1;
 }
 
@@ -366,95 +205,247 @@
 /*                           font instances                             */
 /************************************************************************/
 
+#ifdef USE_XFT
+/* #### all these #defines should probably move to xft-fonts.h */
+
+/*
+  The format of a fontname (as returned by fontconfig) is not well-documented,
+  But the character repertoire is represented in an ASCII-compatible way.  See
+  fccharset.c (FcCharSetUnparse).  So we can use UTF-8 for long names.
+
+  Currently we have a hack where different versions of the unparsed name are
+  used in different contexts fairly arbitrarily.  I don't think this is close
+  to coherency; even without the charset and lang properties fontconfig names
+  are too unwieldy to use.  We need to rethink the approach here.  I think
+  probably Lisp_Font_Instance.name should contain the font name as specified
+  to Lisp (almost surely much shorter than shortname, even, and most likely
+  wildcarded), while Lisp_Font_Instance.truename should contain the longname.
+  For now, I'm going to #ifdef the return values defaulting to short. -- sjt
+*/
+
+/*                DEBUGGING STUFF                */
+
+/* print message to stderr: one internal-format string argument */
+#define DEBUG_XFT0(level,s)		\
+  if (debug_xft > level) stderr_out (s)
+
+/* print message to stderr: one formatted argument */
+#define DEBUG_XFT1(level,format,x1)		\
+  if (debug_xft > level) stderr_out (format, x1)
+
+/* print message to stderr: two formatted arguments */
+#define DEBUG_XFT2(level,format,x1,x2)			\
+  if (debug_xft > level) stderr_out (format, x1, x2)
+
+/* print message to stderr: three formatted arguments */
+#define DEBUG_XFT3(level,format,x1,x2,x3)			\
+  if (debug_xft > level) stderr_out (format, x1, x2, x3)
+
+/* print message to stderr: four formatted arguments */
+#define DEBUG_XFT4(level,format,x1,x2,x3,x4)			\
+  if (debug_xft > level) stderr_out (format, x1, x2, x3, x4)
+
+/* print an Xft pattern to stderr
+   LEVEL is the debug level (to compare to debug_xft)
+   FORMAT is a newline-terminated printf format with one %s for the pattern
+     and must be internal format (eg, pure ASCII)
+   PATTERN is an FcPattern *. */
+#define PRINT_XFT_PATTERN(level,format,pattern)			\
+  do {								\
+    DECLARE_EISTRING (eistrpxft_name);				\
+    FcChar8 *name = FcNameUnparse (pattern);			\
+								\
+    eicpy_ext(eistrpxft_name, name, Qxft_font_name_encoding);	\
+    DEBUG_XFT1 (level, format, eidata(eistrpxft_name));		\
+    free (name);						\
+  } while (0)
+
+/* print a progress message
+   LEVEL is the debug level (to compare to debug_xft)
+   FONT is the Xft font name in UTF-8 (the native encoding of Xft)
+   LANG is the language being checked for support (must be ASCII). */
+#define CHECKING_LANG(level,font,lang)					\
+  do {									\
+    DECLARE_EISTRING (eistrcl_name);					\
+    eicpy_ext(eistrcl_name, font, Qxft_font_name_encoding);		\
+    DEBUG_XFT2 (level, "checking if %s handles %s\n",			\
+			eidata(eistrcl_name), lang);			\
+  } while (0)
+
+#endif /* USE_XFT */
+
 static int
 x_initialize_font_instance (Lisp_Font_Instance *f, Lisp_Object UNUSED (name),
 			    Lisp_Object device, Error_Behavior errb)
 {
   Display *dpy = DEVICE_X_DISPLAY (XDEVICE (device));
-  XFontStruct *xf;
-  const Extbyte *extname;
+  Extbyte *extname;
+  XFontStruct *fs = NULL;	/* _F_ont _S_truct */
+#ifdef USE_XFT
+  XftFont *rf = NULL;		/* _R_ender _F_ont (X Render extension) */
+#else
+#define rf (0)
+#endif
 
+#ifdef USE_XFT
+  DEBUG_XFT1 (2, "attempting to initialize font spec %s\n",
+	      XSTRING_DATA(f->name));
+  /* #### serialize (optimize) these later... */
+  /* #### This function really needs to go away.
+     The problem is that the fontconfig/Xft functions work much too hard
+     to ensure that something is returned; but that something need not be
+     at all close to what we asked for. */
+  LISP_STRING_TO_EXTERNAL (f->name, extname, Qxft_font_name_encoding);
+  rf = xft_open_font_by_name (dpy, extname);
+#endif
   LISP_STRING_TO_EXTERNAL (f->name, extname, Qx_font_name_encoding);
-  xf = XLoadQueryFont (dpy, extname);
-
-  if (!xf)
+  fs = XLoadQueryFont (dpy, extname);
+      
+  if (!fs && !rf)
     {
-      maybe_signal_error (Qgui_error, "Couldn't load font", f->name, Qfont,
-			  errb);
-      return 0;
-    }
-
-  if (!xf->max_bounds.width)
-    {
-      /* yes, this has been known to happen. */
-      XFreeFont (dpy, xf);
-      maybe_signal_error (Qgui_error, "X font is too small", f->name, Qfont,
-			  errb);
+      /* #### should this refer to X and/or Xft? */
+      maybe_signal_error (Qgui_error, "Couldn't load font", f->name,
+			  Qfont, errb);
       return 0;
     }
 
-  /* Don't allocate the data until we're sure that we will succeed,
-     or the finalize method may get fucked. */
+  if (fs && !fs->max_bounds.width)
+    {
+      /* yes, this has been known to happen. */
+      XFreeFont (dpy, fs);
+      fs = NULL;
+      maybe_signal_error (Qgui_error, "X font is too small", f->name, Qfont,
+			  errb);
+      if (!rf)
+	return 0;
+    }
+
+  /* Now that we're sure that we will succeed, we can allocate data without
+     fear that the finalize method may get fucked. */
   f->data = xnew (struct x_font_instance_data);
-  FONT_INSTANCE_X_FONT (f) = xf;
-  f->ascent = xf->ascent;
-  f->descent = xf->descent;
-  f->height = xf->ascent + xf->descent;
-  {
-    /* following change suggested by Ted Phelps <phelps@dstc.edu.au> */
-    int def_char = 'n'; /*xf->default_char;*/
-    int byte1, byte2;
 
-  once_more:
-    byte1 = def_char >> 8;
-    byte2 = def_char & 0xFF;
+#ifdef USE_XFT
+  FONT_INSTANCE_X_XFTFONT (f) = rf;
+  if (rf)
+    /* Have an Xft font, initialize font info from it. */
+    {
+      DEBUG_XFT4 (2, "pre-initial ascent %d descent %d width %d height %d\n",
+		  f->ascent, f->descent, f->width, f->height);
 
-    if (xf->per_char)
+      /* #### This shit is just plain wrong unless we have a character cell
+	 font.  It really hoses us on large repertoire Unicode fonts with
+	 "double-width" characters. */
+      f->ascent = rf->ascent;
+      f->descent = rf->descent;
       {
-	/* Old versions of the R5 font server have garbage (>63k) as
-	   def_char. 'n' might not be a valid character. */
-	if (byte1 < (int) xf->min_byte1         ||
-	    byte1 > (int) xf->max_byte1         ||
-	    byte2 < (int) xf->min_char_or_byte2 ||
-	    byte2 > (int) xf->max_char_or_byte2)
-	  f->width = 0;
-	else
-	  f->width = xf->per_char[(byte1 - xf->min_byte1) *
-				  (xf->max_char_or_byte2 -
-				   xf->min_char_or_byte2 + 1) +
-				  (byte2 - xf->min_char_or_byte2)].width;
+	/* This is an approximation that AFAIK only gets used to compute
+	   cell size for estimating window dimensions.  The test_string8
+	   is an  ASCII string whose characters should approximate the
+	   distribution of widths expected in real text.  */
+	static const char test_string8[] = "Mmneei";
+	static const int len = sizeof (test_string8) - 1;
+	XGlyphInfo glyphinfo;
+
+	XftTextExtents8 (dpy, rf, test_string8, len, &glyphinfo);
+	/* #### maybe should be glyphinfo.xOff - glyphinfo.x? */
+	f->width = (2*glyphinfo.width + len)/(2*len);
       }
-    else
-      f->width = xf->max_bounds.width;
+      f->height = rf->height;
+      f->proportional_p = 1; 	/* we can't recognize monospaced fonts! */
+
+      DEBUG_XFT4 (0, "initialized metrics ascent %d descent %d width %d height %d\n",
+		    f->ascent, f->descent, f->width, f->height);
+      /* we also output on initialization of any font below */
+      DEBUG_XFT1 (2, "initialized Xft font %s\n", XSTRING_DATA(f->name));
+      fs = NULL;		/* we don' need no steenkin' X font */
+    } 
+  else
+    {
+      DEBUG_XFT1 (0, "couldn't initialize Xft font %s\n",
+		  XSTRING_DATA(f->name));
+    }
+#endif
+
+  FONT_INSTANCE_X_FONT (f) = fs;
+  if (fs)
+    /* Have to use a core font, initialize font info from it. */
+    {
+      f->ascent = fs->ascent;
+      f->descent = fs->descent;
+      f->height = fs->ascent + fs->descent;
+      {
+	/* following change suggested by Ted Phelps <phelps@dstc.edu.au> */
+	int def_char = 'n'; /*fs->default_char;*/
+	int byte1, byte2;
 
-    /* Some fonts have a default char whose width is 0.  This is no good.
-       If that's the case, first try 'n' as the default char, and if n has
-       0 width too (unlikely) then just use the max width. */
-    if (f->width == 0)
-      {
-	if (def_char == (int) xf->default_char)
-	  f->width = xf->max_bounds.width;
+      once_more:
+	byte1 = def_char >> 8;
+	byte2 = def_char & 0xFF;
+
+	if (fs->per_char)
+	  {
+	    /* Old versions of the R5 font server have garbage (>63k) as
+	       def_char. 'n' might not be a valid character. */
+	    if (byte1 < (int) fs->min_byte1         ||
+		byte1 > (int) fs->max_byte1         ||
+		byte2 < (int) fs->min_char_or_byte2 ||
+		byte2 > (int) fs->max_char_or_byte2)
+	      f->width = 0;
+	    else
+	      f->width = fs->per_char[(byte1 - fs->min_byte1) *
+				      (fs->max_char_or_byte2 -
+				       fs->min_char_or_byte2 + 1) +
+				      (byte2 - fs->min_char_or_byte2)].width;
+	  }
 	else
+	  f->width = fs->max_bounds.width;
+
+	/* Some fonts have a default char whose width is 0.  This is no good.
+	   If that's the case, first try 'n' as the default char, and if n has
+	   0 width too (unlikely) then just use the max width. */
+	if (f->width == 0)
 	  {
-	    def_char = xf->default_char;
-	    goto once_more;
+	    if (def_char == (int) fs->default_char)
+	      f->width = fs->max_bounds.width;
+	    else
+	      {
+		def_char = fs->default_char;
+		goto once_more;
+	      }
 	  }
       }
-  }
-  /* If all characters don't exist then there could potentially be
-     0-width characters lurking out there.  Not setting this flag
-     trips an optimization that would make them appear to have width
-     to redisplay.  This is bad.  So we set it if not all characters
-     have the same width or if not all characters are defined.
-     */
-  /* #### This sucks.  There is a measurable performance increase
-     when using proportional width fonts if this flag is not set.
-     Unfortunately so many of the fucking X fonts are not fully
-     defined that we could almost just get rid of this damn flag and
-     make it an assertion. */
-  f->proportional_p = (xf->min_bounds.width != xf->max_bounds.width ||
-		       (x_handle_non_fully_specified_fonts &&
-			!xf->all_chars_exist));
+
+      /* If all characters don't exist then there could potentially be
+	 0-width characters lurking out there.  Not setting this flag
+	 trips an optimization that would make them appear to have width
+	 to redisplay.  This is bad.  So we set it if not all characters
+	 have the same width or if not all characters are defined. */
+      /* #### This sucks.  There is a measurable performance increase
+	 when using proportional width fonts if this flag is not set.
+	 Unfortunately so many of the fucking X fonts are not fully
+	 defined that we could almost just get rid of this damn flag and
+	 make it an assertion. */
+      f->proportional_p = (fs->min_bounds.width != fs->max_bounds.width ||
+			   (x_handle_non_fully_specified_fonts &&
+			    !fs->all_chars_exist));
+    }
+
+#ifdef USE_XFT
+  if (debug_xft > 0)
+    {
+      int n = 3, d = 5;
+      /* check for weirdness */
+      if (n * f->height < d * f->width)
+	stderr_out ("font %s: width:height is %d:%d, larger than %d:%d\n",
+		    XSTRING_DATA(f->name), f->width, f->height, n, d);
+      if (f->height <= 0 || f->width <= 0)
+	stderr_out ("bogus dimensions of font %s: width = %d, height = %d\n",
+		    XSTRING_DATA(f->name), f->width, f->height);
+      stderr_out ("initialized font %s\n", XSTRING_DATA(f->name));
+    }
+#else
+#undef rf
+#endif
 
   return 1;
 }
@@ -464,21 +455,39 @@
 		       Lisp_Object printcharfun,
 		       int UNUSED (escapeflag))
 {
-  write_fmt_string (printcharfun, " 0x%lx",
-		    (unsigned long) FONT_INSTANCE_X_FONT (f)->fid);
+  if (FONT_INSTANCE_X_FONT (f))
+    write_fmt_string (printcharfun, " font id: 0x%lx",
+		      (unsigned long) FONT_INSTANCE_X_FONT (f)->fid);
+#ifdef USE_XFT
+  /* #### What should we do here?  For now, print the address. */
+  if (FONT_INSTANCE_X_XFTFONT (f))
+    write_fmt_string (printcharfun, " xft font: 0x%lx",
+		      (unsigned long) FONT_INSTANCE_X_XFTFONT (f));
+#endif
 }
 
 static void
 x_finalize_font_instance (Lisp_Font_Instance *f)
 {
 
+#ifdef USE_XFT
+  DEBUG_XFT1 (0, "finalizing %s\n", (STRINGP (f->name)
+				   ? (char *) XSTRING_DATA (f->name)
+				   : "(unnamed font)"));
+#endif
+
   if (f->data)
     {
       if (DEVICE_LIVE_P (XDEVICE (f->device)))
 	{
 	  Display *dpy = DEVICE_X_DISPLAY (XDEVICE (f->device));
 
-	  XFreeFont (dpy, FONT_INSTANCE_X_FONT (f));
+	  if (FONT_INSTANCE_X_FONT (f))
+	    XFreeFont (dpy, FONT_INSTANCE_X_FONT (f));
+#ifdef USE_XFT
+	  if (FONT_INSTANCE_X_XFTFONT (f))
+	    XftFontClose (dpy, FONT_INSTANCE_X_XFTFONT (f));
+#endif
 	}
       xfree (f->data, void *);
       f->data = 0;
@@ -487,6 +496,13 @@
 
 /* Determining the truename of a font is hard.  (Big surprise.)
 
+   This is not true for fontconfig.  Each font has a (nearly) canonical
+   representation up to permutation of the order of properties.  It is
+   possible to construct a name which exactly identifies the properties of
+   the current font.  However, it is theoretically possible that there exists
+   another font with a super set of those properties that would happen to get
+   selected. -- sjt
+
    By "truename" we mean an XLFD-form name which contains no wildcards, yet
    which resolves to *exactly* the same font as the one which we already have
    the (probably wildcarded) name and `XFontStruct' of.
@@ -695,10 +711,12 @@
 #else
   /* But the world I live in is much more perverse. */
   names = XListFonts (dpy, font_name, MAX_FONT_COUNT, &count);
+  /* Find the lexicographic minimum of names[].
+     (#### Should we be comparing case-insensitively?) */
   while (count--)
-    /* !!#### Not Mule-friendly */
-    /* If names[count] is lexicographically less than result, use it.
-       (#### Should we be comparing case-insensitively?) */
+    /* [[ !!#### Not Mule-friendly ]]
+       Doesn't matter, XLFDs are HPC (old) or Latin1 (modern).  If they
+       aren't, who knows what they are? -- sjt */
     if (result == 0 || (strcmp (result, names [count]) < 0))
       result = names [count];
 #endif
@@ -773,29 +791,61 @@
 x_font_instance_truename (Lisp_Font_Instance *f, Error_Behavior errb)
 {
   struct device *d = XDEVICE (f->device);
+  Display *dpy = DEVICE_X_DISPLAY (d);
+  Extbyte *nameext;
+  char* xlfd;
+
+  /* #### restructure this so that we return a valid truename at the end,
+     and otherwise only return when we return something desperate that
+     doesn't get stored for future use. */
+
+#ifdef USE_XFT
+  /* First, try an Xft font. */
+  if (NILP (FONT_INSTANCE_TRUENAME (f)) && FONT_INSTANCE_X_XFTFONT (f))
+    {
+      /* The font is already open, we just unparse. */
+      FcChar8 *res = FcNameUnparse (FONT_INSTANCE_X_XFTFONT (f)->pattern);
+      if (res)
+	{
+	  FONT_INSTANCE_TRUENAME (f) = make_string (res, strlen (res));
+	  free (res);
+	  return FONT_INSTANCE_TRUENAME (f);
+	}
+      else
+	{
+	  maybe_signal_error (Qgui_error,
+			      "Couldn't unparse Xft font to truename",
+			      Qnil, Qfont, errb);
+	  /* used to return Qnil here */
+	}
+    }
+#endif	/* USE_XFT */
+
+  /* OK, fall back to core font. */
+  if (NILP (FONT_INSTANCE_TRUENAME (f))
+      && FONT_INSTANCE_X_FONT (f))
+    {
+      nameext = &xlfd[0];
+      LISP_STRING_TO_EXTERNAL (f->name, nameext, Qx_font_name_encoding);
+
+      FONT_INSTANCE_TRUENAME (f) =
+	x_font_truename (dpy, nameext, FONT_INSTANCE_X_FONT (f));
+    }
 
   if (NILP (FONT_INSTANCE_TRUENAME (f)))
     {
-      Display *dpy = DEVICE_X_DISPLAY (d);
-      {
-	Extbyte *nameext;
+      /* Urk, no luck.  Whine about our bad luck and exit. */
+      Lisp_Object font_instance = wrap_font_instance (f);
+      
+      
+      maybe_signal_error (Qgui_error, "Couldn't determine font truename",
+			  font_instance, Qfont, errb);
+      /* Ok, just this once, return the font name as the truename.
+	 (This is only used by Fequal() right now.) */
+      return f->name;
+    }
 
-	LISP_STRING_TO_EXTERNAL (f->name, nameext, Qx_font_name_encoding);
-	FONT_INSTANCE_TRUENAME (f) =
-	  x_font_truename (dpy, nameext, FONT_INSTANCE_X_FONT (f));
-      }
-      if (NILP (FONT_INSTANCE_TRUENAME (f)))
-	{
-	  Lisp_Object font_instance = wrap_font_instance (f);
-
-
-	  maybe_signal_error (Qgui_error, "Couldn't determine font truename",
-			      font_instance, Qfont, errb);
-	  /* Ok, just this once, return the font name as the truename.
-	     (This is only used by Fequal() right now.) */
-	  return f->name;
-	}
-    }
+  /* Return what we found. */
   return FONT_INSTANCE_TRUENAME (f);
 }
 
@@ -806,8 +856,13 @@
   int i;
   Lisp_Object result = Qnil;
   Display *dpy = DEVICE_X_DISPLAY (d);
-  XFontProp *props = FONT_INSTANCE_X_FONT (f)->properties;
+  XFontProp *props = NULL;
 
+  /* #### really should hack Xft fonts, too
+     Strategy: fontconfig must have an iterator for this purpose. */
+  if (! FONT_INSTANCE_X_FONT (f)) return result;
+
+  props = FONT_INSTANCE_X_FONT (f)->properties;
   for (i = FONT_INSTANCE_X_FONT (f)->n_properties - 1; i >= 0; i--)
     {
       Lisp_Object name, value;
@@ -887,13 +942,31 @@
 #ifdef MULE
 
 static int
-x_font_spec_matches_charset (struct device *UNUSED (d), Lisp_Object charset,
+x_font_spec_matches_charset (struct device * USED_IF_XFT (d),
+			     Lisp_Object charset,
 			     const Ibyte *nonreloc, Lisp_Object reloc,
 			     Bytecount offset, Bytecount length,
 			     int stage)
 {
   if (stage)
+#ifdef USE_XFT
+    {
+      Display *dpy = DEVICE_X_DISPLAY (d);
+      Extbyte *extname;
+      XftFont *rf;
+      const Ibyte *the_nonreloc;
+
+      if (!NILP(reloc))
+	{
+	  the_nonreloc = XSTRING_DATA (reloc);
+	  LISP_STRING_TO_EXTERNAL (reloc, extname, Qx_font_name_encoding);
+	  rf = xft_open_font_by_name (dpy, extname);
+	  return 0;	 /* #### maybe this will compile and run ;) */
+	}
+    }
+#else
     return 0;
+#endif
 
   if (UNBOUNDP (charset))
     return 1;
@@ -901,6 +974,12 @@
      so we just assume the user knows what they're doing in the
      case of ASCII.  For other charsets, you gotta give the
      long form; sorry buster.
+     #### FMH: this screws fontconfig/Xft?
+     STRATEGY: use fontconfig's ability to hack languages and character
+     sets (lang and charset properties).
+     #### Maybe we can use the fontconfig model to eliminate the difference
+     between faces and fonts?  No - it looks like that would be an abuse
+     (fontconfig doesn't know about colors, although Xft does).
      */
   if (EQ (charset, Vcharset_ascii))
     {
@@ -939,6 +1018,151 @@
 			     ERROR_ME, 0) >= 0);
 }
 
+#ifdef USE_XFT
+/* #### debug functions: find a better place for us */
+const char *FcResultToString (FcResult r);
+const char *
+FcResultToString (FcResult r)
+{
+  static char buffer[256];
+  switch (r)
+    {
+    case FcResultMatch:
+      return "FcResultMatch";
+    case FcResultNoMatch:
+      return "FcResultNoMatch";
+    case FcResultTypeMismatch:
+      return "FcResultTypeMismatch";
+    case FcResultNoId:
+      return "FcResultNoId";
+    default:
+      snprintf (buffer, 255, "FcResultUndocumentedValue (%d)", r);
+      return buffer;
+    }
+}
+
+const char *FcTypeOfValueToString (FcValue v);
+const char *
+FcTypeOfValueToString (FcValue v)
+{
+  static char buffer[256];
+  switch (v.type)
+    {
+    case FcTypeMatrix:
+      return "FcTypeMatrix";
+    case FcTypeString:
+      return "FcTypeString";
+    case FcTypeVoid:
+      return "FcTypeVoid";
+    case FcTypeDouble:
+      return "FcTypeDouble";
+    case FcTypeInteger:
+      return "FcTypeInteger";
+    case FcTypeBool:
+      return "FcTypeBool";
+    case FcTypeCharSet:
+      return "FcTypeCharSet";
+    case FcTypeLangSet:
+      return "FcTypeLangSet";
+    /* #### There is no union member of this type, but there are void* and
+       FcPattern* members, as of fontconfig.h FC_VERSION 10002 */
+    case FcTypeFTFace:
+      return "FcTypeFTFace";
+    default:
+      snprintf (buffer, 255, "FcTypeUndocumentedType (%d)", v.type);
+      return buffer;
+    }
+}
+
+static FcCharSet *
+mule_to_fc_charset (Lisp_Object cs)
+{
+  int ucode, i, j;
+  FcCharSet *fccs;
+
+  CHECK_CHARSET (cs);
+  fccs = FcCharSetCreate ();
+  /* #### do we also need to deal with 94 vs. 96 charsets?
+     ie, how are SP and DEL treated in ASCII?  non-graphic should return -1 */
+  if (1 == XCHARSET_DIMENSION (cs))
+    /* Unicode tables are indexed by offsets from ASCII SP, not by ASCII */
+    for (i = 0; i < 96; i++)
+      {
+	ucode = ((int *) XCHARSET_TO_UNICODE_TABLE (cs))[i];
+	if (ucode >= 0)
+	  /* #### should check for allocation failure */
+	  FcCharSetAddChar (fccs, (FcChar32) ucode);
+      }
+  else if (2 == XCHARSET_DIMENSION (cs))
+    /* Unicode tables are indexed by offsets from ASCII SP, not by ASCII */
+    for (i = 0; i < 96; i++)
+      for (j = 0; j < 96; j++)
+      {
+	ucode = ((int **) XCHARSET_TO_UNICODE_TABLE (cs))[i][j];
+	if (ucode >= 0)
+	  /* #### should check for allocation failure */
+	  FcCharSetAddChar (fccs, (FcChar32) ucode);
+      }
+  else
+    {
+      FcCharSetDestroy (fccs);
+      fccs = NULL;
+    }
+  return fccs;
+}
+
+struct charset_reporter {
+  Lisp_Object *charset;
+  /* This is a debug facility, require ASCII. */
+  Extbyte *language;		/* ASCII, please */
+  FcChar8 *rfc3066;		/* ASCII, please */
+};
+
+static struct charset_reporter charset_table[] =
+  {
+    /* #### It's my branch, my favorite charsets get checked first!
+       That's a joke, Son.
+       Ie, I don't know what I'm doing, so my charsets first is as good as
+       any other arbitrary order.  If you have a better idea, speak up! */
+    { &Vcharset_ascii, "English", "en" },
+    { &Vcharset_japanese_jisx0208, "Japanese", "ja" },
+    { &Vcharset_japanese_jisx0212, "Japanese", "ja" },
+    { &Vcharset_katakana_jisx0201, "Japanese", "ja" },
+    { &Vcharset_latin_jisx0201, "Japanese", "ja" },
+    { &Vcharset_japanese_jisx0208_1978, "Japanese", "ja" },
+    { &Vcharset_greek_iso8859_7, "Greek", "el" },
+    /* #### all the Chinese need checking
+       Damn the blood-sucking ISO anyway. */
+    { &Vcharset_chinese_gb2312, "simplified Chinese", "zh-CN" },
+    { &Vcharset_korean_ksc5601, "Korean", "ko" },
+    { &Vcharset_chinese_cns11643_1, "traditional Chinese", "zh-TW" },
+    { &Vcharset_chinese_cns11643_2, "traditional Chinese", "zh-TW" },
+    { &Vcharset_latin_iso8859_1, NULL, NULL },
+    { &Vcharset_latin_iso8859_2, NULL, NULL },
+    { &Vcharset_latin_iso8859_3, NULL, NULL },
+    { &Vcharset_latin_iso8859_4, NULL, NULL },
+    { &Vcharset_latin_iso8859_9, NULL, NULL },
+    { &Vcharset_latin_iso8859_15, NULL, NULL },
+    { &Vcharset_thai_tis620, NULL, NULL },
+    { &Vcharset_arabic_iso8859_6, NULL, NULL },
+    { &Vcharset_hebrew_iso8859_8, "Hebrew", "he" },
+    { &Vcharset_cyrillic_iso8859_5, NULL, NULL },
+    /* #### these probably are not quite right */
+    { &Vcharset_chinese_big5_1, "traditional Chinese", "zh-TW" },
+    { &Vcharset_chinese_big5_2, "traditional Chinese", "zh-TW" },
+    { NULL, NULL, NULL }
+  };
+
+/* Choose appropriate font name for debug messages.
+   Use only in the top half of next function (enforced with #undef). */
+#define SET_DEBUG_FONTNAME(name)                                     \
+  do { name =                                                        \
+         debug_xft > 2 ? eistr_fullname                              \
+                       : debug_xft > 1 ? eistr_longname              \
+                                       : eistr_shortname } while (0)
+
+#endif /* USE_XFT */
+
 /* find a font spec that matches font spec FONT and also matches
    (the registry of) CHARSET. */
 static Lisp_Object
@@ -947,18 +1171,277 @@
 {
   Extbyte **names;
   int count = 0;
-  Lisp_Object result = Qnil;
   const Extbyte *patternext;
+  Lisp_Object result = Qunbound;
   int i;
 
+  /* #### with Xft need to handle second stage here -- sjt
+     Hm.  Or maybe not.  That would be cool. :-) */
   if (stage)
     return Qnil;
 
+#ifdef USE_XFT
+  /* Fontconfig converts all FreeType names to UTF-8 before passing them
+     back to callers---see fcfreetype.c (FcFreeTypeQuery).
+     I don't believe this is documented.  */
+
+  DEBUG_XFT1 (1, "confirming charset for font instance %s\n", 
+	      XSTRING_DATA(font));
+
+  /* #### this looks like a fair amount of work, but the basic design
+     has never been rethought, and it should be
+
+     what really should happen here is that we use FcFontSort (FcFontList?)
+     to get a list of matching fonts, then pick the first (best) one that
+     gives language or repertoire coverage.
+  */
+
+  FcInit ();			/* No-op if already initialized.
+				   In fontconfig 2.3.2, this cannot return
+				   failure, but that looks like a bug.  We
+				   check for it with FcGetCurrentConfig(),
+				   which *can* fail. */
+  if (!FcConfigGetCurrent())	/* #### We should expose FcInit* interfaces
+				   to LISP and decide when to reinitialize
+				   intelligently. */
+    stderr_out ("Failed fontconfig initialization\n");
+  else
+    {
+      FcPattern *fontxft;	/* long-lived, freed at end of this block */
+      FcResult fcresult;
+      FcConfig *fcc;
+      FcChar8 *lang = "en";	/* #### fix this bogus hack! */
+      FcCharSet *fccs = NULL;
+      DECLARE_EISTRING (eistr_shortname); /* user-friendly nickname */
+      DECLARE_EISTRING (eistr_longname);  /* omit FC_LANG and FC_CHARSET */
+      DECLARE_EISTRING (eistr_fullname);  /* everything */
+
+      LISP_STRING_TO_EXTERNAL (font, patternext, Qxft_font_name_encoding);
+      fcc = FcConfigGetCurrent ();
+
+      /* parse the name, do the substitutions, and match the font */
+
+      {
+	FcPattern *p = FcNameParse (patternext);
+	PRINT_XFT_PATTERN (3, "FcNameParse'ed name is %s\n", p);
+	/* #### Next two return FcBool, but what does the return mean? */
+	/* The order is correct according the fontconfig docs. */
+	FcConfigSubstitute (fcc, p, FcMatchPattern);
+	PRINT_XFT_PATTERN (2, "FcConfigSubstitute'ed name is %s\n", p);
+	FcDefaultSubstitute (p);
+	PRINT_XFT_PATTERN (3, "FcDefaultSubstitute'ed name is %s\n", p);
+	/* #### check fcresult of following match? */
+	fontxft = FcFontMatch (fcc, p, &fcresult);
+	/* this prints the long fontconfig name */
+	PRINT_XFT_PATTERN (1, "FcFontMatch'ed name is %s\n", fontxft);
+	FcPatternDestroy (p);
+      }
+
+      /* heuristic to give reasonable-length names for debug reports
+
+         I considered #ifdef SUPPORT_FULL_FONTCONFIG_NAME etc but that's
+	 pointless.  We're just going to remove this code once the font/
+	 face refactoring is done, but until then it could be very useful.
+      */
+      {
+	FcPattern *p = FcFontRenderPrepare (fcc, fontxft, fontxft);
+	FcChar8 *name;
+
+	/* full name, including language coverage and repertoire */
+	name = FcNameUnparse (p);
+	eicpy_ext (eistr_fullname, name, Qxft_font_name_encoding);
+	free (name);
+
+	/* long name, omitting coverage and repertoire, plus a number
+	   of rarely useful properties */
+	FcPatternDel (p, FC_CHARSET);
+	FcPatternDel (p, FC_LANG);
+	FcPatternDel (p, FC_WIDTH);
+	FcPatternDel (p, FC_SPACING);
+	FcPatternDel (p, FC_HINTING);
+	FcPatternDel (p, FC_VERTICAL_LAYOUT);
+	FcPatternDel (p, FC_AUTOHINT);
+	FcPatternDel (p, FC_GLOBAL_ADVANCE);
+	FcPatternDel (p, FC_INDEX);
+	FcPatternDel (p, FC_SCALE);
+	FcPatternDel (p, FC_FONTVERSION);
+	name = FcNameUnparse (p);
+	eicpy_ext (eistr_longname, name, Qxft_font_name_encoding);
+	free (name);
+
+	/* nickname, just family and size, but
+	   "family" names usually have style, slant, and weight */
+	FcPatternDel (p, FC_FOUNDRY);
+	FcPatternDel (p, FC_STYLE);
+	FcPatternDel (p, FC_SLANT);
+	FcPatternDel (p, FC_WEIGHT);
+	FcPatternDel (p, FC_PIXEL_SIZE);
+	FcPatternDel (p, FC_OUTLINE);
+	FcPatternDel (p, FC_SCALABLE);
+	FcPatternDel (p, FC_DPI);
+	name = FcNameUnparse (p);
+	eicpy_ext (eistr_shortname, name, Qxft_font_name_encoding);
+	free (name);
+
+	FcPatternDestroy (p);
+      }
+
+      /* The language approach may better in the long run, but we can't use
+	 it based on Mule charsets; fontconfig doesn't provide a way to test
+	 for unions of languages, etc.  That will require support from the
+	 text module.
+
+	 Optimization:  cache the generated FcCharSet in the Mule charset.
+         Don't forget to destroy it if the Mule charset gets deallocated. */
+
+      struct charset_reporter *cr;
+      for (cr = charset_table;
+	   cr->charset && !EQ (*(cr->charset), charset);
+	   cr++)
+	;
+
+      if (cr->rfc3066)
+	{
+	  if (debug_xft > 0)
+	    {
+	      SET_DEBUG_FONTNAME (name);
+	      CHECKING_LANG (0, eidata(name), cr->language);
+	    }
+	  lang = cr->rfc3066;
+	}
+      else if (cr->charset)
+	{
+	  /* what the hey, build 'em on the fly */
+	  /* #### in the case of error this could return NULL! */
+	  fccs = mule_to_fc_charset (charset);
+	  lang = XSTRING_DATA (XSYMBOL (XCHARSET_NAME (charset))-> name);
+	}
+      else
+	{
+	  /* OK, we fell off the end of the table */
+	  warn_when_safe_lispobj (intern ("xft"), intern ("alert"),
+				  list2 (build_string ("unchecked charset"),
+					 charset));
+	  /* default to "en"
+	     #### THIS IS WRONG, WRONG, WRONG!!
+	     It is why we never fall through to XLFD-checking. */
+	}
+
+      ASSERT_ASCTEXT_ASCII(lang);
+
+      if (fccs)
+	{
+	  /* check for character set coverage */
+	  int i = 0;
+	  FcCharSet *v;
+	  FcResult r = FcPatternGetCharSet (fontxft, FC_CHARSET, i, &v);
+
+	  if (r == FcResultTypeMismatch)
+	    {
+	      DEBUG_XFT0 (0, "Unexpected type return in charset value\n");
+	      result = Qnil;
+	    }
+	  else if (r == FcResultMatch && FcCharSetIsSubset (fccs, v))
+	    {
+	      /* The full pattern with the bitmap coverage is massively
+		 unwieldy, but the shorter names are's just *wrong*.  We
+		 should have the full thing internally as truename, and
+		 filter stuff the client doesn't want to see on output.
+		 Should we just store it into the truename right here? */
+	      if (debug_xft > 0)
+		{
+		  SET_DEBUG_FONTNAME (name);
+		  DEBUG_XFT2 (0, "Xft font %s supports %s\n",
+			      eidata(name), lang);
+		}
+#ifdef RETURN_LONG_FONTCONFIG_NAMES
+	      result = eimake_string(eistr_fullname);
+#else
+	      result = eimake_string(eistr_longname);
+#endif
+	    }
+	  else
+	    {
+	      if (debug_xft > 0)
+		{
+		  SET_DEBUG_FONTNAME (name);
+		  DEBUG_XFT2 (0, "Xft font %s doesn't support %s\n",
+			      eidata(name), lang);
+		}
+	      result = Qnil;
+	    }
+
+	  /* clean up */
+	  FcCharSetDestroy (fccs);
+	}
+      else
+	{
+	  /* check for language coverage */
+	  int i = 0;
+	  FcValue v;
+	  /* the main event */
+	  FcResult r = FcPatternGet (fontxft, FC_LANG, i, &v);
+
+	  if (r == FcResultMatch)
+	    {
+	      if (v.type != FcTypeLangSet) /* excessive paranoia */
+		{
+		  ASSERT_ASCTEXT_ASCII(FcTypeOfValueToString(v));
+		  /* Urk!  Fall back and punt to core font. */
+		  DEBUG_XFT1 (0, "Unexpected type of lang value (%s)\n",
+			       FcTypeOfValueToString (v));
+		  result = Qnil;
+		}
+	      else if (FcLangSetHasLang (v.u.l, lang) != FcLangDifferentLang)
+		{
+		  if (debug_xft > 0)
+		    {
+		      SET_DEBUG_FONTNAME (name);
+		      DEBUG_XFT2 (0, "Xft font %s supports %s\n",
+				  eidata(name), lang);
+		    }
+#ifdef RETURN_LONG_FONTCONFIG_NAMES
+		  result = eimake_string(eistr_fullname);
+#else
+		  result = eimake_string(eistr_longname);
+#endif
+		}
+	      else
+		{
+		  if (debug_xft > 0)
+		    {
+		      SET_DEBUG_FONTNAME (name);
+		      DEBUG_XFT2 (0, "Xft font %s doesn't support %s\n",
+				  eidata(name), lang);
+		    }
+		  result = Qnil;
+		}
+	    }
+	  else
+	    {
+	      ASSERT_ASCTEXT_ASCII(FcResultToString(r));
+	      DEBUG_XFT1 (0, "Getting lang: unexpected result=%s\n",
+			  FcResultToString (r));
+	      result = Qnil;
+	    }
+	}
+
+      /* clean up and maybe return */
+      FcPatternDestroy (fontxft);
+      if (!UNBOUNDP (result))
+	return result;
+    }
+
+  DEBUG_XFT1 (0, "shit happens, try X11 charset match for %s\n", 
+	      XSTRING_DATA(font));
+#undef SET_DEBUG_FONTNAME
+#endif /* USE_XFT */
+
   LISP_STRING_TO_EXTERNAL (font, patternext, Qx_font_name_encoding);
-
   names = XListFonts (DEVICE_X_DISPLAY (XDEVICE (device)),
 		      patternext, MAX_FONT_COUNT, &count);
   /* #### This code seems awfully bogus -- mrb */
+  /* #### fontconfig does it better -- sjt */
   for (i = 0; i < count; i ++)
     {
       const Ibyte *intname;
@@ -1041,6 +1524,10 @@
 cause problems this is set to nil by default.
 */ );
   x_handle_non_fully_specified_fonts = 0;
+
+#ifdef USE_XFT
+  Fprovide (intern ("xft-fonts"));
+#endif
 }
 
 void