This commit was manufactured by cvs2svn to create branch 'chise-r21-4-19'.
[chise/xemacs-chise.git-] / src / objects-x.c
diff --git a/src/objects-x.c b/src/objects-x.c
new file mode 100644 (file)
index 0000000..25a19c9
--- /dev/null
@@ -0,0 +1,1054 @@
+/* X-specific Lisp objects.
+   Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1995 Board of Trustees, University of Illinois.
+   Copyright (C) 1995 Tinker Systems.
+   Copyright (C) 1995, 1996, 2000 Ben Wing.
+   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. */
+
+/* Authors: Jamie Zawinski, Chuck Thompson, Ben Wing */
+
+/* This file Mule-ized by Ben Wing, 7-10-00. */
+
+#include <config.h>
+#include "lisp.h"
+
+#include "console-x.h"
+#include "objects-x.h"
+
+#include "buffer.h"
+#include "device.h"
+#include "insdel.h"
+
+int x_handle_non_fully_specified_fonts;
+
+\f
+/************************************************************************/
+/*                          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->class == DirectColor || visual->class == 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)
+{
+  Display *dpy   = DEVICE_X_DISPLAY  (d);
+  Colormap cmap  = DEVICE_X_COLORMAP (d);
+  Visual *visual = DEVICE_X_VISUAL   (d);
+  int result;
+
+  xzero (*color);
+  {
+    const Extbyte *extname;
+
+    LISP_STRING_TO_EXTERNAL (name, extname, Qx_color_name_encoding);
+    result = XParseColor (dpy, cmap, extname, color);
+  }
+  if (!result)
+    {
+      maybe_signal_simple_error ("Unrecognized color", name, Qcolor, errb);
+      return 0;
+    }
+  result = allocate_nearest_color (dpy, cmap, visual, color);
+  if (!result)
+    {
+      maybe_signal_simple_error ("Couldn't allocate color", name, Qcolor,
+                                errb);
+      return 0;
+    }
+
+  return result;
+}
+
+static int
+x_initialize_color_instance (Lisp_Color_Instance *c, Lisp_Object name,
+                            Lisp_Object device, Error_behavior errb)
+{
+  XColor color;
+  int result;
+
+  result = x_parse_nearest_color (XDEVICE (device), &color, name, errb);
+
+  if (!result)
+    return 0;
+
+  /* Don't allocate the data until we're sure that we will succeed,
+     or the finalize method may get fucked. */
+  c->data = xnew (struct x_color_instance_data);
+  if (result == 3)
+    COLOR_INSTANCE_X_DEALLOC (c) = 0;
+  else
+    COLOR_INSTANCE_X_DEALLOC (c) = 1;
+  COLOR_INSTANCE_X_COLOR (c) = color;
+  return 1;
+}
+
+static void
+x_print_color_instance (Lisp_Color_Instance *c,
+                       Lisp_Object printcharfun,
+                       int escapeflag)
+{
+  char buf[100];
+  XColor color = COLOR_INSTANCE_X_COLOR (c);
+  sprintf (buf, " %ld=(%X,%X,%X)",
+          color.pixel, color.red, color.green, color.blue);
+  write_c_string (buf, printcharfun);
+}
+
+static void
+x_finalize_color_instance (Lisp_Color_Instance *c)
+{
+  if (c->data)
+    {
+      if (DEVICE_LIVE_P (XDEVICE (c->device)))
+       {
+         if (COLOR_INSTANCE_X_DEALLOC (c))
+           {
+             XFreeColors (DEVICE_X_DISPLAY (XDEVICE (c->device)),
+                          DEVICE_X_COLORMAP (XDEVICE (c->device)),
+                          &COLOR_INSTANCE_X_COLOR (c).pixel, 1, 0);
+           }
+       }
+      xfree (c->data);
+      c->data = 0;
+    }
+}
+
+/* Color instances are equal if they resolve to the same color on the
+   screen (have the same RGB values).  I imagine that
+   "same RGB values" == "same cell in the colormap."  Arguably we should
+   be comparing their names or pixel values instead. */
+
+static int
+x_color_instance_equal (Lisp_Color_Instance *c1,
+                       Lisp_Color_Instance *c2,
+                       int depth)
+{
+  XColor color1 = COLOR_INSTANCE_X_COLOR (c1);
+  XColor color2 = COLOR_INSTANCE_X_COLOR (c2);
+  return ((color1.red == color2.red) &&
+         (color1.green == color2.green) &&
+         (color1.blue == color2.blue));
+}
+
+static unsigned long
+x_color_instance_hash (Lisp_Color_Instance *c, int depth)
+{
+  XColor color = COLOR_INSTANCE_X_COLOR (c);
+  return HASH3 (color.red, color.green, color.blue);
+}
+
+static Lisp_Object
+x_color_instance_rgb_components (Lisp_Color_Instance *c)
+{
+  XColor color = COLOR_INSTANCE_X_COLOR (c);
+  return (list3 (make_int (color.red),
+                make_int (color.green),
+                make_int (color.blue)));
+}
+
+static int
+x_valid_color_name_p (struct device *d, Lisp_Object color)
+{
+  XColor c;
+  Display *dpy = DEVICE_X_DISPLAY (d);
+  Colormap cmap = DEVICE_X_COLORMAP (d);
+  const Extbyte *extname;
+
+  LISP_STRING_TO_EXTERNAL (color, extname, Qx_color_name_encoding);
+
+  return XParseColor (dpy, cmap, extname, &c);
+}
+
+\f
+/************************************************************************/
+/*                           font instances                             */
+/************************************************************************/
+
+static int
+x_initialize_font_instance (Lisp_Font_Instance *f, Lisp_Object name,
+                           Lisp_Object device, Error_behavior errb)
+{
+  Display *dpy = DEVICE_X_DISPLAY (XDEVICE (device));
+  XFontStruct *xf;
+  const Extbyte *extname;
+
+  LISP_STRING_TO_EXTERNAL (f->name, extname, Qx_font_name_encoding);
+  xf = XLoadQueryFont (dpy, extname);
+
+  if (!xf)
+    {
+      maybe_signal_simple_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_simple_error ("X font is too small", 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. */
+  f->data = xnew (struct x_font_instance_data);
+  FONT_INSTANCE_X_TRUENAME (f) = Qnil;
+  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> */
+    unsigned int def_char = 'n'; /*xf->default_char;*/
+    unsigned int byte1, byte2;
+
+  once_more:
+    byte1 = def_char >> 8;
+    byte2 = def_char & 0xFF;
+
+    if (xf->per_char)
+      {
+       /* Old versions of the R5 font server have garbage (>63k) as
+          def_char. 'n' might not be a valid character. */
+       if (byte1 < xf->min_byte1         ||
+           byte1 > xf->max_byte1         ||
+           byte2 < xf->min_char_or_byte2 ||
+           byte2 > 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;
+      }
+    else
+      f->width = xf->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)
+      {
+       if (def_char == xf->default_char)
+         f->width = xf->max_bounds.width;
+       else
+         {
+           def_char = xf->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));
+
+  return 1;
+}
+
+static void
+x_mark_font_instance (Lisp_Font_Instance *f)
+{
+  mark_object (FONT_INSTANCE_X_TRUENAME (f));
+}
+
+static void
+x_print_font_instance (Lisp_Font_Instance *f,
+                      Lisp_Object printcharfun,
+                      int escapeflag)
+{
+  char buf[200];
+  sprintf (buf, " 0x%lx", (unsigned long) FONT_INSTANCE_X_FONT (f)->fid);
+  write_c_string (buf, printcharfun);
+}
+
+static void
+x_finalize_font_instance (Lisp_Font_Instance *f)
+{
+
+  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));
+       }
+      xfree (f->data);
+      f->data = 0;
+    }
+}
+
+/* Determining the truename of a font is hard.  (Big surprise.)
+
+   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.
+
+   One might think that the first font returned by XListFonts would be the one
+   that XOpenFont would pick.  Apparently this is the case on some servers,
+   but not on others.  It would seem not to be specified.
+
+   The MIT R5 server sometimes appears to be picking the lexicographically
+   smallest font which matches the name (thus picking "adobe" fonts before
+   "bitstream" fonts even if the bitstream fonts are earlier in the path, and
+   also picking 100dpi adobe fonts over 75dpi adobe fonts even though the
+   75dpi are in the path earlier) but sometimes appears to be doing something
+   else entirely (for example, removing the bitstream fonts from the path will
+   cause the 75dpi adobe fonts to be used instead of the 100dpi, even though
+   their relative positions in the path (and their names!) have not changed).
+
+   The documentation for XSetFontPath() seems to indicate that the order of
+   entries in the font path means something, but it's pretty noncommittal about
+   it, and the spirit of the law is apparently not being obeyed...
+
+   All the fonts I've seen have a property named `FONT' which contains the
+   truename of the font.  However, there are two problems with using this: the
+   first is that the X Protocol Document is quite explicit that all properties
+   are optional, so we can't depend on it being there.  The second is that
+   it's conceivable that this alleged truename isn't actually accessible as a
+   font, due to some difference of opinion between the font designers and
+   whoever installed the font on the system.
+
+   So, our first attempt is to look for a FONT property, and then verify that
+   the name there is a valid name by running XListFonts on it.  There's still
+   the potential that this could be true but we could still be being lied to,
+   but that seems pretty remote.
+
+     Late breaking news: I've gotten reports that SunOS 4.1.3U1
+     with OpenWound 3.0 has a font whose truename is really
+     "-Adobe-Courier-Medium-R-Normal--12-120-75-75-M-70-ISO8859-1"
+     but whose FONT property contains "Courier".
+
+     So we disbelieve the FONT property unless it begins with a dash and
+     is more than 30 characters long.  X Windows: The defacto substandard.
+     X Windows: Complex nonsolutions to simple nonproblems.  X Windows:
+     Live the nightmare.
+
+   If the FONT property doesn't exist, then we try and construct an XLFD name
+   out of the other font properties (FOUNDRY, FAMILY_NAME, WEIGHT_NAME, etc).
+   This is necessary at least for some versions of OpenWound.  But who knows
+   what the future will bring.
+
+   If that doesn't work, then we use XListFonts and either take the first font
+   (which I think is the most sensible thing) or we find the lexicographically
+   least, depending on whether the preprocessor constant `XOPENFONT_SORTS' is
+   defined.  This sucks because the two behaviors are a property of the server
+   being used, not the architecture on which emacs has been compiled.  Also,
+   as I described above, sorting isn't ALWAYS what the server does.  Really it
+   does something seemingly random.  There is no reliable way to win if the
+   FONT property isn't present.
+
+   Another possibility which I haven't bothered to implement would be to map
+   over all of the matching fonts and find the first one that has the same
+   character metrics as the font we already have loaded.  Even if this didn't
+   return exactly the same font, it would at least return one whose characters
+   were the same sizes, which would probably be good enough.
+
+   More late-breaking news: on RS/6000 AIX 3.2.4, the expression
+        XLoadQueryFont (dpy, "-*-Fixed-Medium-R-*-*-*-130-75-75-*-*-ISO8859-1")
+   actually returns the font
+        -Misc-Fixed-Medium-R-Normal--13-120-75-75-C-80-ISO8859-1
+   which is crazy, because that font doesn't even match that pattern!  It is
+   also not included in the output produced by `xlsfonts' with that pattern.
+
+   So this is yet another example of XListFonts() and XOpenFont() using
+   completely different algorithms.  This, however, is a goofier example of
+   this bug, because in this case, it's not just the search order that is
+   different -- the sets don't even intersect.
+
+   If anyone has any better ideas how to do this, or any insights on what it is
+   that the various servers are actually doing, please let me know!  -- jwz. */
+
+static int
+valid_x_font_name_p (Display *dpy, Extbyte *name)
+{
+  /* Maybe this should be implemented by calling XLoadFont and trapping
+     the error.  That would be a lot of work, and wasteful as hell, but
+     might be more correct.
+   */
+  int nnames = 0;
+  Extbyte **names = 0;
+  if (! name)
+    return 0;
+  names = XListFonts (dpy, name, 1, &nnames);
+  if (names)
+    XFreeFontNames (names);
+  return (nnames != 0);
+}
+
+static Extbyte *
+truename_via_FONT_prop (Display *dpy, XFontStruct *font)
+{
+  unsigned long value = 0;
+  Extbyte *result = 0;
+  if (XGetFontProperty (font, XA_FONT, &value))
+    result = XGetAtomName (dpy, value);
+  /* result is now 0, or the string value of the FONT property. */
+  if (result)
+    {
+      /* Verify that result is an XLFD name (roughly...) */
+      if (result [0] != '-' || strlen (result) < (unsigned int) 30)
+       {
+         XFree (result);
+         result = 0;
+       }
+    }
+  return result;       /* this must be freed by caller if non-0 */
+}
+
+static Extbyte *
+truename_via_random_props (Display *dpy, XFontStruct *font)
+{
+  struct device *d = get_device_from_display (dpy);
+  unsigned long value = 0;
+  Extbyte *foundry, *family, *weight, *slant, *setwidth, *add_style;
+  unsigned long pixel, point, res_x, res_y;
+  Extbyte *spacing;
+  unsigned long avg_width;
+  Extbyte *registry, *encoding;
+  Extbyte composed_name [2048];
+  int ok = 0;
+  Extbyte *result;
+
+#define get_string(atom,var)                           \
+  if (XGetFontProperty (font, (atom), &value))         \
+    var = XGetAtomName (dpy, value);                   \
+  else {                                               \
+    var = 0;                                           \
+    goto FAIL; }
+#define get_number(atom,var)                           \
+  if (!XGetFontProperty (font, (atom), &var) ||                \
+      var > 999)                                       \
+    goto FAIL;
+
+  foundry = family = weight = slant = setwidth = 0;
+  add_style = spacing = registry = encoding = 0;
+
+  get_string (DEVICE_XATOM_FOUNDRY (d), foundry);
+  get_string (DEVICE_XATOM_FAMILY_NAME (d), family);
+  get_string (DEVICE_XATOM_WEIGHT_NAME (d), weight);
+  get_string (DEVICE_XATOM_SLANT (d), slant);
+  get_string (DEVICE_XATOM_SETWIDTH_NAME (d), setwidth);
+  get_string (DEVICE_XATOM_ADD_STYLE_NAME (d), add_style);
+  get_number (DEVICE_XATOM_PIXEL_SIZE (d), pixel);
+  get_number (DEVICE_XATOM_POINT_SIZE (d), point);
+  get_number (DEVICE_XATOM_RESOLUTION_X (d), res_x);
+  get_number (DEVICE_XATOM_RESOLUTION_Y (d), res_y);
+  get_string (DEVICE_XATOM_SPACING (d), spacing);
+  get_number (DEVICE_XATOM_AVERAGE_WIDTH (d), avg_width);
+  get_string (DEVICE_XATOM_CHARSET_REGISTRY (d), registry);
+  get_string (DEVICE_XATOM_CHARSET_ENCODING (d), encoding);
+#undef get_number
+#undef get_string
+
+  sprintf (composed_name,
+          "-%s-%s-%s-%s-%s-%s-%ld-%ld-%ld-%ld-%s-%ld-%s-%s",
+          foundry, family, weight, slant, setwidth, add_style, pixel,
+          point, res_x, res_y, spacing, avg_width, registry, encoding);
+  ok = 1;
+
+ FAIL:
+  if (ok)
+    {
+      int L = strlen (composed_name) + 1;
+      result = (Extbyte *) xmalloc (L);
+      strncpy (result, composed_name, L);
+    }
+  else
+    result = 0;
+
+  if (foundry) XFree (foundry);
+  if (family) XFree (family);
+  if (weight) XFree (weight);
+  if (slant) XFree (slant);
+  if (setwidth) XFree (setwidth);
+  if (add_style) XFree (add_style);
+  if (spacing) XFree (spacing);
+  if (registry) XFree (registry);
+  if (encoding) XFree (encoding);
+
+  return result;
+}
+
+/* Unbounded, for sufficiently small values of infinity... */
+#define MAX_FONT_COUNT INT_MAX
+
+static Extbyte *
+truename_via_XListFonts (Display *dpy, Extbyte *font_name)
+{
+  Extbyte *result = 0;
+  Extbyte **names;
+  int count = 0;
+
+#ifndef XOPENFONT_SORTS
+  /* In a sensible world, the first font returned by XListFonts()
+     would be the font that XOpenFont() would use.  */
+  names = XListFonts (dpy, font_name, 1, &count);
+  if (count) result = names [0];
+#else
+  /* But the world I live in is much more perverse. */
+  names = XListFonts (dpy, font_name, MAX_FONT_COUNT, &count);
+  while (count--)
+    /* !!#### Not Mule-friendly */
+    /* If names[count] is lexicographically less than result, use it.
+       (#### Should we be comparing case-insensitively?) */
+    if (result == 0 || (strcmp (result, names [count]) < 0))
+      result = names [count];
+#endif
+
+  if (result)
+    result = xstrdup (result);
+  if (names)
+    XFreeFontNames (names);
+
+  return result;       /* this must be freed by caller if non-0 */
+}
+
+static Lisp_Object
+x_font_truename (Display *dpy, Extbyte *name, XFontStruct *font)
+{
+  Extbyte *truename_FONT = 0;
+  Extbyte *truename_random = 0;
+  Extbyte *truename = 0;
+
+  /* The search order is:
+     - if FONT property exists, and is a valid name, return it.
+     - if the other props exist, and add up to a valid name, return it.
+     - if we find a matching name with XListFonts, return it.
+     - if FONT property exists, return it regardless.
+     - if other props exist, return the resultant name regardless.
+     - else return 0.
+   */
+
+  truename = truename_FONT = truename_via_FONT_prop (dpy, font);
+  if (truename && !valid_x_font_name_p (dpy, truename))
+    truename = 0;
+  if (!truename)
+    truename = truename_random = truename_via_random_props (dpy, font);
+  if (truename && !valid_x_font_name_p (dpy, truename))
+    truename = 0;
+  if (!truename && name)
+    truename = truename_via_XListFonts (dpy, name);
+
+  if (!truename)
+    {
+      /* Gag - we weren't able to find a seemingly-valid truename.
+        Well, maybe we're on one of those braindead systems where
+        XListFonts() and XLoadFont() are in violent disagreement.
+        If we were able to compute a truename, try using that even
+        if evidence suggests that it's not a valid name - because
+        maybe it is, really, and that's better than nothing.
+        X Windows: You'll envy the dead.
+       */
+      if (truename_FONT)
+       truename = truename_FONT;
+      else if (truename_random)
+       truename = truename_random;
+    }
+
+  /* One or both of these are not being used - free them. */
+  if (truename_FONT && truename_FONT != truename)
+    XFree (truename_FONT);
+  if (truename_random && truename_random != truename)
+    XFree (truename_random);
+
+  if (truename)
+    {
+      Lisp_Object result = build_ext_string (truename, Qx_font_name_encoding);
+      XFree (truename);
+      return result;
+    }
+  else
+    return Qnil;
+}
+
+static Lisp_Object
+x_font_instance_truename (Lisp_Font_Instance *f, Error_behavior errb)
+{
+  struct device *d = XDEVICE (f->device);
+
+  if (NILP (FONT_INSTANCE_X_TRUENAME (f)))
+    {
+      Display *dpy = DEVICE_X_DISPLAY (d);
+      {
+       Extbyte *nameext;
+
+       LISP_STRING_TO_EXTERNAL (f->name, nameext, Qx_font_name_encoding);
+       FONT_INSTANCE_X_TRUENAME (f) =
+         x_font_truename (dpy, nameext, FONT_INSTANCE_X_FONT (f));
+      }
+      if (NILP (FONT_INSTANCE_X_TRUENAME (f)))
+       {
+         Lisp_Object font_instance;
+         XSETFONT_INSTANCE (font_instance, f);
+
+         maybe_signal_simple_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 FONT_INSTANCE_X_TRUENAME (f);
+}
+
+static Lisp_Object
+x_font_instance_properties (Lisp_Font_Instance *f)
+{
+  struct device *d = XDEVICE (f->device);
+  int i;
+  Lisp_Object result = Qnil;
+  Display *dpy = DEVICE_X_DISPLAY (d);
+  XFontProp *props = FONT_INSTANCE_X_FONT (f)->properties;
+
+  for (i = FONT_INSTANCE_X_FONT (f)->n_properties - 1; i >= 0; i--)
+    {
+      Lisp_Object name, value;
+      Atom atom = props [i].name;
+      Bufbyte *name_str = 0;
+      size_t name_len;
+      Extbyte *namestrext = XGetAtomName (dpy, atom);
+
+      if (namestrext)
+       TO_INTERNAL_FORMAT (C_STRING, namestrext,
+                           ALLOCA, (name_str, name_len),
+                           Qx_atom_name_encoding);
+
+      name = (name_str ? intern ((char *) name_str) : Qnil);
+      if (name_str &&
+         (atom == XA_FONT ||
+          atom == DEVICE_XATOM_FOUNDRY (d) ||
+          atom == DEVICE_XATOM_FAMILY_NAME (d) ||
+          atom == DEVICE_XATOM_WEIGHT_NAME (d) ||
+          atom == DEVICE_XATOM_SLANT (d) ||
+          atom == DEVICE_XATOM_SETWIDTH_NAME (d) ||
+          atom == DEVICE_XATOM_ADD_STYLE_NAME (d) ||
+          atom == DEVICE_XATOM_SPACING (d) ||
+          atom == DEVICE_XATOM_CHARSET_REGISTRY (d) ||
+          atom == DEVICE_XATOM_CHARSET_ENCODING (d) ||
+          !bufbyte_strcmp (name_str, "CHARSET_COLLECTIONS") ||
+          !bufbyte_strcmp (name_str, "FONTNAME_REGISTRY") ||
+          !bufbyte_strcmp (name_str, "CLASSIFICATION") ||
+          !bufbyte_strcmp (name_str, "COPYRIGHT") ||
+          !bufbyte_strcmp (name_str, "DEVICE_FONT_NAME") ||
+          !bufbyte_strcmp (name_str, "FULL_NAME") ||
+          !bufbyte_strcmp (name_str, "MONOSPACED") ||
+          !bufbyte_strcmp (name_str, "QUALITY") ||
+          !bufbyte_strcmp (name_str, "RELATIVE_SET") ||
+          !bufbyte_strcmp (name_str, "RELATIVE_WEIGHT") ||
+          !bufbyte_strcmp (name_str, "STYLE")))
+       {
+         Extbyte *val_str = XGetAtomName (dpy, props [i].card32);
+
+         value = (val_str ? build_ext_string (val_str, Qx_atom_name_encoding)
+                  : Qnil);
+       }
+      else
+       value = make_int (props [i].card32);
+      if (namestrext) XFree (namestrext);
+      result = Fcons (Fcons (name, value), result);
+    }
+  return result;
+}
+
+static Lisp_Object
+x_list_fonts (Lisp_Object pattern, Lisp_Object device)
+{
+  Extbyte **names;
+  int count = 0;
+  Lisp_Object result = Qnil;
+  const Extbyte *patternext;
+
+  LISP_STRING_TO_EXTERNAL (pattern, patternext, Qx_font_name_encoding);
+
+  names = XListFonts (DEVICE_X_DISPLAY (XDEVICE (device)),
+                     patternext, MAX_FONT_COUNT, &count);
+  while (count--)
+    result = Fcons (build_ext_string (names[count], Qx_font_name_encoding),
+                   result);
+  if (names)
+    XFreeFontNames (names);
+  return result;
+}
+
+#ifdef MULE
+
+static int
+x_font_spec_matches_charset (struct device *d, Lisp_Object charset,
+                            const Bufbyte *nonreloc, Lisp_Object reloc,
+                            Bytecount offset, Bytecount length)
+{
+  if (UNBOUNDP (charset))
+    return 1;
+  /* Hack! Short font names don't have the registry in them,
+     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.
+     */
+  if (EQ (charset, Vcharset_ascii))
+    {
+      const Bufbyte *the_nonreloc = nonreloc;
+      int i;
+      Bytecount the_length = length;
+
+      if (!the_nonreloc)
+       the_nonreloc = XSTRING_DATA (reloc);
+      fixup_internal_substring (nonreloc, reloc, offset, &the_length);
+      the_nonreloc += offset;
+      if (!memchr (the_nonreloc, '*', the_length))
+       {
+         for (i = 0;; i++)
+           {
+             const Bufbyte *new_nonreloc = (const Bufbyte *)
+               memchr (the_nonreloc, '-', the_length);
+             if (!new_nonreloc)
+               break;
+             new_nonreloc++;
+             the_length -= new_nonreloc - the_nonreloc;
+             the_nonreloc = new_nonreloc;
+           }
+
+         /* If it has less than 5 dashes, it's a short font.
+            Of course, long fonts always have 14 dashes or so, but short
+            fonts never have more than 1 or 2 dashes, so this is some
+            sort of reasonable heuristic. */
+         if (i < 5)
+           return 1;
+       }
+    }
+
+  return (fast_string_match (XCHARSET_REGISTRY (charset),
+                            nonreloc, reloc, offset, length, 1,
+                            ERROR_ME, 0) >= 0);
+}
+
+/* find a font spec that matches font spec FONT and also matches
+   (the registry of) CHARSET. */
+static Lisp_Object
+x_find_charset_font (Lisp_Object device, Lisp_Object font, Lisp_Object charset)
+{
+  Extbyte **names;
+  int count = 0;
+  Lisp_Object result = Qnil;
+  const Extbyte *patternext;
+  int i;
+
+  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 */
+  for (i = 0; i < count; i ++)
+    {
+      const Bufbyte *intname;
+      Bytecount intlen;
+
+      TO_INTERNAL_FORMAT (C_STRING, names[i],
+                         ALLOCA, (intname, intlen),
+                         Qx_font_name_encoding);
+      if (x_font_spec_matches_charset (XDEVICE (device), charset,
+                                      intname, Qnil, 0, -1))
+       {
+         result = make_string (intname, intlen);
+         break;
+       }
+    }
+
+  if (names)
+    XFreeFontNames (names);
+
+  /* Check for a short font name. */
+  if (NILP (result)
+      && x_font_spec_matches_charset (XDEVICE (device), charset, 0,
+                                     font, 0, -1))
+    return font;
+
+  return result;
+}
+
+#endif /* MULE */
+
+\f
+/************************************************************************/
+/*                            initialization                            */
+/************************************************************************/
+
+void
+syms_of_objects_x (void)
+{
+}
+
+void
+console_type_create_objects_x (void)
+{
+  /* object methods */
+
+  CONSOLE_HAS_METHOD (x, initialize_color_instance);
+  CONSOLE_HAS_METHOD (x, print_color_instance);
+  CONSOLE_HAS_METHOD (x, finalize_color_instance);
+  CONSOLE_HAS_METHOD (x, color_instance_equal);
+  CONSOLE_HAS_METHOD (x, color_instance_hash);
+  CONSOLE_HAS_METHOD (x, color_instance_rgb_components);
+  CONSOLE_HAS_METHOD (x, valid_color_name_p);
+
+  CONSOLE_HAS_METHOD (x, initialize_font_instance);
+  CONSOLE_HAS_METHOD (x, mark_font_instance);
+  CONSOLE_HAS_METHOD (x, print_font_instance);
+  CONSOLE_HAS_METHOD (x, finalize_font_instance);
+  CONSOLE_HAS_METHOD (x, font_instance_truename);
+  CONSOLE_HAS_METHOD (x, font_instance_properties);
+  CONSOLE_HAS_METHOD (x, list_fonts);
+#ifdef MULE
+  CONSOLE_HAS_METHOD (x, find_charset_font);
+  CONSOLE_HAS_METHOD (x, font_spec_matches_charset);
+#endif
+}
+
+void
+vars_of_objects_x (void)
+{
+  DEFVAR_BOOL ("x-handle-non-fully-specified-fonts",
+              &x_handle_non_fully_specified_fonts /*
+If this is true then fonts which do not have all characters specified
+will be considered to be proportional width even if they are actually
+fixed-width.  If this is not done then characters which are supposed to
+have 0 width may appear to actually have some width.
+
+Note:  While setting this to t guarantees correct output in all
+circumstances, it also causes a noticeable performance hit when using
+fixed-width fonts.  Since most people don't use characters which could
+cause problems this is set to nil by default.
+*/ );
+  x_handle_non_fully_specified_fonts = 0;
+}
+
+void
+Xatoms_of_objects_x (struct device *d)
+{
+  Display *D = DEVICE_X_DISPLAY (d);
+
+  DEVICE_XATOM_FOUNDRY         (d) = XInternAtom (D, "FOUNDRY",         False);
+  DEVICE_XATOM_FAMILY_NAME     (d) = XInternAtom (D, "FAMILY_NAME",     False);
+  DEVICE_XATOM_WEIGHT_NAME     (d) = XInternAtom (D, "WEIGHT_NAME",     False);
+  DEVICE_XATOM_SLANT           (d) = XInternAtom (D, "SLANT",           False);
+  DEVICE_XATOM_SETWIDTH_NAME   (d) = XInternAtom (D, "SETWIDTH_NAME",   False);
+  DEVICE_XATOM_ADD_STYLE_NAME  (d) = XInternAtom (D, "ADD_STYLE_NAME",  False);
+  DEVICE_XATOM_PIXEL_SIZE      (d) = XInternAtom (D, "PIXEL_SIZE",      False);
+  DEVICE_XATOM_POINT_SIZE      (d) = XInternAtom (D, "POINT_SIZE",      False);
+  DEVICE_XATOM_RESOLUTION_X    (d) = XInternAtom (D, "RESOLUTION_X",    False);
+  DEVICE_XATOM_RESOLUTION_Y    (d) = XInternAtom (D, "RESOLUTION_Y",    False);
+  DEVICE_XATOM_SPACING         (d) = XInternAtom (D, "SPACING",         False);
+  DEVICE_XATOM_AVERAGE_WIDTH   (d) = XInternAtom (D, "AVERAGE_WIDTH",   False);
+  DEVICE_XATOM_CHARSET_REGISTRY(d) = XInternAtom (D, "CHARSET_REGISTRY",False);
+  DEVICE_XATOM_CHARSET_ENCODING(d) = XInternAtom (D, "CHARSET_ENCODING",False);
+}