1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Cache routines
4 Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
5 Copyright (C) Jeroen Meijer <jeroen@oldambt7.com> 2005
6 Copyright 2003-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "precomp.h"
23
24 /* BITMAP CACHE */
25 extern int g_pstcache_fd[];
26
27 #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0]))
28 //#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0)
29 #define TO_TOP -1
30 #define NOT_SET -1
31 #define IS_SET(idx) (idx >= 0)
32
33 /*
34 * TODO: Test for optimal value of BUMP_COUNT. TO_TOP gives lowest cpu utilisation but using
35 * a positive value will hopefully result in less frequently used bitmaps having a greater chance
36 * of being evicted from the cache, and thereby reducing the need to load bitmaps from disk.
37 * (Jeroen)
38 */
39 #define BUMP_COUNT 40
40
41 struct bmpcache_entry
42 {
43 RD_HBITMAP bitmap;
44 sint16 previous;
45 sint16 next;
46 };
47
48 static struct bmpcache_entry g_bmpcache[3][0xa00];
49 static RD_HBITMAP g_volatile_bc[3];
50
51 static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET };
52 static int g_bmpcache_mru[3] = { NOT_SET, NOT_SET, NOT_SET };
53
54 static int g_bmpcache_count[3];
55
56 /* Setup the bitmap cache lru/mru linked list */
57 void
cache_rebuild_bmpcache_linked_list(uint8 id,sint16 * idx,int count)58 cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count)
59 {
60 int n = count, c = 0;
61 sint16 n_idx;
62
63 /* find top, skip evicted bitmaps */
64 while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
65 if (n < 0)
66 {
67 g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET;
68 return;
69 }
70
71 g_bmpcache_mru[id] = idx[n];
72 g_bmpcache[id][idx[n]].next = NOT_SET;
73 n_idx = idx[n];
74 c++;
75
76 /* link list */
77 while (n >= 0)
78 {
79 /* skip evicted bitmaps */
80 while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
81
82 if (n < 0)
83 break;
84
85 g_bmpcache[id][n_idx].previous = idx[n];
86 g_bmpcache[id][idx[n]].next = n_idx;
87 n_idx = idx[n];
88 c++;
89 }
90
91 g_bmpcache[id][n_idx].previous = NOT_SET;
92 g_bmpcache_lru[id] = n_idx;
93
94 if (c != g_bmpcache_count[id])
95 {
96 error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c,
97 g_bmpcache_count[id]);
98 exit(EX_SOFTWARE);
99 }
100 }
101
102 /* Move a bitmap to a new position in the linked list. */
103 void
cache_bump_bitmap(uint8 id,uint16 idx,int bump)104 cache_bump_bitmap(uint8 id, uint16 idx, int bump)
105 {
106 int p_idx, n_idx, n;
107
108 if (!IS_PERSISTENT(id))
109 return;
110
111 if (g_bmpcache_mru[id] == idx)
112 return;
113
114 DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id, idx, bump));
115
116 n_idx = g_bmpcache[id][idx].next;
117 p_idx = g_bmpcache[id][idx].previous;
118
119 if (IS_SET(n_idx))
120 {
121 /* remove */
122 --g_bmpcache_count[id];
123 if (IS_SET(p_idx))
124 g_bmpcache[id][p_idx].next = n_idx;
125 else
126 g_bmpcache_lru[id] = n_idx;
127 if (IS_SET(n_idx))
128 g_bmpcache[id][n_idx].previous = p_idx;
129 else
130 g_bmpcache_mru[id] = p_idx;
131 }
132 else
133 {
134 p_idx = NOT_SET;
135 n_idx = g_bmpcache_lru[id];
136 }
137
138 if (bump >= 0)
139 {
140 for (n = 0; n < bump && IS_SET(n_idx); n++)
141 {
142 p_idx = n_idx;
143 n_idx = g_bmpcache[id][p_idx].next;
144 }
145 }
146 else
147 {
148 p_idx = g_bmpcache_mru[id];
149 n_idx = NOT_SET;
150 }
151
152 /* insert */
153 ++g_bmpcache_count[id];
154 g_bmpcache[id][idx].previous = p_idx;
155 g_bmpcache[id][idx].next = n_idx;
156
157 if (p_idx >= 0)
158 g_bmpcache[id][p_idx].next = idx;
159 else
160 g_bmpcache_lru[id] = idx;
161
162 if (n_idx >= 0)
163 g_bmpcache[id][n_idx].previous = idx;
164 else
165 g_bmpcache_mru[id] = idx;
166 }
167
168 /* Evict the least-recently used bitmap from the cache */
169 void
cache_evict_bitmap(uint8 id)170 cache_evict_bitmap(uint8 id)
171 {
172 uint16 idx;
173 int n_idx;
174
175 if (!IS_PERSISTENT(id))
176 return;
177
178 idx = g_bmpcache_lru[id];
179 n_idx = g_bmpcache[id][idx].next;
180 DEBUG_RDP5(("evict bitmap: id=%d idx=%d n_idx=%d bmp=%p\n", id, idx, n_idx,
181 g_bmpcache[id][idx].bitmap));
182
183 ui_destroy_bitmap(g_bmpcache[id][idx].bitmap);
184 --g_bmpcache_count[id];
185 g_bmpcache[id][idx].bitmap = 0;
186
187 g_bmpcache_lru[id] = n_idx;
188 g_bmpcache[id][n_idx].previous = NOT_SET;
189
190 pstcache_touch_bitmap(id, idx, 0);
191 }
192
193 /* Retrieve a bitmap from the cache */
194 RD_HBITMAP
cache_get_bitmap(uint8 id,uint16 idx)195 cache_get_bitmap(uint8 id, uint16 idx)
196 {
197 if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0])))
198 {
199 if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx))
200 {
201 if (IS_PERSISTENT(id))
202 cache_bump_bitmap(id, idx, BUMP_COUNT);
203
204 return g_bmpcache[id][idx].bitmap;
205 }
206 }
207 else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff))
208 {
209 return g_volatile_bc[id];
210 }
211
212 error("get bitmap %d:%d\n", id, idx);
213 return NULL;
214 }
215
216 /* Store a bitmap in the cache */
217 void
cache_put_bitmap(uint8 id,uint16 idx,RD_HBITMAP bitmap)218 cache_put_bitmap(uint8 id, uint16 idx, RD_HBITMAP bitmap)
219 {
220 RD_HBITMAP old;
221
222 if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0])))
223 {
224 old = g_bmpcache[id][idx].bitmap;
225 if (old != NULL)
226 ui_destroy_bitmap(old);
227 g_bmpcache[id][idx].bitmap = bitmap;
228
229 if (IS_PERSISTENT(id))
230 {
231 if (old == NULL)
232 g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET;
233
234 cache_bump_bitmap(id, idx, TO_TOP);
235 if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS)
236 cache_evict_bitmap(id);
237 }
238 }
239 else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff))
240 {
241 old = g_volatile_bc[id];
242 if (old != NULL)
243 ui_destroy_bitmap(old);
244 g_volatile_bc[id] = bitmap;
245 }
246 else
247 {
248 error("put bitmap %d:%d\n", id, idx);
249 }
250 }
251
252 /* Updates the persistent bitmap cache MRU information on exit */
253 void
cache_save_state(void)254 cache_save_state(void)
255 {
256 uint32 id = 0, t = 0;
257 int idx;
258
259 for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++)
260 if (IS_PERSISTENT(id))
261 {
262 DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id));
263 idx = g_bmpcache_lru[id];
264 while (idx >= 0)
265 {
266 pstcache_touch_bitmap(id, idx, ++t);
267 idx = g_bmpcache[id][idx].next;
268 }
269 DEBUG_RDP5((" %d stamps written.\n", t));
270 }
271 }
272
273
274 /* FONT CACHE */
275 static FONTGLYPH g_fontcache[12][256];
276
277 /* Retrieve a glyph from the font cache */
278 FONTGLYPH *
cache_get_font(uint8 font,uint16 character)279 cache_get_font(uint8 font, uint16 character)
280 {
281 FONTGLYPH *glyph;
282
283 if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0])))
284 {
285 glyph = &g_fontcache[font][character];
286 if (glyph->pixmap != NULL)
287 return glyph;
288 }
289
290 error("get font %d:%d\n", font, character);
291 return NULL;
292 }
293
294 /* Store a glyph in the font cache */
295 void
cache_put_font(uint8 font,uint16 character,uint16 offset,uint16 baseline,uint16 width,uint16 height,RD_HGLYPH pixmap)296 cache_put_font(uint8 font, uint16 character, uint16 offset,
297 uint16 baseline, uint16 width, uint16 height, RD_HGLYPH pixmap)
298 {
299 FONTGLYPH *glyph;
300
301 if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0])))
302 {
303 glyph = &g_fontcache[font][character];
304 if (glyph->pixmap != NULL)
305 ui_destroy_glyph(glyph->pixmap);
306
307 glyph->offset = offset;
308 glyph->baseline = baseline;
309 glyph->width = width;
310 glyph->height = height;
311 glyph->pixmap = pixmap;
312 }
313 else
314 {
315 error("put font %d:%d\n", font, character);
316 }
317 }
318
319
320 /* TEXT CACHE */
321 static DATABLOB g_textcache[256];
322
323 /* Retrieve a text item from the cache */
324 DATABLOB *
cache_get_text(uint8 cache_id)325 cache_get_text(uint8 cache_id)
326 {
327 DATABLOB *text;
328
329 text = &g_textcache[cache_id];
330 return text;
331 }
332
333 /* Store a text item in the cache */
334 void
cache_put_text(uint8 cache_id,void * data,int length)335 cache_put_text(uint8 cache_id, void *data, int length)
336 {
337 DATABLOB *text;
338
339 text = &g_textcache[cache_id];
340 if (text->data != NULL)
341 xfree(text->data);
342 text->data = xmalloc(length);
343 text->size = length;
344 memcpy(text->data, data, length);
345 }
346
347
348 /* DESKTOP CACHE */
349 static uint8 g_deskcache[0x38400 * 4];
350
351 /* Retrieve desktop data from the cache */
352 uint8 *
cache_get_desktop(uint32 offset,int cx,int cy,int bytes_per_pixel)353 cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel)
354 {
355 int length = cx * cy * bytes_per_pixel;
356
357 if (offset > sizeof(g_deskcache))
358 offset = 0;
359
360 if ((offset + length) <= sizeof(g_deskcache))
361 {
362 return &g_deskcache[offset];
363 }
364
365 error("get desktop %d:%d\n", offset, length);
366 return NULL;
367 }
368
369 /* Store desktop data in the cache */
370 void
cache_put_desktop(uint32 offset,int cx,int cy,int scanline,int bytes_per_pixel,uint8 * data)371 cache_put_desktop(uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8 * data)
372 {
373 int length = cx * cy * bytes_per_pixel;
374
375 if (offset > sizeof(g_deskcache))
376 offset = 0;
377
378 if ((offset + length) <= sizeof(g_deskcache))
379 {
380 cx *= bytes_per_pixel;
381 while (cy--)
382 {
383 memcpy(&g_deskcache[offset], data, cx);
384 data += scanline;
385 offset += cx;
386 }
387 }
388 else
389 {
390 error("put desktop %d:%d\n", offset, length);
391 }
392 }
393
394
395 /* CURSOR CACHE */
396 static RD_HCURSOR g_cursorcache[0x20];
397
398 /* Retrieve cursor from cache */
399 RD_HCURSOR
cache_get_cursor(uint16 cache_idx)400 cache_get_cursor(uint16 cache_idx)
401 {
402 RD_HCURSOR cursor;
403
404 if (cache_idx < NUM_ELEMENTS(g_cursorcache))
405 {
406 cursor = g_cursorcache[cache_idx];
407 if (cursor != NULL)
408 return cursor;
409 }
410
411 error("get cursor %d\n", cache_idx);
412 return NULL;
413 }
414
415 /* Store cursor in cache */
416 void
cache_put_cursor(uint16 cache_idx,RD_HCURSOR cursor)417 cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor)
418 {
419 RD_HCURSOR old;
420
421 if (cache_idx < NUM_ELEMENTS(g_cursorcache))
422 {
423 old = g_cursorcache[cache_idx];
424 if (old != NULL)
425 ui_destroy_cursor(old);
426
427 g_cursorcache[cache_idx] = cursor;
428 }
429 else
430 {
431 error("put cursor %d\n", cache_idx);
432 }
433 }
434
435 /* BRUSH CACHE */
436 /* index 0 is 2 colour brush, index 1 is muti colour brush */
437 static BRUSHDATA g_brushcache[2][64];
438
439 /* Retrieve brush from cache */
440 BRUSHDATA *
cache_get_brush_data(uint8 colour_code,uint8 idx)441 cache_get_brush_data(uint8 colour_code, uint8 idx)
442 {
443 colour_code = colour_code == 1 ? 0 : 1;
444 if (idx < NUM_ELEMENTS(g_brushcache[0]))
445 {
446 return &g_brushcache[colour_code][idx];
447 }
448 error("get brush %d %d\n", colour_code, idx);
449 return NULL;
450 }
451
452 /* Store brush in cache */
453 /* this function takes over the data pointer in struct, eg, caller gives it up */
454 void
cache_put_brush_data(uint8 colour_code,uint8 idx,BRUSHDATA * brush_data)455 cache_put_brush_data(uint8 colour_code, uint8 idx, BRUSHDATA * brush_data)
456 {
457 BRUSHDATA *bd;
458
459 colour_code = colour_code == 1 ? 0 : 1;
460 if (idx < NUM_ELEMENTS(g_brushcache[0]))
461 {
462 bd = &g_brushcache[colour_code][idx];
463 if (bd->data != 0)
464 {
465 xfree(bd->data);
466 }
467 memcpy(bd, brush_data, sizeof(BRUSHDATA));
468 }
469 else
470 {
471 error("put brush %d %d\n", colour_code, idx);
472 }
473 }
474