]> icculus.org git repositories - dana/openbox.git/blob - otk/gccache.cc
move Rect and PointerAssassin into the toolkit
[dana/openbox.git] / otk / gccache.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 "gccache.hh"
12 #include "color.hh"
13 #include "assassin.hh"
14 #include "screeninfo.hh"
15
16 namespace otk {
17
18 BGCCacheContext::~BGCCacheContext(void) {
19   if (gc)
20     XFreeGC(OBDisplay::display, gc);
21 }
22
23
24 void BGCCacheContext::set(const BColor &_color,
25                           const XFontStruct * const _font,
26                           const int _function, const int _subwindow,
27                           int _linewidth) {
28   XGCValues gcv;
29   pixel = gcv.foreground = _color.pixel();
30   function = gcv.function = _function;
31   subwindow = gcv.subwindow_mode = _subwindow;
32   linewidth = gcv.line_width = _linewidth;
33   gcv.cap_style = CapProjecting;
34
35   unsigned long mask = GCForeground | GCFunction | GCSubwindowMode |
36     GCLineWidth | GCCapStyle;
37
38   if (_font) {
39     fontid = gcv.font = _font->fid;
40     mask |= GCFont;
41   } else {
42     fontid = 0;
43   }
44
45   XChangeGC(OBDisplay::display, gc, mask, &gcv);
46 }
47
48
49 void BGCCacheContext::set(const XFontStruct * const _font) {
50   if (! _font) {
51     fontid = 0;
52     return;
53   }
54
55   XGCValues gcv;
56   fontid = gcv.font = _font->fid;
57   XChangeGC(OBDisplay::display, gc, GCFont, &gcv);
58 }
59
60
61 BGCCache::BGCCache(unsigned int screen_count)
62   : context_count(128u), cache_size(16u), cache_buckets(8u * screen_count),
63     cache_total_size(cache_size * cache_buckets) {
64
65   contexts = new BGCCacheContext*[context_count];
66   unsigned int i;
67   for (i = 0; i < context_count; i++) {
68     contexts[i] = new BGCCacheContext();
69   }
70
71   cache = new BGCCacheItem*[cache_total_size];
72   for (i = 0; i < cache_total_size; ++i) {
73     cache[i] = new BGCCacheItem;
74   }
75 }
76
77
78 BGCCache::~BGCCache(void) {
79   std::for_each(contexts, contexts + context_count, PointerAssassin());
80   std::for_each(cache, cache + cache_total_size, PointerAssassin());
81   delete [] cache;
82   delete [] contexts;
83 }
84
85
86 BGCCacheContext *BGCCache::nextContext(unsigned int scr) {
87   Window hd = OBDisplay::screenInfo(scr)->getRootWindow();
88
89   BGCCacheContext *c;
90
91   for (unsigned int i = 0; i < context_count; ++i) {
92     c = contexts[i];
93
94     if (! c->gc) {
95       c->gc = XCreateGC(OBDisplay::display, hd, 0, 0);
96       c->used = false;
97       c->screen = scr;
98     }
99     if (! c->used && c->screen == scr)
100       return c;
101   }
102
103   fprintf(stderr, "BGCCache: context fault!\n");
104   abort();
105   return (BGCCacheContext*) 0; // not reached
106 }
107
108
109 void BGCCache::release(BGCCacheContext *ctx) {
110   ctx->used = false;
111 }
112
113
114 BGCCacheItem *BGCCache::find(const BColor &_color,
115                              const XFontStruct * const _font,
116                              int _function, int _subwindow, int _linewidth) {
117   const unsigned long pixel = _color.pixel();
118   const unsigned int screen = _color.screen();
119   const int key = _color.red() ^ _color.green() ^ _color.blue();
120   int k = (key % cache_size) * cache_buckets;
121   unsigned int i = 0; // loop variable
122   BGCCacheItem *c = cache[ k ], *prev = 0;
123
124   /*
125     this will either loop cache_buckets times then return/abort or
126     it will stop matching
127   */
128   while (c->ctx &&
129          (c->ctx->pixel != pixel || c->ctx->function != _function ||
130           c->ctx->subwindow != _subwindow || c->ctx->screen != screen ||
131           c->ctx->linewidth != _linewidth)) {
132     if (i < (cache_buckets - 1)) {
133       prev = c;
134       c = cache[ ++k ];
135       ++i;
136       continue;
137     }
138     if (c->count == 0 && c->ctx->screen == screen) {
139       // use this cache item
140       c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
141       c->ctx->used = true;
142       c->count = 1;
143       c->hits = 1;
144       return c;
145     }
146     // cache fault!
147     fprintf(stderr, "BGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen);
148     abort();
149   }
150
151   if (c->ctx) {
152     // reuse existing context
153     if (_font && _font->fid && _font->fid != c->ctx->fontid)
154       c->ctx->set(_font);
155     c->count++;
156     c->hits++;
157     if (prev && c->hits > prev->hits) {
158       cache[ k     ] = prev;
159       cache[ k - 1 ] = c;
160     }
161   } else {
162     c->ctx = nextContext(screen);
163     c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
164     c->ctx->used = true;
165     c->count = 1;
166     c->hits = 1;
167   }
168
169   return c;
170 }
171
172
173 void BGCCache::release(BGCCacheItem *_item) {
174   _item->count--;
175 }
176
177
178 void BGCCache::purge(void) {
179   for (unsigned int i = 0; i < cache_total_size; ++i) {
180     BGCCacheItem *d = cache[ i ];
181
182     if (d->ctx && d->count == 0) {
183       release(d->ctx);
184       d->ctx = 0;
185     }
186   }
187 }
188
189 }