/* * * Copyright © 2000 SuSE, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "misc.h" #include "scrnintstr.h" #include "os.h" #include "regionstr.h" #include "validate.h" #include "windowstr.h" #include "input.h" #include "resource.h" #include "colormapst.h" #include "cursorstr.h" #include "dixstruct.h" #include "gcstruct.h" #include "servermd.h" #include "picturestr.h" #include "glyphstr.h" /* * From Knuth -- a good choice for hash/rehash values is p, p-2 where * p and p-2 are both prime. These tables are sized to have an extra 10% * free to avoid exponential performance degradation as the hash table fills */ static GlyphHashSetRec glyphHashSets[] = { {32, 43, 41}, {64, 73, 71}, {128, 151, 149}, {256, 283, 281}, {512, 571, 569}, {1024, 1153, 1151}, {2048, 2269, 2267}, {4096, 4519, 4517}, {8192, 9013, 9011}, {16384, 18043, 18041}, {32768, 36109, 36107}, {65536, 72091, 72089}, {131072, 144409, 144407}, {262144, 288361, 288359}, {524288, 576883, 576881}, {1048576, 1153459, 1153457}, {2097152, 2307163, 2307161}, {4194304, 4613893, 4613891}, {8388608, 9227641, 9227639}, {16777216, 18455029, 18455027}, {33554432, 36911011, 36911009}, {67108864, 73819861, 73819859}, {134217728, 147639589, 147639587}, {268435456, 295279081, 295279079}, {536870912, 590559793, 590559791} }; #define NGLYPHHASHSETS (sizeof(glyphHashSets)/sizeof(glyphHashSets[0])) const CARD8 glyphDepths[GlyphFormatNum] = { 1, 4, 8, 16, 32 }; GlyphHashRec globalGlyphs[GlyphFormatNum]; int globalTotalGlyphPrivateSize = 0; static void SetGlyphScreenPrivateOffsets(void) { PictureScreenPtr ps; int offset = 0; int i; for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps && ps->totalGlyphPrivateSize) { ps->glyphPrivateOffset = offset; offset += ps->totalGlyphPrivateSize / sizeof(DevUnion); } } } static void SetGlyphPrivatePointers(GlyphPtr glyph) { PictureScreenPtr ps; int i; char *ptr; DevUnion *ppriv; unsigned *sizes; unsigned size; int len; for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps && ps->totalGlyphPrivateSize) { ppriv = glyph->devPrivates + ps->glyphPrivateOffset; sizes = ps->glyphPrivateSizes; ptr = (char *) (ppriv + ps->glyphPrivateLen); for (len = ps->glyphPrivateLen; --len >= 0; ppriv++, sizes++) { if ((size = *sizes) != 0) { ppriv->ptr = (pointer) ptr; ptr += size; } else ppriv->ptr = (pointer) 0; } } } } static Bool ReallocGlobalGlyphPrivate(GlyphPtr glyph) { PictureScreenPtr ps; DevUnion *devPrivates; char *ptr; int i; devPrivates = malloc(globalTotalGlyphPrivateSize); if (!devPrivates) return FALSE; ptr = (char *) devPrivates; for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps && ps->totalGlyphPrivateSize) { if (ps->glyphPrivateOffset != -1) { memcpy(ptr, glyph->devPrivates + ps->glyphPrivateOffset, ps->totalGlyphPrivateSize); } else if (ps->totalGlyphPrivateSize) { memset(ptr, 0, ps->totalGlyphPrivateSize); } ptr += ps->totalGlyphPrivateSize; } } if (glyph->devPrivates) free(glyph->devPrivates); glyph->devPrivates = devPrivates; return TRUE; } Bool GlyphInit(ScreenPtr pScreen) { PictureScreenPtr ps = GetPictureScreen(pScreen); ps->totalGlyphPrivateSize = 0; ps->glyphPrivateSizes = 0; ps->glyphPrivateLen = 0; ps->glyphPrivateOffset = -1; return TRUE; } Bool GlyphFinishInit(ScreenPtr pScreen) { PictureScreenPtr ps = GetPictureScreen(pScreen); if (ps->totalGlyphPrivateSize) { GlyphPtr glyph; int fdepth, i; globalTotalGlyphPrivateSize += ps->totalGlyphPrivateSize; for (fdepth = 0; fdepth < GlyphFormatNum; fdepth++) { if (!globalGlyphs[fdepth].hashSet) continue; for (i = 0; i < globalGlyphs[fdepth].hashSet->size; i++) { glyph = globalGlyphs[fdepth].table[i].glyph; if (glyph && glyph != DeletedGlyph) { if (!ReallocGlobalGlyphPrivate(glyph)) return FALSE; } } } SetGlyphScreenPrivateOffsets(); for (fdepth = 0; fdepth < GlyphFormatNum; fdepth++) { if (!globalGlyphs[fdepth].hashSet) continue; for (i = 0; i < globalGlyphs[fdepth].hashSet->size; i++) { glyph = globalGlyphs[fdepth].table[i].glyph; if (glyph && glyph != DeletedGlyph) { SetGlyphPrivatePointers(glyph); if (!(*ps->RealizeGlyph) (pScreen, glyph)) return FALSE; } } } } else ps->glyphPrivateOffset = 0; return TRUE; } void GlyphUninit(ScreenPtr pScreen) { PictureScreenPtr ps = GetPictureScreen(pScreen); GlyphPtr glyph; int fdepth, i; globalTotalGlyphPrivateSize -= ps->totalGlyphPrivateSize; for (fdepth = 0; fdepth < GlyphFormatNum; fdepth++) { if (!globalGlyphs[fdepth].hashSet) continue; for (i = 0; i < globalGlyphs[fdepth].hashSet->size; i++) { glyph = globalGlyphs[fdepth].table[i].glyph; if (glyph && glyph != DeletedGlyph) { (*ps->UnrealizeGlyph) (pScreen, glyph); if (globalTotalGlyphPrivateSize) { if (!ReallocGlobalGlyphPrivate(glyph)) return; } else { if (glyph->devPrivates) free(glyph->devPrivates); glyph->devPrivates = NULL; } } } } if (globalTotalGlyphPrivateSize) SetGlyphScreenPrivateOffsets(); for (fdepth = 0; fdepth < GlyphFormatNum; fdepth++) { if (!globalGlyphs[fdepth].hashSet) continue; for (i = 0; i < globalGlyphs[fdepth].hashSet->size; i++) { glyph = globalGlyphs[fdepth].table[i].glyph; if (glyph && glyph != DeletedGlyph) { if (globalTotalGlyphPrivateSize) SetGlyphPrivatePointers(glyph); } } } if (ps->glyphPrivateSizes) free(ps->glyphPrivateSizes); } GlyphHashSetPtr FindGlyphHashSet(CARD32 filled) { int i; for (i = 0; i < NGLYPHHASHSETS; i++) if (glyphHashSets[i].entries >= filled) return &glyphHashSets[i]; return 0; } static int _GlyphSetPrivateAllocateIndex = 0; void ResetGlyphSetPrivateIndex(void) { _GlyphSetPrivateAllocateIndex = 0; } Bool _GlyphSetSetNewPrivate(GlyphSetPtr glyphSet, int n, pointer ptr) { pointer *new; if (n > glyphSet->maxPrivate) { if (glyphSet->devPrivates && glyphSet->devPrivates != (pointer) (&glyphSet[1])) { new = (pointer *) realloc(glyphSet->devPrivates, (n + 1) * sizeof(pointer)); if (!new) return FALSE; } else { new = malloc((n + 1) * sizeof(pointer)); if (!new) return FALSE; if (glyphSet->devPrivates) memcpy(new, glyphSet->devPrivates, (glyphSet->maxPrivate + 1) * sizeof(pointer)); } glyphSet->devPrivates = new; /* Zero out new, uninitialize privates */ while (++glyphSet->maxPrivate < n) glyphSet->devPrivates[glyphSet->maxPrivate] = (pointer) 0; } glyphSet->devPrivates[n] = ptr; return TRUE; } GlyphRefPtr FindGlyphRef(GlyphHashPtr hash, CARD32 signature, Bool match, GlyphPtr compare) { CARD32 elt, step, s; GlyphPtr glyph; GlyphRefPtr table, gr, del; CARD32 tableSize = hash->hashSet->size; table = hash->table; elt = signature % tableSize; step = 0; del = 0; for (;;) { gr = &table[elt]; s = gr->signature; glyph = gr->glyph; if (!glyph) { if (del) gr = del; break; } if (glyph == DeletedGlyph) { if (!del) del = gr; else if (gr == del) break; } else if (s == signature && (!match || memcmp(&compare->info, &glyph->info, compare->size) == 0)) { break; } if (!step) { step = signature % hash->hashSet->rehash; if (!step) step = 1; } elt += step; if (elt >= tableSize) elt -= tableSize; } return gr; } CARD32 HashGlyph(GlyphPtr glyph) { CARD32 *bits = (CARD32 *) &(glyph->info); CARD32 hash; int n = glyph->size / sizeof(CARD32); hash = 0; while (n--) hash ^= *bits++; return hash; } #ifdef CHECK_DUPLICATES void DuplicateRef(GlyphPtr glyph, char *where) { ErrorF("Duplicate Glyph 0x%x from %s\n", glyph, where); } void CheckDuplicates(GlyphHashPtr hash, char *where) { GlyphPtr g; int i, j; for (i = 0; i < hash->hashSet->size; i++) { g = hash->table[i].glyph; if (!g || g == DeletedGlyph) continue; for (j = i + 1; j < hash->hashSet->size; j++) if (hash->table[j].glyph == g) DuplicateRef(g, where); } } #else #define CheckDuplicates(a,b) #define DuplicateRef(a,b) #endif void FreeGlyph(GlyphPtr glyph, int format) { CheckDuplicates(&globalGlyphs[format], "FreeGlyph"); if (--glyph->refcnt == 0) { PictureScreenPtr ps; GlyphRefPtr gr; int i; int first; first = -1; for (i = 0; i < globalGlyphs[format].hashSet->size; i++) if (globalGlyphs[format].table[i].glyph == glyph) { if (first != -1) DuplicateRef(glyph, "FreeGlyph check"); first = i; } gr = FindGlyphRef(&globalGlyphs[format], HashGlyph(glyph), TRUE, glyph); if (gr - globalGlyphs[format].table != first) DuplicateRef(glyph, "Found wrong one"); if (gr->glyph && gr->glyph != DeletedGlyph) { gr->glyph = DeletedGlyph; gr->signature = 0; globalGlyphs[format].tableEntries--; } for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps) (*ps->UnrealizeGlyph) (screenInfo.screens[i], glyph); } if (glyph->devPrivates) free(glyph->devPrivates); free(glyph); } } void AddGlyph(GlyphSetPtr glyphSet, GlyphPtr glyph, Glyph id) { GlyphRefPtr gr; CARD32 hash; CheckDuplicates(&globalGlyphs[glyphSet->fdepth], "AddGlyph top global"); /* Locate existing matching glyph */ hash = HashGlyph(glyph); gr = FindGlyphRef(&globalGlyphs[glyphSet->fdepth], hash, TRUE, glyph); if (gr->glyph && gr->glyph != DeletedGlyph) { PictureScreenPtr ps; int i; for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps) (*ps->UnrealizeGlyph) (screenInfo.screens[i], glyph); } if (glyph->devPrivates) free(glyph->devPrivates); free(glyph); glyph = gr->glyph; } else { gr->glyph = glyph; gr->signature = hash; globalGlyphs[glyphSet->fdepth].tableEntries++; } /* Insert/replace glyphset value */ gr = FindGlyphRef(&glyphSet->hash, id, FALSE, 0); ++glyph->refcnt; if (gr->glyph && gr->glyph != DeletedGlyph) FreeGlyph(gr->glyph, glyphSet->fdepth); else glyphSet->hash.tableEntries++; gr->glyph = glyph; gr->signature = id; CheckDuplicates(&globalGlyphs[glyphSet->fdepth], "AddGlyph bottom"); } Bool DeleteGlyph(GlyphSetPtr glyphSet, Glyph id) { GlyphRefPtr gr; GlyphPtr glyph; gr = FindGlyphRef(&glyphSet->hash, id, FALSE, 0); glyph = gr->glyph; if (glyph && glyph != DeletedGlyph) { gr->glyph = DeletedGlyph; glyphSet->hash.tableEntries--; FreeGlyph(glyph, glyphSet->fdepth); return TRUE; } return FALSE; } GlyphPtr FindGlyph(GlyphSetPtr glyphSet, Glyph id) { GlyphPtr glyph; glyph = FindGlyphRef(&glyphSet->hash, id, FALSE, 0)->glyph; if (glyph == DeletedGlyph) glyph = 0; return glyph; } GlyphPtr AllocateGlyph(xGlyphInfo * gi, int fdepth) { PictureScreenPtr ps; int size; GlyphPtr glyph; int i; size = gi->height * PixmapBytePad(gi->width, glyphDepths[fdepth]); glyph = malloc(size + sizeof(GlyphRec)); if (!glyph) return 0; glyph->refcnt = 0; glyph->size = size + sizeof(xGlyphInfo); glyph->info = *gi; if (globalTotalGlyphPrivateSize) { glyph->devPrivates = malloc(globalTotalGlyphPrivateSize); if (!glyph->devPrivates) return 0; SetGlyphPrivatePointers(glyph); } else glyph->devPrivates = NULL; for (i = 0; i < screenInfo.numScreens; i++) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps) { if (!(*ps->RealizeGlyph) (screenInfo.screens[i], glyph)) { while (i--) { ps = GetPictureScreenIfSet(screenInfo.screens[i]); if (ps) (*ps->UnrealizeGlyph) (screenInfo.screens[i], glyph); } if (glyph->devPrivates) free(glyph->devPrivates); free(glyph); return 0; } } } return glyph; } Bool AllocateGlyphHash(GlyphHashPtr hash, GlyphHashSetPtr hashSet) { hash->table = malloc(hashSet->size * sizeof(GlyphRefRec)); if (!hash->table) return FALSE; memset(hash->table, 0, hashSet->size * sizeof(GlyphRefRec)); hash->hashSet = hashSet; hash->tableEntries = 0; return TRUE; } Bool ResizeGlyphHash(GlyphHashPtr hash, CARD32 change, Bool global) { CARD32 tableEntries; GlyphHashSetPtr hashSet; GlyphHashRec newHash; GlyphRefPtr gr; GlyphPtr glyph; int i; int oldSize; CARD32 s; tableEntries = hash->tableEntries + change; hashSet = FindGlyphHashSet(tableEntries); if (hashSet == hash->hashSet) return TRUE; if (global) CheckDuplicates(hash, "ResizeGlyphHash top"); if (!AllocateGlyphHash(&newHash, hashSet)) return FALSE; if (hash->table) { oldSize = hash->hashSet->size; for (i = 0; i < oldSize; i++) { glyph = hash->table[i].glyph; if (glyph && glyph != DeletedGlyph) { s = hash->table[i].signature; gr = FindGlyphRef(&newHash, s, global, glyph); gr->signature = s; gr->glyph = glyph; ++newHash.tableEntries; } } free(hash->table); } *hash = newHash; if (global) CheckDuplicates(hash, "ResizeGlyphHash bottom"); return TRUE; } Bool ResizeGlyphSet(GlyphSetPtr glyphSet, CARD32 change) { return (ResizeGlyphHash(&glyphSet->hash, change, FALSE) && ResizeGlyphHash(&globalGlyphs[glyphSet->fdepth], change, TRUE)); } GlyphSetPtr AllocateGlyphSet(int fdepth, PictFormatPtr format) { GlyphSetPtr glyphSet; int size; if (!globalGlyphs[fdepth].hashSet) { if (!AllocateGlyphHash(&globalGlyphs[fdepth], &glyphHashSets[0])) return FALSE; } size = (sizeof(GlyphSetRec) + (sizeof(pointer) * _GlyphSetPrivateAllocateIndex)); glyphSet = malloc(size); if (!glyphSet) return FALSE; bzero((char *) glyphSet, size); glyphSet->maxPrivate = _GlyphSetPrivateAllocateIndex - 1; if (_GlyphSetPrivateAllocateIndex) glyphSet->devPrivates = (pointer) (&glyphSet[1]); if (!AllocateGlyphHash(&glyphSet->hash, &glyphHashSets[0])) { free(glyphSet); return FALSE; } glyphSet->refcnt = 1; glyphSet->fdepth = fdepth; glyphSet->format = format; return glyphSet; } int FreeGlyphSet(pointer value, XID gid) { GlyphSetPtr glyphSet = (GlyphSetPtr) value; if (--glyphSet->refcnt == 0) { CARD32 i, tableSize = glyphSet->hash.hashSet->size; GlyphRefPtr table = glyphSet->hash.table; GlyphPtr glyph; for (i = 0; i < tableSize; i++) { glyph = table[i].glyph; if (glyph && glyph != DeletedGlyph) FreeGlyph(glyph, glyphSet->fdepth); } if (!globalGlyphs[glyphSet->fdepth].tableEntries) { free(globalGlyphs[glyphSet->fdepth].table); globalGlyphs[glyphSet->fdepth].table = 0; globalGlyphs[glyphSet->fdepth].hashSet = 0; } else ResizeGlyphHash(&globalGlyphs[glyphSet->fdepth], 0, TRUE); free(table); if (glyphSet->devPrivates && glyphSet->devPrivates != (pointer) (&glyphSet[1])) free(glyphSet->devPrivates); free(glyphSet); } return Success; }