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