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 52static int (*dmxSaveProcVector[256])(ClientPtr); 53static int dmxFontLastError; 54 55static int dmxFontErrorHandler(Display *dpy, XErrorEvent *ev) 56{ 57 dmxFontLastError = ev->error_code; 58 59 return 0; 60} 61 62static 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 92static void dmxFreeFontPath(char **fp) 93{ 94 free(fp[0]); 95 free(fp); 96} 97 98static 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 114static 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 147static 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 169static 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. */ 222void 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. */ 234void 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. */ 246Bool 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. */ 428Bool 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. */ 461Bool 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. */ 477Bool 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