make rect a proper pyobject. use "typesafety"
[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 = NULL;
14
15 static void parseColorName(OtkColor *self, const char *name) {
16   XColor xcol;
17
18   // get rgb values from colorname
19   xcol.red = 0;
20   xcol.green = 0;
21   xcol.blue = 0;
22   xcol.pixel = 0;
23
24   if (!XParseColor(OBDisplay->display,
25                    OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap,
26                    name, &xcol)) {
27     fprintf(stderr, "OtkColor: color parse error: \"%s\"\n", name);
28     self->red = self->green = self->blue = 0;
29   } else {
30     self->red = xcol.red >> 8;
31     self->green = xcol.green >> 8;
32     self->blue = xcol.blue >> 8;
33   }
34 }
35
36 static void doCacheCleanup() {
37   unsigned long *pixels;
38   int i, ppos;
39   unsigned int count;
40   PyObject *key; // this is a color too, but i dont need to use it as such
41   OtkColor *color;
42
43   // ### TODO - support multiple displays!
44   if (!PyDict_Size(colorcache)) return; // nothing to do
45
46   pixels = malloc(sizeof(unsigned long) * PyDict_Size(colorcache));
47
48   for (i = 0; i < ScreenCount(OBDisplay->display); i++) {
49     count = 0;
50     ppos = 0;
51
52     while (PyDict_Next(colorcache, &ppos, &key, (PyObject**)&color)) {
53       // get the screen from the hash
54       if (color->screen != i) continue; // wrong screen
55
56       // does someone other than the cache have a reference? (the cache gets 2)
57       if (color->ob_refcnt > 2)
58         continue;
59
60       pixels[count++] = color->pixel;
61       PyDict_DelItem(colorcache, key);
62       --ppos; // back up one in the iteration
63     }
64
65     if (count > 0)
66       XFreeColors(OBDisplay->display,
67                   OtkDisplay_ScreenInfo(OBDisplay, i)->colormap,
68                   pixels, count, 0);
69   }
70
71   free(pixels);
72   cleancache = False;
73 }
74
75 static void allocate(OtkColor *self) {
76   XColor xcol;
77
78   assert(!self->allocated);
79
80   // allocate color from rgb values
81   xcol.red =   self->red   | self->red   << 8;
82   xcol.green = self->green | self->green << 8;
83   xcol.blue =  self->blue  | self->blue  << 8;
84   xcol.pixel = 0;
85   
86   if (!XAllocColor(OBDisplay->display,
87                    OtkDisplay_ScreenInfo(OBDisplay, self->screen)->colormap,
88                    &xcol)) {
89     fprintf(stderr, "OtkColor: color alloc error: rgb:%x/%x/%x\n",
90             self->red, self->green, self->blue);
91     xcol.pixel = 0;
92   }
93   
94   self->pixel = xcol.pixel;
95   self->allocated = True;
96   
97   if (cleancache)
98     doCacheCleanup();
99 }
100
101 PyObject *OtkColor_FromRGB(int r, int g, int b, int screen)
102 {
103   OtkColor *self = PyObject_New(OtkColor, &OtkColor_Type);
104   PyObject *cached;
105
106   assert(screen >= 0); assert(r >= 0); assert(g >= 0); assert(b >= 0);
107   assert(r <= 0xff); assert(g <= 0xff); assert(b <= 0xff);
108
109   if (!colorcache) colorcache = PyDict_New();
110
111   self->allocated = False;
112   self->red = r;
113   self->green = g;
114   self->blue = b;
115   self->pixel = 0;
116   self->screen = screen;
117
118   // does this color already exist in the cache?
119   cached = PyDict_GetItem(colorcache, (PyObject*)self);
120   if (cached) {
121     Py_INCREF(cached);
122     return cached;
123   }
124
125   // add it to the cache
126   PyDict_SetItem(colorcache, (PyObject*)self, (PyObject*)self);
127   return (PyObject*)self;
128 }
129
130 PyObject *OtkColor_FromName(const char *name, int screen)
131 {
132   OtkColor *self = PyObject_New(OtkColor, &OtkColor_Type);
133   PyObject *cached;
134
135   assert(screen >= 0); assert(name);
136
137   if (!colorcache) colorcache = PyDict_New();
138   
139   self->allocated = False;
140   self->red = -1;
141   self->green = -1;
142   self->blue = -1;
143   self->pixel = 0;
144   self->screen = screen;
145
146   parseColorName(self, name);
147
148   // does this color already exist in the cache?
149   cached = PyDict_GetItem(colorcache, (PyObject*)self);
150   if (cached) {
151     Py_INCREF(cached);
152     return cached;
153   }
154
155   // add it to the cache
156   PyDict_SetItem(colorcache, (PyObject*)self, (PyObject*)self);
157   return (PyObject*)self;
158 }
159
160 unsigned long OtkColor_Pixel(OtkColor *self)
161 {
162   if (!self->allocated)
163     allocate(self);
164   return self->pixel;
165 }
166
167 void OtkColor_CleanupColorCache()
168 {
169   cleancache = True;
170 }
171
172
173
174 static void otkcolor_dealloc(OtkColor* self)
175 {
176   // when this is called, the color has already been cleaned out of the cache
177   PyObject_Del((PyObject*)self);
178 }
179
180 static int otkcolor_compare(OtkColor *c1, OtkColor *c2)
181 {
182   long result;
183   unsigned long p1, p2;
184
185   p1 = c1->red << 16 | c1->green << 8 | c1->blue;
186   p2 = c2->red << 16 | c2->green << 8 | c2->blue;
187
188   if (p1 < p2)
189     result = -1;
190   else if (p1 > p2)
191     result = 1;
192   else
193     result = 0;
194   return result;
195 }
196
197 static PyObject *otkcolor_repr(OtkColor *self)
198 {
199   return PyString_FromFormat("rgb:%x/%x/%x", self->red, self->green,
200                              self->blue);
201 }
202
203 static long otkcolor_hash(OtkColor *self)
204 {
205   return self->screen << 24 | self->red << 16 | self->green << 8 | self->blue;
206 }
207
208 PyTypeObject OtkColor_Type = {
209   PyObject_HEAD_INIT(NULL)
210   0,
211   "OtkColor",
212   sizeof(OtkColor),
213   0,
214   (destructor)otkcolor_dealloc, /*tp_dealloc*/
215   0,                            /*tp_print*/
216   0,                            /*tp_getattr*/
217   0,                            /*tp_setattr*/
218   (cmpfunc)otkcolor_compare,    /*tp_compare*/
219   (reprfunc)otkcolor_repr,      /*tp_repr*/
220   0,                            /*tp_as_number*/
221   0,                            /*tp_as_sequence*/
222   0,                            /*tp_as_mapping*/
223   (hashfunc)otkcolor_hash,      /*tp_hash */
224 };