From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / utils / xft.c
1 /* xscreensaver, Copyright (c) 2014-2015 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* Compatibility layer using XDrawString, XDrawString16() or Xutf8DrawString().
13    This layer is used by X11 systems without Xft, and by MacOS / iOS.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #ifndef HAVE_XFT
21
22 # include "utils.h"
23 # include "resources.h"
24 # include "xft.h"
25 # include "utf8wc.h"
26
27 extern const char *progname;
28
29 struct _XftDraw {
30   Display   *dpy;
31   Drawable  drawable;
32   GC gc;
33   unsigned long pixel;
34   Font fid;
35   Visual *visual;
36   Colormap  colormap;
37 };
38
39
40 XftFont *
41 XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd)
42 {
43   XftFont *ff = (XftFont *) calloc (1, sizeof(*ff));
44
45   if (!dpy || !xlfd) abort();
46   if (!ff) return 0;
47   ff->xfont = XLoadQueryFont (dpy, xlfd);
48   if (!ff->xfont)
49     {
50       free (ff);
51       return 0;
52     }
53
54   ff->name = strdup (xlfd);
55   ff->ascent  = ff->xfont->ascent;
56   ff->descent = ff->xfont->descent;
57   ff->height = ff->ascent + ff->descent;
58
59 # ifdef HAVE_XUTF8DRAWSTRING
60   {
61     unsigned i;
62
63     // In the event of -*-random-* (under JWXYZ), get the actual XLFD,
64     // otherwise we'll get another random font that doesn't match ff->xfont.
65     char *xlfd_resolved = NULL;
66
67     char **missing_charset_list_return;
68     int missing_charset_count_return;
69     char *def_string_return;
70
71     char *ss;
72
73     for (i = 0; i != ff->xfont->n_properties; ++i) {
74       if (ff->xfont->properties[i].name == XA_FONT) {
75         xlfd_resolved = XGetAtomName (dpy, ff->xfont->properties[i].card32);
76         if (xlfd_resolved)
77           xlfd = xlfd_resolved;
78         break;
79       }
80     }
81
82     ss = (char *) malloc (strlen(xlfd) + 10);
83     strcpy (ss, xlfd);
84     strcat (ss, ",*");
85     ff->fontset = XCreateFontSet (dpy, ss,
86                                   &missing_charset_list_return,
87                                   &missing_charset_count_return,
88                                   &def_string_return);
89
90 # if 0
91     {
92       int i;
93       for (i = 0; i < missing_charset_count_return; i++)
94         fprintf (stderr, "%s: missing charset: %s\n",
95                  ss, missing_charset_list_return[i]);
96     }
97 # endif
98
99     /* Apparently this is not to be freed. */
100     /* if (def_string_return) XFree (def_string_return); */
101
102     if (missing_charset_list_return)
103       XFreeStringList (missing_charset_list_return);
104
105     free (ss);
106     free (xlfd_resolved);
107   }
108 # endif
109
110   return ff;
111 }
112
113
114 void
115 XftFontClose (Display *dpy, XftFont *font)
116 {
117   if (!dpy || !font) abort();
118   free (font->name);
119   XFreeFont (dpy, font->xfont);
120 # ifdef HAVE_XUTF8DRAWSTRING
121   XFreeFontSet (dpy, font->fontset);
122 # endif
123   free (font);
124 }
125
126
127 Bool
128 XftColorAllocName (Display  *dpy,
129                    _Xconst Visual *visual,
130                    Colormap cmap,
131                    _Xconst char *name,
132                    XftColor *result)
133 {
134   XColor color;
135   if (!dpy || !visual || !name || !result) abort();
136
137   if (! XParseColor (dpy, cmap, name, &color))
138     {
139       fprintf (stderr, "%s: can't parse color %s", progname, name);
140       return False;
141     }
142   else if (! XAllocColor (dpy, cmap, &color))
143     {
144       fprintf (stderr, "%s: couldn't allocate color %s", progname, name);
145       return False;
146     }
147   else
148     {
149       XRenderColor color2;
150       color2.red    = color.red;
151       color2.green  = color.green;
152       color2.blue   = color.blue;
153       color2.alpha  = 0xFFFF;
154       XftColorAllocValue (dpy, visual, cmap, &color2, result);
155       result->pixel = color.pixel;
156       return True;
157     }
158 }
159
160
161 static short
162 maskbase (unsigned long m)
163 {
164   short i;
165   if (!m)
166     return 0;
167   i = 0;
168   while (! (m&1))
169     {
170       m >>= 1;
171       i++;
172     }
173   return i;
174 }
175
176
177 static short
178 masklen (unsigned long m)
179 {
180   unsigned long y;
181   y = (m >> 1) & 033333333333;
182   y = m - y - ((y >>1) & 033333333333);
183   return (short) (((y + (y >> 3)) & 030707070707) % 077);
184 }
185
186
187 Bool
188 XftColorAllocValue (Display *dpy,
189                     _Xconst Visual *visual,
190                     Colormap cmap,
191                     _Xconst XRenderColor *color,
192                     XftColor *result)
193 {
194   if (!dpy || !visual || !color || !result) abort();
195   if (visual->class == TrueColor)
196     {
197       int red_shift, red_len;
198       int green_shift, green_len;
199       int blue_shift, blue_len;
200
201       red_shift   = maskbase (visual->red_mask);
202       red_len     = masklen  (visual->red_mask);
203       green_shift = maskbase (visual->green_mask);
204       green_len   = masklen (visual->green_mask);
205       blue_shift  = maskbase (visual->blue_mask);
206       blue_len    = masklen (visual->blue_mask);
207       result->pixel = (((color->red   >> (16 - red_len))   << red_shift)   |
208                        ((color->green >> (16 - green_len)) << green_shift) |
209                        ((color->blue  >> (16 - blue_len))  << blue_shift));
210 # ifdef HAVE_COCOA
211       result->pixel |= BlackPixel(dpy, 0);  /* alpha */
212 # endif
213     }
214   else
215     {
216       XColor xcolor;
217       xcolor.red   = color->red;
218       xcolor.green = color->green;
219       xcolor.blue  = color->blue;
220       if (!XAllocColor (dpy, cmap, &xcolor))
221         return False;
222       result->pixel = xcolor.pixel;
223     }
224   result->color.red   = color->red;
225   result->color.green = color->green;
226   result->color.blue  = color->blue;
227   result->color.alpha = color->alpha;
228   return True;
229 }
230
231
232 void
233 XftColorFree (Display *dpy,
234               Visual *visual,
235               Colormap cmap,
236               XftColor *color)
237 {
238   if (!dpy || !visual || !color) abort();
239   if (visual->class != TrueColor)
240     XFreeColors (dpy, cmap, &color->pixel, 1, 0);
241 }
242
243
244 XftDraw *
245 XftDrawCreate (Display   *dpy,
246                Drawable  drawable,
247                Visual    *visual,
248                Colormap  colormap)
249 {
250   XftDraw *dd = (XftDraw *) calloc (1, sizeof(*dd));
251   if (!dpy || !drawable || !visual) abort();
252   if (!dd) return 0;
253
254   dd->dpy = dpy;
255   dd->drawable = drawable;
256   dd->visual = visual;
257   dd->colormap = colormap;
258   dd->gc = XCreateGC (dpy, drawable, 0, 0);
259   return dd;
260 }
261
262
263 void
264 XftDrawDestroy (XftDraw *draw)
265 {
266   if (!draw) abort();
267   XFreeGC (draw->dpy, draw->gc);
268   free (draw);
269 }
270
271
272 void
273 XftTextExtentsUtf8 (Display         *dpy,
274                     XftFont         *font,
275                     _Xconst FcChar8 *string,
276                     int             len,
277                     XGlyphInfo      *extents)
278 {
279   XCharStruct overall;
280
281   if (!dpy || !font || !string || !extents) abort();
282
283 # ifdef HAVE_XUTF8DRAWSTRING
284   {
285     XRectangle ink;
286     int advancement =
287       Xutf8TextExtents (font->fontset, (const char *) string, len, &ink, 0);
288     XmbRectangle_to_XCharStruct (ink, overall, advancement);
289   }
290 # else  /* !HAVE_XUTF8DRAWSTRING */
291   {
292     char *s2 = (char *) malloc (len + 1);
293     int direction, ascent, descent;
294     XChar2b *s16;
295     int s16_len = 0;
296     strncpy (s2, (char *) string, len);
297     s2[len] = 0;
298     s16 = utf8_to_XChar2b (s2, &s16_len);
299     XTextExtents16 (font->xfont, s16, s16_len,
300                     &direction, &ascent, &descent, &overall);
301     free (s2);
302     free (s16);
303   }
304 # endif /* !HAVE_XUTF8DRAWSTRING */
305
306   XCharStruct_to_XGlyphInfo (overall, *extents);
307 }
308
309
310 void
311 XftDrawStringUtf8 (XftDraw          *draw,
312                    _Xconst XftColor *color,
313                    XftFont          *font,
314                    int              x,
315                    int              y,
316                    _Xconst FcChar8  *string,
317                    int              len)
318 {
319   if (!draw || !color || !font || !string) abort();
320
321   if (color->pixel != draw->pixel)
322     {
323       XSetForeground (draw->dpy, draw->gc, color->pixel);
324       draw->pixel = color->pixel;
325     }
326   if (font->xfont->fid != draw->fid)
327     {
328       XSetFont (draw->dpy, draw->gc, font->xfont->fid);
329       draw->fid = font->xfont->fid;
330     }
331
332 # ifdef HAVE_XUTF8DRAWSTRING
333   /* If we have Xutf8DrawString, use it instead of XDrawString16 because
334      there is some chance it will handle characters of more than 16 bits
335      (beyond the Basic Multilingual Plane).
336    */
337
338   /* #### I guess I don't really understand how FontSet works, because when
339           using the real X11 implementation of Xutf8DrawString, this seems
340           to just truncate the text at the first non-ASCII character.
341
342           The XDrawString16() path works, however, at the expense of losing
343           everything above Basic Multilingual.  However, that path is only
344           taken on X11 systems that are old enough to not have libXft,
345           which means that the chance of Unicode working was already slim.
346    */
347   Xutf8DrawString (draw->dpy, draw->drawable, font->fontset, draw->gc, x, y, 
348                    (const char *) string, len);
349 # else
350   {
351     int s16_len = 0;
352     char *s2 = (char *) malloc (len + 1);
353     XChar2b *s16;
354     strncpy (s2, (char *) string, len);
355     s2[len] = 0;
356     s16 = utf8_to_XChar2b (s2, &s16_len);
357     free (s2);
358     XDrawString16 (draw->dpy, draw->drawable, draw->gc, x, y, s16, s16_len);
359     free (s16);
360   }
361 # endif
362 }
363
364 #else  /* HAVE_XFT */
365
366 const int Wempty_translation_unit_is_a_dumb_warning = 0;
367
368 #endif /* HAVE_XFT */