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(__CYGWIN__)
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 #include "protocol-versions.h"
77 #include <X11/extensions/xf86bigfproto.h>
78 #include "xf86bigfontsrv.h"
80 static void XF86BigfontResetProc(ExtensionEntry
* /* extEntry */
85 /* A random signature, transmitted to the clients so they can verify that the
86 shared memory segment they are attaching to was really established by the
87 X server they are talking to. */
88 static CARD32 signature
;
90 /* Index for additional information stored in a FontRec's devPrivates array. */
91 static int FontShmdescIndex
;
93 static unsigned int pagesize
;
95 static Bool badSysCall
= FALSE
;
97 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__DragonFly__)
99 #include <sys/signal.h>
102 SigSysHandler(int signo
)
108 CheckForShmSyscall(void)
110 void (*oldHandler
) (int);
113 /* If no SHM support in the kernel, the bad syscall will generate SIGSYS */
114 oldHandler
= signal(SIGSYS
, SigSysHandler
);
117 shmid
= shmget(IPC_PRIVATE
, 4096, IPC_CREAT
);
119 /* Successful allocation - clean up */
120 shmctl(shmid
, IPC_RMID
, NULL
);
123 /* Allocation failed */
126 signal(SIGSYS
, oldHandler
);
130 #define MUST_CHECK_FOR_SHM_SYSCALL
136 /* ========== Management of shared memory segments ========== */
141 /* On Linux, shared memory marked as "removed" can still be attached.
142 Nice feature, because the kernel will automatically free the associated
143 storage when the server and all clients are gone. */
147 typedef struct _ShmDesc
{
148 struct _ShmDesc
*next
;
149 struct _ShmDesc
**prev
;
152 } ShmDescRec
, *ShmDescPtr
;
154 static ShmDescPtr ShmList
= (ShmDescPtr
) NULL
;
157 shmalloc(unsigned int size
)
163 #ifdef MUST_CHECK_FOR_SHM_SYSCALL
165 return (ShmDescPtr
) NULL
;
168 /* On some older Linux systems, the number of shared memory segments
169 system-wide is 127. In Linux 2.4, it is 4095.
170 Therefore there is a tradeoff to be made between allocating a
171 shared memory segment on one hand, and allocating memory and piping
172 the glyph metrics on the other hand. If the glyph metrics size is
173 small, we prefer the traditional way. */
175 return (ShmDescPtr
) NULL
;
177 pDesc
= malloc(sizeof(ShmDescRec
));
179 return (ShmDescPtr
) NULL
;
181 size
= (size
+ pagesize
- 1) & -pagesize
;
182 shmid
= shmget(IPC_PRIVATE
, size
, S_IWUSR
| S_IRUSR
| S_IRGRP
| S_IROTH
);
184 ErrorF(XF86BIGFONTNAME
" extension: shmget() failed, size = %u, %s\n",
185 size
, strerror(errno
));
187 return (ShmDescPtr
) NULL
;
190 if ((addr
= shmat(shmid
, 0, 0)) == (char *) -1) {
191 ErrorF(XF86BIGFONTNAME
" extension: shmat() failed, size = %u, %s\n",
192 size
, strerror(errno
));
193 shmctl(shmid
, IPC_RMID
, (void *) 0);
195 return (ShmDescPtr
) NULL
;
199 shmctl(shmid
, IPC_RMID
, (void *) 0);
202 pDesc
->shmid
= shmid
;
203 pDesc
->attach_addr
= addr
;
205 ShmList
->prev
= &pDesc
->next
;
206 pDesc
->next
= ShmList
;
207 pDesc
->prev
= &ShmList
;
214 shmdealloc(ShmDescPtr pDesc
)
217 shmctl(pDesc
->shmid
, IPC_RMID
, (void *) 0);
219 shmdt(pDesc
->attach_addr
);
222 pDesc
->next
->prev
= pDesc
->prev
;
223 *pDesc
->prev
= pDesc
->next
;
229 /* Called when a font is closed. */
231 XF86BigfontFreeFontShm(FontPtr pFont
)
236 /* If during shutdown of the server, XF86BigfontCleanup() has already
237 * called shmdealloc() for all segments, we don't need to do it here.
242 pDesc
= (ShmDescPtr
) FontGetPrivate(pFont
, FontShmdescIndex
);
248 /* Called upon fatal signal. */
250 XF86BigfontCleanup(void)
258 /* Called when a server generation dies. */
260 XF86BigfontResetProc(ExtensionEntry
* extEntry
)
262 /* This function is normally called from CloseDownExtensions(), called
263 * from main(). It will be followed by a call to FreeAllResources(),
264 * which will call XF86BigfontFreeFontShm() for each font. Thus it
265 * appears that we do not need to do anything in this function. --
266 * But I prefer to write robust code, and not keep shared memory lying
267 * around when it's not needed any more. (Someone might close down the
268 * extension without calling FreeAllResources()...)
270 XF86BigfontCleanup();
273 /* ========== Handling of extension specific requests ========== */
276 ProcXF86BigfontQueryVersion(ClientPtr client
)
278 xXF86BigfontQueryVersionReply reply
;
280 REQUEST_SIZE_MATCH(xXF86BigfontQueryVersionReq
);
281 reply
= (xXF86BigfontQueryVersionReply
) {
283 .sequenceNumber
= client
->sequence
,
285 .majorVersion
= SERVER_XF86BIGFONT_MAJOR_VERSION
,
286 .minorVersion
= SERVER_XF86BIGFONT_MINOR_VERSION
,
290 .signature
= signature
,
291 .capabilities
= (client
->local
&& !client
->swapped
)
292 ? XF86Bigfont_CAP_LocalShm
: 0
298 if (client
->swapped
) {
299 swaps(&reply
.sequenceNumber
);
300 swapl(&reply
.length
);
301 swaps(&reply
.majorVersion
);
302 swaps(&reply
.minorVersion
);
305 swapl(&reply
.signature
);
307 WriteToClient(client
, sizeof(xXF86BigfontQueryVersionReply
), &reply
);
312 swapCharInfo(xCharInfo
* pCI
)
314 swaps(&pCI
->leftSideBearing
);
315 swaps(&pCI
->rightSideBearing
);
316 swaps(&pCI
->characterWidth
);
318 swaps(&pCI
->descent
);
319 swaps(&pCI
->attributes
);
322 /* static CARD32 hashCI (xCharInfo *p); */
324 (CARD32)(((p->leftSideBearing << 27) + (p->leftSideBearing >> 5) + \
325 (p->rightSideBearing << 23) + (p->rightSideBearing >> 9) + \
326 (p->characterWidth << 16) + \
327 (p->ascent << 11) + (p->descent << 6)) ^ p->attributes)
330 ProcXF86BigfontQueryFont(ClientPtr client
)
334 REQUEST(xXF86BigfontQueryFontReq
);
342 ShmDescPtr pDesc
= NULL
;
347 CARD16
*pIndex2UniqIndex
;
348 CARD16
*pUniqIndex2Index
;
349 CARD32 nUniqCharInfos
;
352 REQUEST_SIZE_MATCH(xXF86BigfontQueryFontReq
);
354 switch (client
->req_len
) {
355 case 2: /* client with version 1.0 libX11 */
356 stuff_flags
= (client
->local
&&
357 !client
->swapped
? XF86Bigfont_FLAGS_Shm
: 0);
359 case 3: /* client with version 1.1 libX11 */
360 stuff_flags
= stuff
->flags
;
366 if (dixLookupFontable(&pFont
, stuff
->id
, client
, DixGetAttrAccess
) !=
368 return BadFont
; /* procotol spec says only error is BadFont */
370 pmax
= FONTINKMAX(pFont
);
371 pmin
= FONTINKMIN(pFont
);
373 (pmax
->rightSideBearing
== pmin
->rightSideBearing
374 && pmax
->leftSideBearing
== pmin
->leftSideBearing
375 && pmax
->descent
== pmin
->descent
376 && pmax
->ascent
== pmin
->ascent
377 && pmax
->characterWidth
== pmin
->characterWidth
)
378 ? 0 : N2dChars(pFont
);
381 pIndex2UniqIndex
= NULL
;
382 pUniqIndex2Index
= NULL
;
385 if (nCharInfos
> 0) {
388 pDesc
= (ShmDescPtr
) FontGetPrivate(pFont
, FontShmdescIndex
);
390 pCI
= (xCharInfo
*) pDesc
->attach_addr
;
391 if (stuff_flags
& XF86Bigfont_FLAGS_Shm
)
392 shmid
= pDesc
->shmid
;
395 if (stuff_flags
& XF86Bigfont_FLAGS_Shm
&& !badSysCall
)
396 pDesc
= shmalloc(nCharInfos
* sizeof(xCharInfo
)
399 pCI
= (xCharInfo
*) pDesc
->attach_addr
;
400 shmid
= pDesc
->shmid
;
404 pCI
= malloc(nCharInfos
* sizeof(xCharInfo
));
410 /* Fill nCharInfos starting at pCI. */
412 xCharInfo
*prCI
= pCI
;
414 int ncols
= pFont
->info
.lastCol
- pFont
->info
.firstCol
+ 1;
417 for (row
= pFont
->info
.firstRow
;
418 row
<= pFont
->info
.lastRow
&& ninfos
< nCharInfos
; row
++) {
419 unsigned char chars
[512];
420 xCharInfo
*tmpCharInfos
[256];
426 for (col
= pFont
->info
.firstCol
;
427 col
<= pFont
->info
.lastCol
; col
++) {
431 (*pFont
->get_metrics
) (pFont
, ncols
, chars
, TwoD16Bit
,
432 &count
, tmpCharInfos
);
433 for (i
= 0; i
< count
&& ninfos
< nCharInfos
; i
++) {
434 *prCI
++ = *tmpCharInfos
[i
];
440 if (pDesc
&& !badSysCall
) {
441 *(CARD32
*) (pCI
+ nCharInfos
) = signature
;
442 if (!FontSetPrivate(pFont
, FontShmdescIndex
, pDesc
)) {
450 /* Cannot use shared memory, so remove-duplicates the xCharInfos
451 using a temporary hash table. */
452 /* Note that CARD16 is suitable as index type, because
453 nCharInfos <= 0x10000. */
455 CARD16
*pHash2UniqIndex
;
456 CARD16
*pUniqIndex2NextUniqIndex
;
458 CARD32 NextUniqIndex
;
463 if (hashModulus
> nCharInfos
+ 1)
464 hashModulus
= nCharInfos
+ 1;
466 tmp
= malloc((4 * nCharInfos
+ 1) * sizeof(CARD16
));
472 pIndex2UniqIndex
= tmp
;
473 /* nCharInfos elements */
474 pUniqIndex2Index
= tmp
+ nCharInfos
;
475 /* max. nCharInfos elements */
476 pUniqIndex2NextUniqIndex
= tmp
+ 2 * nCharInfos
;
477 /* max. nCharInfos elements */
478 pHash2UniqIndex
= tmp
+ 3 * nCharInfos
;
479 /* hashModulus (<= nCharInfos+1) elements */
481 /* Note that we can use 0xffff as end-of-list indicator, because
482 even if nCharInfos = 0x10000, 0xffff can not occur as valid
483 entry before the last element has been inserted. And once the
484 last element has been inserted, we don't need the hash table
486 for (j
= 0; j
< hashModulus
; j
++)
487 pHash2UniqIndex
[j
] = (CARD16
) (-1);
490 for (NextIndex
= 0; NextIndex
< nCharInfos
; NextIndex
++) {
491 xCharInfo
*p
= &pCI
[NextIndex
];
492 CARD32 hashCode
= hashCI(p
) % hashModulus
;
494 for (i
= pHash2UniqIndex
[hashCode
];
495 i
!= (CARD16
) (-1); i
= pUniqIndex2NextUniqIndex
[i
]) {
496 j
= pUniqIndex2Index
[i
];
497 if (pCI
[j
].leftSideBearing
== p
->leftSideBearing
498 && pCI
[j
].rightSideBearing
== p
->rightSideBearing
499 && pCI
[j
].characterWidth
== p
->characterWidth
500 && pCI
[j
].ascent
== p
->ascent
501 && pCI
[j
].descent
== p
->descent
502 && pCI
[j
].attributes
== p
->attributes
)
505 if (i
!= (CARD16
) (-1)) {
506 /* Found *p at Index j, UniqIndex i */
507 pIndex2UniqIndex
[NextIndex
] = i
;
510 /* Allocate a new entry in the Uniq table */
511 if (hashModulus
<= 2 * NextUniqIndex
512 && hashModulus
< nCharInfos
+ 1) {
513 /* Time to increate hash table size */
514 hashModulus
= 2 * hashModulus
+ 1;
515 if (hashModulus
> nCharInfos
+ 1)
516 hashModulus
= nCharInfos
+ 1;
517 for (j
= 0; j
< hashModulus
; j
++)
518 pHash2UniqIndex
[j
] = (CARD16
) (-1);
519 for (i
= 0; i
< NextUniqIndex
; i
++)
520 pUniqIndex2NextUniqIndex
[i
] = (CARD16
) (-1);
521 for (i
= 0; i
< NextUniqIndex
; i
++) {
522 j
= pUniqIndex2Index
[i
];
524 hashCode
= hashCI(p
) % hashModulus
;
525 pUniqIndex2NextUniqIndex
[i
] =
526 pHash2UniqIndex
[hashCode
];
527 pHash2UniqIndex
[hashCode
] = i
;
530 hashCode
= hashCI(p
) % hashModulus
;
533 pUniqIndex2NextUniqIndex
[i
] = pHash2UniqIndex
[hashCode
];
534 pHash2UniqIndex
[hashCode
] = i
;
535 pUniqIndex2Index
[i
] = NextIndex
;
536 pIndex2UniqIndex
[NextIndex
] = i
;
539 nUniqCharInfos
= NextUniqIndex
;
540 /* fprintf(stderr, "font metrics: nCharInfos = %d, nUniqCharInfos = %d, hashModulus = %d\n", nCharInfos, nUniqCharInfos, hashModulus); */
545 int nfontprops
= pFont
->info
.nprops
;
546 int rlength
= sizeof(xXF86BigfontQueryFontReply
)
547 + nfontprops
* sizeof(xFontProp
)
548 + (nCharInfos
> 0 && shmid
== -1
549 ? nUniqCharInfos
* sizeof(xCharInfo
)
550 + (nCharInfos
+ 1) / 2 * 2 * sizeof(CARD16
)
552 xXF86BigfontQueryFontReply
*reply
= calloc(1, rlength
);
556 if (nCharInfos
> 0) {
558 free(pIndex2UniqIndex
);
564 reply
->type
= X_Reply
;
565 reply
->length
= bytes_to_int32(rlength
- sizeof(xGenericReply
));
566 reply
->sequenceNumber
= client
->sequence
;
567 reply
->minBounds
= pFont
->info
.ink_minbounds
;
568 reply
->maxBounds
= pFont
->info
.ink_maxbounds
;
569 reply
->minCharOrByte2
= pFont
->info
.firstCol
;
570 reply
->maxCharOrByte2
= pFont
->info
.lastCol
;
571 reply
->defaultChar
= pFont
->info
.defaultCh
;
572 reply
->nFontProps
= pFont
->info
.nprops
;
573 reply
->drawDirection
= pFont
->info
.drawDirection
;
574 reply
->minByte1
= pFont
->info
.firstRow
;
575 reply
->maxByte1
= pFont
->info
.lastRow
;
576 reply
->allCharsExist
= pFont
->info
.allExist
;
577 reply
->fontAscent
= pFont
->info
.fontAscent
;
578 reply
->fontDescent
= pFont
->info
.fontDescent
;
579 reply
->nCharInfos
= nCharInfos
;
580 reply
->nUniqCharInfos
= nUniqCharInfos
;
581 reply
->shmid
= shmid
;
582 reply
->shmsegoffset
= 0;
583 if (client
->swapped
) {
584 swaps(&reply
->sequenceNumber
);
585 swapl(&reply
->length
);
586 swapCharInfo(&reply
->minBounds
);
587 swapCharInfo(&reply
->maxBounds
);
588 swaps(&reply
->minCharOrByte2
);
589 swaps(&reply
->maxCharOrByte2
);
590 swaps(&reply
->defaultChar
);
591 swaps(&reply
->nFontProps
);
592 swaps(&reply
->fontAscent
);
593 swaps(&reply
->fontDescent
);
594 swapl(&reply
->nCharInfos
);
595 swapl(&reply
->nUniqCharInfos
);
596 swapl(&reply
->shmid
);
597 swapl(&reply
->shmsegoffset
);
599 p
= (char *) &reply
[1];
605 for (i
= 0, pFP
= pFont
->info
.props
, prFP
= (xFontProp
*) p
;
606 i
< nfontprops
; i
++, pFP
++, prFP
++) {
607 prFP
->name
= pFP
->name
;
608 prFP
->value
= pFP
->value
;
609 if (client
->swapped
) {
616 if (nCharInfos
> 0 && shmid
== -1) {
621 pci
= (xCharInfo
*) p
;
622 for (i
= 0; i
< nUniqCharInfos
; i
++, pci
++) {
623 *pci
= pCI
[pUniqIndex2Index
[i
]];
628 for (j
= 0; j
< nCharInfos
; j
++, ps
++) {
629 *ps
= pIndex2UniqIndex
[j
];
630 if (client
->swapped
) {
635 WriteToClient(client
, rlength
, reply
);
637 if (nCharInfos
> 0) {
639 free(pIndex2UniqIndex
);
648 ProcXF86BigfontDispatch(ClientPtr client
)
652 switch (stuff
->data
) {
653 case X_XF86BigfontQueryVersion
:
654 return ProcXF86BigfontQueryVersion(client
);
655 case X_XF86BigfontQueryFont
:
656 return ProcXF86BigfontQueryFont(client
);
663 SProcXF86BigfontQueryVersion(ClientPtr client
)
665 REQUEST(xXF86BigfontQueryVersionReq
);
667 swaps(&stuff
->length
);
668 return ProcXF86BigfontQueryVersion(client
);
672 SProcXF86BigfontQueryFont(ClientPtr client
)
674 REQUEST(xXF86BigfontQueryFontReq
);
676 swaps(&stuff
->length
);
677 REQUEST_SIZE_MATCH(xXF86BigfontQueryFontReq
);
679 return ProcXF86BigfontQueryFont(client
);
683 SProcXF86BigfontDispatch(ClientPtr client
)
687 switch (stuff
->data
) {
688 case X_XF86BigfontQueryVersion
:
689 return SProcXF86BigfontQueryVersion(client
);
690 case X_XF86BigfontQueryFont
:
691 return SProcXF86BigfontQueryFont(client
);
698 XFree86BigfontExtensionInit(void)
700 if (AddExtension(XF86BIGFONTNAME
,
701 XF86BigfontNumberEvents
,
702 XF86BigfontNumberErrors
,
703 ProcXF86BigfontDispatch
,
704 SProcXF86BigfontDispatch
,
705 XF86BigfontResetProc
, StandardMinorOpcode
)) {
707 #ifdef MUST_CHECK_FOR_SHM_SYSCALL
709 * Note: Local-clients will not be optimized without shared memory
710 * support. Remote-client optimization does not depend on shared
711 * memory support. Thus, the extension is still registered even
712 * when shared memory support is not functional.
714 if (!CheckForShmSyscall()) {
715 ErrorF(XF86BIGFONTNAME
716 " extension local-client optimization disabled due to lack of shared memory support in the kernel\n");
721 srand((unsigned int) time(NULL
));
722 signature
= ((unsigned int) (65536.0 / (RAND_MAX
+ 1.0) * rand()) << 16)
723 + (unsigned int) (65536.0 / (RAND_MAX
+ 1.0) * rand());
724 /* fprintf(stderr, "signature = 0x%08X\n", signature); */
726 FontShmdescIndex
= AllocateFontPrivateIndex();
728 #if !defined(CSRG_BASED) && !defined(__CYGWIN__)
732 pagesize
= sysconf(_SC_PAGESIZE
);
734 pagesize
= getpagesize();