Commit | Line | Data |
---|---|---|
3144ee8a AT |
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 */ |