it compiles.. does it work?
[mikachu/openbox.git] / otk_c / color.c
1 // -*- mode: C; indent-tabs-mode: nil; -*-
2
3 #include "../config.h"
4 #include "color.h"
5 #include "display.h"
6 #include "screeninfo.h"
7
8 #ifdef HAVE_STDLIB_H
9 # include <stdlib.h>
10 #endif
11
12 static Bool cleancache = False;
13 static PyObject *colorcache;
14
15 // global color allocator/deallocator
16 typedef struct RGB {
17   PyObject_HEAD
18   int screen;
19   int r, g, b;
20 } RGB;
21
22 static void rgb_dealloc(PyObject* self)
23 {
24   PyObject_Del(self);
25 }
26
27 static int rgb_compare(PyObject *py1, PyObject *py2)
28 {
29   long result;
30   unsigned long p1, p2;
31   RGB *r1, *r2;
32
33   r1 = (RGB*) r1;
34   r2 = (RGB*) r2;
35   p1 = (r1->screen << 24 | r1->r << 16 | r1->g << 8 | r1->b) & 0x00ffffff;
36   p2 = (r2->screen << 24 | r2->r << 16 | r2->g << 8 | r2->b) & 0x00ffffff;
37
38   if (p1 < p2)
39     result = -1;
40   else if (p1 > p2)
41     result = 1;
42   else
43     result = 0;
44   return result;
45 }
46
47 static PyTypeObject RGB_Type = {
48   PyObject_HEAD_INIT(NULL)
49   0,
50   "RGB",
51   sizeof(RGB),
52   0,
53   rgb_dealloc, /*tp_dealloc*/
54   0,          /*tp_print*/
55   0,          /*tp_getattr*/
56   0,          /*tp_setattr*/
57   rgb_compare,          /*tp_compare*/
58   0,          /*tp_repr*/
59   0,          /*tp_as_number*/
60   0,          /*tp_as_sequence*/
61   0,          /*tp_as_mapping*/
62   0,          /*tp_hash */
63 };
64
65 static PyObject *RGB_New(int screen, int r, int g, int b) {
66   RGB *self = (RGB*) PyObject_New(RGB, &RGB_Type);
67   self->screen = screen;
68   self->r = r;
69   self->g = g;
70   self->b = b;
71   return (PyObject*)self;
72 }
73
74 typedef struct PixelRef {
75   unsigned long p;
76   unsigned int count;
77 } PixelRef;
78
79 static PixelRef *PixelRef_New(unsigned long p) {
80   PixelRef* self = malloc(sizeof(PixelRef));
81   self->p = p;
82   self->count = 1;
83   return self;
84 }
85
86 static void OtkColor_ParseColorName(OtkColor *self) {
87   XColor xcol;
88
89   if (!self->colorname) {
90     fprintf(stderr, "OtkColor: empty colorname, cannot parse (using black)\n");
91     OtkColor_SetRGB(self, 0, 0, 0);
92   }
93
94   // get rgb values from colorname
95   xcol.red = 0;
96   xcol.green = 0;
97   xcol.blue = 0;
98   xcol.pixel = 0;
99
100   if (!XParseColor(OBDisplay->display, self->colormap,
101                    PyString_AsString(self->colorname), &xcol)) {
102     fprintf(stderr, "BColor::allocate: color parse error: \"%s\"\n",
103             PyString_AsString(self->colorname));
104     OtkColor_SetRGB(self, 0, 0, 0);
105     return;
106   }
107
108   OtkColor_SetRGB(self, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
109 }
110
111 static void OtkColor_DoCacheCleanup() {
112   unsigned long *pixels;
113   int i;
114   unsigned int count;
115   PyObject *rgb, *pixref;
116   int ppos;
117
118   // ### TODO - support multiple displays!
119   if (!PyDict_Size(colorcache)) {
120     // nothing to do
121     return;
122   }
123
124   pixels = malloc(sizeof(unsigned long) * PyDict_Size(colorcache));
125
126   for (i = 0; i < ScreenCount(OBDisplay->display); i++) {
127     count = 0;
128     ppos = 0;
129
130     while (PyDict_Next(colorcache, &ppos, &rgb, &pixref)) {
131       if (((PixelRef*)pixref)->count != 0 || ((RGB*)rgb)->screen != i)
132         continue;
133
134       pixels[count++] = ((PixelRef*)pixref)->p;
135       PyDict_DelItem(colorcache, rgb);
136       free(pixref); // not really a PyObject, it just pretends
137       --ppos; // back up one in the iteration
138     }
139
140     if (count > 0)
141       XFreeColors(OBDisplay->display,
142                   OtkDisplay_ScreenInfo(OBDisplay, i)->colormap,
143                   pixels, count, 0);
144   }
145
146   free(pixels);
147   cleancache = False;
148 }
149
150 static void OtkColor_Allocate(OtkColor *self) {
151   XColor xcol;
152   PyObject *rgb, *pixref;
153
154   if (!OtkColor_IsValid(self)) {
155     if (!self->colorname) {
156       fprintf(stderr, "BColor: cannot allocate invalid color (using black)\n");
157       OtkColor_SetRGB(self, 0, 0, 0);
158     } else {
159       OtkColor_ParseColorName(self);
160     }
161   }
162
163   // see if we have allocated this color before
164   rgb = RGB_New(self->screen, self->red, self->green, self->blue);
165   pixref = PyDict_GetItem((PyObject*)colorcache, rgb);
166   if (pixref) {
167     // found
168     self->allocated = True;
169     self->pixel = ((PixelRef*)pixref)->p;
170     ((PixelRef*)pixref)->count++;
171     return;
172   }
173
174   // allocate color from rgb values
175   xcol.red =   self->red   | self->red   << 8;
176   xcol.green = self->green | self->green << 8;
177   xcol.blue =  self->blue  | self->blue  << 8;
178   xcol.pixel = 0;
179
180   if (!XAllocColor(OBDisplay->display, self->colormap, &xcol)) {
181     fprintf(stderr, "BColor::allocate: color alloc error: rgb:%x/%x/%x\n",
182             self->red, self->green, self->blue);
183     xcol.pixel = 0;
184   }
185
186   self->pixel = xcol.pixel;
187   self->allocated = True;
188
189   PyDict_SetItem(colorcache, rgb, (PyObject*)PixelRef_New(self->pixel));
190
191   if (cleancache)
192     OtkColor_DoCacheCleanup();
193 }
194
195 static void OtkColor_Deallocate(OtkColor *self) {
196   PyObject *rgb, *pixref;
197
198   if (!self->allocated)
199     return;
200
201   rgb = RGB_New(self->screen, self->red, self->green, self->blue);
202   pixref = PyDict_GetItem(colorcache, rgb);
203   if (pixref) {
204     if (((PixelRef*)pixref)->count >= 1)
205       ((PixelRef*)pixref)->count--;
206   }
207
208   if (cleancache)
209     OtkColor_DoCacheCleanup();
210
211   self->allocated = False;
212 }
213
214
215 OtkColor *OtkColor_New(int screen)
216 {
217   OtkColor *self = malloc(sizeof(OtkColor));
218
219   self->allocated = False;
220   self->red = -1;
221   self->green = -1;
222   self->blue = -1;
223   self->pixel = 0;
224   self->screen = screen;
225   self->colorname = NULL;
226   self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap;
227
228   return self;
229 }
230
231 OtkColor *OtkColor_FromRGB(int r, int g, int b, int screen)
232 {
233   OtkColor *self = malloc(sizeof(OtkColor));
234
235   self->allocated = False;
236   self->red = r;
237   self->green = g;
238   self->blue = b;
239   self->pixel = 0;
240   self->screen = screen;
241   self->colorname = NULL;
242   self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap;
243
244   return self;
245 }
246
247 OtkColor *OtkColor_FromName(const char *name, int screen)
248 {
249   OtkColor *self = malloc(sizeof(OtkColor));
250
251   self->allocated = False;
252   self->red = -1;
253   self->green = -1;
254   self->blue = -1;
255   self->pixel = 0;
256   self->screen = screen;
257   self->colorname = PyString_FromString(name);
258   self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap;
259
260   return self;
261 }
262
263 void OtkColor_Destroy(OtkColor *self)
264 {
265   if (self->colorname)
266     Py_DECREF(self->colorname);
267   free(self);
268 }
269
270 void OtkColor_SetRGB(OtkColor *self, int r, int g, int b)
271 {
272   OtkColor_Deallocate(self);
273   self->red = r;
274   self->green = g;
275   self->blue = b;
276 }
277
278 void OtkColor_SetScreen(OtkColor *self, int screen)
279 {
280   if (screen == self->screen) {
281     // nothing to do
282     return;
283   }
284
285   Otk_Deallocate(self);
286   
287   self->colormap = OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap;
288
289   self->screen = screen;
290
291   if (self->colorname)
292     parseColorName();
293 }
294
295 Bool OtkColor_IsValid(OtkColor *self)
296 {
297   return self->red != -1 && self->blue != -1 && self->green != -1;
298 }
299
300 unsigned long OtkColor_Pixel(OtkColor *self)
301 {
302   if (!self->allocated)
303     OtkColor_Allocate(self);
304   return self->pixel;
305 }
306
307 void OtkColor_InitializeCache()
308 {
309   colorcache = PyDict_New();
310 }
311
312 void OtkColor_DestroyCache()
313 {
314   Py_DECREF(colorcache);
315   colorcache = NULL;
316 }
317
318 void OtkColor_CleanupColorCache()
319 {
320   cleancache = True;
321 }