it compiles.. does it work?
[mikachu/openbox.git] / otk_c / gccache.c
1 // -*- mode: C; indent-tabs-mode: nil; -*-
2
3 #include "../config.h"
4 #include "gccache.h"
5 #include "screeninfo.h"
6
7 #ifdef HAVE_STDLIB_H
8 # include <stdlib.h>
9 #endif
10
11 static OtkGCCache *gccache;
12
13 OtkGCCacheContext *OtkGCCacheContext_New()
14 {
15   OtkGCCacheContext *self = malloc(sizeof(OtkGCCacheContext));
16
17   self->gc = 0;
18   self->pixel = 0ul;
19   self->fontid = 0ul;
20   self->function = 0;
21   self->subwindow = 0;
22   self->used = False;
23   self->screen = ~0;
24   self->linewidth = 0;
25
26   return self;
27 }
28
29 void OtkGCCacheContext_Destroy(OtkGCCacheContext *self)
30 {
31   if (self->gc)
32     XFreeGC(OBDisplay->display, self->gc);
33   free(self);
34 }
35
36 void OtkGCCacheContext_Set(OtkGCCacheContext *self,
37                            OtkColor *color, XFontStruct *font,
38                            int function, int subwindow, int linewidth)
39 {
40   XGCValues gcv;
41   unsigned long mask;
42   
43   self->pixel = gcv.foreground = OtkColor_Pixel(color);
44   self->function = gcv.function = function;
45   self->subwindow = gcv.subwindow_mode = subwindow;
46   self->linewidth = gcv.line_width = linewidth;
47   gcv.cap_style = CapProjecting;
48
49   mask = GCForeground | GCFunction | GCSubwindowMode | GCLineWidth |
50          GCCapStyle;
51
52   if (font) {
53     self->fontid = gcv.font = font->fid;
54     mask |= GCFont;
55   } else {
56     self->fontid = 0;
57   }
58
59   XChangeGC(OBDisplay->display, self->gc, mask, &gcv);
60 }
61
62 void OtkGCCacheContext_SetFont(OtkGCCacheContext *self,
63                                XFontStruct *font)
64 {
65   if (!font) {
66     self->fontid = 0;
67     return;
68   }
69
70   XGCValues gcv;
71   self->fontid = gcv.font = font->fid;
72   XChangeGC(OBDisplay->display, self->gc, GCFont, &gcv);
73 }
74
75
76 OtkGCCacheItem *OtkGCCacheItem_New()
77 {
78   OtkGCCacheItem *self = malloc(sizeof(OtkGCCacheItem));
79
80   self->ctx = 0;
81   self->count = 0;
82   self->hits = 0;
83   self->fault = False;
84 }
85
86
87 void OtkGCCache_Initialize(int screen_count)
88 {
89   int i;
90
91   gccache = malloc(sizeof(OtkGCCache));
92
93   gccache->context_count = 128;
94   gccache->cache_size = 16;
95   gccache->cache_buckets = 8 * screen_count;
96   gccache->cache_total_size = gccache->cache_size * gccache->cache_buckets;
97
98   gccache->contexts = malloc(sizeof(OtkGCCacheContext*) *
99                              gccache->context_count);
100   for (i = 0; i < gccache->context_count; ++i)
101     gccache->contexts[i] = OtkGCCacheContext_New();
102
103   gccache->cache = malloc(sizeof(OtkGCCacheItem*) * gccache->cache_total_size);
104   for (i = 0; i < gccache->cache_total_size; ++i)
105     gccache->cache[i] = OtkGCCacheItem_New();
106 }
107
108
109 void OtkGCCache_Destroy()
110 {
111   int i;
112
113   for (i = 0; i < gccache->context_count; ++i)
114     OtkGCCacheContext_Destroy(gccache->contexts[i]);
115
116   for (i = 0; i < gccache->cache_total_size; ++i)
117     free(gccache->cache[i]);
118
119   free(gccache->contexts);
120   free(gccache->cache);
121   free(gccache);
122   gccache = NULL;
123 }
124
125 OtkGCCacheContext *OtkGCCache_NextContext(int screen)
126 {
127   Window hd = OtkDisplay_ScreenInfo(OBDisplay, screen)->root_window;
128   OtkGCCacheContext *c;
129   int i;
130
131   for (i = 0; i < gccache->context_count; ++i) {
132     c = gccache->contexts[i];
133
134     if (! c->gc) {
135       c->gc = XCreateGC(OBDisplay->display, hd, 0, 0);
136       c->used = False;
137       c->screen = screen;
138     }
139     if (! c->used && c->screen == screen)
140       return c;
141   }
142
143   fprintf(stderr, "OtkGCCache: context fault!\n");
144   abort();
145   return NULL; // shut gcc up
146 }
147
148
149 static void OtkGCCache_InternalRelease(OtkGCCacheContext *ctx)
150 {
151   ctx->used = False;
152 }
153
154 OtkGCCacheItem *OtkGCCache_Find(OtkColor *color, XFontStruct *font,
155                                 int function, int subwindow, int linewidth)
156 {
157   const unsigned long pixel = OtkColor_Pixel(color);
158   const unsigned int screen = color->screen;
159   const int key = color->red ^ color->green ^ color->blue;
160   int k = (key % gccache->cache_size) * gccache->cache_buckets;
161   int i = 0; // loop variable
162   OtkGCCacheItem *c = gccache->cache[k], *prev = 0;
163
164   /*
165     this will either loop cache_buckets times then return/abort or
166     it will stop matching
167   */
168   while (c->ctx &&
169          (c->ctx->pixel != pixel || c->ctx->function != function ||
170           c->ctx->subwindow != subwindow || c->ctx->screen != screen ||
171           c->ctx->linewidth != linewidth)) {
172     if (i < (gccache->cache_buckets - 1)) {
173       prev = c;
174       c = gccache->cache[++k];
175       ++i;
176       continue;
177     }
178     if (c->count == 0 && c->ctx->screen == screen) {
179       // use this cache item
180       OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow,
181                             linewidth);
182       c->ctx->used = True;
183       c->count = 1;
184       c->hits = 1;
185       return c;
186     }
187     // cache fault!
188     fprintf(stderr, "OtkGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen);
189     abort();
190   }
191
192   if (c->ctx) {
193     // reuse existing context
194     if (font && font->fid && font->fid != c->ctx->fontid)
195       OtkGCCacheContext_SetFont(c->ctx, font);
196     c->count++;
197     c->hits++;
198     if (prev && c->hits > prev->hits) {
199       gccache->cache[k] = prev;
200       gccache->cache[k-1] = c;
201     }
202   } else {
203     c->ctx = OtkGCCache_NextContext(screen);
204     OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow, linewidth);
205     c->ctx->used = True;
206     c->count = 1;
207     c->hits = 1;
208   }
209
210   return c;
211 }
212
213
214 void OtkGCCache_Release(OtkGCCacheItem *item)
215 {
216   item->count--;
217 }
218
219
220 void OtkGCCache_Purge()
221 {
222   int i;
223   
224   for (i = 0; i < gccache->cache_total_size; ++i) {
225     OtkGCCacheItem *d = gccache->cache[i];
226
227     if (d->ctx && d->count == 0) {
228       release(d->ctx);
229       d->ctx = 0;
230     }
231   }
232 }