dont have any languages yet
[dana/openbox.git] / otk / color.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 #  include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #include <stdio.h>
9 }
10
11 #include <assert.h>
12
13 #include "color.hh"
14 #include "basedisplay.hh"
15
16
17 BColor::ColorCache BColor::colorcache;
18 bool BColor::cleancache = false;
19
20 BColor::BColor(const BaseDisplay * const _display, unsigned int _screen)
21   : allocated(false), r(-1), g(-1), b(-1), p(0), dpy(_display), scrn(_screen)
22 {}
23
24 BColor::BColor(int _r, int _g, int _b,
25                const BaseDisplay * const _display, unsigned int _screen)
26   : allocated(false), r(_r), g(_g), b(_b), p(0), dpy(_display), scrn(_screen)
27 {}
28
29
30 BColor::BColor(const std::string &_name,
31                const BaseDisplay * const _display, unsigned int _screen)
32   : allocated(false), r(-1), g(-1), b(-1), p(0), dpy(_display), scrn(_screen),
33     colorname(_name) {
34   parseColorName();
35 }
36
37
38 BColor::~BColor(void) {
39   deallocate();
40 }
41
42
43 void BColor::setDisplay(const BaseDisplay * const _display,
44                         unsigned int _screen) {
45   if (_display == display() && _screen == screen()) {
46     // nothing to do
47     return;
48   }
49
50   deallocate();
51
52   dpy = _display;
53   scrn = _screen;
54
55   if (! colorname.empty()) {
56     parseColorName();
57   }
58 }
59
60
61 unsigned long BColor::pixel(void) const {
62   if (! allocated) {
63     // mutable
64     BColor *that = (BColor *) this;
65     that->allocate();
66   }
67
68   return p;
69 }
70
71
72 void BColor::parseColorName(void) {
73   assert(dpy != 0);
74
75   if (colorname.empty()) {
76     fprintf(stderr, "BColor: empty colorname, cannot parse (using black)\n");
77     setRGB(0, 0, 0);
78   }
79
80   if (scrn == ~(0u))
81     scrn = DefaultScreen(display()->getXDisplay());
82   Colormap colormap = display()->getScreenInfo(scrn)->getColormap();
83
84   // get rgb values from colorname
85   XColor xcol;
86   xcol.red = 0;
87   xcol.green = 0;
88   xcol.blue = 0;
89   xcol.pixel = 0;
90
91   if (! XParseColor(display()->getXDisplay(), colormap,
92                     colorname.c_str(), &xcol)) {
93     fprintf(stderr, "BColor::allocate: color parse error: \"%s\"\n",
94             colorname.c_str());
95     setRGB(0, 0, 0);
96     return;
97   }
98
99   setRGB(xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
100 }
101
102
103 void BColor::allocate(void) {
104   assert(dpy != 0);
105
106   if (scrn == ~(0u)) scrn = DefaultScreen(display()->getXDisplay());
107   Colormap colormap = display()->getScreenInfo(scrn)->getColormap();
108
109   if (! isValid()) {
110     if (colorname.empty()) {
111       fprintf(stderr, "BColor: cannot allocate invalid color (using black)\n");
112       setRGB(0, 0, 0);
113     } else {
114       parseColorName();
115     }
116   }
117
118   // see if we have allocated this color before
119   RGB rgb(display(), scrn, r, g, b);
120   ColorCache::iterator it = colorcache.find(rgb);
121   if (it != colorcache.end()) {
122     // found
123     allocated = true;
124     p = (*it).second.p;
125     (*it).second.count++;
126     return;
127   }
128
129   // allocate color from rgb values
130   XColor xcol;
131   xcol.red =   r | r << 8;
132   xcol.green = g | g << 8;
133   xcol.blue =  b | b << 8;
134   xcol.pixel = 0;
135
136   if (! XAllocColor(display()->getXDisplay(), colormap, &xcol)) {
137     fprintf(stderr, "BColor::allocate: color alloc error: rgb:%x/%x/%x\n",
138             r, g, b);
139     xcol.pixel = 0;
140   }
141
142   p = xcol.pixel;
143   allocated = true;
144
145   colorcache.insert(ColorCacheItem(rgb, PixelRef(p)));
146
147   if (cleancache)
148     doCacheCleanup();
149 }
150
151
152 void BColor::deallocate(void) {
153   if (! allocated)
154     return;
155
156   assert(dpy != 0);
157
158   ColorCache::iterator it = colorcache.find(RGB(display(), scrn, r, g, b));
159   if (it != colorcache.end()) {
160     if ((*it).second.count >= 1)
161       (*it).second.count--;
162   }
163
164   if (cleancache)
165     doCacheCleanup();
166
167   allocated = false;
168 }
169
170
171 BColor &BColor::operator=(const BColor &c) {
172   deallocate();
173
174   setRGB(c.r, c.g, c.b);
175   colorname = c.colorname;
176   dpy = c.dpy;
177   scrn = c.scrn;
178   return *this;
179 }
180
181
182 void BColor::cleanupColorCache(void) {
183   cleancache = true;
184 }
185
186
187 void BColor::doCacheCleanup(void) {
188   // ### TODO - support multiple displays!
189   ColorCache::iterator it = colorcache.begin();
190   if (it == colorcache.end()) {
191     // nothing to do
192     return;
193   }
194
195   const BaseDisplay* const display = (*it).first.display;
196   unsigned long *pixels = new unsigned long[ colorcache.size() ];
197   unsigned int i, count;
198
199   for (i = 0; i < display->getNumberOfScreens(); i++) {
200     count = 0;
201     it = colorcache.begin();
202
203     while (it != colorcache.end()) {
204       if ((*it).second.count != 0 || (*it).first.screen != i) {
205         ++it;
206         continue;
207       }
208
209       pixels[ count++ ] = (*it).second.p;
210       ColorCache::iterator it2 = it;
211       ++it;
212       colorcache.erase(it2);
213     }
214
215     if (count > 0)
216       XFreeColors(display->getXDisplay(),
217                   display->getScreenInfo(i)->getColormap(),
218                   pixels, count, 0);
219   }
220
221   delete [] pixels;
222   cleancache = false;
223 }