don't deref null
[blackbox.git] / lib / Color.cc
bloba632ba46c8533a2b614db732888f0709e764f6f7
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Color.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 // Bradley T Hughes <bhughes at trolltech.com>
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
25 #include "Color.hh"
26 #include "Display.hh"
28 #include <map>
30 #include <X11/Xlib.h>
32 #include <assert.h>
33 #include <stdio.h>
35 // #define COLORCACHE_DEBUG
38 namespace bt {
40 class ColorCache {
41 public:
42 ColorCache(const Display &display);
43 ~ColorCache(void);
46 Finds a color matching the specified rgb on the given screen.
48 The color is allocated if needed; otherwise it is reference
49 counted and freed when no more references for the color exist.
51 unsigned long find(unsigned int screen, int r, int g, int b);
53 Releases the specified rgb on the given screen.
55 If the reference count for a particular color is zero, it will
56 be freed by calling clear().
58 void release(unsigned int screen, int r, int g, int b);
61 Clears the color cache. All colors with a zero reference count
62 are freed.
64 If force is true, then all colors are freed, regardless of the
65 reference count. This is done when destroying the cache.
67 void clear(bool force);
69 private:
70 const Display &_display;
72 struct RGB {
73 const unsigned int screen;
74 const int r, g, b;
76 inline RGB(void)
77 : screen(~0u), r(-1), g(-1), b(-1)
78 { }
79 inline RGB(const unsigned int s, const int x, const int y, const int z)
80 : screen(s), r(x), g(y), b(z)
81 { }
82 inline RGB(const RGB &x)
83 : screen(x.screen), r(x.r), g(x.g), b(x.b)
84 { }
86 inline bool operator==(const RGB &x) const
87 { return screen == x.screen && r == x.r && g == x.g && b == x.b; }
89 inline bool operator<(const RGB &x) const {
90 const unsigned long p1 =
91 (screen << 24 | r << 16 | g << 8 | b) & 0xffffffff;
92 const unsigned long p2 =
93 (x.screen << 24 | x.r << 16 | x.g << 8 | x.b) & 0xffffffff;
94 return p1 < p2;
97 struct PixelRef {
98 const unsigned long pixel;
99 unsigned int count;
100 inline PixelRef(void)
101 : pixel(0ul), count(0u)
103 inline PixelRef(const unsigned long x)
104 : pixel(x), count(1u)
108 typedef std::map<RGB,PixelRef> Cache;
109 typedef Cache::value_type CacheItem;
110 Cache cache;
114 static ColorCache *colorcache = 0;
117 void createColorCache(const Display &display) {
118 assert(colorcache == 0);
119 colorcache = new ColorCache(display);
123 void destroyColorCache(void) {
124 delete colorcache;
125 colorcache = 0;
128 } // namespace bt
131 bt::ColorCache::ColorCache(const Display &display)
132 : _display(display)
136 bt::ColorCache::~ColorCache(void)
137 { clear(true); }
140 unsigned long bt::ColorCache::find(unsigned int screen, int r, int g, int b) {
141 if (r < 0 && r > 255)
142 r = 0;
143 if (g < 0 && g > 255)
144 g = 0;
145 if (b < 0 && b > 255)
146 b = 0;
148 // see if we have allocated this color before
149 RGB rgb(screen, r, g, b);
150 Cache::iterator it = cache.find(rgb);
151 if (it != cache.end()) {
152 // found a cached color, use it
153 ++it->second.count;
155 #ifdef COLORCACHE_DEBUG
156 fprintf(stderr, "bt::ColorCache: use %02x/%02x/%02x, count %4u\n",
157 r, g, b, it->second.count);
158 #endif // COLORCACHE_DEBUG
160 return it->second.pixel;
163 XColor xcol;
164 xcol.red = r | r << 8;
165 xcol.green = g | g << 8;
166 xcol.blue = b | b << 8;
167 xcol.pixel = 0;
168 xcol.flags = DoRed | DoGreen | DoBlue;
170 Colormap colormap = _display.screenInfo(screen).colormap();
171 if (!XAllocColor(_display.XDisplay(), colormap, &xcol)) {
172 fprintf(stderr,
173 "bt::Color::pixel: cannot allocate color 'rgb:%02x/%02x/%02x'\n",
174 r, g, b);
175 xcol.pixel = BlackPixel(_display.XDisplay(), screen);
178 #ifdef COLORCACHE_DEBUG
179 fprintf(stderr, "bt::ColorCache: add %02x/%02x/%02x, pixel %08lx\n",
180 r, g, b, xcol.pixel);
181 #endif // COLORCACHE_DEBUG
183 cache.insert(CacheItem(rgb, PixelRef(xcol.pixel)));
185 return xcol.pixel;
189 void bt::ColorCache::release(unsigned int screen, int r, int g, int b) {
190 if (r < 0 && r > 255)
191 r = 0;
192 if (g < 0 && g > 255)
193 g = 0;
194 if (b < 0 && b > 255)
195 b = 0;
197 RGB rgb(screen, r, g, b);
198 Cache::iterator it = cache.find(rgb);
200 assert(it != cache.end() && it->second.count > 0);
201 --it->second.count;
203 #ifdef COLORCACHE_DEBUG
204 fprintf(stderr, "bt::ColorCache: rel %02x/%02x/%02x, count %4u\n",
205 r, g, b, it->second.count);
206 #endif // COLORCACHE_DEBUG
210 void bt::ColorCache::clear(bool force) {
211 Cache::iterator it = cache.begin();
212 if (it == cache.end())
213 return; // nothing to do
215 #ifdef COLORCACHE_DEBUG
216 fprintf(stderr, "bt::ColorCache: clearing cache, %u entries\n",
217 cache.size());
218 #endif // COLORCACHE_DEBUG
220 unsigned long *pixels = new unsigned long[ cache.size() ];
221 unsigned int screen, count;
223 for (screen = 0; screen < _display.screenCount(); ++screen) {
224 count = 0;
225 it = cache.begin();
226 while (it != cache.end()) {
227 if (it->second.count != 0 && !force) {
228 ++it;
229 continue;
232 #ifdef COLORCACHE_DEBUG
233 fprintf(stderr, "bt::ColorCache: fre %02x/%02x/%02x, pixel %08lx\n",
234 it->first.r, it->first.g, it->first.b, it->second.pixel);
235 #endif // COLORCACHE_DEBUG
237 pixels[count++] = it->second.pixel;
239 Cache::iterator r = it++;
240 cache.erase(r);
243 if (count > 0u) {
244 XFreeColors(_display.XDisplay(),
245 _display.screenInfo(screen).colormap(),
246 pixels, count, 0);
250 delete [] pixels;
252 #ifdef COLORCACHE_DEBUG
253 fprintf(stderr, "bt::ColorCache: cleared, %u entries remain\n",
254 cache.size());
255 #endif // COLORCACHE_DEBUG
259 void bt::Color::clearCache(void)
261 if (colorcache)
262 colorcache->clear(false);
266 bt::Color bt::Color::namedColor(const Display &display, unsigned int screen,
267 const std::string &colorname) {
268 if (colorname.empty()) {
269 fprintf(stderr, "bt::Color::namedColor: empty colorname\n");
270 return Color();
273 // get rgb values from colorname
274 XColor xcol;
275 xcol.red = 0;
276 xcol.green = 0;
277 xcol.blue = 0;
278 xcol.pixel = 0;
280 Colormap colormap = display.screenInfo(screen).colormap();
281 if (!XParseColor(display.XDisplay(), colormap, colorname.c_str(), &xcol)) {
282 fprintf(stderr, "bt::Color::namedColor: invalid color '%s'\n",
283 colorname.c_str());
284 return Color();
287 return Color(xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8);
291 unsigned long bt::Color::pixel(unsigned int screen) const {
292 if (_screen == screen)
293 return _pixel; // already allocated on this screen
295 assert(colorcache != 0);
296 // deallocate() isn't const, so we can't call it from here
297 if (_screen != ~0u)
298 colorcache->release(_screen, _red, _green, _blue);
300 _screen = screen;
301 _pixel = colorcache->find(_screen, _red, _green, _blue);
302 return _pixel;
306 void bt::Color::deallocate(void) {
307 if (_screen == ~0u)
308 return; // not allocated
310 assert(colorcache != 0);
311 colorcache->release(_screen, _red, _green, _blue);
313 _screen = ~0u;
314 _pixel = 0ul;