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