Mercurial > hg > xemacs-beta
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