1 /* -*- c-basic-offset: 8 -*- 2 rdesktop: A Remote Desktop Protocol client. 3 Cache routines 4 Copyright (C) Matthew Chapman 1999-2005 5 Copyright (C) Jeroen Meijer 2005 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License along 18 with this program; if not, write to the Free Software Foundation, Inc., 19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 */ 21 22 #include "rdesktop.h" 23 24 #undef IS_SET // !!!FIXME!!! 25 26 /* BITMAP CACHE */ 27 #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0])) 28 #define IS_PERSISTENT(id) (This->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 therby reducing the need to load bitmaps from disk. 37 * (Jeroen) 38 */ 39 #define BUMP_COUNT 40 40 41 /* Setup the bitmap cache lru/mru linked list */ 42 void 43 cache_rebuild_bmpcache_linked_list(RDPCLIENT * This, uint8 id, sint16 * idx, int count) 44 { 45 int n = count, c = 0; 46 sint16 n_idx; 47 48 /* find top, skip evicted bitmaps */ 49 while (--n >= 0 && This->cache.bmpcache[id][idx[n]].bitmap == NULL); 50 if (n < 0) 51 { 52 This->cache.bmpcache_mru[id] = This->cache.bmpcache_lru[id] = NOT_SET; 53 return; 54 } 55 56 This->cache.bmpcache_mru[id] = idx[n]; 57 This->cache.bmpcache[id][idx[n]].next = NOT_SET; 58 n_idx = idx[n]; 59 c++; 60 61 /* link list */ 62 while (n >= 0) 63 { 64 /* skip evicted bitmaps */ 65 while (--n >= 0 && This->cache.bmpcache[id][idx[n]].bitmap == NULL); 66 67 if (n < 0) 68 break; 69 70 This->cache.bmpcache[id][n_idx].previous = idx[n]; 71 This->cache.bmpcache[id][idx[n]].next = n_idx; 72 n_idx = idx[n]; 73 c++; 74 } 75 76 This->cache.bmpcache[id][n_idx].previous = NOT_SET; 77 This->cache.bmpcache_lru[id] = n_idx; 78 79 if (c != This->cache.bmpcache_count[id]) 80 { 81 error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c, 82 This->cache.bmpcache_count[id]); 83 exit(1); 84 } 85 } 86 87 /* Move a bitmap to a new position in the linked list. */ 88 void 89 cache_bump_bitmap(RDPCLIENT * This, uint8 id, uint16 idx, int bump) 90 { 91 int p_idx, n_idx, n; 92 93 if (!IS_PERSISTENT(id)) 94 return; 95 96 if (This->cache.bmpcache_mru[id] == idx) 97 return; 98 99 DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id, idx, bump)); 100 101 n_idx = This->cache.bmpcache[id][idx].next; 102 p_idx = This->cache.bmpcache[id][idx].previous; 103 104 if (IS_SET(n_idx)) 105 { 106 /* remove */ 107 --This->cache.bmpcache_count[id]; 108 if (IS_SET(p_idx)) 109 This->cache.bmpcache[id][p_idx].next = n_idx; 110 else 111 This->cache.bmpcache_lru[id] = n_idx; 112 if (IS_SET(n_idx)) 113 This->cache.bmpcache[id][n_idx].previous = p_idx; 114 else 115 This->cache.bmpcache_mru[id] = p_idx; 116 } 117 else 118 { 119 p_idx = NOT_SET; 120 n_idx = This->cache.bmpcache_lru[id]; 121 } 122 123 if (bump >= 0) 124 { 125 for (n = 0; n < bump && IS_SET(n_idx); n++) 126 { 127 p_idx = n_idx; 128 n_idx = This->cache.bmpcache[id][p_idx].next; 129 } 130 } 131 else 132 { 133 p_idx = This->cache.bmpcache_mru[id]; 134 n_idx = NOT_SET; 135 } 136 137 /* insert */ 138 ++This->cache.bmpcache_count[id]; 139 This->cache.bmpcache[id][idx].previous = p_idx; 140 This->cache.bmpcache[id][idx].next = n_idx; 141 142 if (p_idx >= 0) 143 This->cache.bmpcache[id][p_idx].next = idx; 144 else 145 This->cache.bmpcache_lru[id] = idx; 146 147 if (n_idx >= 0) 148 This->cache.bmpcache[id][n_idx].previous = idx; 149 else 150 This->cache.bmpcache_mru[id] = idx; 151 } 152 153 /* Evict the least-recently used bitmap from the cache */ 154 void 155 cache_evict_bitmap(RDPCLIENT * This, uint8 id) 156 { 157 uint16 idx; 158 int n_idx; 159 160 if (!IS_PERSISTENT(id)) 161 return; 162 163 idx = This->cache.bmpcache_lru[id]; 164 n_idx = This->cache.bmpcache[id][idx].next; 165 DEBUG_RDP5(("evict bitmap: id=%d idx=%d n_idx=%d bmp=0x%x\n", id, idx, n_idx, 166 This->cache.bmpcache[id][idx].bitmap)); 167 168 ui_destroy_bitmap(This, This->cache.bmpcache[id][idx].bitmap); 169 --This->cache.bmpcache_count[id]; 170 This->cache.bmpcache[id][idx].bitmap = 0; 171 172 This->cache.bmpcache_lru[id] = n_idx; 173 This->cache.bmpcache[id][n_idx].previous = NOT_SET; 174 175 pstcache_touch_bitmap(This, id, idx, 0); 176 } 177 178 /* Retrieve a bitmap from the cache */ 179 HBITMAP 180 cache_get_bitmap(RDPCLIENT * This, uint8 id, uint16 idx) 181 { 182 if ((id < NUM_ELEMENTS(This->cache.bmpcache)) && (idx < NUM_ELEMENTS(This->cache.bmpcache[0]))) 183 { 184 if (This->cache.bmpcache[id][idx].bitmap || pstcache_load_bitmap(This, id, idx)) 185 { 186 if (IS_PERSISTENT(id)) 187 cache_bump_bitmap(This, id, idx, BUMP_COUNT); 188 189 return This->cache.bmpcache[id][idx].bitmap; 190 } 191 } 192 else if ((id < NUM_ELEMENTS(This->cache.volatile_bc)) && (idx == 0x7fff)) 193 { 194 return This->cache.volatile_bc[id]; 195 } 196 197 error("get bitmap %d:%d\n", id, idx); 198 return NULL; 199 } 200 201 /* Store a bitmap in the cache */ 202 void 203 cache_put_bitmap(RDPCLIENT * This, uint8 id, uint16 idx, HBITMAP bitmap) 204 { 205 HBITMAP old; 206 207 if ((id < NUM_ELEMENTS(This->cache.bmpcache)) && (idx < NUM_ELEMENTS(This->cache.bmpcache[0]))) 208 { 209 old = This->cache.bmpcache[id][idx].bitmap; 210 if (old != NULL) 211 ui_destroy_bitmap(This, old); 212 This->cache.bmpcache[id][idx].bitmap = bitmap; 213 214 if (IS_PERSISTENT(id)) 215 { 216 if (old == NULL) 217 This->cache.bmpcache[id][idx].previous = This->cache.bmpcache[id][idx].next = NOT_SET; 218 219 cache_bump_bitmap(This, id, idx, TO_TOP); 220 if (This->cache.bmpcache_count[id] > BMPCACHE2_C2_CELLS) 221 cache_evict_bitmap(This, id); 222 } 223 } 224 else if ((id < NUM_ELEMENTS(This->cache.volatile_bc)) && (idx == 0x7fff)) 225 { 226 old = This->cache.volatile_bc[id]; 227 if (old != NULL) 228 ui_destroy_bitmap(This, old); 229 This->cache.volatile_bc[id] = bitmap; 230 } 231 else 232 { 233 error("put bitmap %d:%d\n", id, idx); 234 } 235 } 236 237 /* Updates the persistent bitmap cache MRU information on exit */ 238 void 239 cache_save_state(RDPCLIENT * This) 240 { 241 uint32 id = 0, t = 0; 242 int idx; 243 244 for (id = 0; id < NUM_ELEMENTS(This->cache.bmpcache); id++) 245 if (IS_PERSISTENT(id)) 246 { 247 DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id)); 248 idx = This->cache.bmpcache_lru[id]; 249 while (idx >= 0) 250 { 251 pstcache_touch_bitmap(This, id, idx, ++t); 252 idx = This->cache.bmpcache[id][idx].next; 253 } 254 DEBUG_RDP5((" %d stamps written.\n", t)); 255 } 256 } 257 258 259 /* FONT CACHE */ 260 /* Retrieve a glyph from the font cache */ 261 FONTGLYPH * 262 cache_get_font(RDPCLIENT * This, uint8 font, uint16 character) 263 { 264 FONTGLYPH *glyph; 265 266 if ((font < NUM_ELEMENTS(This->cache.fontcache)) && (character < NUM_ELEMENTS(This->cache.fontcache[0]))) 267 { 268 glyph = &This->cache.fontcache[font][character]; 269 if (glyph->pixmap != NULL) 270 return glyph; 271 } 272 273 error("get font %d:%d\n", font, character); 274 return NULL; 275 } 276 277 /* Store a glyph in the font cache */ 278 void 279 cache_put_font(RDPCLIENT * This, uint8 font, uint16 character, uint16 offset, 280 uint16 baseline, uint16 width, uint16 height, HGLYPH pixmap) 281 { 282 FONTGLYPH *glyph; 283 284 if ((font < NUM_ELEMENTS(This->cache.fontcache)) && (character < NUM_ELEMENTS(This->cache.fontcache[0]))) 285 { 286 glyph = &This->cache.fontcache[font][character]; 287 if (glyph->pixmap != NULL) 288 ui_destroy_glyph(This, glyph->pixmap); 289 290 glyph->offset = offset; 291 glyph->baseline = baseline; 292 glyph->width = width; 293 glyph->height = height; 294 glyph->pixmap = pixmap; 295 } 296 else 297 { 298 error("put font %d:%d\n", font, character); 299 } 300 } 301 302 303 /* TEXT CACHE */ 304 /* Retrieve a text item from the cache */ 305 DATABLOB * 306 cache_get_text(RDPCLIENT * This, uint8 cache_id) 307 { 308 DATABLOB *text; 309 310 text = &This->cache.textcache[cache_id]; 311 return text; 312 } 313 314 /* Store a text item in the cache */ 315 void 316 cache_put_text(RDPCLIENT * This, uint8 cache_id, void *data, int length) 317 { 318 DATABLOB *text; 319 void * p = malloc(length); 320 321 if(p == NULL) 322 return; 323 324 text = &This->cache.textcache[cache_id]; 325 if (text->data != NULL) 326 free(text->data); 327 text->data = p; 328 text->size = length; 329 memcpy(text->data, data, length); 330 } 331 332 333 /* DESKTOP CACHE */ 334 /* Retrieve desktop data from the cache */ 335 uint8 * 336 cache_get_desktop(RDPCLIENT * This, uint32 offset, int cx, int cy, int bytes_per_pixel) 337 { 338 int length = cx * cy * bytes_per_pixel; 339 340 if (offset > sizeof(This->cache.deskcache)) 341 offset = 0; 342 343 if ((offset + length) <= sizeof(This->cache.deskcache)) 344 { 345 return &This->cache.deskcache[offset]; 346 } 347 348 error("get desktop %d:%d\n", offset, length); 349 return NULL; 350 } 351 352 /* Store desktop data in the cache */ 353 void 354 cache_put_desktop(RDPCLIENT * This, uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8 * data) 355 { 356 int length = cx * cy * bytes_per_pixel; 357 358 if (offset > sizeof(This->cache.deskcache)) 359 offset = 0; 360 361 if ((offset + length) <= sizeof(This->cache.deskcache)) 362 { 363 cx *= bytes_per_pixel; 364 while (cy--) 365 { 366 memcpy(&This->cache.deskcache[offset], data, cx); 367 data += scanline; 368 offset += cx; 369 } 370 } 371 else 372 { 373 error("put desktop %d:%d\n", offset, length); 374 } 375 } 376 377 378 /* CURSOR CACHE */ 379 /* Retrieve cursor from cache */ 380 HCURSOR 381 cache_get_cursor(RDPCLIENT * This, uint16 cache_idx) 382 { 383 HCURSOR cursor; 384 385 if (cache_idx < NUM_ELEMENTS(This->cache.cursorcache)) 386 { 387 cursor = This->cache.cursorcache[cache_idx]; 388 if (cursor != NULL) 389 return cursor; 390 } 391 392 error("get cursor %d\n", cache_idx); 393 return NULL; 394 } 395 396 /* Store cursor in cache */ 397 void 398 cache_put_cursor(RDPCLIENT * This, uint16 cache_idx, HCURSOR cursor) 399 { 400 HCURSOR old; 401 402 if (cache_idx < NUM_ELEMENTS(This->cache.cursorcache)) 403 { 404 old = This->cache.cursorcache[cache_idx]; 405 if (old != NULL) 406 ui_destroy_cursor(This, old); 407 408 This->cache.cursorcache[cache_idx] = cursor; 409 } 410 else 411 { 412 error("put cursor %d\n", cache_idx); 413 } 414 } 415