make rect a proper pyobject. use "typesafety"
[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 = NULL;
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   return self;
86 }
87
88
89 void OtkGCCache_Initialize()
90 {
91   unsigned int i;
92
93   gccache = malloc(sizeof(OtkGCCache));
94
95   gccache->context_count = 128;
96   gccache->cache_size = 16;
97   gccache->cache_buckets = 8 * ScreenCount(OBDisplay->display);
98   gccache->cache_total_size = gccache->cache_size * gccache->cache_buckets;
99
100   gccache->contexts = malloc(sizeof(OtkGCCacheContext*) *
101                              gccache->context_count);
102   for (i = 0; i < gccache->context_count; ++i)
103     gccache->contexts[i] = OtkGCCacheContext_New();
104
105   gccache->cache = malloc(sizeof(OtkGCCacheItem*) * gccache->cache_total_size);
106   for (i = 0; i < gccache->cache_total_size; ++i)
107     gccache->cache[i] = OtkGCCacheItem_New();
108 }
109
110
111 /*void OtkGCCache_Destroy()
112 {
113   unsigned int i;
114
115   for (i = 0; i < gccache->context_count; ++i)
116     OtkGCCacheContext_Destroy(gccache->contexts[i]);
117
118   for (i = 0; i < gccache->cache_total_size; ++i)
119     free(gccache->cache[i]);
120
121   free(gccache->contexts);
122   free(gccache->cache);
123   free(gccache);
124   gccache = NULL;
125 }*/
126
127 static OtkGCCacheContext *nextContext(int screen)
128 {
129   Window hd = OtkDisplay_ScreenInfo(OBDisplay, screen)->root_window;
130   OtkGCCacheContext *c;
131   unsigned int i;
132
133   for (i = 0; i < gccache->context_count; ++i) {
134     c = gccache->contexts[i];
135
136     if (! c->gc) {
137       c->gc = XCreateGC(OBDisplay->display, hd, 0, 0);
138       c->used = False;
139       c->screen = screen;
140     }
141     if (! c->used && c->screen == screen)
142       return c;
143   }
144
145   fprintf(stderr, "OtkGCCache: context fault!\n");
146   abort();
147   return NULL; // shut gcc up
148 }
149
150
151 static void OtkGCCache_InternalRelease(OtkGCCacheContext *ctx)
152 {
153   ctx->used = False;
154 }
155
156 OtkGCCacheItem *OtkGCCache_Find(OtkColor *color, XFontStruct *font,
157                                 int function, int subwindow, int linewidth)
158 {
159   const unsigned long pixel = OtkColor_Pixel(color);
160   const int screen = color->screen;
161   const int key = color->red ^ color->green ^ color->blue;
162   int k = (key % gccache->cache_size) * gccache->cache_buckets;
163   unsigned int i = 0; // loop variable
164   OtkGCCacheItem *c = gccache->cache[k], *prev = 0;
165
166   /*
167     this will either loop cache_buckets times then return/abort or
168     it will stop matching
169   */
170   while (c->ctx &&
171          (c->ctx->pixel != pixel || c->ctx->function != function ||
172           c->ctx->subwindow != subwindow || c->ctx->screen != screen ||
173           c->ctx->linewidth != linewidth)) {
174     if (i < (gccache->cache_buckets - 1)) {
175       prev = c;
176       c = gccache->cache[++k];
177       ++i;
178       continue;
179     }
180     if (c->count == 0 && c->ctx->screen == screen) {
181       // use this cache item
182       OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow,
183                             linewidth);
184       c->ctx->used = True;
185       c->count = 1;
186       c->hits = 1;
187       return c;
188     }
189     // cache fault!
190     fprintf(stderr, "OtkGCCache: cache fault, count: %d, screen: %d, item screen: %d\n", c->count, screen, c->ctx->screen);
191     abort();
192   }
193
194   if (c->ctx) {
195     // reuse existing context
196     if (font && font->fid && font->fid != c->ctx->fontid)
197       OtkGCCacheContext_SetFont(c->ctx, font);
198     c->count++;
199     c->hits++;
200     if (prev && c->hits > prev->hits) {
201       gccache->cache[k] = prev;
202       gccache->cache[k-1] = c;
203     }
204   } else {
205     c->ctx = nextContext(screen);
206     OtkGCCacheContext_Set(c->ctx, color, font, function, subwindow, linewidth);
207     c->ctx->used = True;
208     c->count = 1;
209     c->hits = 1;
210   }
211
212   return c;
213 }
214
215
216 void OtkGCCache_Release(OtkGCCacheItem *item)
217 {
218   item->count--;
219 }
220
221
222 void OtkGCCache_Purge()
223 {
224   unsigned int i;
225   
226   for (i = 0; i < gccache->cache_total_size; ++i) {
227     OtkGCCacheItem *d = gccache->cache[i];
228
229     if (d->ctx && d->count == 0) {
230       OtkGCCache_InternalRelease(d->ctx);
231       d->ctx = 0;
232     }
233   }
234 }