1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // PixmapCache.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 //         Bradley T Hughes <bhughes at trolltech.com>
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
24 
25 
26 #include "PixmapCache.hh"
27 #include "Display.hh"
28 #include "Image.hh"
29 #include "Texture.hh"
30 
31 #include <X11/Xlib.h>
32 #include <assert.h>
33 #include <stdio.h>
34 
35 #include <algorithm>
36 #include <list>
37 
38 // #define PIXMAPCACHE_DEBUG
39 
40 
41 namespace bt {
42 
43   class RealPixmapCache {
44   public:
45     RealPixmapCache(const Display &display);
46     ~RealPixmapCache(void);
47 
48     Pixmap find(unsigned int screen,
49                 const Texture &texture,
50                 unsigned int width, unsigned int height,
51                 Pixmap old_pixmap = 0ul);
52     void release(Pixmap pixmap);
53 
54     void clear(bool force);
55 
56     struct CacheItem {
57       const Texture texture;
58       const unsigned int screen;
59       const unsigned int width;
60       const unsigned int height;
61       Pixmap pixmap;
62       unsigned int count;
63 
CacheItembt::RealPixmapCache::CacheItem64       inline CacheItem(void)
65         : screen(~0u), width(0u), height(0u),
66           pixmap(0ul), count(0u)
67       { }
CacheItembt::RealPixmapCache::CacheItem68       inline CacheItem(const unsigned int s, const Texture &t,
69                        const unsigned int w, const unsigned int h)
70         : texture(t), screen(s), width(w), height(h),
71           pixmap(0ul), count(1u)
72       { }
73 
operator ==bt::RealPixmapCache::CacheItem74       inline bool operator==(const CacheItem &x) const {
75         return texture == x.texture &&
76                 screen == x.screen &&
77                  width == x.width &&
78                 height == x.height;
79       }
80     };
81 
82     struct PixmapMatch {
PixmapMatchbt::RealPixmapCache::PixmapMatch83       inline PixmapMatch(Pixmap p)
84         : pixmap(p)
85       { }
operator ()bt::RealPixmapCache::PixmapMatch86       inline bool operator()(const RealPixmapCache::CacheItem& item) const
87       { return item.pixmap == pixmap; }
88 
89       const Pixmap pixmap;
90     };
91 
92     const Display &_display;
93 
94     typedef std::list<CacheItem> Cache;
95     Cache cache;
96   };
97 
98 
99   static RealPixmapCache *realpixmapcache = 0;
100   static unsigned long maxmem_usage = 2ul*1024ul*1024ul; // 2mb default
101   static unsigned long mem_usage = 0ul;
102 
103 
createPixmapCache(const Display & display)104   void createPixmapCache(const Display &display) {
105     assert(realpixmapcache == 0);
106     realpixmapcache = new RealPixmapCache(display);
107   }
108 
109 
destroyPixmapCache(void)110   void destroyPixmapCache(void) {
111     delete realpixmapcache;
112     realpixmapcache = 0;
113 
114     assert(mem_usage == 0ul);
115   }
116 
117 } // namespace bt
118 
119 
RealPixmapCache(const Display & display)120 bt::RealPixmapCache::RealPixmapCache(const Display &display)
121   : _display(display)
122 { }
123 
124 
~RealPixmapCache(void)125 bt::RealPixmapCache::~RealPixmapCache(void)
126 { clear(true); }
127 
128 
find(unsigned int screen,const Texture & texture,unsigned int width,unsigned int height,Pixmap old_pixmap)129 Pixmap bt::RealPixmapCache::find(unsigned int screen,
130                                  const Texture &texture,
131                                  unsigned int width, unsigned int height,
132                                  Pixmap old_pixmap) {
133   release(old_pixmap);
134 
135   if (texture.texture() == (Texture::Flat | Texture::Solid))
136     return None;
137 
138   if (texture.texture() == Texture::Parent_Relative)
139     return ParentRelative;
140 
141   Pixmap p;
142   // find one in the cache
143   CacheItem item(screen, texture, width, height);
144   Cache::iterator it = std::find(cache.begin(), cache.end(), item);
145 
146   if (it != cache.end()) {
147     // found
148     ++(it->count);
149 
150     p = it->pixmap;
151 
152 #ifdef PIXMAPCACHE_DEBUG
153     fprintf(stderr, "bt::PixmapCache: use %08lx %4ux%4u, count %4u\n",
154             it->pixmap, width, height, it->count);
155 #endif // PIXMAPCACHE_DEBUG
156   } else {
157     Image image(width, height);
158     p = image.render(_display, screen, texture);
159 
160     if (p) {
161       item.pixmap = p;
162 
163 #ifdef PIXMAPCACHE_DEBUG
164       fprintf(stderr,
165               "bt::PixmapCache: add %08lx %4ux%4u\n"
166               "                 mem %8lu max %8lu\n",
167               p, width, height, mem_usage, maxmem_usage);
168 #endif // PIXMAPCACHE_DEBUG
169 
170       cache.push_front(item);
171 
172       // keep track of memory usage server side
173       const unsigned long mem =
174         ( ( width * height ) * (_display.screenInfo(screen).depth() / 8 ) );
175       mem_usage += mem;
176       if (mem_usage > maxmem_usage)
177         clear(false);
178 
179 #ifdef PIXMAPCACHE_DEBUG
180       if (mem_usage > maxmem_usage) {
181         fprintf(stderr,
182                 "bt::PixmapCache: maximum size (%lu kb) exceeded\n"
183                 "bt::PixmapCache: current size: %lu kb\n",
184                 maxmem_usage / 1024, mem_usage / 1024);
185       }
186 #endif // PIXMAPCACHE_DEBUG
187     }
188   }
189 
190   return p;
191 }
192 
193 
release(Pixmap pixmap)194 void bt::RealPixmapCache::release(Pixmap pixmap) {
195   if (!pixmap || pixmap == ParentRelative)
196     return;
197 
198   Cache::iterator it = std::find_if(cache.begin(), cache.end(),
199                                     PixmapMatch(pixmap));
200   assert(it != cache.end() && it->count > 0);
201 
202   // decrement the refcount
203   --(it->count);
204 
205 #ifdef PIXMAPCACHE_DEBUG
206   fprintf(stderr, "bt::PixmapCache: rel %08lx %4ux%4u, count %4u\n",
207           it->pixmap, it->width, it->height, it->count);
208 #endif // PIXMAPCACHE_DEBUG
209 }
210 
211 
clear(bool force)212 void bt::RealPixmapCache::clear(bool force) {
213   if (cache.empty())
214     return; // nothing to do
215 
216 #ifdef PIXMAPCACHE_DEBUG
217   fprintf(stderr, "bt::PixmapCache: clearing cache, %u entries\n",
218           cache.size());
219 #endif // PIXMAPCACHE_DEBUG
220 
221   Cache::iterator it = cache.begin();
222   while (it != cache.end()) {
223     if (it->count != 0 && !force) {
224 #ifdef PIXMAPCACHE_DEBUG
225       fprintf(stderr, "bt::PixmapCache: skp %08lx %4ux%4u, count %4u\n",
226               it->pixmap, it->width, it->height, it->count);
227 #endif // PIXMAPCACHE_DEBUG
228 
229       ++it;
230       continue;
231     }
232 
233 #ifdef PIXMAPCACHE_DEBUG
234     fprintf(stderr, "bt::PixmapCache: fre %08lx %4ux%4u\n",
235             it->pixmap, it->width, it->height);
236 #endif // PIXMAPCACHE_DEBUG
237 
238     // keep track of memory usage server side
239     const unsigned long mem =
240       ( ( it->width * it->height ) *
241         (_display.screenInfo(it->screen).depth() / 8 ) );
242     assert(mem <= mem_usage);
243     mem_usage -= mem;
244 
245     // free pixmap
246     XFreePixmap(_display.XDisplay(), it->pixmap);
247 
248     // remove from cache
249     it = cache.erase(it);
250   }
251 
252 #ifdef PIXMAPCACHE_DEBUG
253   fprintf(stderr,
254           "bt::PixmapCache: cleared, %u entries remain\n"
255           "                 mem %8lu max %8lu\n",
256           cache.size(), mem_usage, maxmem_usage);
257 #endif // PIXMAPCACHE_DEBUG
258 }
259 
260 
cacheLimit(void)261 unsigned long bt::PixmapCache::cacheLimit(void)
262 { return maxmem_usage / 1024; }
263 
264 
setCacheLimit(unsigned long limit)265 void bt::PixmapCache::setCacheLimit(unsigned long limit)
266 { maxmem_usage = limit * 1024; }
267 
268 
memoryUsage(void)269 unsigned long bt::PixmapCache::memoryUsage(void)
270 { return mem_usage / 1024; }
271 
272 
find(unsigned int screen,const Texture & texture,unsigned int width,unsigned int height,Pixmap old_pixmap)273 Pixmap bt::PixmapCache::find(unsigned int screen,
274                              const Texture &texture,
275                              unsigned int width, unsigned int height,
276                              Pixmap old_pixmap)
277 { return realpixmapcache->find(screen, texture, width, height, old_pixmap); }
278 
279 
release(Pixmap pixmap)280 void bt::PixmapCache::release(Pixmap pixmap)
281 { realpixmapcache->release(pixmap); }
282 
283 
clearCache(void)284 void bt::PixmapCache::clearCache(void)
285 { realpixmapcache->clear(false); }
286