From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / utils / xft.c
1 /* xscreensaver, Copyright (c) 2014-2018 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   = maskbase (visual->red_mask);
198       int red_len     = masklen  (visual->red_mask);
199       int green_shift = maskbase (visual->green_mask);
200       int green_len   = masklen (visual->green_mask);
201       int blue_shift  = maskbase (visual->blue_mask);
202       int blue_len    = masklen (visual->blue_mask);
203       result->pixel = (((color->red   >> (16 - red_len))   << red_shift)   |
204                        ((color->green >> (16 - green_len)) << green_shift) |
205                        ((color->blue  >> (16 - blue_len))  << blue_shift));
206 # ifdef HAVE_JWXYZ
207       result->pixel |= BlackPixel(dpy, 0);  /* alpha */
208 # endif
209     }
210   else
211     {
212       XColor xcolor;
213       xcolor.red   = color->red;
214       xcolor.green = color->green;
215       xcolor.blue  = color->blue;
216       if (!XAllocColor (dpy, cmap, &xcolor))
217         return False;
218       result->pixel = xcolor.pixel;
219     }
220   result->color.red   = color->red;
221   result->color.green = color->green;
222   result->color.blue  = color->blue;
223   result->color.alpha = color->alpha;
224   return True;
225 }
226
227
228 void
229 XftColorFree (Display *dpy,
230               Visual *visual,
231               Colormap cmap,
232               XftColor *color)
233 {
234   if (!dpy || !visual || !color) abort();
235   if (visual->class != TrueColor)
236     XFreeColors (dpy, cmap, &color->pixel, 1, 0);
237 }
238
239
240 XftDraw *
241 XftDrawCreate (Display   *dpy,
242                Drawable  drawable,
243                Visual    *visual,
244                Colormap  colormap)
245 {
246   XftDraw *dd = (XftDraw *) calloc (1, sizeof(*dd));
247   if (!dpy || !drawable || !visual) abort();
248   if (!dd) return 0;
249
250   dd->dpy = dpy;
251   dd->drawable = drawable;
252   dd->visual = visual;
253   dd->colormap = colormap;
254   dd->gc = XCreateGC (dpy, drawable, 0, 0);
255   return dd;
256 }
257
258
259 void
260 XftDrawDestroy (XftDraw *draw)
261 {
262   if (!draw) abort();
263   XFreeGC (draw->dpy, draw->gc);
264   free (draw);
265 }
266
267
268 void
269 XftTextExtentsUtf8 (Display         *dpy,
270                     XftFont         *font,
271                     _Xconst FcChar8 *string,
272                     int             len,
273                     XGlyphInfo      *extents)
274 {
275   XCharStruct overall;
276
277   if (!dpy || !font || !string || !extents) abort();
278
279 # ifdef HAVE_XUTF8DRAWSTRING
280   {
281     XRectangle ink;
282     int advancement =
283       Xutf8TextExtents (font->fontset, (const char *) string, len, &ink, 0);
284     XmbRectangle_to_XCharStruct (ink, overall, advancement);
285   }
286 # else  /* !HAVE_XUTF8DRAWSTRING */
287   {
288     char *s2 = (char *) malloc (len + 1);
289     int direction, ascent, descent;
290     XChar2b *s16;
291     int s16_len = 0;
292     strncpy (s2, (char *) string, len);
293     s2[len] = 0;
294     s16 = utf8_to_XChar2b (s2, &s16_len);
295     XTextExtents16 (font->xfont, s16, s16_len,
296                     &direction, &ascent, &descent, &overall);
297     free (s2);
298     free (s16);
299   }
300 # endif /* !HAVE_XUTF8DRAWSTRING */
301
302   XCharStruct_to_XGlyphInfo (overall, *extents);
303 }
304
305
306 void
307 XftDrawStringUtf8 (XftDraw          *draw,
308                    _Xconst XftColor *color,
309                    XftFont          *font,
310                    int              x,
311                    int              y,
312                    _Xconst FcChar8  *string,
313                    int              len)
314 {
315   if (!draw || !color || !font || !string) abort();
316
317   if (color->pixel != draw->pixel)
318     {
319       XSetForeground (draw->dpy, draw->gc, color->pixel);
320       draw->pixel = color->pixel;
321     }
322   if (font->xfont->fid != draw->fid)
323     {
324       XSetFont (draw->dpy, draw->gc, font->xfont->fid);
325       draw->fid = font->xfont->fid;
326     }
327
328 # ifdef HAVE_XUTF8DRAWSTRING
329   /* If we have Xutf8DrawString, use it instead of XDrawString16 because
330      there is some chance it will handle characters of more than 16 bits
331      (beyond the Basic Multilingual Plane).
332    */
333
334   /* #### I guess I don't really understand how FontSet works, because when
335           using the real X11 implementation of Xutf8DrawString, this seems
336           to just truncate the text at the first non-ASCII character.
337
338           The XDrawString16() path works, however, at the expense of losing
339           everything above Basic Multilingual.  However, that path is only
340           taken on X11 systems that are old enough to not have libXft,
341           which means that the chance of Unicode working was already slim.
342    */
343   Xutf8DrawString (draw->dpy, draw->drawable, font->fontset, draw->gc, x, y, 
344                    (const char *) string, len);
345 # else
346   {
347     int s16_len = 0;
348     char *s2 = (char *) malloc (len + 1);
349     XChar2b *s16;
350     strncpy (s2, (char *) string, len);
351     s2[len] = 0;
352     s16 = utf8_to_XChar2b (s2, &s16_len);
353     free (s2);
354     XDrawString16 (draw->dpy, draw->drawable, draw->gc, x, y, s16, s16_len);
355     free (s16);
356   }
357 # endif
358 }
359
360 #else  /* HAVE_XFT */
361
362 const int Wempty_translation_unit_is_a_dumb_warning = 0;
363
364 #endif /* HAVE_XFT */