dmxfont.c 18 KB

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