From 7f836f0384761d20f3a04ba693a7d4154129fa6a Mon Sep 17 00:00:00 2001 From: Jay Sorg Date: Sun, 27 Jun 2004 17:51:54 +0000 Subject: [PATCH] added persistant bitmap chaching from Jeroen Meijer, slightly modified git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@724 423420c4-83ab-492f-b58f-81f9feb106b5 --- Makefile | 2 +- cache.c | 73 +++++++++++++++++-- constants.h | 14 +++- doc/rdesktop.1 | 11 ++- orders.c | 112 +++++++++++++++++++++++++--- orders.h | 16 +++- proto.h | 17 ++++- pstcache.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ rdesktop.c | 106 ++++++++++++++++++++++++++- rdp.c | 184 +++++++++++++++++++++++++++++++++++----------- types.h | 22 ++++++ xwin.c | 14 ++-- 12 files changed, 688 insertions(+), 77 deletions(-) create mode 100644 pstcache.c diff --git a/Makefile b/Makefile index ba6c514..96db942 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ datadir = $(prefix)/share/rdesktop VERSION = 1.3.1 KEYMAP_PATH = $(datadir)/keymaps/ -RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o +RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o CRYPTOBJ = crypto/rc4_enc.o crypto/rc4_skey.o crypto/md5_dgst.o crypto/sha1dgst.o crypto/bn_exp.o crypto/bn_mul.o crypto/bn_div.o crypto/bn_sqr.o crypto/bn_add.o crypto/bn_shift.o crypto/bn_asm.o crypto/bn_ctx.o crypto/bn_lib.o diff --git a/cache.c b/cache.c index 866c192..0b7d9ad 100644 --- a/cache.c +++ b/cache.c @@ -21,22 +21,59 @@ #include "rdesktop.h" #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0])) +#define TOUCH(id, idx) (g_bmpcache[id][idx].usage = ++g_stamp) +#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0) + +extern int g_pstcache_fd[]; + +uint32 g_stamp; +int g_num_bitmaps_in_memory[3]; /* BITMAP CACHE */ -static HBITMAP g_bmpcache[3][600]; +static BMPCACHEENTRY g_bmpcache[3][0xa00]; + +/* Remove the least-recently used bitmap from the cache */ +void +cache_remove_lru_bitmap(uint8 cache_id) +{ + int i; + uint16 cache_idx = 0; + uint32 m = -1; + BMPCACHEENTRY *pbce; + + for (i = 0; i < NUM_ELEMENTS(g_bmpcache[cache_id]); i++) + { + if (g_bmpcache[cache_id][i].bitmap && g_bmpcache[cache_id][i].usage < m) + { + cache_idx = i; + m = g_bmpcache[cache_id][i].usage; + } + } + + pbce = &g_bmpcache[cache_id][cache_idx]; + ui_destroy_bitmap(pbce->bitmap); + --g_num_bitmaps_in_memory[cache_id]; + pbce->bitmap = 0; + pbce->usage = 0; +} /* Retrieve a bitmap from the cache */ HBITMAP cache_get_bitmap(uint8 cache_id, uint16 cache_idx) { - HBITMAP bitmap; + HBITMAP *pbitmap; if ((cache_id < NUM_ELEMENTS(g_bmpcache)) && (cache_idx < NUM_ELEMENTS(g_bmpcache[0]))) { - bitmap = g_bmpcache[cache_id][cache_idx]; - if (bitmap != NULL) - return bitmap; + pbitmap = &g_bmpcache[cache_id][cache_idx].bitmap; + if ((*pbitmap != 0) || pstcache_load_bitmap(cache_id, cache_idx)) + { + if (IS_PERSISTENT(cache_id)) + TOUCH(cache_id, cache_idx); + + return *pbitmap; + } } error("get bitmap %d:%d\n", cache_id, cache_idx); @@ -45,17 +82,25 @@ cache_get_bitmap(uint8 cache_id, uint16 cache_idx) /* Store a bitmap in the cache */ void -cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap) +cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap, uint32 stamp) { HBITMAP old; if ((cache_id < NUM_ELEMENTS(g_bmpcache)) && (cache_idx < NUM_ELEMENTS(g_bmpcache[0]))) { - old = g_bmpcache[cache_id][cache_idx]; + old = g_bmpcache[cache_id][cache_idx].bitmap; if (old != NULL) + { ui_destroy_bitmap(old); + } + else + { + if (++g_num_bitmaps_in_memory[cache_id] > BMPCACHE2_C2_CELLS) + cache_remove_lru_bitmap(cache_id); + } - g_bmpcache[cache_id][cache_idx] = bitmap; + g_bmpcache[cache_id][cache_idx].bitmap = bitmap; + g_bmpcache[cache_id][cache_idx].usage = stamp; } else { @@ -63,6 +108,18 @@ cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap) } } +/* Updates the persistent bitmap cache MRU information on exit */ +void +cache_save_state(void) +{ + int id, idx; + + for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++) + if (IS_PERSISTENT(id)) + for (idx = 0; idx < NUM_ELEMENTS(g_bmpcache[id]); idx++) + pstcache_touch_bitmap(id, idx, g_bmpcache[id][idx].usage); +} + /* FONT CACHE */ static FONTGLYPH g_fontcache[12][256]; diff --git a/constants.h b/constants.h index 57ae013..cc08840 100644 --- a/constants.h +++ b/constants.h @@ -199,6 +199,15 @@ enum RDP_INPUT_DEVICE #define TEXT2_VERTICAL 0x04 #define TEXT2_IMPLICIT_X 0x20 +/* RDP bitmap cache (version 2) constants */ +#define BMPCACHE2_C0_CELLS 0x78 +#define BMPCACHE2_C1_CELLS 0x78 +#define BMPCACHE2_C2_CELLS 0x150 +#define BMPCACHE2_NUM_PSTCELLS 0x9f6 + +#define PDU_FLAG_FIRST 0x01 +#define PDU_FLAG_LAST 0x02 + /* RDP capabilities */ #define RDP_CAPSET_GENERAL 1 /* Maps to generalCapabilitySet in T.128 page 138 */ #define RDP_CAPLEN_GENERAL 0x18 @@ -231,8 +240,9 @@ enum RDP_INPUT_DEVICE #define RDP_CAPSET_COLCACHE 10 #define RDP_CAPLEN_COLCACHE 0x08 -#define RDP_CAPSET_UNKNOWN 13 -#define RDP_CAPLEN_UNKNOWN 0x9C +#define RDP_CAPSET_BMPCACHE2 19 +#define RDP_CAPLEN_BMPCACHE2 0x28 +#define BMPCACHE2_FLAG_PERSIST (1<<31) #define RDP_SOURCE "MSTSC" diff --git a/doc/rdesktop.1 b/doc/rdesktop.1 index b33265d..bf1ac4b 100644 --- a/doc/rdesktop.1 +++ b/doc/rdesktop.1 @@ -108,14 +108,21 @@ More than 8 bpp are only supported when connecting to Windows XP limited by the server configuration. .TP .BR "-x " -Alters default bandwith performance behaviour for RDP5. By default only +Changes default bandwidth performance behaviour for RDP5. By default only theming is enabled, and all other options are disabled (corresponding to modem (56 Kbps)). Setting experience to b[roadband] enables menu animations and full window dragging. Setting experience to l[an] will also enable the desktop wallpaper. Setting experience to m[odem] -disables all (including theming). Experience can also be a hexidecimal +disables all (including themes). Experience can also be a hexidecimal number containing the flags. .TP +.BR "-P" +Enable caching of bitmaps to disk (persistent bitmap caching). This generally +improves performance (especially on low bandwidth connections) and reduces +network traffic at the cost of slightly longer startup and some disk space. +(10MB for 8-bit colour, 20MB for 15/16-bit colour and 30MB for 24-bit colour +sessions) +.TP .BR "-r " Enable redirection of the specified device on the client, such that it appears on the server. Note that the allowed diff --git a/orders.c b/orders.c index d24eb4b..1f96157 100644 --- a/orders.c +++ b/orders.c @@ -670,7 +670,7 @@ process_raw_bmpcache(STREAM s) bitmap = ui_create_bitmap(width, height, inverted); xfree(inverted); - cache_put_bitmap(cache_id, cache_idx, bitmap); + cache_put_bitmap(cache_id, cache_idx, bitmap, 0); } /* Process a bitmap cache order */ @@ -695,7 +695,11 @@ process_bmpcache(STREAM s) in_uint16_le(s, bufsize); /* bufsize */ in_uint16_le(s, cache_idx); - if (!g_use_rdp5) + if (g_use_rdp5) + { + size = bufsize; + } + else { /* Begin compressedBitmapData */ @@ -706,10 +710,6 @@ process_bmpcache(STREAM s) in_uint16_le(s, final_size); } - else - { - size = bufsize; - } in_uint8p(s, data, size); DEBUG(("BMPCACHE(cx=%d,cy=%d,id=%d,idx=%d,bpp=%d,size=%d,pad1=%d,bufsize=%d,pad2=%d,rs=%d,fs=%d)\n", width, height, cache_id, cache_idx, bpp, size, pad1, bufsize, pad2, row_size, final_size)); @@ -719,7 +719,7 @@ process_bmpcache(STREAM s) if (bitmap_decompress(bmpdata, width, height, data, size, Bpp)) { bitmap = ui_create_bitmap(width, height, bmpdata); - cache_put_bitmap(cache_id, cache_idx, bitmap); + cache_put_bitmap(cache_id, cache_idx, bitmap, 0); } else { @@ -729,6 +729,86 @@ process_bmpcache(STREAM s) xfree(bmpdata); } +/* Process a bitmap cache v2 order */ +static void +process_bmpcache2(STREAM s, uint16 flags, BOOL compressed) +{ + HBITMAP bitmap; + int y; + uint8 cache_id, cache_idx_low, width, height, Bpp; + uint16 cache_idx, bufsize; + uint8 *data, *bmpdata, *bitmap_id; + + bitmap_id = NULL; /* prevent compiler warning */ + cache_id = flags & ID_MASK; + Bpp = ((flags & MODE_MASK) >> MODE_SHIFT) - 2; + + if (flags & PERSIST) + { + in_uint8p(s, bitmap_id, 8); + } + + if (flags & SQUARE) + { + in_uint8(s, width); + height = width; + } + else + { + in_uint8(s, width); + in_uint8(s, height); + } + + in_uint16_be(s, bufsize); + bufsize &= BUFSIZE_MASK; + in_uint8(s, cache_idx); + + if (cache_idx & LONG_FORMAT) + { + in_uint8(s, cache_idx_low); + cache_idx = ((cache_idx ^ LONG_FORMAT) << 8) + cache_idx_low; + } + + in_uint8p(s, data, bufsize); + + DEBUG(("BMPCACHE2(compr=%d,flags=%x,cx=%d,cy=%d,id=%d,idx=%d,Bpp=%d,bs=%d)\n", + compressed, flags, width, height, cache_id, cache_idx, Bpp, bufsize)); + + bmpdata = (uint8 *) xmalloc(width * height * Bpp); + + if (compressed) + { + if (!bitmap_decompress(bmpdata, width, height, data, bufsize, Bpp)) + { + DEBUG(("Failed to decompress bitmap data\n")); + xfree(bmpdata); + return; + } + } + else + { + for (y = 0; y < height; y++) + memcpy(&bmpdata[(height - y - 1) * (width * Bpp)], + &data[y * (width * Bpp)], width * Bpp); + } + + bitmap = ui_create_bitmap(width, height, bmpdata); + + if (bitmap) + { + cache_put_bitmap(cache_id, cache_idx, bitmap, 0); + if (flags & PERSIST) + pstcache_put_bitmap(cache_id, cache_idx, bitmap_id, width, height, + width * height * Bpp, bmpdata); + } + else + { + DEBUG(("process_bmpcache2: ui_create_bitmap failed\n")); + } + + xfree(bmpdata); +} + /* Process a colourmap cache order */ static void process_colcache(STREAM s) @@ -796,12 +876,16 @@ process_fontcache(STREAM s) static void process_secondary_order(STREAM s) { - uint16 length; + /* The length isn't calculated correctly by the server. + * For very compact orders the length becomes negative + * so a signed integer must be used. */ + sint16 length; + uint16 flags; uint8 type; uint8 *next_order; - in_uint16_le(s, length); - in_uint8s(s, 2); /* flags */ + in_uint16_le(s, (uint16) length); + in_uint16_le(s, flags); /* used by bmpcache2 */ in_uint8(s, type); next_order = s->p + length + 7; @@ -824,6 +908,14 @@ process_secondary_order(STREAM s) process_fontcache(s); break; + case RDP_ORDER_RAW_BMPCACHE2: + process_bmpcache2(s, flags, False); /* uncompressed */ + break; + + case RDP_ORDER_BMPCACHE2: + process_bmpcache2(s, flags, True); /* compressed */ + break; + default: unimpl("secondary order %d\n", type); } diff --git a/orders.h b/orders.h index 3d1f8e6..e28e537 100644 --- a/orders.h +++ b/orders.h @@ -46,7 +46,9 @@ enum RDP_SECONDARY_ORDER_TYPE RDP_ORDER_RAW_BMPCACHE = 0, RDP_ORDER_COLCACHE = 1, RDP_ORDER_BMPCACHE = 2, - RDP_ORDER_FONTCACHE = 3 + RDP_ORDER_FONTCACHE = 3, + RDP_ORDER_RAW_BMPCACHE2 = 4, + RDP_ORDER_BMPCACHE2 = 5 }; typedef struct _DESTBLT_ORDER @@ -252,6 +254,18 @@ typedef struct _RDP_BMPCACHE_ORDER } RDP_BMPCACHE_ORDER; +/* RDP_BMPCACHE2_ORDER */ +#define ID_MASK 0x0007 +#define MODE_MASK 0x0038 +#define SQUARE 0x0080 +#define PERSIST 0x0100 +#define FLAG_51_UNKNOWN 0x0800 + +#define MODE_SHIFT 3 + +#define LONG_FORMAT 0x80 +#define BUFSIZE_MASK 0x3FFF /* or 0x1FFF? */ + #define MAX_GLYPH 32 typedef struct _RDP_FONT_GLYPH diff --git a/proto.h b/proto.h index 2b44a19..008a180 100644 --- a/proto.h +++ b/proto.h @@ -2,7 +2,8 @@ BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp); /* cache.c */ HBITMAP cache_get_bitmap(uint8 cache_id, uint16 cache_idx); -void cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap); +void cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap, uint32 stamp); +void cache_save_state(void); FONTGLYPH *cache_get_font(uint8 font, uint16 character); void cache_put_font(uint8 font, uint16 character, uint16 offset, uint16 baseline, uint16 width, uint16 height, HGLYPH pixmap); @@ -60,6 +61,13 @@ int printer_enum_devices(uint32 * id, char *optarg); /* printercache.c */ int printercache_load_blob(char *printer_name, uint8 ** data); void printercache_process(STREAM s); +/* pstcache.c */ +void pstcache_touch_bitmap(uint8 id, uint16 idx, uint32 stamp); +BOOL pstcache_load_bitmap(uint8 id, uint16 idx); +BOOL pstcache_put_bitmap(uint8 id, uint16 idx, uint8 *bmp_id, uint16 wd, + uint16 ht, uint16 len, uint8 *data); +int pstcache_enumerate(uint8 id, uint8 *list); +BOOL pstcache_init(uint8 id); /* rdesktop.c */ int main(int argc, char *argv[]); void generate_random(uint8 * random); @@ -75,6 +83,13 @@ void toupper_str(char *p); char *l_to_a(long N, int base); int load_licence(unsigned char **data); void save_licence(unsigned char *data, int length); +BOOL rd_pstcache_mkdir(void); +int rd_open_file(char *filename); +void rd_close_file(int fd); +int rd_read_file(int fd, void *ptr, int len); +int rd_write_file(int fd, void* ptr, int len); +int rd_lseek_file(int fd, int offset); +BOOL rd_lock_file(int fd, int start, int len); /* rdp5.c */ void rdp5_process(STREAM s, BOOL encryption); /* rdp.c */ diff --git a/pstcache.c b/pstcache.c new file mode 100644 index 0000000..345ddc6 --- /dev/null +++ b/pstcache.c @@ -0,0 +1,194 @@ +/* + rdesktop: A Remote Desktop Protocol client. + Persistent Bitmap Cache routines + Copyright (C) Jeroen Meijer 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "rdesktop.h" + +#define MAX_CELL_SIZE 0x1000 /* pixels */ + +#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0) + +extern int g_server_bpp; +extern uint32 g_stamp; +extern BOOL g_bitmap_cache; +extern BOOL g_bitmap_cache_persist_enable; +extern BOOL g_bitmap_cache_precache; + +int g_pstcache_fd[8]; +int g_pstcache_Bpp; +BOOL g_pstcache_enumerated = False; +uint8 zero_id[] = {0, 0, 0, 0, 0, 0, 0, 0}; + + +/* Update usage info for a bitmap */ +void +pstcache_touch_bitmap(uint8 cache_id, uint16 cache_idx, uint32 stamp) +{ + int fd; + + if (!IS_PERSISTENT(cache_id)) + return; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, 12 + cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_write_file(fd, &stamp, sizeof(stamp)); +} + +/* Load a bitmap from the persistent cache */ +BOOL +pstcache_load_bitmap(uint8 cache_id, uint16 cache_idx) +{ + uint8 *celldata; + int fd; + CELLHEADER cellhdr; + HBITMAP bitmap; + + if (!(g_bitmap_cache_persist_enable && IS_PERSISTENT(cache_id))) + return False; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)); + celldata = (uint8 *)xmalloc(cellhdr.length); + rd_read_file(fd, celldata, cellhdr.length); + + DEBUG(("Loading bitmap from disk (%d:%d)\n", cache_id, cache_idx)); + + bitmap = ui_create_bitmap(cellhdr.width, cellhdr.height, celldata); + cache_put_bitmap(cache_id, cache_idx, bitmap, cellhdr.stamp); + + xfree(celldata); + return True; +} + +/* Store a bitmap in the persistent cache */ +BOOL +pstcache_put_bitmap(uint8 cache_id, uint16 cache_idx, uint8 *bitmap_id, + uint16 width, uint16 height, uint16 length, uint8 *data) +{ + int fd; + CELLHEADER cellhdr; + + if (!IS_PERSISTENT(cache_id)) + return False; + + memcpy(cellhdr.bitmap_id, bitmap_id, sizeof(BITMAP_ID)); + cellhdr.width = width; + cellhdr.height = height; + cellhdr.length = length; + cellhdr.stamp = 0; + + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, cache_idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + rd_write_file(fd, &cellhdr, sizeof(CELLHEADER)); + rd_write_file(fd, data, length); + + return True; +} + +/* list the bitmaps from the persistent cache file */ +int +pstcache_enumerate(uint8 cache_id, uint8 *idlist) +{ + int fd, n, c = 0; + CELLHEADER cellhdr; + + if (!(g_bitmap_cache && g_bitmap_cache_persist_enable && IS_PERSISTENT(cache_id))) + return 0; + + /* The server disconnects if the bitmap cache content is sent more than once */ + if (g_pstcache_enumerated) + return 0; + + DEBUG(("pstcache enumeration... ")); + for (n = 0; n < BMPCACHE2_NUM_PSTCELLS; n++) + { + fd = g_pstcache_fd[cache_id]; + rd_lseek_file(fd, n * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + if (rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)) <= 0) + break; + + if (memcmp(cellhdr.bitmap_id, zero_id, sizeof(BITMAP_ID)) != 0) + { + memcpy(idlist + n * sizeof(BITMAP_ID), cellhdr.bitmap_id, + sizeof(BITMAP_ID)); + + if (cellhdr.stamp) + { + /* Pre-caching is not possible with 8bpp because a colourmap + * is needed to load them */ + if (g_bitmap_cache_precache && (g_server_bpp > 8)) + { + if (pstcache_load_bitmap(cache_id, n)) + c++; + } + + g_stamp = MAX(g_stamp, cellhdr.stamp); + } + } + else + { + break; + } + } + + DEBUG(("%d bitmaps in persistent cache, %d bitmaps loaded in memory\n", n, c)); + g_pstcache_enumerated = True; + return n; +} + +/* initialise the persistent bitmap cache */ +BOOL +pstcache_init(uint8 cache_id) +{ + int fd; + char filename[256]; + + if (g_pstcache_enumerated) + return True; + + g_pstcache_fd[cache_id] = 0; + + if (!(g_bitmap_cache && g_bitmap_cache_persist_enable)) + return False; + + if (!rd_pstcache_mkdir()) + { + DEBUG(("failed to get/make cache directory!\n")); + return False; + } + + g_pstcache_Bpp = (g_server_bpp + 7) / 8; + sprintf(filename, "cache/pstcache_%d_%d", cache_id, g_pstcache_Bpp); + DEBUG(("persistent bitmap cache file: %s\n", filename)); + + fd = rd_open_file(filename); + if (fd == -1) + return False; + + if (!rd_lock_file(fd, 0, 0)) + { + warning("Persistent bitmap caching is disabled. (The file is already in use)\n"); + rd_close_file(fd); + return False; + } + + g_pstcache_fd[cache_id] = fd; + return True; +} diff --git a/rdesktop.c b/rdesktop.c index 9869e49..fac965d 100644 --- a/rdesktop.c +++ b/rdesktop.c @@ -59,6 +59,8 @@ int g_win_button_size = 0; /* If zero, disable single app mode */ BOOL g_bitmap_compression = True; BOOL g_sendmotion = True; BOOL g_bitmap_cache = True; +BOOL g_bitmap_cache_persist_enable = False; +BOOL g_bitmap_cache_precache = True; BOOL g_encryption = True; BOOL packet_encryption = True; BOOL g_desktop_save = True; @@ -124,8 +126,8 @@ usage(char *program) fprintf(stderr, " -N: enable numlock syncronization\n"); fprintf(stderr, " -X: embed into another window with a given id.\n"); fprintf(stderr, " -a: connection colour depth\n"); - fprintf(stderr, - " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex number)\n"); + fprintf(stderr, " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex nr.)\n"); + fprintf(stderr, " -P: use persistent bitmap caching\n"); fprintf(stderr, " -r: enable specified device redirection (this flag can be repeated)\n"); fprintf(stderr, " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n"); @@ -364,7 +366,8 @@ main(int argc, char *argv[]) #define VNCOPT #endif - while ((c = getopt(argc, argv, VNCOPT "u:d:s:c:p:n:k:g:fbBeEmCDKS:T:NX:a:x:r:045h?")) != -1) + while ((c = getopt(argc, argv, + VNCOPT "u:d:s:c:p:n:k:g:fbBeEmCDKS:T:NX:a:x:Pr:045h?")) != -1) { switch (c) { @@ -547,6 +550,10 @@ main(int argc, char *argv[]) } break; + case 'P': + g_bitmap_cache_persist_enable = True; + break; + case 'r': if (strncmp("sound", optarg, 5) == 0) @@ -713,6 +720,7 @@ main(int argc, char *argv[]) } DEBUG(("Disconnecting...\n")); + cache_save_state(); rdp_disconnect(); ui_deinit(); @@ -1121,3 +1129,95 @@ save_licence(unsigned char *data, int length) xfree(tmppath); xfree(path); } + +/* Create the bitmap cache directory */ +BOOL +rd_pstcache_mkdir(void) +{ + char *home; + char bmpcache_dir[256]; + + home = getenv("HOME"); + + if (home == NULL) + return False; + + sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop"); + + if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST) + { + perror(bmpcache_dir); + return False; + } + + sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop/cache"); + + if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST) + { + perror(bmpcache_dir); + return False; + } + + return True; +} + +/* open a file in the .rdesktop directory */ +int +rd_open_file(char *filename) +{ + char *home; + char fn[256]; + int fd; + + home = getenv("HOME"); + if (home == NULL) + return -1; + sprintf(fn, "%s/.rdesktop/%s", home, filename); + fd = open(fn, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) + perror(fn); + return fd; +} + +/* close file */ +void +rd_close_file(int fd) +{ + close(fd); +} + +/* read from file*/ +int +rd_read_file(int fd, void *ptr, int len) +{ + return read(fd, ptr, len); +} + +/* write to file */ +int +rd_write_file(int fd, void* ptr, int len) +{ + return write(fd, ptr, len); +} + +/* move file pointer */ +int +rd_lseek_file(int fd, int offset) +{ + return lseek(fd, offset, SEEK_SET); +} + +/* do a write lock on a file */ +BOOL +rd_lock_file(int fd, int start, int len) +{ + struct flock lock; + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = start; + lock.l_len = len; + if (fcntl(fd, F_SETLK, &lock) == -1) + return False; + return True; +} diff --git a/rdp.c b/rdp.c index 05c5d97..7ff3cad 100644 --- a/rdp.c +++ b/rdp.c @@ -34,6 +34,7 @@ extern int g_server_bpp; extern int g_width; extern int g_height; extern BOOL g_bitmap_cache; +extern BOOL g_bitmap_cache_persist_enable; uint8 *g_next_packet; uint32 g_rdp_shareid; @@ -366,6 +367,51 @@ rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 par rdp_send_data(s, RDP_DATA_PDU_INPUT); } +/* Inform the server on the contents of the persistent bitmap cache */ +static void +rdp_enum_bmpcache2(void) +{ + STREAM s; + uint8 idlist[BMPCACHE2_NUM_PSTCELLS * sizeof(BITMAP_ID)]; + uint32 nids, offset, count, flags; + + offset = 0; + nids = pstcache_enumerate(2, idlist); + + while (offset < nids) + { + count = MIN(nids - offset, 169); + + s = rdp_init_data(24 + count * sizeof(BITMAP_ID)); + + flags = 0; + if (offset == 0) + flags |= PDU_FLAG_FIRST; + if (nids - offset <= 169) + flags |= PDU_FLAG_LAST; + + /* header */ + out_uint32_le(s, 0); + out_uint16_le(s, count); + out_uint16_le(s, 0); + out_uint16_le(s, 0); + out_uint16_le(s, 0); + out_uint16_le(s, 0); + out_uint16_le(s, nids); + out_uint32_le(s, 0); + out_uint32_le(s, flags); + + /* list */ + out_uint8a(s, idlist + offset * sizeof(BITMAP_ID), + count * sizeof(BITMAP_ID)); + + s_mark_end(s); + rdp_send_data(s, 0x2b); + + offset += 169; + } +} + /* Send an (empty) font information PDU */ static void rdp_send_fonts(uint16 seq) @@ -486,6 +532,30 @@ rdp_out_bmpcache_caps(STREAM s) out_uint16_le(s, 0x1000 * Bpp); /* max cell size */ } +/* Output bitmap cache v2 capability set */ +static void +rdp_out_bmpcache2_caps(STREAM s) +{ + out_uint16_le(s, RDP_CAPSET_BMPCACHE2); + out_uint16_le(s, RDP_CAPLEN_BMPCACHE2); + + out_uint16_le(s, g_bitmap_cache_persist_enable ? 1 : 0); /* version */ + + out_uint16_le(s, 0x0300); /* flags? number of caches? */ + + out_uint32_le(s, BMPCACHE2_C0_CELLS); + out_uint32_le(s, BMPCACHE2_C1_CELLS); + if (pstcache_init(2)) + { + out_uint32_le(s, BMPCACHE2_NUM_PSTCELLS | BMPCACHE2_FLAG_PERSIST); + } + else + { + out_uint32_le(s, BMPCACHE2_C2_CELLS); + } + out_uint8s(s, 20); /* other bitmap caches not used */ +} + /* Output control capability set */ static void rdp_out_control_caps(STREAM s) @@ -545,36 +615,41 @@ rdp_out_colcache_caps(STREAM s) out_uint16(s, 0); /* pad */ } -static uint8 canned_caps[] = { - 0x01, 0x00, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x0E, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x34, 0x00, 0xFE, - 0x00, 0x04, 0x00, 0xFE, 0x00, 0x04, 0x00, 0xFE, 0x00, 0x08, 0x00, - 0xFE, 0x00, 0x08, 0x00, 0xFE, - 0x00, 0x10, 0x00, 0xFE, 0x00, 0x20, 0x00, 0xFE, 0x00, 0x40, 0x00, - 0xFE, 0x00, 0x80, 0x00, 0xFE, - 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, - 0x02, 0x00, 0x00, 0x00 +static uint8 caps_0x0d[] = { + 0x01, 0x00, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; -/* Output unknown capability sets (number 13, 12, 14 and 16) */ -static void -rdp_out_unknown_caps(STREAM s) -{ - out_uint16_le(s, RDP_CAPSET_UNKNOWN); - out_uint16_le(s, 0x58); +static uint8 caps_0x0c[] = { 0x01, 0x00, 0x00, 0x00 }; - out_uint8p(s, canned_caps, RDP_CAPLEN_UNKNOWN - 4); +static uint8 caps_0x0e[] = { 0x01, 0x00, 0x00, 0x00 }; + +static uint8 caps_0x10[] = { + 0xFE, 0x00, 0x04, 0x00, 0xFE, 0x00, 0x04, 0x00, + 0xFE, 0x00, 0x08, 0x00, 0xFE, 0x00, 0x08, 0x00, + 0xFE, 0x00, 0x10, 0x00, 0xFE, 0x00, 0x20, 0x00, + 0xFE, 0x00, 0x40, 0x00, 0xFE, 0x00, 0x80, 0x00, + 0xFE, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x08, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00 +}; + +/* Output unknown capability sets */ +static void +rdp_out_unknown_caps(STREAM s, uint16 id, uint16 length, uint8 *caps) +{ + out_uint16_le(s, id); + out_uint16_le(s, length); + + out_uint8p(s, caps, length - 4); } #define RDP5_FLAG 0x0030 @@ -588,7 +663,9 @@ rdp_send_confirm_active(void) RDP_CAPLEN_GENERAL + RDP_CAPLEN_BITMAP + RDP_CAPLEN_ORDER + RDP_CAPLEN_BMPCACHE + RDP_CAPLEN_COLCACHE + RDP_CAPLEN_ACTIVATE + RDP_CAPLEN_CONTROL + - RDP_CAPLEN_POINTER + RDP_CAPLEN_SHARE + RDP_CAPLEN_UNKNOWN + 4 /* w2k fix, why? */ ; + RDP_CAPLEN_POINTER + RDP_CAPLEN_SHARE + + 0x58 + 0x08 + 0x08 + 0x34 /* unknown caps */ + + 4 /* w2k fix, why? */ ; s = sec_init(sec_flags, 6 + 14 + caplen + sizeof(RDP_SOURCE)); @@ -608,14 +685,18 @@ rdp_send_confirm_active(void) rdp_out_general_caps(s); rdp_out_bitmap_caps(s); rdp_out_order_caps(s); - rdp_out_bmpcache_caps(s); + g_use_rdp5 ? rdp_out_bmpcache2_caps(s) : rdp_out_bmpcache_caps(s); rdp_out_colcache_caps(s); rdp_out_activate_caps(s); rdp_out_control_caps(s); rdp_out_pointer_caps(s); rdp_out_share_caps(s); - rdp_out_unknown_caps(s); + rdp_out_unknown_caps(s, 0x0d, 0x58, caps_0x0d); /* international? */ + rdp_out_unknown_caps(s, 0x0c, 0x08, caps_0x0c); + rdp_out_unknown_caps(s, 0x0e, 0x08, caps_0x0e); + rdp_out_unknown_caps(s, 0x10, 0x34, caps_0x10); /* glyph cache? */ + s_mark_end(s); sec_send(s, sec_flags); } @@ -659,33 +740,31 @@ rdp_process_bitmap_caps(STREAM s) if (g_width != width || g_height != height) { warning("screen size changed from %dx%d to %dx%d\n", g_width, g_height, - width, height); + width, height); g_width = width; g_height = height; ui_resize_window(); } } -/* Respond to a demand active PDU */ -static void -process_demand_active(STREAM s) +/* Process server capabilities */ +void +rdp_process_server_caps(STREAM s, uint16 length) { int n; - uint8 type, *next; - uint16 len_src_descriptor, len_combined_caps, num_capsets, capset_type, capset_length; + uint8 *next, *start; + uint16 ncapsets, capset_type, capset_length; - in_uint32_le(s, g_rdp_shareid); - in_uint16_le(s, len_src_descriptor); - in_uint16_le(s, len_combined_caps); - in_uint8s(s, len_src_descriptor); + start = s->p; - in_uint16_le(s, num_capsets); + in_uint16_le(s, ncapsets); in_uint8s(s, 2); /* pad */ - DEBUG(("DEMAND_ACTIVE(id=0x%x,num_caps=%d)\n", g_rdp_shareid, num_capsets)); - - for (n = 0; n < num_capsets; n++) + for (n = 0; n < ncapsets; n++) { + if (s->p > start + length) + return; + in_uint16_le(s, capset_type); in_uint16_le(s, capset_length); @@ -704,6 +783,22 @@ process_demand_active(STREAM s) s->p = next; } +} + +/* Respond to a demand active PDU */ +static void +process_demand_active(STREAM s) +{ + uint8 type; + uint16 len_src_descriptor, len_combined_caps; + + in_uint32_le(s, g_rdp_shareid); + in_uint16_le(s, len_src_descriptor); + in_uint16_le(s, len_combined_caps); + in_uint8s(s, len_src_descriptor); + + DEBUG(("DEMAND_ACTIVE(id=0x%x)\n", g_rdp_shareid)); + rdp_process_server_caps(s, len_combined_caps); rdp_send_confirm_active(); rdp_send_synchronise(); @@ -716,6 +811,7 @@ process_demand_active(STREAM s) if (g_use_rdp5) { + rdp_enum_bmpcache2(); rdp_send_fonts(3); } else diff --git a/types.h b/types.h index abc79db..c63dc64 100644 --- a/types.h +++ b/types.h @@ -119,6 +119,28 @@ typedef struct _VCHANNEL } VCHANNEL; +typedef struct _BMPCACHEENTRY +{ + HBITMAP bitmap; + uint32 usage; + +} +BMPCACHEENTRY; + +/* PSTCACHE */ + +typedef uint8 BITMAP_ID[8]; + +/* Header for an entry in the persistent bitmap cache file */ +typedef struct _PSTCACHE_CELLHEADER +{ + BITMAP_ID bitmap_id; + uint8 width, height; + uint16 length; + uint32 stamp; +} +CELLHEADER; + #define MAX_CBSIZE 256 /* RDPSND */ diff --git a/xwin.c b/xwin.c index b364599..990c1cd 100644 --- a/xwin.c +++ b/xwin.c @@ -47,6 +47,8 @@ extern uint32 g_embed_wnd; BOOL g_enable_compose = False; BOOL g_Unobscured; /* used for screenblt */ static GC g_gc = NULL; +static GC g_create_bitmap_gc = NULL; +static GC g_create_glyph_gc = NULL; static Visual *g_visual; static int g_depth; static int g_bpp; @@ -969,6 +971,9 @@ ui_create_window(void) if (g_gc == NULL) g_gc = XCreateGC(g_display, g_wnd, 0, NULL); + if (g_create_bitmap_gc == NULL) + g_create_bitmap_gc = XCreateGC(g_display, g_wnd, 0, NULL); + if ((g_ownbackstore) && (g_backstore == 0)) { g_backstore = XCreatePixmap(g_display, g_wnd, g_width, g_height, g_depth); @@ -1481,7 +1486,7 @@ ui_create_bitmap(int width, int height, uint8 * data) image = XCreateImage(g_display, g_visual, g_depth, ZPixmap, 0, (char *) tdata, width, height, bitmap_pad, 0); - XPutImage(g_display, bitmap, g_gc, image, 0, 0, 0, 0, width, height); + XPutImage(g_display, bitmap, g_create_bitmap_gc, image, 0, 0, 0, 0, width, height); XFree(image); if (tdata != data) @@ -1539,12 +1544,12 @@ ui_create_glyph(int width, int height, uint8 * data) XImage *image; Pixmap bitmap; int scanline; - GC gc; scanline = (width + 7) / 8; bitmap = XCreatePixmap(g_display, g_wnd, width, height, 1); - gc = XCreateGC(g_display, bitmap, 0, NULL); + if (g_create_glyph_gc == 0) + g_create_glyph_gc = XCreateGC(g_display, bitmap, 0, NULL); image = XCreateImage(g_display, g_visual, 1, ZPixmap, 0, (char *) data, width, height, 8, scanline); @@ -1552,10 +1557,9 @@ ui_create_glyph(int width, int height, uint8 * data) image->bitmap_bit_order = MSBFirst; XInitImage(image); - XPutImage(g_display, bitmap, gc, image, 0, 0, 0, 0, width, height); + XPutImage(g_display, bitmap, g_create_glyph_gc, image, 0, 0, 0, 0, width, height); XFree(image); - XFreeGC(g_display, gc); return (HGLYPH) bitmap; }