2 * BIGFONT extension for sharing font metrics between clients (if possible)
3 * and for transmitting font metrics to clients in a compressed form.
5 * Copyright (c) 1999-2000 Bruno Haible
6 * Copyright (c) 1999-2000 The XFree86 Project, Inc.
9 /* THIS IS NOT AN X CONSORTIUM STANDARD */
12 * Big fonts suffer from the following: All clients that have opened a
13 * font can access the complete glyph metrics array (the XFontStruct member
14 * `per_char') directly, without going through a macro. Moreover these
15 * glyph metrics are ink metrics, i.e. are not redundant even for a
16 * fixed-width font. For a Unicode font, the size of this array is 768 KB.
18 * Problems: 1. It eats a lot of memory in each client. 2. All this glyph
19 * metrics data is piped through the socket when the font is opened.
21 * This extension addresses these two problems for local clients, by using
22 * shared memory. It also addresses the second problem for non-local clients,
23 * by compressing the data before transmit by a factor of nearly 6.
25 * If you use this extension, your OS ought to nicely support shared memory.
26 * This means: Shared memory should be swappable to the swap, and the limits
27 * should be high enough (SHMMNI at least 64, SHMMAX at least 768 KB,
28 * SHMALL at least 48 MB). It is a plus if your OS allows shmat() calls
29 * on segments that have already been marked "removed", because it permits
30 * these segments to be cleaned up by the OS if the X server is killed with
33 * This extension is transparently exploited by Xlib (functions XQueryFont,
37 #ifdef HAVE_DIX_CONFIG_H
38 #include <dix-config.h>
41 #include <sys/types.h>
43 #if defined(linux) && (!defined(__GNU_LIBRARY__) || __GNU_LIBRARY__ < 2)
44 /* libc4 does not define __GNU_LIBRARY__, libc5 defines __GNU_LIBRARY__ as 1 */
45 /* Linux libc4 and libc5 only (because glibc doesn't include kernel headers):
46 Linux 2.0.x and 2.2.x define SHMLBA as PAGE_SIZE, but forget to define
47 PAGE_SIZE. It is defined in <asm/page.h>. */
51 #include <sys/sysmacros.h>
53 #if defined(ISC) || defined(__CYGWIN__) || defined(__SCO__)
54 #include <sys/param.h>
55 #include <sys/sysmacros.h>
67 #include <X11/Xproto.h>
70 #include "dixstruct.h"
72 #include "dixfontstr.h"
73 #include "extnsionst.h"
75 #define _XF86BIGFONT_SERVER_
76 #include <X11/extensions/xf86bigfstr.h>
78 static void XF86BigfontResetProc(
79 ExtensionEntry
* /* extEntry */
82 static DISPATCH_PROC(ProcXF86BigfontDispatch
);
83 static DISPATCH_PROC(ProcXF86BigfontQueryVersion
);
84 static DISPATCH_PROC(ProcXF86BigfontQueryFont
);
85 static DISPATCH_PROC(SProcXF86BigfontDispatch
);
86 static DISPATCH_PROC(SProcXF86BigfontQueryVersion
);
87 static DISPATCH_PROC(SProcXF86BigfontQueryFont
);
90 static unsigned char XF86BigfontReqCode
;
95 /* A random signature, transmitted to the clients so they can verify that the
96 shared memory segment they are attaching to was really established by the
97 X server they are talking to. */
98 static CARD32 signature
;
100 /* Index for additional information stored in a FontRec's devPrivates array. */
101 static int FontShmdescIndex
;
103 static unsigned int pagesize
;
105 static Bool badSysCall
= FALSE
;
107 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
109 #include <sys/signal.h>
119 CheckForShmSyscall(void)
121 void (*oldHandler
)(int);
124 /* If no SHM support in the kernel, the bad syscall will generate SIGSYS */
125 oldHandler
= signal(SIGSYS
, SigSysHandler
);
128 shmid
= shmget(IPC_PRIVATE
, 4096, IPC_CREAT
);
131 /* Successful allocation - clean up */
132 shmctl(shmid
, IPC_RMID
, (struct shmid_ds
*)NULL
);
136 /* Allocation failed */
139 signal(SIGSYS
, oldHandler
);
140 return (!badSysCall
);
143 #define MUST_CHECK_FOR_SHM_SYSCALL
150 XFree86BigfontExtensionInit()
153 ExtensionEntry
* extEntry
;
155 if ((extEntry
= AddExtension(XF86BIGFONTNAME
,
156 XF86BigfontNumberEvents
,
157 XF86BigfontNumberErrors
,
158 ProcXF86BigfontDispatch
,
159 SProcXF86BigfontDispatch
,
160 XF86BigfontResetProc
,
161 StandardMinorOpcode
))) {
162 XF86BigfontReqCode
= (unsigned char) extEntry
->base
;
164 if (AddExtension(XF86BIGFONTNAME
,
165 XF86BigfontNumberEvents
,
166 XF86BigfontNumberErrors
,
167 ProcXF86BigfontDispatch
,
168 SProcXF86BigfontDispatch
,
169 XF86BigfontResetProc
,
170 StandardMinorOpcode
)) {
173 #ifdef MUST_CHECK_FOR_SHM_SYSCALL
175 * Note: Local-clients will not be optimized without shared memory
176 * support. Remote-client optimization does not depend on shared
177 * memory support. Thus, the extension is still registered even
178 * when shared memory support is not functional.
180 if (!CheckForShmSyscall()) {
181 ErrorF(XF86BIGFONTNAME
" extension local-client optimization disabled due to lack of shared memory support in the kernel\n");
186 srand((unsigned int) time(NULL
));
187 signature
= ((unsigned int) (65536.0/(RAND_MAX
+1.0) * rand()) << 16)
188 + (unsigned int) (65536.0/(RAND_MAX
+1.0) * rand());
189 /* fprintf(stderr, "signature = 0x%08X\n", signature); */
191 FontShmdescIndex
= AllocateFontPrivateIndex();
193 #if !defined(CSRG_BASED) && !defined(__CYGWIN__)
197 pagesize
= sysconf(_SC_PAGESIZE
);
199 pagesize
= getpagesize();
207 /* ========== Management of shared memory segments ========== */
212 /* On Linux, shared memory marked as "removed" can still be attached.
213 Nice feature, because the kernel will automatically free the associated
214 storage when the server and all clients are gone. */
218 typedef struct _ShmDesc
{
219 struct _ShmDesc
*next
;
220 struct _ShmDesc
**prev
;
223 } ShmDescRec
, *ShmDescPtr
;
225 static ShmDescPtr ShmList
= (ShmDescPtr
) NULL
;
235 #ifdef MUST_CHECK_FOR_SHM_SYSCALL
237 return (ShmDescPtr
) NULL
;
240 /* On some older Linux systems, the number of shared memory segments
241 system-wide is 127. In Linux 2.4, it is 4095.
242 Therefore there is a tradeoff to be made between allocating a
243 shared memory segment on one hand, and allocating memory and piping
244 the glyph metrics on the other hand. If the glyph metrics size is
245 small, we prefer the traditional way. */
247 return (ShmDescPtr
) NULL
;
249 pDesc
= (ShmDescRec
*) xalloc(sizeof(ShmDescRec
));
251 return (ShmDescPtr
) NULL
;
253 size
= (size
+ pagesize
-1) & -pagesize
;
254 shmid
= shmget(IPC_PRIVATE
, size
, S_IWUSR
| S_IRUSR
| S_IRGRP
| S_IROTH
);
256 ErrorF(XF86BIGFONTNAME
" extension: shmget() failed, size = %u, errno = %d\n",
259 return (ShmDescPtr
) NULL
;
262 if ((addr
= shmat(shmid
, 0, 0)) == (char *)-1) {
263 ErrorF(XF86BIGFONTNAME
" extension: shmat() failed, size = %u, errno = %d\n",
265 shmctl(shmid
, IPC_RMID
, (void *) 0);
267 return (ShmDescPtr
) NULL
;
271 shmctl(shmid
, IPC_RMID
, (void *) 0);
274 pDesc
->shmid
= shmid
;
275 pDesc
->attach_addr
= addr
;
276 if (ShmList
) ShmList
->prev
= &pDesc
->next
;
277 pDesc
->next
= ShmList
;
278 pDesc
->prev
= &ShmList
;
289 shmctl(pDesc
->shmid
, IPC_RMID
, (void *) 0);
291 shmdt(pDesc
->attach_addr
);
293 if (pDesc
->next
) pDesc
->next
->prev
= pDesc
->prev
;
294 *pDesc
->prev
= pDesc
->next
;
300 /* Called when a font is closed. */
302 XF86BigfontFreeFontShm(
308 /* If during shutdown of the server, XF86BigfontCleanup() has already
309 * called shmdealloc() for all segments, we don't need to do it here.
314 pDesc
= (ShmDescPtr
) FontGetPrivate(pFont
, FontShmdescIndex
);
320 /* Called upon fatal signal. */
330 /* Called when a server generation dies. */
332 XF86BigfontResetProc(
333 ExtensionEntry
* extEntry
)
335 /* This function is normally called from CloseDownExtensions(), called
336 * from main(). It will be followed by a call to FreeAllResources(),
337 * which will call XF86BigfontFreeFontShm() for each font. Thus it
338 * appears that we do not need to do anything in this function. --
339 * But I prefer to write robust code, and not keep shared memory lying
340 * around when it's not needed any more. (Someone might close down the
341 * extension without calling FreeAllResources()...)
343 XF86BigfontCleanup();
347 /* ========== Handling of extension specific requests ========== */
350 ProcXF86BigfontQueryVersion(
353 xXF86BigfontQueryVersionReply reply
;
355 REQUEST_SIZE_MATCH(xXF86BigfontQueryVersionReq
);
356 reply
.type
= X_Reply
;
358 reply
.sequenceNumber
= client
->sequence
;
359 reply
.majorVersion
= XF86BIGFONT_MAJOR_VERSION
;
360 reply
.minorVersion
= XF86BIGFONT_MINOR_VERSION
;
361 reply
.uid
= geteuid();
362 reply
.gid
= getegid();
364 reply
.signature
= signature
;
366 reply
.signature
= 0; /* This is redundant. Avoids uninitialized memory. */
370 (LocalClient(client
) && !client
->swapped
? XF86Bigfont_CAP_LocalShm
: 0)
374 ; /* may add more bits here in future versions */
375 if (client
->swapped
) {
377 swaps(&reply
.sequenceNumber
, tmp
);
378 swapl(&reply
.length
, tmp
);
379 swaps(&reply
.majorVersion
, tmp
);
380 swaps(&reply
.minorVersion
, tmp
);
381 swapl(&reply
.uid
, tmp
);
382 swapl(&reply
.gid
, tmp
);
383 swapl(&reply
.signature
, tmp
);
385 WriteToClient(client
,
386 sizeof(xXF86BigfontQueryVersionReply
), (char *)&reply
);
387 return client
->noClientException
;
396 swaps(&pCI
->leftSideBearing
, tmp
);
397 swaps(&pCI
->rightSideBearing
, tmp
);
398 swaps(&pCI
->characterWidth
, tmp
);
399 swaps(&pCI
->ascent
, tmp
);
400 swaps(&pCI
->descent
, tmp
);
401 swaps(&pCI
->attributes
, tmp
);
404 /* static CARD32 hashCI (xCharInfo *p); */
406 (CARD32)(((p->leftSideBearing << 27) + (p->leftSideBearing >> 5) + \
407 (p->rightSideBearing << 23) + (p->rightSideBearing >> 9) + \
408 (p->characterWidth << 16) + \
409 (p->ascent << 11) + (p->descent << 6)) ^ p->attributes)
412 ProcXF86BigfontQueryFont(
416 REQUEST(xXF86BigfontQueryFontReq
);
428 CARD16
* pIndex2UniqIndex
;
429 CARD16
* pUniqIndex2Index
;
430 CARD32 nUniqCharInfos
;
433 REQUEST_SIZE_MATCH(xXF86BigfontQueryFontReq
);
435 switch (client
->req_len
) {
436 case 2: /* client with version 1.0 libX11 */
437 stuff_flags
= (LocalClient(client
) && !client
->swapped
? XF86Bigfont_FLAGS_Shm
: 0);
439 case 3: /* client with version 1.1 libX11 */
440 stuff_flags
= stuff
->flags
;
446 client
->errorValue
= stuff
->id
; /* EITHER font or gc */
447 pFont
= (FontPtr
)SecurityLookupIDByType(client
, stuff
->id
, RT_FONT
,
450 GC
*pGC
= (GC
*) SecurityLookupIDByType(client
, stuff
->id
, RT_GC
,
453 client
->errorValue
= stuff
->id
;
454 return BadFont
; /* procotol spec says only error is BadFont */
459 pmax
= FONTINKMAX(pFont
);
460 pmin
= FONTINKMIN(pFont
);
462 (pmax
->rightSideBearing
== pmin
->rightSideBearing
463 && pmax
->leftSideBearing
== pmin
->leftSideBearing
464 && pmax
->descent
== pmin
->descent
465 && pmax
->ascent
== pmin
->ascent
466 && pmax
->characterWidth
== pmin
->characterWidth
)
467 ? 0 : N2dChars(pFont
);
470 pIndex2UniqIndex
= NULL
;
471 pUniqIndex2Index
= NULL
;
474 if (nCharInfos
> 0) {
477 pDesc
= (ShmDescPtr
) FontGetPrivate(pFont
, FontShmdescIndex
);
481 pCI
= (xCharInfo
*) pDesc
->attach_addr
;
482 if (stuff_flags
& XF86Bigfont_FLAGS_Shm
)
483 shmid
= pDesc
->shmid
;
485 if (stuff_flags
& XF86Bigfont_FLAGS_Shm
&& !badSysCall
)
486 pDesc
= shmalloc(nCharInfos
* sizeof(xCharInfo
)
489 pCI
= (xCharInfo
*) pDesc
->attach_addr
;
490 shmid
= pDesc
->shmid
;
494 ALLOCATE_LOCAL(nCharInfos
* sizeof(xCharInfo
));
500 /* Fill nCharInfos starting at pCI. */
502 xCharInfo
* prCI
= pCI
;
504 int ncols
= pFont
->info
.lastCol
- pFont
->info
.firstCol
+ 1;
506 for (row
= pFont
->info
.firstRow
;
507 row
<= pFont
->info
.lastRow
&& ninfos
< nCharInfos
;
509 unsigned char chars
[512];
510 xCharInfo
* tmpCharInfos
[256];
515 for (col
= pFont
->info
.firstCol
;
516 col
<= pFont
->info
.lastCol
;
521 (*pFont
->get_metrics
) (pFont
, ncols
, chars
, TwoD16Bit
,
522 &count
, tmpCharInfos
);
523 for (i
= 0; i
< count
&& ninfos
< nCharInfos
; i
++) {
524 *prCI
++ = *tmpCharInfos
[i
];
530 if (pDesc
&& !badSysCall
) {
531 *(CARD32
*)(pCI
+ nCharInfos
) = signature
;
532 if (!FontSetPrivate(pFont
, FontShmdescIndex
, pDesc
)) {
540 /* Cannot use shared memory, so remove-duplicates the xCharInfos
541 using a temporary hash table. */
542 /* Note that CARD16 is suitable as index type, because
543 nCharInfos <= 0x10000. */
545 CARD16
* pHash2UniqIndex
;
546 CARD16
* pUniqIndex2NextUniqIndex
;
548 CARD32 NextUniqIndex
;
553 if (hashModulus
> nCharInfos
+1)
554 hashModulus
= nCharInfos
+1;
557 ALLOCATE_LOCAL((4*nCharInfos
+1) * sizeof(CARD16
));
559 if (!pDesc
) DEALLOCATE_LOCAL(pCI
);
562 pIndex2UniqIndex
= tmp
;
563 /* nCharInfos elements */
564 pUniqIndex2Index
= tmp
+ nCharInfos
;
565 /* max. nCharInfos elements */
566 pUniqIndex2NextUniqIndex
= tmp
+ 2*nCharInfos
;
567 /* max. nCharInfos elements */
568 pHash2UniqIndex
= tmp
+ 3*nCharInfos
;
569 /* hashModulus (<= nCharInfos+1) elements */
571 /* Note that we can use 0xffff as end-of-list indicator, because
572 even if nCharInfos = 0x10000, 0xffff can not occur as valid
573 entry before the last element has been inserted. And once the
574 last element has been inserted, we don't need the hash table
576 for (j
= 0; j
< hashModulus
; j
++)
577 pHash2UniqIndex
[j
] = (CARD16
)(-1);
580 for (NextIndex
= 0; NextIndex
< nCharInfos
; NextIndex
++) {
581 xCharInfo
* p
= &pCI
[NextIndex
];
582 CARD32 hashCode
= hashCI(p
) % hashModulus
;
583 for (i
= pHash2UniqIndex
[hashCode
];
585 i
= pUniqIndex2NextUniqIndex
[i
]) {
586 j
= pUniqIndex2Index
[i
];
587 if (pCI
[j
].leftSideBearing
== p
->leftSideBearing
588 && pCI
[j
].rightSideBearing
== p
->rightSideBearing
589 && pCI
[j
].characterWidth
== p
->characterWidth
590 && pCI
[j
].ascent
== p
->ascent
591 && pCI
[j
].descent
== p
->descent
592 && pCI
[j
].attributes
== p
->attributes
)
595 if (i
!= (CARD16
)(-1)) {
596 /* Found *p at Index j, UniqIndex i */
597 pIndex2UniqIndex
[NextIndex
] = i
;
599 /* Allocate a new entry in the Uniq table */
600 if (hashModulus
<= 2*NextUniqIndex
601 && hashModulus
< nCharInfos
+1) {
602 /* Time to increate hash table size */
603 hashModulus
= 2*hashModulus
+1;
604 if (hashModulus
> nCharInfos
+1)
605 hashModulus
= nCharInfos
+1;
606 for (j
= 0; j
< hashModulus
; j
++)
607 pHash2UniqIndex
[j
] = (CARD16
)(-1);
608 for (i
= 0; i
< NextUniqIndex
; i
++)
609 pUniqIndex2NextUniqIndex
[i
] = (CARD16
)(-1);
610 for (i
= 0; i
< NextUniqIndex
; i
++) {
611 j
= pUniqIndex2Index
[i
];
613 hashCode
= hashCI(p
) % hashModulus
;
614 pUniqIndex2NextUniqIndex
[i
] = pHash2UniqIndex
[hashCode
];
615 pHash2UniqIndex
[hashCode
] = i
;
618 hashCode
= hashCI(p
) % hashModulus
;
621 pUniqIndex2NextUniqIndex
[i
] = pHash2UniqIndex
[hashCode
];
622 pHash2UniqIndex
[hashCode
] = i
;
623 pUniqIndex2Index
[i
] = NextIndex
;
624 pIndex2UniqIndex
[NextIndex
] = i
;
627 nUniqCharInfos
= NextUniqIndex
;
628 /* fprintf(stderr, "font metrics: nCharInfos = %d, nUniqCharInfos = %d, hashModulus = %d\n", nCharInfos, nUniqCharInfos, hashModulus); */
633 int nfontprops
= pFont
->info
.nprops
;
635 sizeof(xXF86BigfontQueryFontReply
)
636 + nfontprops
* sizeof(xFontProp
)
637 + (nCharInfos
> 0 && shmid
== -1
638 ? nUniqCharInfos
* sizeof(xCharInfo
)
639 + (nCharInfos
+1)/2 * 2 * sizeof(CARD16
)
641 xXF86BigfontQueryFontReply
* reply
=
642 (xXF86BigfontQueryFontReply
*) ALLOCATE_LOCAL(rlength
);
645 if (nCharInfos
> 0) {
646 if (shmid
== -1) DEALLOCATE_LOCAL(pIndex2UniqIndex
);
647 if (!pDesc
) DEALLOCATE_LOCAL(pCI
);
651 reply
->type
= X_Reply
;
652 reply
->length
= (rlength
- sizeof(xGenericReply
)) >> 2;
653 reply
->sequenceNumber
= client
->sequence
;
654 reply
->minBounds
= pFont
->info
.ink_minbounds
;
655 reply
->maxBounds
= pFont
->info
.ink_maxbounds
;
656 reply
->minCharOrByte2
= pFont
->info
.firstCol
;
657 reply
->maxCharOrByte2
= pFont
->info
.lastCol
;
658 reply
->defaultChar
= pFont
->info
.defaultCh
;
659 reply
->nFontProps
= pFont
->info
.nprops
;
660 reply
->drawDirection
= pFont
->info
.drawDirection
;
661 reply
->minByte1
= pFont
->info
.firstRow
;
662 reply
->maxByte1
= pFont
->info
.lastRow
;
663 reply
->allCharsExist
= pFont
->info
.allExist
;
664 reply
->fontAscent
= pFont
->info
.fontAscent
;
665 reply
->fontDescent
= pFont
->info
.fontDescent
;
666 reply
->nCharInfos
= nCharInfos
;
667 reply
->nUniqCharInfos
= nUniqCharInfos
;
668 reply
->shmid
= shmid
;
669 reply
->shmsegoffset
= 0;
670 if (client
->swapped
) {
672 swaps(&reply
->sequenceNumber
, tmp
);
673 swapl(&reply
->length
, tmp
);
674 swapCharInfo(&reply
->minBounds
);
675 swapCharInfo(&reply
->maxBounds
);
676 swaps(&reply
->minCharOrByte2
, tmp
);
677 swaps(&reply
->maxCharOrByte2
, tmp
);
678 swaps(&reply
->defaultChar
, tmp
);
679 swaps(&reply
->nFontProps
, tmp
);
680 swaps(&reply
->fontAscent
, tmp
);
681 swaps(&reply
->fontDescent
, tmp
);
682 swapl(&reply
->nCharInfos
, tmp
);
683 swapl(&reply
->nUniqCharInfos
, tmp
);
684 swapl(&reply
->shmid
, tmp
);
685 swapl(&reply
->shmsegoffset
, tmp
);
687 p
= (char*) &reply
[1];
692 for (i
= 0, pFP
= pFont
->info
.props
, prFP
= (xFontProp
*) p
;
694 i
++, pFP
++, prFP
++) {
695 prFP
->name
= pFP
->name
;
696 prFP
->value
= pFP
->value
;
697 if (client
->swapped
) {
699 swapl(&prFP
->name
, tmp
);
700 swapl(&prFP
->value
, tmp
);
705 if (nCharInfos
> 0 && shmid
== -1) {
709 pci
= (xCharInfo
*) p
;
710 for (i
= 0; i
< nUniqCharInfos
; i
++, pci
++) {
711 *pci
= pCI
[pUniqIndex2Index
[i
]];
716 for (j
= 0; j
< nCharInfos
; j
++, ps
++) {
717 *ps
= pIndex2UniqIndex
[j
];
718 if (client
->swapped
) {
724 WriteToClient(client
, rlength
, (char *)reply
);
725 DEALLOCATE_LOCAL(reply
);
726 if (nCharInfos
> 0) {
727 if (shmid
== -1) DEALLOCATE_LOCAL(pIndex2UniqIndex
);
728 if (!pDesc
) DEALLOCATE_LOCAL(pCI
);
730 return (client
->noClientException
);
735 ProcXF86BigfontDispatch(
740 switch (stuff
->data
) {
741 case X_XF86BigfontQueryVersion
:
742 return ProcXF86BigfontQueryVersion(client
);
743 case X_XF86BigfontQueryFont
:
744 return ProcXF86BigfontQueryFont(client
);
751 SProcXF86BigfontQueryVersion(
754 REQUEST(xXF86BigfontQueryVersionReq
);
757 swaps(&stuff
->length
, tmp
);
758 return ProcXF86BigfontQueryVersion(client
);
762 SProcXF86BigfontQueryFont(
765 REQUEST(xXF86BigfontQueryFontReq
);
768 swaps(&stuff
->length
, tmp
);
769 REQUEST_SIZE_MATCH(xXF86BigfontQueryFontReq
);
770 swapl(&stuff
->id
, tmp
);
771 return ProcXF86BigfontQueryFont(client
);
775 SProcXF86BigfontDispatch(
780 switch (stuff
->data
) {
781 case X_XF86BigfontQueryVersion
:
782 return SProcXF86BigfontQueryVersion(client
);
783 case X_XF86BigfontQueryFont
:
784 return SProcXF86BigfontQueryFont(client
);