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