From 59b65db2cac9f359dfcff3ab988e70eab053bdb4 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sat, 21 Dec 2002 02:09:54 +0000 Subject: [PATCH] it compiles.. does it work? --- otk_c/color.c | 321 ++++++++++++++++++++++++++++++++++++++++++++++++ otk_c/color.h | 33 +++++ otk_c/gccache.c | 232 ++++++++++++++++++++++++++++++++++ otk_c/gccache.h | 98 +++++++++++++++ 4 files changed, 684 insertions(+) create mode 100644 otk_c/color.c create mode 100644 otk_c/color.h create mode 100644 otk_c/gccache.c create mode 100644 otk_c/gccache.h diff --git a/otk_c/color.c b/otk_c/color.c new file mode 100644 index 00000000..9f718217 --- /dev/null +++ b/otk_c/color.c @@ -0,0 +1,321 @@ +// -*- mode: C; indent-tabs-mode: nil; -*- + +#include "../config.h" +#include "color.h" +#include "display.h" +#include "screeninfo.h" + +#ifdef HAVE_STDLIB_H +# include +#endif + +static Bool cleancache = False; +static PyObject *colorcache; + +// global color allocator/deallocator +typedef struct RGB { + PyObject_HEAD + int screen; + int r, g, b; +} RGB; + +static void rgb_dealloc(PyObject* self) +{ + PyObject_Del(self); +} + +static int rgb_compare(PyObject *py1, PyObject *py2) +{ + long result; + unsigned long p1, p2; + RGB *r1, *r2; + + r1 = (RGB*) r1; + r2 = (RGB*) r2; + p1 = (r1->screen << 24 | r1->r << 16 | r1->g << 8 | r1->b) & 0x00ffffff; + p2 = (r2->screen << 24 | r2->r << 16 | r2->g << 8 | r2->b) & 0x00ffffff; + + if (p1 < p2) + result = -1; + else if (p1 > p2) + result = 1; + else + result = 0; + return result; +} + +static PyTypeObject RGB_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "RGB", + sizeof(RGB), + 0, + rgb_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + rgb_compare, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ +}; + +static PyObject *RGB_New(int screen, int r, int g, int b) { + RGB *self = (RGB*) PyObject_New(RGB, &RGB_Type); + self->screen = screen; + self->r = r; + self->g = g; + self->b = b; + return (PyObject*)self; +} + +typedef struct PixelRef { + unsigned long p; + unsigned int count; +} PixelRef; + +static PixelRef *PixelRef_New(unsigned long p) { + PixelRef* self = malloc(sizeof(PixelRef)); + self->p = p; + self->count = 1; + return self; +} + +static void OtkColor_ParseColorName(OtkColor *self) { + XColor xcol; + + if (!self->colorname) { + fprintf(stderr, "OtkColor: empty colorname, cannot parse (using black)\n"); + OtkColor_SetRGB(self, 0, 0, 0); + } + + // get rgb values from colorname + xcol.red = 0; + xcol.green = 0; + xcol.blue = 0; + xcol.pixel = 0; + + if (!XParseColor(OBDisplay->display, self->colormap, + PyString_AsString(self->colorname), &xcol)) { + fprintf(stderr, "BColor::allocate: color parse error: \"%s\"\n", + PyString_AsString(self->colorname)); + OtkColor_SetRGB(self, 0, 0, 0); + return; + } + + OtkColor_SetRGB(self, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8); +} + +static void OtkColor_DoCacheCleanup() { + unsigned long *pixels; + int i; + unsigned int count; + PyObject *rgb, *pixref; + int ppos; + + // ### TODO - support multiple displays! + if (!PyDict_Size(colorcache)) { + // nothing to do + return; + } + + pixels = malloc(sizeof(unsigned long) * PyDict_Size(colorcache)); + + for (i = 0; i < ScreenCount(OBDisplay->display); i++) { + count = 0; + ppos = 0; + + while (PyDict_Next(colorcache, &ppos, &rgb, &pixref)) { + if (((PixelRef*)pixref)->count != 0 || ((RGB*)rgb)->screen != i) + continue; + + pixels[count++] = ((PixelRef*)pixref)->p; + PyDict_DelItem(colorcache, rgb); + free(pixref); // not really a PyObject, it just pretends + --ppos; // back up one in the iteration + } + + if (count > 0) + XFreeColors(OBDisplay->display, + OtkDisplay_ScreenInfo(OBDisplay, i)->colormap, + pixels, count, 0); + } + + free(pixels); + cleancache = False; +} + +static void OtkColor_Allocate(OtkColor *self) { + XColor xcol; + PyObject *rgb, *pixref; + + if (!OtkColor_IsValid(self)) { + if (!self->colorname) { + fprintf(stderr, "BColor: cannot allocate invalid color (using black)\n"); + OtkColor_SetRGB(self, 0, 0, 0); + } else { + OtkColor_ParseColorName(self); + } + } + + // see if we have allocated this color before + rgb = RGB_New(self->screen, self->red, self->green, self->blue); + pixref = PyDict_GetItem((PyObject*)colorcache, rgb); + if (pixref) { + // found + self->allocated = True; + self->pixel = ((PixelRef*)pixref)->p; + ((PixelRef*)pixref)->count++; + return; + } + + // allocate color from rgb values + xcol.red = self->red | self->red << 8; + xcol.green = self->green | self->green << 8; + xcol.blue = self->blue | self->blue << 8; + xcol.pixel = 0; + + if (!XAllocColor(OBDisplay->display, self->colormap, &xcol)) { + fprintf(stderr, "BColor::allocate: color alloc error: rgb:%x/%x/%x\n", + self->red, self->green, self->blue); + xcol.pixel = 0; + } + + self->pixel = xcol.pixel; + self->allocated = True; + + PyDict_SetItem(colorcache, rgb, (PyObject*)PixelRef_New(self->pixel)); + + if (cleancache) + OtkColor_DoCacheCleanup(); +} + +static void OtkColor_Deallocate(OtkColor *self) { + PyObject *rgb, *pixref; + + if (!self->allocated) + return; + + rgb = RGB_New(self->screen, self->red, self->green, self->blue); + pixref = PyDict_GetItem(colorcache, rgb); + if (pixref) { + if (((PixelRef*)pixref)->count >= 1) + ((PixelRef*)pixref)->count--; + } + + if (cleancache) + OtkColor_DoCacheCleanup(); + + self->allocated = False; +} + + +OtkColor *OtkColor_New(int screen) +{ + OtkColor *self = malloc(sizeof(OtkColor)); + + self->allocated = False; + self->red = -1; + self->green = -1; + self->blue = -1; + self->pixel = 0; + self->screen = screen; + self->colorname = NULL; + self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap; + + return self; +} + +OtkColor *OtkColor_FromRGB(int r, int g, int b, int screen) +{ + OtkColor *self = malloc(sizeof(OtkColor)); + + self->allocated = False; + self->red = r; + self->green = g; + self->blue = b; + self->pixel = 0; + self->screen = screen; + self->colorname = NULL; + self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap; + + return self; +} + +OtkColor *OtkColor_FromName(const char *name, int screen) +{ + OtkColor *self = malloc(sizeof(OtkColor)); + + self->allocated = False; + self->red = -1; + self->green = -1; + self->blue = -1; + self->pixel = 0; + self->screen = screen; + self->colorname = PyString_FromString(name); + self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap; + + return self; +} + +void OtkColor_Destroy(OtkColor *self) +{ + if (self->colorname) + Py_DECREF(self->colorname); + free(self); +} + +void OtkColor_SetRGB(OtkColor *self, int r, int g, int b) +{ + OtkColor_Deallocate(self); + self->red = r; + self->green = g; + self->blue = b; +} + +void OtkColor_SetScreen(OtkColor *self, int screen) +{ + if (screen == self->screen) { + // nothing to do + return; + } + + Otk_Deallocate(self); + + self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap; + + self->screen = screen; + + if (self->colorname) + parseColorName(); +} + +Bool OtkColor_IsValid(OtkColor *self) +{ + return self->red != -1 && self->blue != -1 && self->green != -1; +} + +unsigned long OtkColor_Pixel(OtkColor *self) +{ + if (!self->allocated) + OtkColor_Allocate(self); + return self->pixel; +} + +void OtkColor_InitializeCache() +{ + colorcache = PyDict_New(); +} + +void OtkColor_DestroyCache() +{ + Py_DECREF(colorcache); + colorcache = NULL; +} + +void OtkColor_CleanupColorCache() +{ + cleancache = True; +} diff --git a/otk_c/color.h b/otk_c/color.h new file mode 100644 index 00000000..91b83e01 --- /dev/null +++ b/otk_c/color.h @@ -0,0 +1,33 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef __color_h +#define __color_h + +#include +#include + + +typedef struct OtkColor { + int red, green, blue; + int screen; + Bool allocated; + unsigned long pixel; + PyObject *colorname; // PyStringObject + Colormap colormap; +} OtkColor; + +OtkColor *OtkColor_New(int screen); +OtkColor *OtkColor_FromRGB(int r, int g, int b, int screen); +OtkColor *OtkColor_FromName(const char *name, int screen); + +void OtkColor_Destroy(OtkColor *self); + +void OtkColor_SetRGB(OtkColor *self, int r, int g, int b); +void OtkColor_SetScreen(OtkColor *self, int screen); +Bool OtkColor_IsValid(OtkColor *self); +unsigned long OtkColor_Pixel(OtkColor *self); + +void OtkColor_InitializeCache(); +void OtkColor_DestroyCache(); +void OtkColor_CleanupColorCache(); + +#endif // __color_h diff --git a/otk_c/gccache.c b/otk_c/gccache.c new file mode 100644 index 00000000..7d96677c --- /dev/null +++ b/otk_c/gccache.c @@ -0,0 +1,232 @@ +// -*- mode: C; indent-tabs-mode: nil; -*- + +#include "../config.h" +#include "gccache.h" +#include "screeninfo.h" + +#ifdef HAVE_STDLIB_H +# include +#endif + +static OtkGCCache *gccache; + +OtkGCCacheContext *OtkGCCacheContext_New() +{ + OtkGCCacheContext *self = malloc(sizeof(OtkGCCacheContext)); + + self->gc = 0; + self->pixel = 0ul; + self->fontid = 0ul; + self->function = 0; + self->subwindow = 0; + self->used = False; + self->screen = ~0; + self->linewidth = 0; + + return self; +} + +void OtkGCCacheContext_Destroy(OtkGCCacheContext *self) +{ + if (self->gc) + XFreeGC(OBDisplay->display, self->gc); + free(self); +} + +void OtkGCCacheContext_Set(OtkGCCacheContext *self, + OtkColor *color, XFontStruct *font, + int function, int subwindow, int linewidth) +{ + XGCValues gcv; + unsigned long mask; + + self->pixel = gcv.foreground = OtkColor_Pixel(color); + self->function = gcv.function = function; + self->subwindow = gcv.subwindow_mode = subwindow; + self->linewidth = gcv.line_width = linewidth; + gcv.cap_style = CapProjecting; + + mask = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth | + GCCapStyle; + + if (font) { + self->fontid = gcv.font = font->fid; + mask |= GCFont; + } else { + self->fontid = 0; + } + + XChangeGC(OBDisplay->display, self->gc, mask, &gcv); +} + +void OtkGCCacheContext_SetFont(OtkGCCacheContext *self, + XFontStruct *font) +{ + if (!font) { + self->fontid = 0; + return; + } + + XGCValues gcv; + self->fontid = gcv.font = font->fid; + XChangeGC(OBDisplay->display, self->gc, GCFont, &gcv); +} + + +OtkGCCacheItem *OtkGCCacheItem_New() +{ + OtkGCCacheItem *self = malloc(sizeof(OtkGCCacheItem)); + + self->ctx = 0; + self->count = 0; + self->hits = 0; + self->fault = False; +} + + +void OtkGCCache_Initialize(int screen_count) +{ + int i; + + gccache = malloc(sizeof(OtkGCCache)); + + gccache->context_count = 128; + gccache->cache_size = 16; + gccache->cache_buckets = 8 * screen_count; + gccache->cache_total_size = gccache->cache_size * gccache->cache_buckets; + + gccache->contexts = malloc(sizeof(OtkGCCacheContext*) * + gccache->context_count); + for (i = 0; i < gccache->context_count; ++i) + gccache->contexts[i] = OtkGCCacheContext_New(); + + gccache->cache = malloc(sizeof(OtkGCCacheItem*) * gccache->cache_total_size); + for (i = 0; i < gccache->cache_total_size; ++i) + gccache->cache[i] = OtkGCCacheItem_New(); +} + + +void OtkGCCache_Destroy() +{ + int i; + + for (i = 0; i < gccache->context_count; ++i) + OtkGCCacheContext_Destroy(gccache->contexts[i]); + + for (i = 0; i < gccache->cache_total_size; ++i) + free(gccache->cache[i]); + + free(gccache->contexts); + free(gccache->cache); + free(gccache); + gccache = NULL; +} + +OtkGCCacheContext *OtkGCCache_NextContext(int screen) +{ + Window hd = OtkDisplay_ScreenInfo(OBDisplay, screen)->root_window; + OtkGCCacheContext *c; + int i; + + for (i = 0; i < gccache->context_count; ++i) { + c = gccache->contexts[i]; + + if (! c->gc) { + c->gc = XCreateGC(OBDisplay->display, hd, 0, 0); + c->used = False; + c->screen = screen; + } + if (! c->used && c->screen == screen) + return c; + } + + fprintf(stderr, "OtkGCCache: context fault!\n"); + abort(); + return NULL; // shut gcc up +} + + +static void OtkGCCache_InternalRelease(OtkGCCacheContext *ctx) +{ + ctx->used = False; +} + +OtkGCCacheItem *OtkGCCache_Find(OtkColor *color, XFontStruct *font, + int function, int subwindow, int linewidth) +{ + const unsigned long pixel = OtkColor_Pixel(color); + const unsigned int screen = color->screen; + const int key = color->red ^ color->green ^ color->blue; + int k = (key % gccache->cache_size) * gccache->cache_buckets; + int i = 0; // loop variable + OtkGCCacheItem *c = gccache->cache[k], *prev = 0; + + /* + this will either loop cache_buckets times then return/abort or + it will stop matching + */ + while (c->ctx && + (c->ctx->pixel != pixel || c->ctx->function != function || + c->ctx->subwindow != subwindow || c->ctx->screen != screen || + c->ctx->linewidth != linewidth)) { + if (i < (gccache->cache_buckets - 1)) { + prev = c; + c = gccache->cache[++k]; + ++i; + continue; + } + if (c->count == 0 && c->ctx->screen == screen) { + // use this cache item + OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow, + linewidth); + c->ctx->used = True; + c->count = 1; + c->hits = 1; + return c; + } + // cache fault! + fprintf(stderr, "OtkGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen); + abort(); + } + + if (c->ctx) { + // reuse existing context + if (font && font->fid && font->fid != c->ctx->fontid) + OtkGCCacheContext_SetFont(c->ctx, font); + c->count++; + c->hits++; + if (prev && c->hits > prev->hits) { + gccache->cache[k] = prev; + gccache->cache[k-1] = c; + } + } else { + c->ctx = OtkGCCache_NextContext(screen); + OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow, linewidth); + c->ctx->used = True; + c->count = 1; + c->hits = 1; + } + + return c; +} + + +void OtkGCCache_Release(OtkGCCacheItem *item) +{ + item->count--; +} + + +void OtkGCCache_Purge() +{ + int i; + + for (i = 0; i < gccache->cache_total_size; ++i) { + OtkGCCacheItem *d = gccache->cache[i]; + + if (d->ctx && d->count == 0) { + release(d->ctx); + d->ctx = 0; + } + } +} diff --git a/otk_c/gccache.h b/otk_c/gccache.h new file mode 100644 index 00000000..f6657f48 --- /dev/null +++ b/otk_c/gccache.h @@ -0,0 +1,98 @@ +// -*- mode: C; indent-tabs-mode: nil; -*- +#ifndef __gccache_h +#define __gccache_h + +#include + +#include "display.h" +#include "color.h" + +struct OtkGCCacheItem; + +typedef struct OtkGCCacheContext { + GC gc; + unsigned long pixel; + unsigned long fontid; + int function; + int subwindow; + Bool used; + unsigned int screen; + int linewidth; +} OtkGCCacheContext; + +OtkGCCacheContext *OtkGCCacheContext_New(); +void OtkGCCacheContext_Destroy(OtkGCCacheContext *self); + +void OtkGCCacheContext_Set(OtkGCCacheContext *self, + OtkColor *color, XFontStruct *font, + int function, int subwindow, int linewidth); +void OtkGCCacheContext_SetFont(OtkGCCacheContext *self, + XFontStruct *font); + + +typedef struct OtkGCCacheItem { + OtkGCCacheContext *ctx; + unsigned int count; + unsigned int hits; + Bool fault; +} OtkGCCacheItem; + +OtkGCCacheItem *OtkGCCacheItem_New(); + + +typedef struct OtkGCCache { + // this is closely modelled after the Qt GC cache, but with some of the + // complexity stripped out + unsigned int context_count; + unsigned int cache_size; + unsigned int cache_buckets; + unsigned int cache_total_size; + OtkGCCacheContext **contexts; + OtkGCCacheItem **cache; +} OtkGCCache; + +void OtkGCCache_Initialize(int screen_count); +void OtkGCCache_Destroy(); + +// cleans up the cache +void OtkGCCache_Purge(); + +OtkGCCacheItem *OtkGCCache_Find(OtkColor *color, + XFontStruct *font, int function, + int subwindow, int linewidth); +void OtkGCCache_Release(OtkGCCacheItem *item); + + +/* + + +class BPen { +public: + inline BPen(const BColor &_color, const XFontStruct * const _font = 0, + int _linewidth = 0, int _function = GXcopy, + int _subwindow = ClipByChildren) + : color(_color), font(_font), linewidth(_linewidth), function(_function), + subwindow(_subwindow), cache(OBDisplay::gcCache()), item(0) { } + + inline ~BPen(void) { if (item) cache->release(item); } + + inline const GC &gc(void) const { + if (! item) item = cache->find(color, font, function, subwindow, + linewidth); + return item->gc(); + } + +private: + const BColor &color; + const XFontStruct *font; + int linewidth; + int function; + int subwindow; + + mutable BGCCache *cache; + mutable BGCCacheItem *item; +}; + +}*/ + +#endif // __gccache_h -- 2.39.2