2 * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
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:
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.
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
30 * Kevin E. Martin <kem@redhat.com>
35 * This file provides support for fonts. */
37 #ifdef HAVE_DMX_CONFIG_H
38 #include <dmx-config.h>
41 #define DMX_FONTPATH_DEBUG 0
48 #include <X11/fonts/fontstruct.h>
50 #include "dixstruct.h"
52 static int (*dmxSaveProcVector
[256]) (ClientPtr
);
53 static int dmxFontLastError
;
56 dmxFontErrorHandler(Display
* dpy
, XErrorEvent
* ev
)
58 dmxFontLastError
= ev
->error_code
;
64 dmxGetFontPath(int *npaths
)
67 unsigned char *c
, *paths
;
71 GetFontPath(serverClient
, npaths
, &len
, &paths
);
73 newfp
= malloc(*npaths
+ len
);
74 c
= (unsigned char *) newfp
;
75 fp
= malloc(*npaths
* sizeof(*fp
));
77 memmove(newfp
, paths
+ 1, *npaths
+ len
- 1);
79 for (i
= 0; i
< *npaths
; i
++) {
86 #if DMX_FONTPATH_DEBUG
87 for (i
= 0; i
< *npaths
; i
++)
88 dmxLog(dmxDebug
, "FontPath[%d] = %s\n", i
, fp
[i
]);
95 dmxFreeFontPath(char **fp
)
102 dmxCheckFontPathElement(DMXScreenInfo
* dmxScreen
, char *fp
)
104 int (*oldErrorHandler
) (Display
*, XErrorEvent
*);
106 if (!dmxScreen
->beDisplay
)
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
);
115 return dmxFontLastError
== 0;
119 dmxSetFontPath(DMXScreenInfo
* dmxScreen
)
121 int (*oldErrorHandler
) (Display
*, XErrorEvent
*);
123 int result
= Success
;
126 if (!dmxScreen
->beDisplay
)
129 fp
= dmxGetFontPath(&npaths
);
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
);
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.
154 dmxCheckFontPath(DMXScreenInfo
* dmxScreen
, int *error
)
158 int result
= Success
;
160 if (!dmxScreen
->beDisplay
)
163 /* Save old font path */
164 oldFontPath
= XGetFontPath(dmxScreen
->beDisplay
, &nOldPaths
);
166 result
= dmxSetFontPath(dmxScreen
);
168 /* Restore old font path */
169 XSetFontPath(dmxScreen
->beDisplay
, oldFontPath
, nOldPaths
);
170 XFreeFontPath(oldFontPath
);
171 dmxSync(dmxScreen
, FALSE
);
177 dmxProcSetFontPath(ClientPtr client
)
180 unsigned long nbytes
, total
, n
;
183 unsigned char *oldFontPath
, *tmpFontPath
;
187 REQUEST(xSetFontPathReq
);
189 REQUEST_AT_LEAST_SIZE(xSetFontPathReq
);
191 nbytes
= (client
->req_len
<< 2) - sizeof(xSetFontPathReq
);
193 ptr
= (unsigned char *) &stuff
[1];
194 nfonts
= stuff
->nFonts
;
196 while (--nfonts
>= 0) {
197 if ((total
== 0) || (total
< (n
= (*ptr
+ 1))))
205 GetFontPath(serverClient
, &nOldPaths
, &lenOldPaths
, &tmpFontPath
);
206 oldFontPath
= malloc(nOldPaths
+ lenOldPaths
);
207 memmove(oldFontPath
, tmpFontPath
, nOldPaths
+ lenOldPaths
);
209 result
= SetFontPath(client
, stuff
->nFonts
, (unsigned char *) &stuff
[1]);
213 for (i
= 0; i
< dmxNumScreens
; i
++)
214 if ((result
= dmxCheckFontPath(&dmxScreens
[i
], &error
)))
218 /* Restore old fontpath in the DMX server */
219 SetFontPath(client
, nOldPaths
, oldFontPath
);
220 client
->errorValue
= error
;
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. */
237 for (i
= 0; i
< 256; i
++)
238 dmxSaveProcVector
[i
] = ProcVector
[i
];
240 ProcVector
[X_SetFontPath
] = dmxProcSetFontPath
;
243 /** Reset font support by restoring the original ProcVector function
250 for (i
= 0; i
< 256; i
++)
251 ProcVector
[i
] = dmxSaveProcVector
[i
];
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. */
259 dmxBELoadFont(ScreenPtr pScreen
, FontPtr pFont
)
261 DMXScreenInfo
*dmxScreen
= &dmxScreens
[pScreen
->myNum
];
262 dmxFontPrivPtr pFontPriv
= FontGetPrivate(pFont
, dmxFontPrivateIndex
);
264 char **oldFontPath
= NULL
;
266 Atom name_atom
, value_atom
;
269 /* Make sure we have a font private struct to work with */
273 /* Don't load a font over top of itself */
274 if (pFontPriv
->font
[pScreen
->myNum
]) {
275 return TRUE
; /* Already loaded font */
278 /* Save old font path */
279 oldFontPath
= XGetFontPath(dmxScreen
->beDisplay
, &nOldPaths
);
281 /* Set the font path for the font about to be loaded on the back-end */
282 if (dmxSetFontPath(dmxScreen
)) {
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.
295 fp
= dmxGetFontPath(&npaths
);
297 dmxLog(dmxError
, "No default font path set.\n");
299 "Please see the Xdmx man page for information on how to\n");
301 "initialize the DMX server's default font path.\n");
302 XFreeFontPath(oldFontPath
);
307 dmxLog(dmxWarning
, "No default font path is set.\n");
309 goodfps
= malloc(npaths
* sizeof(*goodfps
));
312 "The DMX server failed to set the following font paths on "
313 "screen #%d:\n", pScreen
->myNum
);
315 for (i
= 0; i
< npaths
; i
++)
316 if (!(goodfps
[i
] = dmxCheckFontPathElement(dmxScreen
, fp
[i
])))
317 dmxLog(dmxError
, " %s\n", fp
[i
]);
319 if (dmxIgnoreBadFontPaths
) {
326 "These font paths will not be used because the "
327 "\"-ignorebadfontpaths\"\n");
328 dmxLog(dmxError
, "option is set.\n");
330 for (i
= 0; i
< npaths
; i
++)
332 len
+= strlen(fp
[i
]) + 1;
337 /* No valid font paths were found */
339 "After removing the font paths above, no valid font "
342 "available. Please check that the font paths set on "
345 "line or in the configuration file via the "
346 "\"-fontpath\" option\n");
348 "are valid on all back-end servers. See the Xdmx man "
350 dmxLog(dmxError
, "more information on font paths.\n");
352 XFreeFontPath(oldFontPath
);
357 newfp
= malloc(len
* sizeof(*newfp
));
358 for (i
= 0; i
< npaths
; i
++) {
360 int n
= strlen(fp
[i
]);
363 strncpy(&newfp
[j
], fp
[i
], n
);
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");
374 else if (dmxFontPath
) {
376 "Please remove these font paths from the command line "
379 "configuration file, or set the \"-ignorebadfontpaths\" "
382 "ignore them. For more information on these options, see "
384 dmxLog(dmxError
, "Xdmx man page.\n");
388 "Please specify the font paths that are available on all "
391 "servers with the \"-fontpath\" option, or use the "
392 "\"-ignorebadfontpaths\"\n");
394 "to ignore bad defaults. For more information on "
395 "these and other\n");
397 "font-path-related options, see the Xdmx man page.\n");
400 if (!dmxIgnoreBadFontPaths
||
401 (dmxIgnoreBadFontPaths
&& dmxSetFontPath(dmxScreen
))) {
402 /* We still have errors so return with error */
404 XFreeFontPath(oldFontPath
);
410 /* Find requested font on back-end server */
411 name_atom
= MakeAtom("FONT", 4, TRUE
);
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
;
423 name
= NameForAtom(value_atom
);
427 pFontPriv
->font
[pScreen
->myNum
] =
428 XLoadQueryFont(dmxScreen
->beDisplay
, name
);
430 /* Restore old font path */
431 XSetFontPath(dmxScreen
->beDisplay
, oldFontPath
, nOldPaths
);
432 XFreeFontPath(oldFontPath
);
433 dmxSync(dmxScreen
, FALSE
);
435 if (!pFontPriv
->font
[pScreen
->myNum
])
441 /** Realize the font, \a pFont, on the back-end server associated with
444 dmxRealizeFont(ScreenPtr pScreen
, FontPtr pFont
)
446 DMXScreenInfo
*dmxScreen
= &dmxScreens
[pScreen
->myNum
];
447 dmxFontPrivPtr pFontPriv
;
449 if (!(pFontPriv
= FontGetPrivate(pFont
, dmxFontPrivateIndex
))) {
450 FontSetPrivate(pFont
, dmxFontPrivateIndex
, NULL
);
451 pFontPriv
= malloc(sizeof(dmxFontPrivRec
));
454 pFontPriv
->font
= NULL
;
455 MAXSCREENSALLOC(pFontPriv
->font
);
456 if (!pFontPriv
->font
) {
460 pFontPriv
->refcnt
= 0;
463 FontSetPrivate(pFont
, dmxFontPrivateIndex
, (pointer
) pFontPriv
);
465 if (dmxScreen
->beDisplay
) {
466 if (!dmxBELoadFont(pScreen
, pFont
))
472 pFontPriv
->font
[pScreen
->myNum
] = NULL
;
478 /** Free \a pFont on the back-end associated with \a pScreen. */
480 dmxBEFreeFont(ScreenPtr pScreen
, FontPtr pFont
)
482 DMXScreenInfo
*dmxScreen
= &dmxScreens
[pScreen
->myNum
];
483 dmxFontPrivPtr pFontPriv
= FontGetPrivate(pFont
, dmxFontPrivateIndex
);
485 if (pFontPriv
&& pFontPriv
->font
[pScreen
->myNum
]) {
486 XFreeFont(dmxScreen
->beDisplay
, pFontPriv
->font
[pScreen
->myNum
]);
487 pFontPriv
->font
[pScreen
->myNum
] = NULL
;
494 /** Unrealize the font, \a pFont, on the back-end server associated with
497 dmxUnrealizeFont(ScreenPtr pScreen
, FontPtr pFont
)
499 DMXScreenInfo
*dmxScreen
= &dmxScreens
[pScreen
->myNum
];
500 dmxFontPrivPtr pFontPriv
;
502 if ((pFontPriv
= FontGetPrivate(pFont
, dmxFontPrivateIndex
))) {
503 /* In case the font failed to load properly */
504 if (!pFontPriv
->refcnt
) {
505 MAXSCREENSFREE(pFontPriv
->font
);
507 FontSetPrivate(pFont
, dmxFontPrivateIndex
, NULL
);
509 else if (pFontPriv
->font
[pScreen
->myNum
]) {
510 if (dmxScreen
->beDisplay
)
511 dmxBEFreeFont(pScreen
, pFont
);
513 /* The code below is non-obvious, so here's an explanation...
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.
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.
540 * This is a bug in dix.
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.
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.
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.
558 if (--pFontPriv
->refcnt
== 0
560 /* Remove this code when the dix bug is fixed */
561 || screenInfo
.numScreens
== 1
564 MAXSCREENSFREE(pFontPriv
->font
);
566 FontSetPrivate(pFont
, dmxFontPrivateIndex
, NULL
);