1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Pen.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@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.
34 # include <X11/Xft/Xft.h>
40 // #define PENCACHE_DEBUG
43 The Pen cache is comprised of multiple arrays of cache buckets. The
44 bucket arrays are small to allow for fast lookups. A simple hash is
45 used to determine which array is used.
47 The 'cache_size' variable above controls the number of bucket
48 arrays; the 'cache_buckets' variable controls the size of each
51 Each bucket stores a hit count, and the buckets are reordered at
52 runtime from highest to lowest hit count. When no suitable item can
53 be found in the cache, the first bucket that is not in use will be
56 A cache fault happens if all buckets are in use, at the cache will
57 print a diagnostic message to 'stderr' and call abort(). Normally,
58 a cache fault indicates that the cache was not big enough, and the
59 variables above should be increased accordingly.
61 --------------------------------------------------------------------
63 > +----+----+----+----+----+----+----+----+
64 | | 14 | 10 | 4 | 3 | 2 | 2 | 2 | 0 |
65 | +----+----+----+----+----+----+----+----+
67 | +----+----+----+----+----+----+----+----+
68 | | 9 | 8 | 7 | 6 | 4 | 3 | 0 | 0 |
69 | +----+----+----+----+----+----+----+----+
71 | ... more bucket arrays ...
74 ^------------- cache_buckets -----------^
76 static const unsigned int cache_size
= 32u;
77 static const unsigned int cache_buckets
= 8u;
78 static const unsigned int context_count
= cache_size
* cache_buckets
;
83 class PenCacheContext
: public NoCopy
{
85 inline PenCacheContext(void)
86 : _screen(~0u), _gc(0), _function(0), _linewidth(1), _subwindow(0),
89 ~PenCacheContext(void);
91 void set(const Color
&color
, const int function
, const int linewidth
,
103 class PenCacheItem
: public NoCopy
{
105 inline PenCacheItem(void)
106 : _ctx(0), _count(0u), _hits(0u)
108 inline const GC
&gc(void) const
109 { return _ctx
->_gc
; }
111 PenCacheContext
*_ctx
;
117 class XftCacheContext
: public NoCopy
{
119 inline XftCacheContext(void)
120 : _screen(~0u), _drawable(None
), _xftdraw(0), _used(false)
122 ~XftCacheContext(void);
124 void set(Drawable drawable
);
126 unsigned int _screen
;
132 class XftCacheItem
: public NoCopy
{
134 inline XftCacheItem(void)
135 : _ctx(0), _count(0u), _hits(0u)
137 inline Drawable
drawable(void) const
138 { return _ctx
->_drawable
; }
139 inline XftDraw
*xftdraw(void) const
140 { return _ctx
->_xftdraw
; }
142 XftCacheContext
*_ctx
;
150 PenCache(const Display
&display
);
153 // cleans up the cache
156 PenCacheContext
*contexts
;
157 PenCacheItem
**cache
;
159 PenCacheContext
*nextContext(unsigned int screen
);
160 void release(PenCacheContext
*ctx
);
162 PenCacheItem
*find(unsigned int screen
,
167 void release(PenCacheItem
*item
);
170 XftCacheContext
*xftcontexts
;
171 XftCacheItem
**xftcache
;
173 XftCacheContext
*nextXftContext(unsigned int screen
);
174 void release(XftCacheContext
*ctx
);
176 XftCacheItem
*findXft(unsigned int screen
, Drawable drawable
);
177 void release(XftCacheItem
*xftitem
);
180 const Display
&_display
;
181 const unsigned int cache_total_size
;
185 static PenCache
*pencache
= 0;
188 void createPenCache(const Display
&display
)
190 assert(pencache
== 0);
191 pencache
= new PenCache(display
);
195 void destroyPenCache(void) {
203 bt::PenCacheContext::~PenCacheContext(void) {
205 XFreeGC(pencache
->_display
.XDisplay(), _gc
);
210 void bt::PenCacheContext::set(const Color
&color
,
213 const int subwindow
) {
216 gcv
.foreground
= _color
.pixel(_screen
);
217 _function
= gcv
.function
= function
;
218 _linewidth
= gcv
.line_width
= linewidth
;
219 _subwindow
= gcv
.subwindow_mode
= subwindow
;
222 (GCForeground
| GCFunction
| GCLineWidth
| GCSubwindowMode
);
224 XChangeGC(pencache
->_display
.XDisplay(), _gc
, mask
, &gcv
);
229 bt::XftCacheContext::~XftCacheContext(void) {
231 XftDrawDestroy(_xftdraw
);
236 void bt::XftCacheContext::set(Drawable drawable
) {
237 XftDrawChange(_xftdraw
, drawable
);
238 _drawable
= drawable
;
243 bt::PenCache::PenCache(const Display
&display
)
245 cache_total_size(context_count
* _display
.screenCount())
249 contexts
= new PenCacheContext
[cache_total_size
];
250 cache
= new PenCacheItem
*[cache_total_size
];
251 for (i
= 0; i
< cache_total_size
; ++i
) {
252 cache
[i
] = new PenCacheItem
;
256 xftcontexts
= new XftCacheContext
[cache_total_size
];
257 xftcache
= new XftCacheItem
*[cache_total_size
];
258 for (i
= 0; i
< cache_total_size
; ++i
) {
259 xftcache
[i
] = new XftCacheItem
;
265 bt::PenCache::~PenCache(void) {
266 std::for_each(cache
, cache
+ cache_total_size
, PointerAssassin());
271 std::for_each(xftcache
, xftcache
+ cache_total_size
, PointerAssassin());
273 delete [] xftcontexts
;
278 void bt::PenCache::purge(void) {
279 for (unsigned int i
= 0; i
< cache_total_size
; ++i
) {
280 PenCacheItem
*d
= cache
[ i
];
282 if (d
->_ctx
&& d
->_count
== 0) {
283 #ifdef PENCACHE_DEBUG
284 fprintf(stderr
, "bt::PenCache: GC : context %03u release\n", i
);
293 bt::PenCacheContext
*bt::PenCache::nextContext(unsigned int screen
) {
294 Window hd
= pencache
->_display
.screenInfo(screen
).rootWindow();
298 for (i
= 0; i
< cache_total_size
; ++i
) {
302 #ifdef PENCACHE_DEBUG
303 fprintf(stderr
, "bt::PenCache: GC : context %03u create\n", i
);
305 c
->_gc
= XCreateGC(pencache
->_display
.XDisplay(), hd
, 0, 0);
309 if (!c
->_used
&& c
->_screen
== screen
)
313 fprintf(stderr
, "bt::PenCache: context fault at %u of %u\n",
314 i
, cache_total_size
);
316 return 0; // not reached
320 void bt::PenCache::release(PenCacheContext
*ctx
) {
322 ctx
->_color
.deallocate(); // allows unused colors to be freed
326 bt::PenCacheItem
*bt::PenCache::find(unsigned int screen
,
331 int k
= color
.red() ^ color
.green() ^ color
.blue();
332 k
= (screen
* context_count
) + ((k
% cache_size
) * cache_buckets
);
333 unsigned int i
= 0; // loop variable
334 PenCacheItem
*c
= cache
[ k
], *prev
= 0;
337 this will either loop cache_buckets times then return/abort or
338 it will stop matching
341 && (c
->_ctx
->_color
!= color
342 || c
->_ctx
->_function
!= function
343 || c
->_ctx
->_linewidth
!= linewidth
344 || c
->_ctx
->_subwindow
!= subwindow
)) {
345 if (i
< (cache_buckets
- 1)) {
351 if (c
->_count
== 0 && c
->_ctx
->_screen
== screen
) {
352 #ifdef PENCACHE_DEBUG
353 fprintf(stderr
, "bt::PenCache: GC : key %03d hijack\n", k
);
355 // use this cache item
356 c
->_ctx
->set(color
, function
, linewidth
, subwindow
);
357 c
->_ctx
->_used
= true;
364 "bt::PenCache: cache fault at %d\n"
365 " count: %u, screen: %u, item screen: %u\n",
366 k
, c
->_count
, screen
, c
->_ctx
->_screen
);
371 #ifdef PENCACHE_DEBUG
372 fprintf(stderr
, "bt::PenCache: GC : key %03d cache hit\n", k
);
374 // reuse existing context
377 if (prev
&& c
->_hits
> prev
->_hits
) {
382 c
->_ctx
= nextContext(screen
);
383 #ifdef PENCACHE_DEBUG
384 fprintf(stderr
, "bt::PenCache: GC : key %03d new context\n", k
);
386 c
->_ctx
->set(color
, function
, linewidth
, subwindow
);
387 c
->_ctx
->_used
= true;
396 void bt::PenCache::release(PenCacheItem
*item
)
401 bt::XftCacheContext
*bt::PenCache::nextXftContext(unsigned int screen
) {
402 const ScreenInfo
&screeninfo
= _display
.screenInfo(screen
);
406 for (i
= 0; i
< cache_total_size
; ++i
) {
410 #ifdef PENCACHE_DEBUG
411 fprintf(stderr
, "bt::PenCache: Xft: context %03u create\n", i
);
414 XftDrawCreate(_display
.XDisplay(), screeninfo
.rootWindow(),
415 screeninfo
.visual(), screeninfo
.colormap());
419 if (!c
->_used
&& c
->_screen
== screen
)
423 fprintf(stderr
, "bt::PenCache: Xft context fault at %u of %u\n",
424 i
, cache_total_size
);
426 return 0; // not reached
430 void bt::PenCache::release(XftCacheContext
*context
)
431 { context
->_used
= false; }
434 bt::XftCacheItem
*bt::PenCache::findXft(unsigned int screen
,
436 int k
= (screen
* context_count
) + ((drawable
% cache_size
) * cache_buckets
);
437 unsigned int i
= 0; // loop variable
438 XftCacheItem
*c
= xftcache
[ k
], *prev
= 0;
441 this will either loop cache_buckets times then return/abort or
442 it will stop matching
445 (c
->_ctx
->_drawable
!= drawable
|| c
->_ctx
->_screen
!= screen
)) {
446 if (i
< (cache_buckets
- 1)) {
452 if (c
->_count
== 0 && c
->_ctx
->_screen
== screen
) {
453 #ifdef PENCACHE_DEBUG
454 fprintf(stderr
, "bt::PenCache: Xft: key %03d hijack\n", k
);
456 // use this cache item
457 if (drawable
!= c
->_ctx
->_drawable
)
458 c
->_ctx
->set(drawable
);
459 c
->_ctx
->_used
= true;
464 // cache fault... try
466 "bt::PenCache: Xft cache fault at %d\n"
467 " count: %u, screen: %u, item screen: %u\n",
468 k
, c
->_count
, screen
, c
->_ctx
->_screen
);
473 #ifdef PENCACHE_DEBUG
474 fprintf(stderr
, "bt::PenCache: Xft: key %03d cache hit\n", k
);
476 // reuse existing context
477 if (drawable
!= c
->_ctx
->_drawable
)
478 c
->_ctx
->set(drawable
);
481 if (prev
&& c
->_hits
> prev
->_hits
) {
482 xftcache
[ k
] = prev
;
483 xftcache
[ k
- 1 ] = c
;
486 c
->_ctx
= nextXftContext(screen
);
487 #ifdef PENCACHE_DEBUG
488 fprintf(stderr
, "bt::PenCache: Xft: key %03d new context\n", k
);
490 c
->_ctx
->set(drawable
);
491 c
->_ctx
->_used
= true;
500 void bt::PenCache::release(XftCacheItem
*xftitem
)
501 { --xftitem
->_count
; }
505 bt::Pen::Pen(unsigned int screen_
, const Color
&color_
)
506 : _screen(screen_
), _color(color_
), _function(GXcopy
), _linewidth(0),
507 _subwindow(ClipByChildren
), _item(0), _xftitem(0)
511 bt::Pen::~Pen(void) {
513 pencache
->release(_item
);
518 pencache
->release(_xftitem
);
524 void bt::Pen::setGCFunction(int function
) {
526 pencache
->release(_item
);
529 _function
= function
;
533 void bt::Pen::setLineWidth(int linewidth
) {
535 pencache
->release(_item
);
538 _linewidth
= linewidth
;
542 void bt::Pen::setSubWindowMode(int subwindow
) {
544 pencache
->release(_item
);
547 _subwindow
= subwindow
;
551 ::Display
*bt::Pen::XDisplay(void) const
552 { return pencache
->_display
.XDisplay(); }
555 const bt::Display
&bt::Pen::display(void) const
556 { return pencache
->_display
; }
559 const GC
&bt::Pen::gc(void) const {
561 _item
= pencache
->find(_screen
, _color
,
562 _function
, _linewidth
, _subwindow
);
569 XftDraw
*bt::Pen::xftDraw(Drawable drawable
) const {
571 if (_xftitem
&& _xftitem
->drawable() != drawable
) {
572 pencache
->release(_xftitem
);
576 _xftitem
= pencache
->findXft(_screen
, drawable
);
578 assert(_xftitem
!= 0);
579 return _xftitem
->xftdraw();
585 void bt::Pen::clearCache(void)
586 { pencache
->purge(); }