1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
4 Copyright (C) Matthew Chapman 1999-2005
5 Copyright (C) Jeroen Meijer 2005
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.
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.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #undef IS_SET // !!!FIXME!!!
27 #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0]))
28 #define IS_PERSISTENT(id) (This->pstcache_fd[id] > 0)
31 #define IS_SET(idx) (idx >= 0)
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.
41 /* Setup the bitmap cache lru/mru linked list */
43 cache_rebuild_bmpcache_linked_list(RDPCLIENT
* This
, uint8 id
, sint16
* idx
, int count
)
48 /* find top, skip evicted bitmaps */
49 while (--n
>= 0 && This
->cache
.bmpcache
[id
][idx
[n
]].bitmap
== NULL
);
52 This
->cache
.bmpcache_mru
[id
] = This
->cache
.bmpcache_lru
[id
] = NOT_SET
;
56 This
->cache
.bmpcache_mru
[id
] = idx
[n
];
57 This
->cache
.bmpcache
[id
][idx
[n
]].next
= NOT_SET
;
64 /* skip evicted bitmaps */
65 while (--n
>= 0 && This
->cache
.bmpcache
[id
][idx
[n
]].bitmap
== NULL
);
70 This
->cache
.bmpcache
[id
][n_idx
].previous
= idx
[n
];
71 This
->cache
.bmpcache
[id
][idx
[n
]].next
= n_idx
;
76 This
->cache
.bmpcache
[id
][n_idx
].previous
= NOT_SET
;
77 This
->cache
.bmpcache_lru
[id
] = n_idx
;
79 if (c
!= This
->cache
.bmpcache_count
[id
])
81 error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c
,
82 This
->cache
.bmpcache_count
[id
]);
87 /* Move a bitmap to a new position in the linked list. */
89 cache_bump_bitmap(RDPCLIENT
* This
, uint8 id
, uint16 idx
, int bump
)
93 if (!IS_PERSISTENT(id
))
96 if (This
->cache
.bmpcache_mru
[id
] == idx
)
99 DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id
, idx
, bump
));
101 n_idx
= This
->cache
.bmpcache
[id
][idx
].next
;
102 p_idx
= This
->cache
.bmpcache
[id
][idx
].previous
;
107 --This
->cache
.bmpcache_count
[id
];
109 This
->cache
.bmpcache
[id
][p_idx
].next
= n_idx
;
111 This
->cache
.bmpcache_lru
[id
] = n_idx
;
113 This
->cache
.bmpcache
[id
][n_idx
].previous
= p_idx
;
115 This
->cache
.bmpcache_mru
[id
] = p_idx
;
120 n_idx
= This
->cache
.bmpcache_lru
[id
];
125 for (n
= 0; n
< bump
&& IS_SET(n_idx
); n
++)
128 n_idx
= This
->cache
.bmpcache
[id
][p_idx
].next
;
133 p_idx
= This
->cache
.bmpcache_mru
[id
];
138 ++This
->cache
.bmpcache_count
[id
];
139 This
->cache
.bmpcache
[id
][idx
].previous
= p_idx
;
140 This
->cache
.bmpcache
[id
][idx
].next
= n_idx
;
143 This
->cache
.bmpcache
[id
][p_idx
].next
= idx
;
145 This
->cache
.bmpcache_lru
[id
] = idx
;
148 This
->cache
.bmpcache
[id
][n_idx
].previous
= idx
;
150 This
->cache
.bmpcache_mru
[id
] = idx
;
153 /* Evict the least-recently used bitmap from the cache */
155 cache_evict_bitmap(RDPCLIENT
* This
, uint8 id
)
160 if (!IS_PERSISTENT(id
))
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
));
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;
172 This
->cache
.bmpcache_lru
[id
] = n_idx
;
173 This
->cache
.bmpcache
[id
][n_idx
].previous
= NOT_SET
;
175 pstcache_touch_bitmap(This
, id
, idx
, 0);
178 /* Retrieve a bitmap from the cache */
180 cache_get_bitmap(RDPCLIENT
* This
, uint8 id
, uint16 idx
)
182 if ((id
< NUM_ELEMENTS(This
->cache
.bmpcache
)) && (idx
< NUM_ELEMENTS(This
->cache
.bmpcache
[0])))
184 if (This
->cache
.bmpcache
[id
][idx
].bitmap
|| pstcache_load_bitmap(This
, id
, idx
))
186 if (IS_PERSISTENT(id
))
187 cache_bump_bitmap(This
, id
, idx
, BUMP_COUNT
);
189 return This
->cache
.bmpcache
[id
][idx
].bitmap
;
192 else if ((id
< NUM_ELEMENTS(This
->cache
.volatile_bc
)) && (idx
== 0x7fff))
194 return This
->cache
.volatile_bc
[id
];
197 error("get bitmap %d:%d\n", id
, idx
);
201 /* Store a bitmap in the cache */
203 cache_put_bitmap(RDPCLIENT
* This
, uint8 id
, uint16 idx
, HBITMAP bitmap
)
207 if ((id
< NUM_ELEMENTS(This
->cache
.bmpcache
)) && (idx
< NUM_ELEMENTS(This
->cache
.bmpcache
[0])))
209 old
= This
->cache
.bmpcache
[id
][idx
].bitmap
;
211 ui_destroy_bitmap(This
, old
);
212 This
->cache
.bmpcache
[id
][idx
].bitmap
= bitmap
;
214 if (IS_PERSISTENT(id
))
217 This
->cache
.bmpcache
[id
][idx
].previous
= This
->cache
.bmpcache
[id
][idx
].next
= NOT_SET
;
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
);
224 else if ((id
< NUM_ELEMENTS(This
->cache
.volatile_bc
)) && (idx
== 0x7fff))
226 old
= This
->cache
.volatile_bc
[id
];
228 ui_destroy_bitmap(This
, old
);
229 This
->cache
.volatile_bc
[id
] = bitmap
;
233 error("put bitmap %d:%d\n", id
, idx
);
237 /* Updates the persistent bitmap cache MRU information on exit */
239 cache_save_state(RDPCLIENT
* This
)
241 uint32 id
= 0, t
= 0;
244 for (id
= 0; id
< NUM_ELEMENTS(This
->cache
.bmpcache
); id
++)
245 if (IS_PERSISTENT(id
))
247 DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id
));
248 idx
= This
->cache
.bmpcache_lru
[id
];
251 pstcache_touch_bitmap(This
, id
, idx
, ++t
);
252 idx
= This
->cache
.bmpcache
[id
][idx
].next
;
254 DEBUG_RDP5((" %d stamps written.\n", t
));
260 /* Retrieve a glyph from the font cache */
262 cache_get_font(RDPCLIENT
* This
, uint8 font
, uint16 character
)
266 if ((font
< NUM_ELEMENTS(This
->cache
.fontcache
)) && (character
< NUM_ELEMENTS(This
->cache
.fontcache
[0])))
268 glyph
= &This
->cache
.fontcache
[font
][character
];
269 if (glyph
->pixmap
!= NULL
)
273 error("get font %d:%d\n", font
, character
);
277 /* Store a glyph in the font cache */
279 cache_put_font(RDPCLIENT
* This
, uint8 font
, uint16 character
, uint16 offset
,
280 uint16 baseline
, uint16 width
, uint16 height
, HGLYPH pixmap
)
284 if ((font
< NUM_ELEMENTS(This
->cache
.fontcache
)) && (character
< NUM_ELEMENTS(This
->cache
.fontcache
[0])))
286 glyph
= &This
->cache
.fontcache
[font
][character
];
287 if (glyph
->pixmap
!= NULL
)
288 ui_destroy_glyph(This
, glyph
->pixmap
);
290 glyph
->offset
= offset
;
291 glyph
->baseline
= baseline
;
292 glyph
->width
= width
;
293 glyph
->height
= height
;
294 glyph
->pixmap
= pixmap
;
298 error("put font %d:%d\n", font
, character
);
304 /* Retrieve a text item from the cache */
306 cache_get_text(RDPCLIENT
* This
, uint8 cache_id
)
310 text
= &This
->cache
.textcache
[cache_id
];
314 /* Store a text item in the cache */
316 cache_put_text(RDPCLIENT
* This
, uint8 cache_id
, void *data
, int length
)
319 void * p
= malloc(length
);
324 text
= &This
->cache
.textcache
[cache_id
];
325 if (text
->data
!= NULL
)
329 memcpy(text
->data
, data
, length
);
334 /* Retrieve desktop data from the cache */
336 cache_get_desktop(RDPCLIENT
* This
, uint32 offset
, int cx
, int cy
, int bytes_per_pixel
)
338 int length
= cx
* cy
* bytes_per_pixel
;
340 if (offset
> sizeof(This
->cache
.deskcache
))
343 if ((offset
+ length
) <= sizeof(This
->cache
.deskcache
))
345 return &This
->cache
.deskcache
[offset
];
348 error("get desktop %d:%d\n", offset
, length
);
352 /* Store desktop data in the cache */
354 cache_put_desktop(RDPCLIENT
* This
, uint32 offset
, int cx
, int cy
, int scanline
, int bytes_per_pixel
, uint8
* data
)
356 int length
= cx
* cy
* bytes_per_pixel
;
358 if (offset
> sizeof(This
->cache
.deskcache
))
361 if ((offset
+ length
) <= sizeof(This
->cache
.deskcache
))
363 cx
*= bytes_per_pixel
;
366 memcpy(&This
->cache
.deskcache
[offset
], data
, cx
);
373 error("put desktop %d:%d\n", offset
, length
);
379 /* Retrieve cursor from cache */
381 cache_get_cursor(RDPCLIENT
* This
, uint16 cache_idx
)
385 if (cache_idx
< NUM_ELEMENTS(This
->cache
.cursorcache
))
387 cursor
= This
->cache
.cursorcache
[cache_idx
];
392 error("get cursor %d\n", cache_idx
);
396 /* Store cursor in cache */
398 cache_put_cursor(RDPCLIENT
* This
, uint16 cache_idx
, HCURSOR cursor
)
402 if (cache_idx
< NUM_ELEMENTS(This
->cache
.cursorcache
))
404 old
= This
->cache
.cursorcache
[cache_idx
];
406 ui_destroy_cursor(This
, old
);
408 This
->cache
.cursorcache
[cache_idx
] = cursor
;
412 error("put cursor %d\n", cache_idx
);