]> icculus.org git repositories - mikachu/openbox.git/blob - src/GCCache.cc
no more menus, at last. woop
[mikachu/openbox.git] / src / GCCache.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // GCCache.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Bradley T Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #include <stdio.h>
30 }
31
32 #include "GCCache.hh"
33 #include "BaseDisplay.hh"
34 #include "Color.hh"
35 #include "Util.hh"
36
37
38 BGCCacheContext::~BGCCacheContext(void) {
39   if (gc)
40     XFreeGC(display->getXDisplay(), gc);
41 }
42
43
44 void BGCCacheContext::set(const BColor &_color,
45                           const XFontStruct * const _font,
46                           const int _function, const int _subwindow,
47                           int _linewidth) {
48   XGCValues gcv;
49   pixel = gcv.foreground = _color.pixel();
50   function = gcv.function = _function;
51   subwindow = gcv.subwindow_mode = _subwindow;
52   linewidth = gcv.line_width = _linewidth;
53   gcv.cap_style = CapProjecting;
54
55   unsigned long mask = GCForeground | GCFunction | GCSubwindowMode |
56     GCLineWidth | GCCapStyle;
57
58   if (_font) {
59     fontid = gcv.font = _font->fid;
60     mask |= GCFont;
61   } else {
62     fontid = 0;
63   }
64
65   XChangeGC(display->getXDisplay(), gc, mask, &gcv);
66 }
67
68
69 void BGCCacheContext::set(const XFontStruct * const _font) {
70   if (! _font) {
71     fontid = 0;
72     return;
73   }
74
75   XGCValues gcv;
76   fontid = gcv.font = _font->fid;
77   XChangeGC(display->getXDisplay(), gc, GCFont, &gcv);
78 }
79
80
81 BGCCache::BGCCache(const BaseDisplay * const _display,
82                    unsigned int screen_count)
83   : display(_display),  context_count(128u),
84     cache_size(16u), cache_buckets(8u * screen_count),
85     cache_total_size(cache_size * cache_buckets) {
86
87   contexts = new BGCCacheContext*[context_count];
88   unsigned int i;
89   for (i = 0; i < context_count; i++) {
90     contexts[i] = new BGCCacheContext(display);
91   }
92
93   cache = new BGCCacheItem*[cache_total_size];
94   for (i = 0; i < cache_total_size; ++i) {
95     cache[i] = new BGCCacheItem;
96   }
97 }
98
99
100 BGCCache::~BGCCache(void) {
101   std::for_each(contexts, contexts + context_count, PointerAssassin());
102   std::for_each(cache, cache + cache_total_size, PointerAssassin());
103   delete [] cache;
104   delete [] contexts;
105 }
106
107
108 BGCCacheContext *BGCCache::nextContext(unsigned int scr) {
109   Window hd = display->getScreenInfo(scr)->getRootWindow();
110
111   BGCCacheContext *c;
112
113   for (unsigned int i = 0; i < context_count; ++i) {
114     c = contexts[i];
115
116     if (! c->gc) {
117       c->gc = XCreateGC(display->getXDisplay(), hd, 0, 0);
118       c->used = false;
119       c->screen = scr;
120     }
121     if (! c->used && c->screen == scr)
122       return c;
123   }
124
125   fprintf(stderr, "BGCCache: context fault!\n");
126   abort();
127   return (BGCCacheContext*) 0; // not reached
128 }
129
130
131 void BGCCache::release(BGCCacheContext *ctx) {
132   ctx->used = false;
133 }
134
135
136 BGCCacheItem *BGCCache::find(const BColor &_color,
137                              const XFontStruct * const _font,
138                              int _function, int _subwindow, int _linewidth) {
139   const unsigned long pixel = _color.pixel();
140   const unsigned int screen = _color.screen();
141   const int key = _color.red() ^ _color.green() ^ _color.blue();
142   int k = (key % cache_size) * cache_buckets;
143   unsigned int i = 0; // loop variable
144   BGCCacheItem *c = cache[ k ], *prev = 0;
145
146   /*
147     this will either loop cache_buckets times then return/abort or
148     it will stop matching
149   */
150   while (c->ctx &&
151          (c->ctx->pixel != pixel || c->ctx->function != _function ||
152           c->ctx->subwindow != _subwindow || c->ctx->screen != screen ||
153           c->ctx->linewidth != _linewidth)) {
154     if (i < (cache_buckets - 1)) {
155       prev = c;
156       c = cache[ ++k ];
157       ++i;
158       continue;
159     }
160     if (c->count == 0 && c->ctx->screen == screen) {
161       // use this cache item
162       c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
163       c->ctx->used = true;
164       c->count = 1;
165       c->hits = 1;
166       return c;
167     }
168     // cache fault!
169     fprintf(stderr, "BGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen);
170     abort();
171   }
172
173   if (c->ctx) {
174     // reuse existing context
175     if (_font && _font->fid && _font->fid != c->ctx->fontid)
176       c->ctx->set(_font);
177     c->count++;
178     c->hits++;
179     if (prev && c->hits > prev->hits) {
180       cache[ k     ] = prev;
181       cache[ k - 1 ] = c;
182     }
183   } else {
184     c->ctx = nextContext(screen);
185     c->ctx->set(_color, _font, _function, _subwindow, _linewidth);
186     c->ctx->used = true;
187     c->count = 1;
188     c->hits = 1;
189   }
190
191   return c;
192 }
193
194
195 void BGCCache::release(BGCCacheItem *_item) {
196   _item->count--;
197 }
198
199
200 void BGCCache::purge(void) {
201   for (unsigned int i = 0; i < cache_total_size; ++i) {
202     BGCCacheItem *d = cache[ i ];
203
204     if (d->ctx && d->count == 0) {
205       release(d->ctx);
206       d->ctx = 0;
207     }
208   }
209 }