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 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 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 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 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 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 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 * 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 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 * 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 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 * 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 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 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 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 * 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 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