322 lines
6.8 KiB
C
322 lines
6.8 KiB
C
|
// -*- mode: C; indent-tabs-mode: nil; -*-
|
||
|
|
||
|
#include "../config.h"
|
||
|
#include "color.h"
|
||
|
#include "display.h"
|
||
|
#include "screeninfo.h"
|
||
|
|
||
|
#ifdef HAVE_STDLIB_H
|
||
|
# include <stdlib.h>
|
||
|
#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;
|
||
|
}
|