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>
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.
35 // #define COLORCACHE_DEBUG
42 ColorCache(const Display
&display
);
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
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
);
70 const Display
&_display
;
73 const unsigned int screen
;
77 : screen(~0u), r(-1), g(-1), b(-1)
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
)
82 inline RGB(const RGB
&x
)
83 : screen(x
.screen
), r(x
.r
), g(x
.g
), b(x
.b
)
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;
98 const unsigned long pixel
;
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
;
114 static ColorCache
*colorcache
= 0;
117 void createColorCache(const Display
&display
) {
118 assert(colorcache
== 0);
119 colorcache
= new ColorCache(display
);
123 void destroyColorCache(void) {
131 bt::ColorCache::ColorCache(const Display
&display
)
136 bt::ColorCache::~ColorCache(void)
140 unsigned long bt::ColorCache::find(unsigned int screen
, int r
, int g
, int b
) {
141 if (r
< 0 && r
> 255)
143 if (g
< 0 && g
> 255)
145 if (b
< 0 && b
> 255)
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
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
;
164 xcol
.red
= r
| r
<< 8;
165 xcol
.green
= g
| g
<< 8;
166 xcol
.blue
= b
| b
<< 8;
168 xcol
.flags
= DoRed
| DoGreen
| DoBlue
;
170 Colormap colormap
= _display
.screenInfo(screen
).colormap();
171 if (!XAllocColor(_display
.XDisplay(), colormap
, &xcol
)) {
173 "bt::Color::pixel: cannot allocate color 'rgb:%02x/%02x/%02x'\n",
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
)));
189 void bt::ColorCache::release(unsigned int screen
, int r
, int g
, int b
) {
190 if (r
< 0 && r
> 255)
192 if (g
< 0 && g
> 255)
194 if (b
< 0 && b
> 255)
197 RGB
rgb(screen
, r
, g
, b
);
198 Cache::iterator it
= cache
.find(rgb
);
200 assert(it
!= cache
.end() && it
->second
.count
> 0);
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",
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
) {
226 while (it
!= cache
.end()) {
227 if (it
->second
.count
!= 0 && !force
) {
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
++;
244 XFreeColors(_display
.XDisplay(),
245 _display
.screenInfo(screen
).colormap(),
252 #ifdef COLORCACHE_DEBUG
253 fprintf(stderr
, "bt::ColorCache: cleared, %u entries remain\n",
255 #endif // COLORCACHE_DEBUG
259 void bt::Color::clearCache(void)
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");
273 // get rgb values from colorname
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",
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
298 colorcache
->release(_screen
, _red
, _green
, _blue
);
301 _pixel
= colorcache
->find(_screen
, _red
, _green
, _blue
);
306 void bt::Color::deallocate(void) {
308 return; // not allocated
310 assert(colorcache
!= 0);
311 colorcache
->release(_screen
, _red
, _green
, _blue
);