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