/* * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina. * * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation on the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Authors: * Kevin E. Martin * */ /** \file * This file provides support for fonts. */ #ifdef HAVE_DMX_CONFIG_H #include #endif #define DMX_FONTPATH_DEBUG 0 #include "dmx.h" #include "dmxsync.h" #include "dmxfont.h" #include "dmxlog.h" #include #include "dixfont.h" #include "dixstruct.h" static int (*dmxSaveProcVector[256])(ClientPtr); static int dmxFontLastError; static int dmxFontErrorHandler(Display *dpy, XErrorEvent *ev) { dmxFontLastError = ev->error_code; return 0; } static char **dmxGetFontPath(int *npaths) { char **fp; unsigned char *c, *paths; char *newfp; int len, l, i; GetFontPath(serverClient, npaths, &len, &paths); newfp = xalloc(*npaths + len); c = (unsigned char *)newfp; fp = xalloc(*npaths * sizeof(*fp)); memmove(newfp, paths+1, *npaths + len - 1); l = *paths; for (i = 0; i < *npaths; i++) { fp[i] = (char *)c; c += l; l = *c; *c++ = '\0'; } #if DMX_FONTPATH_DEBUG for (i = 0; i < *npaths; i++) dmxLog(dmxDebug, "FontPath[%d] = %s\n", i, fp[i]); #endif return fp; } static void dmxFreeFontPath(char **fp) { xfree(fp[0]); xfree(fp); } static Bool dmxCheckFontPathElement(DMXScreenInfo *dmxScreen, char *fp) { int (*oldErrorHandler)(Display *, XErrorEvent *); if (!dmxScreen->beDisplay) return TRUE; dmxFontLastError = 0; oldErrorHandler = XSetErrorHandler(dmxFontErrorHandler); XSetFontPath(dmxScreen->beDisplay, &fp, 1); dmxSync(dmxScreen, TRUE); /* Must complete before removing handler */ XSetErrorHandler(oldErrorHandler); return (dmxFontLastError == 0); } static int dmxSetFontPath(DMXScreenInfo *dmxScreen) { int (*oldErrorHandler)(Display *, XErrorEvent *); char **fp; int result = Success; int npaths; if (!dmxScreen->beDisplay) return result; fp = dmxGetFontPath(&npaths); if (!fp) return BadAlloc; dmxFontLastError = 0; oldErrorHandler = XSetErrorHandler(dmxFontErrorHandler); XSetFontPath(dmxScreen->beDisplay, fp, npaths); dmxSync(dmxScreen, TRUE); /* Must complete before removing handler */ XSetErrorHandler(oldErrorHandler); if (dmxFontLastError) { result = dmxFontLastError; /* We could set *error here to the offending path, but it is * ignored, so we don't bother figuring out which path is bad. * If we do add this support in the future, we'll need to add * error to the function's argument list. */ } dmxFreeFontPath(fp); return result; } static int dmxCheckFontPath(DMXScreenInfo *dmxScreen, int *error) { char **oldFontPath; int nOldPaths; int result = Success; if (!dmxScreen->beDisplay) return result; /* Save old font path */ oldFontPath = XGetFontPath(dmxScreen->beDisplay, &nOldPaths); result = dmxSetFontPath(dmxScreen); /* Restore old font path */ XSetFontPath(dmxScreen->beDisplay, oldFontPath, nOldPaths); XFreeFontPath(oldFontPath); dmxSync(dmxScreen, FALSE); return result; } static int dmxProcSetFontPath(ClientPtr client) { unsigned char *ptr; unsigned long nbytes, total, n; long nfonts; int i, result; int error; unsigned char *oldFontPath, *tmpFontPath; int nOldPaths; int lenOldPaths; REQUEST(xSetFontPathReq); REQUEST_AT_LEAST_SIZE(xSetFontPathReq); nbytes = (client->req_len << 2) - sizeof(xSetFontPathReq); total = nbytes; ptr = (unsigned char *)&stuff[1]; nfonts = stuff->nFonts; while (--nfonts >= 0) { if ((total == 0) || (total < (n = (*ptr + 1)))) return BadLength; total -= n; ptr += n; } if (total >= 4) return BadLength; GetFontPath(serverClient, &nOldPaths, &lenOldPaths, &tmpFontPath); oldFontPath = xalloc(nOldPaths + lenOldPaths); memmove(oldFontPath, tmpFontPath, nOldPaths + lenOldPaths); result = SetFontPath(client, stuff->nFonts, (unsigned char *)&stuff[1], &error); if (!result) { for (i = 0; i < dmxNumScreens; i++) if ((result = dmxCheckFontPath(&dmxScreens[i], &error))) break; if (result) { int ignoreresult, ignoreerror; /* Restore old fontpath in the DMX server */ ignoreresult = SetFontPath(client, nOldPaths, oldFontPath, &ignoreerror); } else { result = client->noClientException; client->errorValue = error; } } xfree(oldFontPath); return result; } /** Initialize font support. In addition to the screen function call * pointers, DMX also hooks in at the ProcVector[] level. Here the old * ProcVector function pointers are saved and the new ProcVector * function pointers are initialized. */ void dmxInitFonts(void) { int i; for (i = 0; i < 256; i++) dmxSaveProcVector[i] = ProcVector[i]; ProcVector[X_SetFontPath] = dmxProcSetFontPath; } /** Reset font support by restoring the original ProcVector function * pointers. */ void dmxResetFonts(void) { int i; for (i = 0; i < 256; i++) ProcVector[i] = dmxSaveProcVector[i]; } /** Load the font, \a pFont, on the back-end server associated with \a * pScreen. When a font is loaded, the font path on back-end server is * first initialized to that specified on the command line with the * -fontpath options, and then the font is loaded. */ Bool dmxBELoadFont(ScreenPtr pScreen, FontPtr pFont) { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; dmxFontPrivPtr pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex); char *name; char **oldFontPath = NULL; int nOldPaths; Atom name_atom, value_atom; int i; /* Make sure we have a font private struct to work with */ if (!pFontPriv) return FALSE; /* Don't load a font over top of itself */ if (pFontPriv->font[pScreen->myNum]) { return TRUE; /* Already loaded font */ } /* Save old font path */ oldFontPath = XGetFontPath(dmxScreen->beDisplay, &nOldPaths); /* Set the font path for the font about to be loaded on the back-end */ if (dmxSetFontPath(dmxScreen)) { char **fp; int npaths; Bool *goodfps; /* This could fail only when first starting the X server and * loading the default font. If it fails here, then the default * font path is invalid, no default font path will be set, the * DMX server will fail to load the default font, and it will * exit with an error unless we remove the offending font paths * with the -ignorebadfontpaths command line option. */ fp = dmxGetFontPath(&npaths); if (!fp) { dmxLog(dmxError, "No default font path set.\n"); dmxLog(dmxError, "Please see the Xdmx man page for information on how to\n"); dmxLog(dmxError, "initialize the DMX server's default font path.\n"); XFreeFontPath(oldFontPath); return FALSE; } if (!dmxFontPath) dmxLog(dmxWarning, "No default font path is set.\n"); goodfps = xalloc(npaths * sizeof(*goodfps)); dmxLog(dmxError, "The DMX server failed to set the following font paths on " "screen #%d:\n", pScreen->myNum); for (i = 0; i < npaths; i++) if (!(goodfps[i] = dmxCheckFontPathElement(dmxScreen, fp[i]))) dmxLog(dmxError, " %s\n", fp[i]); if (dmxIgnoreBadFontPaths) { char *newfp; int newnpaths = 0; int len = 0; int j = 0; int error; dmxLog(dmxError, "These font paths will not be used because the " "\"-ignorebadfontpaths\"\n"); dmxLog(dmxError, "option is set.\n"); for (i = 0; i < npaths; i++) if (goodfps[i]) { len += strlen(fp[i]) + 1; newnpaths++; } if (!newnpaths) { /* No valid font paths were found */ dmxLog(dmxError, "After removing the font paths above, no valid font " "paths were\n"); dmxLog(dmxError, "available. Please check that the font paths set on " "the command\n"); dmxLog(dmxError, "line or in the configuration file via the " "\"-fontpath\" option\n"); dmxLog(dmxError, "are valid on all back-end servers. See the Xdmx man " "page for\n"); dmxLog(dmxError, "more information on font paths.\n"); dmxFreeFontPath(fp); XFreeFontPath(oldFontPath); xfree(goodfps); return FALSE; } newfp = xalloc(len * sizeof(*newfp)); for (i = 0; i < npaths; i++) { if (goodfps[i]) { int n = strlen(fp[i]); newfp[j++] = n; strncpy(&newfp[j], fp[i], n); j += n; } } if (SetFontPath(NULL, newnpaths, (unsigned char *)newfp, &error)) { /* Note that this should never happen since all of the * FPEs were previously valid. */ dmxLog(dmxError, "Cannot reset the default font path.\n"); } } else if (dmxFontPath) { dmxLog(dmxError, "Please remove these font paths from the command line " "or\n"); dmxLog(dmxError, "configuration file, or set the \"-ignorebadfontpaths\" " "option to\n"); dmxLog(dmxError, "ignore them. For more information on these options, see " "the\n"); dmxLog(dmxError, "Xdmx man page.\n"); } else { dmxLog(dmxError, "Please specify the font paths that are available on all " "back-end\n"); dmxLog(dmxError, "servers with the \"-fontpath\" option, or use the " "\"-ignorebadfontpaths\"\n"); dmxLog(dmxError, "to ignore bad defaults. For more information on " "these and other\n"); dmxLog(dmxError, "font-path-related options, see the Xdmx man page.\n"); } if (!dmxIgnoreBadFontPaths || (dmxIgnoreBadFontPaths && dmxSetFontPath(dmxScreen))) { /* We still have errors so return with error */ dmxFreeFontPath(fp); XFreeFontPath(oldFontPath); xfree(goodfps); return FALSE; } } /* Find requested font on back-end server */ name_atom = MakeAtom("FONT", 4, TRUE); value_atom = 0L; for (i = 0; i < pFont->info.nprops; i++) { if ((Atom)pFont->info.props[i].name == name_atom) { value_atom = pFont->info.props[i].value; break; } } if (!value_atom) return FALSE; name = (char *)NameForAtom(value_atom); if (!name) return FALSE; pFontPriv->font[pScreen->myNum] = XLoadQueryFont(dmxScreen->beDisplay, name); /* Restore old font path */ XSetFontPath(dmxScreen->beDisplay, oldFontPath, nOldPaths); XFreeFontPath(oldFontPath); dmxSync(dmxScreen, FALSE); if (!pFontPriv->font[pScreen->myNum]) return FALSE; return TRUE; } /** Realize the font, \a pFont, on the back-end server associated with * \a pScreen. */ Bool dmxRealizeFont(ScreenPtr pScreen, FontPtr pFont) { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; dmxFontPrivPtr pFontPriv; if (!(pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex))) { FontSetPrivate(pFont, dmxFontPrivateIndex, NULL); pFontPriv = xalloc(sizeof(dmxFontPrivRec)); if (!pFontPriv) return FALSE; pFontPriv->font = NULL; MAXSCREENSALLOC(pFontPriv->font); if (!pFontPriv->font) { xfree(pFontPriv); return FALSE; } pFontPriv->refcnt = 0; } FontSetPrivate(pFont, dmxFontPrivateIndex, (pointer)pFontPriv); if (dmxScreen->beDisplay) { if (!dmxBELoadFont(pScreen, pFont)) return FALSE; pFontPriv->refcnt++; } else { pFontPriv->font[pScreen->myNum] = NULL; } return TRUE; } /** Free \a pFont on the back-end associated with \a pScreen. */ Bool dmxBEFreeFont(ScreenPtr pScreen, FontPtr pFont) { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; dmxFontPrivPtr pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex); if (pFontPriv && pFontPriv->font[pScreen->myNum]) { XFreeFont(dmxScreen->beDisplay, pFontPriv->font[pScreen->myNum]); pFontPriv->font[pScreen->myNum] = NULL; return TRUE; } return FALSE; } /** Unrealize the font, \a pFont, on the back-end server associated with * \a pScreen. */ Bool dmxUnrealizeFont(ScreenPtr pScreen, FontPtr pFont) { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; dmxFontPrivPtr pFontPriv; if ((pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex))) { /* In case the font failed to load properly */ if (!pFontPriv->refcnt) { MAXSCREENSFREE(pFontPriv->font); xfree(pFontPriv); FontSetPrivate(pFont, dmxFontPrivateIndex, NULL); } else if (pFontPriv->font[pScreen->myNum]) { if (dmxScreen->beDisplay) dmxBEFreeFont(pScreen, pFont); /* The code below is non-obvious, so here's an explanation... * * When creating the default GC, the server opens up the * default font once for each screen, which in turn calls * the RealizeFont function pointer once for each screen. * During this process both dix's font refcnt and DMX's font * refcnt are incremented once for each screen. * * Later, when shutting down the X server, dix shuts down * each screen in reverse order. During this shutdown * procedure, each screen's default GC is freed and then * that screen is closed by calling the CloseScreen function * pointer. screenInfo.numScreens is then decremented after * closing each screen. This procedure means that the dix's * font refcnt for the font used by the default GC's is * decremented once for each screen # greater than 0. * However, since dix's refcnt for the default font is not * yet 0 for each screen greater than 0, no call to the * UnrealizeFont function pointer is made for those screens. * Then, when screen 0 is being closed, dix's font refcnt * for the default GC's font is finally 0 and the font is * unrealized. However, since screenInfo.numScreens has * been decremented already down to 1, only one call to * UnrealizeFont is made (for screen 0). Thus, even though * RealizeFont was called once for each screen, * UnrealizeFont is only called for screen 0. * * This is a bug in dix. * * To avoid the memory leak of pFontPriv for each server * generation, we can also free pFontPriv if the refcnt is * not yet 0 but the # of screens is 1 -- i.e., the case * described in the dix bug above. This is only a temporary * workaround until the bug in dix is solved. * * The other problem is that the font structure allocated by * XLoadQueryFont() above is not freed for screens > 0. * This problem cannot be worked around here since the back- * end displays for screens > 0 have already been closed by * the time this code is called from dix. * * When the bug in dix described above is fixed, then we can * remove the "|| screenInfo.numScreens == 1" code below and * the memory leaks will be eliminated. */ if (--pFontPriv->refcnt == 0 #if 1 /* Remove this code when the dix bug is fixed */ || screenInfo.numScreens == 1 #endif ) { MAXSCREENSFREE(pFontPriv->font); xfree(pFontPriv); FontSetPrivate(pFont, dmxFontPrivateIndex, NULL); } } } return TRUE; }