Home | History | Annotate | Line # | Download | only in dmx
      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 (at) 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 }
    552