]> Dogcows Code - chaz/openbox/blobdiff - otk_c/color.c
typesafety
[chaz/openbox] / otk_c / color.c
index 9f71821799761868e0b9aa9d1d21c0d94bf77606..732f7b7d8d5baf5ef7915649b3e62f28d2f1822c 100644 (file)
 #endif
 
 static Bool cleancache = False;
-static PyObject *colorcache;
+static PyObject *colorcache = NULL;
 
-// 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) {
+static void parseColorName(OtkColor *self, const char *name) {
   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;
+  if (!XParseColor(OBDisplay->display,
+                   OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap,
+                   name, &xcol)) {
+    fprintf(stderr, "OtkColor: color parse error: \"%s\"\n", name);
+    self->red = self->green = self->blue = 0;
+  } else {
+    self->red = xcol.red >> 8;
+    self->green = xcol.green >> 8;
+    self->blue = xcol.blue >> 8;
   }
-
-  OtkColor_SetRGB(self, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
 }
 
-static void OtkColor_DoCacheCleanup() {
+static void doCacheCleanup() {
   unsigned long *pixels;
-  int i;
+  int i, ppos;
   unsigned int count;
-  PyObject *rgb, *pixref;
-  int ppos;
+  PyObject *key; // this is a color too, but i dont need to use it as such
+  OtkColor *color;
 
   // ### TODO - support multiple displays!
-  if (!PyDict_Size(colorcache)) {
-    // nothing to do
-    return;
-  }
+  if (!PyDict_Size(colorcache)) return; // nothing to do
 
   pixels = malloc(sizeof(unsigned long) * PyDict_Size(colorcache));
 
@@ -127,19 +49,22 @@ static void OtkColor_DoCacheCleanup() {
     count = 0;
     ppos = 0;
 
-    while (PyDict_Next(colorcache, &ppos, &rgb, &pixref)) {
-      if (((PixelRef*)pixref)->count != 0 || ((RGB*)rgb)->screen != i)
+    while (PyDict_Next(colorcache, &ppos, &key, (PyObject**)&color)) {
+      // get the screen from the hash
+      if (color->screen != i) continue; // wrong screen
+
+      // does someone other than the cache have a reference? (the cache gets 2)
+      if (color->ob_refcnt > 2)
         continue;
 
-      pixels[count++] = ((PixelRef*)pixref)->p;
-      PyDict_DelItem(colorcache, rgb);
-      free(pixref); // not really a PyObject, it just pretends
+      pixels[count++] = color->pixel;
+      PyDict_DelItem(colorcache, key);
       --ppos; // back up one in the iteration
     }
 
     if (count > 0)
       XFreeColors(OBDisplay->display,
-                 OtkDisplay_ScreenInfo(OBDisplay, i)->colormap,
+                  OtkDisplay_ScreenInfo(OBDisplay, i)->colormap,
                   pixels, count, 0);
   }
 
@@ -147,175 +72,141 @@ static void OtkColor_DoCacheCleanup() {
   cleancache = False;
 }
 
-static void OtkColor_Allocate(OtkColor *self) {
+static void 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",
+  
+  if (!XAllocColor(OBDisplay->display,
+                   OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap,
+                   &xcol)) {
+    fprintf(stderr, "OtkColor: 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;
+    doCacheCleanup();
 }
 
-
-OtkColor *OtkColor_New(int screen)
+PyObject *OtkColor_FromRGB(int r, int g, int b, int screen)
 {
-  OtkColor *self = malloc(sizeof(OtkColor));
+  OtkColor *self = PyObject_New(OtkColor, &OtkColor_Type);
+  PyObject *cached;
 
-  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;
-}
+  assert(screen >= 0); assert(r >= 0); assert(g >= 0); assert(b >= 0);
+  assert(r <= 0xff); assert(g <= 0xff); assert(b <= 0xff);
 
-OtkColor *OtkColor_FromRGB(int r, int g, int b, int screen)
-{
-  OtkColor *self = malloc(sizeof(OtkColor));
+  if (!colorcache) colorcache = PyDict_New();
 
-  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;
+  // does this color already exist in the cache?
+  cached = PyDict_GetItem(colorcache, (PyObject*)self);
+  if (cached) {
+    Py_INCREF(cached);
+    return cached;
+  }
+
+  // add it to the cache
+  PyDict_SetItem(colorcache, (PyObject*)self, (PyObject*)self);
+  allocate(self);
+  return (PyObject*)self;
 }
 
-OtkColor *OtkColor_FromName(const char *name, int screen)
+PyObject *OtkColor_FromName(const char *name, int screen)
 {
-  OtkColor *self = malloc(sizeof(OtkColor));
+  OtkColor *self = PyObject_New(OtkColor, &OtkColor_Type);
+  PyObject *cached;
+
+  assert(screen >= 0); assert(name);
 
-  self->allocated = False;
+  if (!colorcache) colorcache = PyDict_New();
+  
   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;
-}
+  parseColorName(self, name);
 
-void OtkColor_Destroy(OtkColor *self)
-{
-  if (self->colorname)
-    Py_DECREF(self->colorname);
-  free(self);
-}
+  // does this color already exist in the cache?
+  cached = PyDict_GetItem(colorcache, (PyObject*)self);
+  if (cached) {
+    Py_INCREF(cached);
+    return cached;
+  }
 
-void OtkColor_SetRGB(OtkColor *self, int r, int g, int b)
-{
-  OtkColor_Deallocate(self);
-  self->red = r;
-  self->green = g;
-  self->blue = b;
+  // add it to the cache
+  PyDict_SetItem(colorcache, (PyObject*)self, (PyObject*)self);
+  allocate(self);
+  return (PyObject*)self;
 }
 
-void OtkColor_SetScreen(OtkColor *self, int screen)
+void OtkColor_CleanupColorCache()
 {
-  if (screen == self->screen) {
-    // nothing to do
-    return;
-  }
-
-  Otk_Deallocate(self);
-  
-  self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap;
+  cleancache = True;
+}
 
-  self->screen = screen;
 
-  if (self->colorname)
-    parseColorName();
-}
 
-Bool OtkColor_IsValid(OtkColor *self)
+static void otkcolor_dealloc(OtkColor* self)
 {
-  return self->red != -1 && self->blue != -1 && self->green != -1;
+  // when this is called, the color has already been cleaned out of the cache
+  PyObject_Del((PyObject*)self);
 }
 
-unsigned long OtkColor_Pixel(OtkColor *self)
+static int otkcolor_compare(OtkColor *c1, OtkColor *c2)
 {
-  if (!self->allocated)
-    OtkColor_Allocate(self);
-  return self->pixel;
-}
+  long result;
+  unsigned long p1, p2;
 
-void OtkColor_InitializeCache()
-{
-  colorcache = PyDict_New();
+  p1 = c1->red << 16 | c1->green << 8 | c1->blue;
+  p2 = c2->red << 16 | c2->green << 8 | c2->blue;
+
+  if (p1 < p2)
+    result = -1;
+  else if (p1 > p2)
+    result = 1;
+  else
+    result = 0;
+  return result;
 }
 
-void OtkColor_DestroyCache()
+static PyObject *otkcolor_repr(OtkColor *self)
 {
-  Py_DECREF(colorcache);
-  colorcache = NULL;
+  return PyString_FromFormat("rgb:%x/%x/%x", self->red, self->green,
+                             self->blue);
 }
 
-void OtkColor_CleanupColorCache()
+static long otkcolor_hash(OtkColor *self)
 {
-  cleancache = True;
+  return self->screen << 24 | self->red << 16 | self->green << 8 | self->blue;
 }
+
+PyTypeObject OtkColor_Type = {
+  PyObject_HEAD_INIT(NULL)
+  0,
+  "OtkColor",
+  sizeof(OtkColor),
+  0,
+  (destructor)otkcolor_dealloc, /*tp_dealloc*/
+  0,                            /*tp_print*/
+  0,                            /*tp_getattr*/
+  0,                            /*tp_setattr*/
+  (cmpfunc)otkcolor_compare,    /*tp_compare*/
+  (reprfunc)otkcolor_repr,      /*tp_repr*/
+  0,                            /*tp_as_number*/
+  0,                            /*tp_as_sequence*/
+  0,                            /*tp_as_mapping*/
+  (hashfunc)otkcolor_hash,      /*tp_hash */
+};
This page took 0.028955 seconds and 4 git commands to generate.