Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / dmx / dmxfont.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 * Kevin E. Martin <kem@redhat.com>
31 *
32 */
33
34/** \file
35 * This file provides support for fonts. */
36
37#ifdef HAVE_DMX_CONFIG_H
38#include <dmx-config.h>
39#endif
40
41#define DMX_FONTPATH_DEBUG 0
42
43#include "dmx.h"
44#include "dmxsync.h"
45#include "dmxfont.h"
46#include "dmxlog.h"
47
48#include <X11/fonts/fontstruct.h>
49#include "dixfont.h"
50#include "dixstruct.h"
51
52static int (*dmxSaveProcVector[256]) (ClientPtr);
53static int dmxFontLastError;
54
55static int
56dmxFontErrorHandler(Display * dpy, XErrorEvent * ev)
57{
58 dmxFontLastError = ev->error_code;
59
60 return 0;
61}
62
63static char **
64dmxGetFontPath(int *npaths)
65{
66 char **fp;
67 unsigned char *c, *paths;
68 char *newfp;
69 int len, l, i;
70
71 GetFontPath(serverClient, npaths, &len, &paths);
72
73 newfp = malloc(*npaths + len);
74 c = (unsigned char *) newfp;
75 fp = malloc(*npaths * sizeof(*fp));
76
77 memmove(newfp, paths + 1, *npaths + len - 1);
78 l = *paths;
79 for (i = 0; i < *npaths; i++) {
80 fp[i] = (char *) c;
81 c += l;
82 l = *c;
83 *c++ = '\0';
84 }
85
86#if DMX_FONTPATH_DEBUG
87 for (i = 0; i < *npaths; i++)
88 dmxLog(dmxDebug, "FontPath[%d] = %s\n", i, fp[i]);
89#endif
90
91 return fp;
92}
93
94static void
95dmxFreeFontPath(char **fp)
96{
97 free(fp[0]);
98 free(fp);
99}
100
101static Bool
102dmxCheckFontPathElement(DMXScreenInfo * dmxScreen, char *fp)
103{
104 int (*oldErrorHandler) (Display *, XErrorEvent *);
105
106 if (!dmxScreen->beDisplay)
107 return TRUE;
108
109 dmxFontLastError = 0;
110 oldErrorHandler = XSetErrorHandler(dmxFontErrorHandler);
111 XSetFontPath(dmxScreen->beDisplay, &fp, 1);
112 dmxSync(dmxScreen, TRUE); /* Must complete before removing handler */
113 XSetErrorHandler(oldErrorHandler);
114
115 return dmxFontLastError == 0;
116}
117
118static int
119dmxSetFontPath(DMXScreenInfo * dmxScreen)
120{
121 int (*oldErrorHandler) (Display *, XErrorEvent *);
122 char **fp;
123 int result = Success;
124 int npaths;
125
126 if (!dmxScreen->beDisplay)
127 return result;
128
129 fp = dmxGetFontPath(&npaths);
130 if (!fp)
131 return BadAlloc;
132
133 dmxFontLastError = 0;
134 oldErrorHandler = XSetErrorHandler(dmxFontErrorHandler);
135 XSetFontPath(dmxScreen->beDisplay, fp, npaths);
136 dmxSync(dmxScreen, TRUE); /* Must complete before removing handler */
137 XSetErrorHandler(oldErrorHandler);
138
139 if (dmxFontLastError) {
140 result = dmxFontLastError;
141 /* We could set *error here to the offending path, but it is
142 * ignored, so we don't bother figuring out which path is bad.
143 * If we do add this support in the future, we'll need to add
144 * error to the function's argument list.
145 */
146 }
147
148 dmxFreeFontPath(fp);
149
150 return result;
151}
152
153static int
154dmxCheckFontPath(DMXScreenInfo * dmxScreen, int *error)
155{
156 char **oldFontPath;
157 int nOldPaths;
158 int result = Success;
159
160 if (!dmxScreen->beDisplay)
161 return result;
162
163 /* Save old font path */
164 oldFontPath = XGetFontPath(dmxScreen->beDisplay, &nOldPaths);
165
166 result = dmxSetFontPath(dmxScreen);
167
168 /* Restore old font path */
169 XSetFontPath(dmxScreen->beDisplay, oldFontPath, nOldPaths);
170 XFreeFontPath(oldFontPath);
171 dmxSync(dmxScreen, FALSE);
172
173 return result;
174}
175
176static int
177dmxProcSetFontPath(ClientPtr client)
178{
179 unsigned char *ptr;
180 unsigned long nbytes, total, n;
181 long nfonts;
182 int i, result;
183 unsigned char *oldFontPath, *tmpFontPath;
184 int nOldPaths;
185 int lenOldPaths;
186
187 REQUEST(xSetFontPathReq);
188
189 REQUEST_AT_LEAST_SIZE(xSetFontPathReq);
190
191 nbytes = (client->req_len << 2) - sizeof(xSetFontPathReq);
192 total = nbytes;
193 ptr = (unsigned char *) &stuff[1];
194 nfonts = stuff->nFonts;
195
196 while (--nfonts >= 0) {
197 if ((total == 0) || (total < (n = (*ptr + 1))))
198 return BadLength;
199 total -= n;
200 ptr += n;
201 }
202 if (total >= 4)
203 return BadLength;
204
205 GetFontPath(serverClient, &nOldPaths, &lenOldPaths, &tmpFontPath);
206 oldFontPath = malloc(nOldPaths + lenOldPaths);
207 memmove(oldFontPath, tmpFontPath, nOldPaths + lenOldPaths);
208
209 result = SetFontPath(client, stuff->nFonts, (unsigned char *) &stuff[1]);
210 if (!result) {
211 int error = 0;
212
213 for (i = 0; i < dmxNumScreens; i++)
214 if ((result = dmxCheckFontPath(&dmxScreens[i], &error)))
215 break;
216
217 if (result) {
218 /* Restore old fontpath in the DMX server */
219 SetFontPath(client, nOldPaths, oldFontPath);
220 client->errorValue = error;
221 }
222 }
223
224 free(oldFontPath);
225 return result;
226}
227
228/** Initialize font support. In addition to the screen function call
229 * pointers, DMX also hooks in at the ProcVector[] level. Here the old
230 * ProcVector function pointers are saved and the new ProcVector
231 * function pointers are initialized. */
232void
233dmxInitFonts(void)
234{
235 int i;
236
237 for (i = 0; i < 256; i++)
238 dmxSaveProcVector[i] = ProcVector[i];
239
240 ProcVector[X_SetFontPath] = dmxProcSetFontPath;
241}
242
243/** Reset font support by restoring the original ProcVector function
244 * pointers. */
245void
246dmxResetFonts(void)
247{
248 int i;
249
250 for (i = 0; i < 256; i++)
251 ProcVector[i] = dmxSaveProcVector[i];
252}
253
254/** Load the font, \a pFont, on the back-end server associated with \a
255 * pScreen. When a font is loaded, the font path on back-end server is
256 * first initialized to that specified on the command line with the
257 * -fontpath options, and then the font is loaded. */
258Bool
259dmxBELoadFont(ScreenPtr pScreen, FontPtr pFont)
260{
261 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
262 dmxFontPrivPtr pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex);
263 const char *name;
264 char **oldFontPath = NULL;
265 int nOldPaths;
266 Atom name_atom, value_atom;
267 int i;
268
269 /* Make sure we have a font private struct to work with */
270 if (!pFontPriv)
271 return FALSE;
272
273 /* Don't load a font over top of itself */
274 if (pFontPriv->font[pScreen->myNum]) {
275 return TRUE; /* Already loaded font */
276 }
277
278 /* Save old font path */
279 oldFontPath = XGetFontPath(dmxScreen->beDisplay, &nOldPaths);
280
281 /* Set the font path for the font about to be loaded on the back-end */
282 if (dmxSetFontPath(dmxScreen)) {
283 char **fp;
284 int npaths;
285 Bool *goodfps;
286
287 /* This could fail only when first starting the X server and
288 * loading the default font. If it fails here, then the default
289 * font path is invalid, no default font path will be set, the
290 * DMX server will fail to load the default font, and it will
291 * exit with an error unless we remove the offending font paths
292 * with the -ignorebadfontpaths command line option.
293 */
294
295 fp = dmxGetFontPath(&npaths);
296 if (!fp) {
297 dmxLog(dmxError, "No default font path set.\n");
298 dmxLog(dmxError,
299 "Please see the Xdmx man page for information on how to\n");
300 dmxLog(dmxError,
301 "initialize the DMX server's default font path.\n");
302 XFreeFontPath(oldFontPath);
303 return FALSE;
304 }
305
306 if (!dmxFontPath)
307 dmxLog(dmxWarning, "No default font path is set.\n");
308
309 goodfps = malloc(npaths * sizeof(*goodfps));
310
311 dmxLog(dmxError,
312 "The DMX server failed to set the following font paths on "
313 "screen #%d:\n", pScreen->myNum);
314
315 for (i = 0; i < npaths; i++)
316 if (!(goodfps[i] = dmxCheckFontPathElement(dmxScreen, fp[i])))
317 dmxLog(dmxError, " %s\n", fp[i]);
318
319 if (dmxIgnoreBadFontPaths) {
320 char *newfp;
321 int newnpaths = 0;
322 int len = 0;
323 int j = 0;
324
325 dmxLog(dmxError,
326 "These font paths will not be used because the "
327 "\"-ignorebadfontpaths\"\n");
328 dmxLog(dmxError, "option is set.\n");
329
330 for (i = 0; i < npaths; i++)
331 if (goodfps[i]) {
332 len += strlen(fp[i]) + 1;
333 newnpaths++;
334 }
335
336 if (!newnpaths) {
337 /* No valid font paths were found */
338 dmxLog(dmxError,
339 "After removing the font paths above, no valid font "
340 "paths were\n");
341 dmxLog(dmxError,
342 "available. Please check that the font paths set on "
343 "the command\n");
344 dmxLog(dmxError,
345 "line or in the configuration file via the "
346 "\"-fontpath\" option\n");
347 dmxLog(dmxError,
348 "are valid on all back-end servers. See the Xdmx man "
349 "page for\n");
350 dmxLog(dmxError, "more information on font paths.\n");
351 dmxFreeFontPath(fp);
352 XFreeFontPath(oldFontPath);
353 free(goodfps);
354 return FALSE;
355 }
356
357 newfp = malloc(len * sizeof(*newfp));
358 for (i = 0; i < npaths; i++) {
359 if (goodfps[i]) {
360 int n = strlen(fp[i]);
361
362 newfp[j++] = n;
363 strncpy(&newfp[j], fp[i], n);
364 j += n;
365 }
366 }
367
368 if (SetFontPath(serverClient, newnpaths, (unsigned char *) newfp)) {
369 /* Note that this should never happen since all of the
370 * FPEs were previously valid. */
371 dmxLog(dmxError, "Cannot reset the default font path.\n");
372 }
373 }
374 else if (dmxFontPath) {
375 dmxLog(dmxError,
376 "Please remove these font paths from the command line "
377 "or\n");
378 dmxLog(dmxError,
379 "configuration file, or set the \"-ignorebadfontpaths\" "
380 "option to\n");
381 dmxLog(dmxError,
382 "ignore them. For more information on these options, see "
383 "the\n");
384 dmxLog(dmxError, "Xdmx man page.\n");
385 }
386 else {
387 dmxLog(dmxError,
388 "Please specify the font paths that are available on all "
389 "back-end\n");
390 dmxLog(dmxError,
391 "servers with the \"-fontpath\" option, or use the "
392 "\"-ignorebadfontpaths\"\n");
393 dmxLog(dmxError,
394 "to ignore bad defaults. For more information on "
395 "these and other\n");
396 dmxLog(dmxError,
397 "font-path-related options, see the Xdmx man page.\n");
398 }
399
400 if (!dmxIgnoreBadFontPaths ||
401 (dmxIgnoreBadFontPaths && dmxSetFontPath(dmxScreen))) {
402 /* We still have errors so return with error */
403 dmxFreeFontPath(fp);
404 XFreeFontPath(oldFontPath);
405 free(goodfps);
406 return FALSE;
407 }
408 }
409
410 /* Find requested font on back-end server */
411 name_atom = MakeAtom("FONT", 4, TRUE);
412 value_atom = 0L;
413
414 for (i = 0; i < pFont->info.nprops; i++) {
415 if ((Atom) pFont->info.props[i].name == name_atom) {
416 value_atom = pFont->info.props[i].value;
417 break;
418 }
419 }
420 if (!value_atom)
421 return FALSE;
422
423 name = NameForAtom(value_atom);
424 if (!name)
425 return FALSE;
426
427 pFontPriv->font[pScreen->myNum] =
428 XLoadQueryFont(dmxScreen->beDisplay, name);
429
430 /* Restore old font path */
431 XSetFontPath(dmxScreen->beDisplay, oldFontPath, nOldPaths);
432 XFreeFontPath(oldFontPath);
433 dmxSync(dmxScreen, FALSE);
434
435 if (!pFontPriv->font[pScreen->myNum])
436 return FALSE;
437
438 return TRUE;
439}
440
441/** Realize the font, \a pFont, on the back-end server associated with
442 * \a pScreen. */
443Bool
444dmxRealizeFont(ScreenPtr pScreen, FontPtr pFont)
445{
446 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
447 dmxFontPrivPtr pFontPriv;
448
449 if (!(pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex))) {
450 FontSetPrivate(pFont, dmxFontPrivateIndex, NULL);
451 pFontPriv = malloc(sizeof(dmxFontPrivRec));
452 if (!pFontPriv)
453 return FALSE;
454 pFontPriv->font = NULL;
455 MAXSCREENSALLOC(pFontPriv->font);
456 if (!pFontPriv->font) {
457 free(pFontPriv);
458 return FALSE;
459 }
460 pFontPriv->refcnt = 0;
461 }
462
463 FontSetPrivate(pFont, dmxFontPrivateIndex, (pointer) pFontPriv);
464
465 if (dmxScreen->beDisplay) {
466 if (!dmxBELoadFont(pScreen, pFont))
467 return FALSE;
468
469 pFontPriv->refcnt++;
470 }
471 else {
472 pFontPriv->font[pScreen->myNum] = NULL;
473 }
474
475 return TRUE;
476}
477
478/** Free \a pFont on the back-end associated with \a pScreen. */
479Bool
480dmxBEFreeFont(ScreenPtr pScreen, FontPtr pFont)
481{
482 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
483 dmxFontPrivPtr pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex);
484
485 if (pFontPriv && pFontPriv->font[pScreen->myNum]) {
486 XFreeFont(dmxScreen->beDisplay, pFontPriv->font[pScreen->myNum]);
487 pFontPriv->font[pScreen->myNum] = NULL;
488 return TRUE;
489 }
490
491 return FALSE;
492}
493
494/** Unrealize the font, \a pFont, on the back-end server associated with
495 * \a pScreen. */
496Bool
497dmxUnrealizeFont(ScreenPtr pScreen, FontPtr pFont)
498{
499 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
500 dmxFontPrivPtr pFontPriv;
501
502 if ((pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex))) {
503 /* In case the font failed to load properly */
504 if (!pFontPriv->refcnt) {
505 MAXSCREENSFREE(pFontPriv->font);
506 free(pFontPriv);
507 FontSetPrivate(pFont, dmxFontPrivateIndex, NULL);
508 }
509 else if (pFontPriv->font[pScreen->myNum]) {
510 if (dmxScreen->beDisplay)
511 dmxBEFreeFont(pScreen, pFont);
512
513 /* The code below is non-obvious, so here's an explanation...
514 *
515 * When creating the default GC, the server opens up the
516 * default font once for each screen, which in turn calls
517 * the RealizeFont function pointer once for each screen.
518 * During this process both dix's font refcnt and DMX's font
519 * refcnt are incremented once for each screen.
520 *
521 * Later, when shutting down the X server, dix shuts down
522 * each screen in reverse order. During this shutdown
523 * procedure, each screen's default GC is freed and then
524 * that screen is closed by calling the CloseScreen function
525 * pointer. screenInfo.numScreens is then decremented after
526 * closing each screen. This procedure means that the dix's
527 * font refcnt for the font used by the default GC's is
528 * decremented once for each screen # greater than 0.
529 * However, since dix's refcnt for the default font is not
530 * yet 0 for each screen greater than 0, no call to the
531 * UnrealizeFont function pointer is made for those screens.
532 * Then, when screen 0 is being closed, dix's font refcnt
533 * for the default GC's font is finally 0 and the font is
534 * unrealized. However, since screenInfo.numScreens has
535 * been decremented already down to 1, only one call to
536 * UnrealizeFont is made (for screen 0). Thus, even though
537 * RealizeFont was called once for each screen,
538 * UnrealizeFont is only called for screen 0.
539 *
540 * This is a bug in dix.
541 *
542 * To avoid the memory leak of pFontPriv for each server
543 * generation, we can also free pFontPriv if the refcnt is
544 * not yet 0 but the # of screens is 1 -- i.e., the case
545 * described in the dix bug above. This is only a temporary
546 * workaround until the bug in dix is solved.
547 *
548 * The other problem is that the font structure allocated by
549 * XLoadQueryFont() above is not freed for screens > 0.
550 * This problem cannot be worked around here since the back-
551 * end displays for screens > 0 have already been closed by
552 * the time this code is called from dix.
553 *
554 * When the bug in dix described above is fixed, then we can
555 * remove the "|| screenInfo.numScreens == 1" code below and
556 * the memory leaks will be eliminated.
557 */
558 if (--pFontPriv->refcnt == 0
559#if 1
560 /* Remove this code when the dix bug is fixed */
561 || screenInfo.numScreens == 1
562#endif
563 ) {
564 MAXSCREENSFREE(pFontPriv->font);
565 free(pFontPriv);
566 FontSetPrivate(pFont, dmxFontPrivateIndex, NULL);
567 }
568 }
569 }
570
571 return TRUE;
572}