From: Ian Jackson Date: Thu, 1 May 2008 09:59:32 +0000 (+0100) Subject: Copy across vnc* from xen-unstable 17318:b5fea3aeb04b; not yet edited to merge X-Git-Tag: xen-3.3.0-rc1~220 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=aee38c794011462038b43a59758a361c5021c074;p=qemu-xen-3.3-testing.git Copy across vnc* from xen-unstable 17318:b5fea3aeb04b; not yet edited to merge --- diff --git a/vnc.c b/vnc.c index ecb23c93..6cfba55b 100644 --- a/vnc.c +++ b/vnc.c @@ -1,9 +1,10 @@ /* * QEMU VNC display driver - * + * * Copyright (C) 2006 Anthony Liguori * Copyright (C) 2006 Fabrice Bellard - * + * Copyright (C) 2006 Christian Limpach + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -23,13 +24,29 @@ * THE SOFTWARE. */ -#include "qemu-common.h" -#include "console.h" -#include "sysemu.h" +#include +#include +#include +#include "vl.h" #include "qemu_socket.h" -#include "qemu-timer.h" +#include +#ifdef CONFIG_STUBDOM +#include +#endif -#define VNC_REFRESH_INTERVAL (1000 / 30) +/* The refresh interval starts at BASE. If we scan the buffer and + find no change, we increase by INC, up to MAX. If the mouse moves + or we get a keypress, the interval is set back to BASE. If we find + an update, halve the interval. + + All times in milliseconds. */ +#define VNC_REFRESH_INTERVAL_BASE 30 +#define VNC_REFRESH_INTERVAL_INC 50 +#define VNC_REFRESH_INTERVAL_MAX 2000 + +/* Wait at most one second between updates, so that we can detect a + minimised vncviewer reasonably quickly. */ +#define VNC_MAX_UPDATE_INTERVAL 5000 #include "vnc_keysym.h" #include "keymaps.c" @@ -71,13 +88,15 @@ typedef void VncWritePixels(VncState *vs, void *data, int size); typedef void VncSendHextileTile(VncState *vs, int x, int y, int w, int h, - uint32_t *last_bg, - uint32_t *last_fg, + void *last_bg, + void *last_fg, int *has_bg, int *has_fg); +#if 0 #define VNC_MAX_WIDTH 2048 #define VNC_MAX_HEIGHT 2048 #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) +#endif #define VNC_AUTH_CHALLENGE_SIZE 16 @@ -118,21 +137,47 @@ enum { #endif /* CONFIG_VNC_TLS */ +#define QUEUE_ALLOC_UNIT 10 + +typedef struct _QueueItem +{ + int x, y, w, h; + int32_t enc; + struct _QueueItem *next; +} QueueItem; + +typedef struct _Queue +{ + QueueItem *queue_start; + int start_count; + QueueItem *queue_end; + int end_count; +} Queue; + struct VncState { QEMUTimer *timer; + int timer_interval; + int64_t last_update_time; int lsock; int csock; DisplayState *ds; - int need_update; int width; int height; - uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; - char *old_data; + uint64_t *dirty_row; /* screen regions which are possibly dirty */ + int dirty_pixel_shift; + uint64_t *update_row; /* outstanding updates */ + int has_update; /* there's outstanding updates in the + * visible area */ + + int update_requested; /* the client requested an update */ + + uint8_t *old_data; int depth; /* internal VNC frame buffer byte per pixel */ int has_resize; int has_hextile; int has_pointer_type_change; + int has_WMVi; int absolute; int last_x; int last_y; @@ -161,23 +206,38 @@ struct VncState Buffer output; Buffer input; + + Queue upqueue; + kbd_layout_t *kbd_layout; /* current output mode information */ VncWritePixels *write_pixels; VncSendHextileTile *send_hextile_tile; int pix_bpp, pix_big_endian; - int red_shift, red_max, red_shift1; - int green_shift, green_max, green_shift1; - int blue_shift, blue_max, blue_shift1; + int red_shift, red_max, red_shift1, red_max1; + int green_shift, green_max, green_shift1, green_max1; + int blue_shift, blue_max, blue_shift1, blue_max1; VncReadEvent *read_handler; size_t read_handler_expect; + + int visible_x; + int visible_y; + int visible_w; + int visible_h; + /* input */ uint8_t modifiers_state[256]; }; static VncState *vnc_state; /* needed for info vnc */ +#define DIRTY_PIXEL_BITS 64 +#define X2DP_DOWN(vs, x) ((x) >> (vs)->dirty_pixel_shift) +#define X2DP_UP(vs, x) \ + (((x) + (1ULL << (vs)->dirty_pixel_shift) - 1) >> (vs)->dirty_pixel_shift) +#define DP2X(vs, x) ((x) << (vs)->dirty_pixel_shift) + void do_info_vnc(void) { if (vnc_state == NULL) @@ -207,9 +267,17 @@ static void vnc_write_s32(VncState *vs, int32_t value); static void vnc_write_u16(VncState *vs, uint16_t value); static void vnc_write_u8(VncState *vs, uint8_t value); static void vnc_flush(VncState *vs); +static void _vnc_update_client(void *opaque); static void vnc_update_client(void *opaque); static void vnc_client_read(void *opaque); - +static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h); +static void pixel_format_message (VncState *vs); +static void enqueue_framebuffer_update(VncState *vs, int x, int y, int w, int h, int32_t encoding); +static void dequeue_framebuffer_update(VncState *vs); +static int is_empty_queue(VncState *vs); +static void free_queue(VncState *vs); + +#if 0 static inline void vnc_set_bit(uint32_t *d, int k) { d[k >> 5] |= 1 << (k & 0x1f); @@ -229,7 +297,7 @@ static inline void vnc_set_bits(uint32_t *d, int n, int nb_words) d[j++] = -1; n -= 32; } - if (n > 0) + if (n > 0) d[j++] = (1 << n) - 1; while (j < nb_words) d[j++] = 0; @@ -240,7 +308,7 @@ static inline int vnc_get_bit(const uint32_t *d, int k) return (d[k >> 5] >> (k & 0x1f)) & 1; } -static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, +static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, int nb_words) { int i; @@ -250,24 +318,37 @@ static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, } return 0; } +#endif -static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +static void set_bits_in_row(VncState *vs, uint64_t *row, + int x, int y, int w, int h) { - VncState *vs = ds->opaque; - int i; + int x1, x2; + uint64_t mask; - h += y; + if (w == 0) + return; + + x1 = X2DP_DOWN(vs, x); + x2 = X2DP_UP(vs, x + w); - /* round x down to ensure the loop only spans one 16-pixel block per, - iteration. otherwise, if (x % 16) != 0, the last iteration may span - two 16-pixel blocks but we only mark the first as dirty - */ - w += (x % 16); - x -= (x % 16); + if (X2DP_UP(vs, w) != DIRTY_PIXEL_BITS) + mask = ((1ULL << (x2 - x1)) - 1) << x1; + else + mask = ~(0ULL); + h += y; + if (h > vs->ds->height) + h = vs->ds->height; for (; y < h; y++) - for (i = 0; i < w; i += 16) - vnc_set_bit(vs->dirty_row[y], (x + i) / 16); + row[y] |= mask; +} + +static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +{ + VncState *vs = ds->opaque; + + set_bits_in_row(vs, vs->dirty_row, x, y, w, h); } static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, @@ -281,36 +362,62 @@ static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, vnc_write_s32(vs, encoding); } -static void vnc_dpy_resize(DisplayState *ds, int w, int h) +static void vnc_dpy_resize(DisplayState *ds, int w, int h, int linesize) { + static int allocated; int size_changed; VncState *vs = ds->opaque; + int o; - ds->data = realloc(ds->data, w * h * vs->depth); - vs->old_data = realloc(vs->old_data, w * h * vs->depth); + if (!ds->shared_buf) { + ds->linesize = w * vs->depth; + if (allocated) + ds->data = realloc(ds->data, h * ds->linesize); + else + ds->data = malloc(h * ds->linesize); + allocated = 1; + } else { + ds->linesize = linesize; + if (allocated) { + free(ds->data); + allocated = 0; + } + } + vs->old_data = realloc(vs->old_data, h * ds->linesize); + vs->dirty_row = realloc(vs->dirty_row, h * sizeof(vs->dirty_row[0])); + vs->update_row = realloc(vs->update_row, h * sizeof(vs->dirty_row[0])); - if (ds->data == NULL || vs->old_data == NULL) { + if (ds->data == NULL || vs->old_data == NULL || + vs->dirty_row == NULL || vs->update_row == NULL) { fprintf(stderr, "vnc: memory allocation failed\n"); exit(1); } if (ds->depth != vs->depth * 8) { ds->depth = vs->depth * 8; - console_color_init(ds); + set_color_table(ds); } size_changed = ds->width != w || ds->height != h; ds->width = w; ds->height = h; - ds->linesize = w * vs->depth; if (vs->csock != -1 && vs->has_resize && size_changed) { - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223); - vnc_flush(vs); - vs->width = ds->width; - vs->height = ds->height; + vs->width = ds->width; + vs->height = ds->height; + if (vs->update_requested) { + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223); + vnc_flush(vs); + vs->update_requested--; + } else { + enqueue_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223); + } } + vs->dirty_pixel_shift = 0; + for (o = DIRTY_PIXEL_BITS; o < ds->width; o *= 2) + vs->dirty_pixel_shift++; + framebuffer_set_updated(vs, 0, 0, ds->width, ds->height); } /* fastest code */ @@ -322,54 +429,67 @@ static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) /* slowest but generic code. */ static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) { - unsigned int r, g, b; + uint8_t r, g, b; - r = (v >> vs->red_shift1) & vs->red_max; - g = (v >> vs->green_shift1) & vs->green_max; - b = (v >> vs->blue_shift1) & vs->blue_max; - v = (r << vs->red_shift) | - (g << vs->green_shift) | - (b << vs->blue_shift); + r = ((v >> vs->red_shift1) & vs->red_max1) * (vs->red_max + 1) / (vs->red_max1 + 1); + g = ((v >> vs->green_shift1) & vs->green_max1) * (vs->green_max + 1) / (vs->green_max1 + 1); + b = ((v >> vs->blue_shift1) & vs->blue_max1) * (vs->blue_max + 1) / (vs->blue_max1 + 1); switch(vs->pix_bpp) { case 1: - buf[0] = v; + buf[0] = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift); break; case 2: + { + uint16_t *p = (uint16_t *) buf; + *p = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift); if (vs->pix_big_endian) { - buf[0] = v >> 8; - buf[1] = v; - } else { - buf[1] = v >> 8; - buf[0] = v; + *p = htons(*p); } + } break; default: case 4: + { + uint32_t *p = (uint32_t *) buf; + *p = (r << vs->red_shift) | (g << vs->green_shift) | (b << vs->blue_shift); if (vs->pix_big_endian) { - buf[0] = v >> 24; - buf[1] = v >> 16; - buf[2] = v >> 8; - buf[3] = v; - } else { - buf[3] = v >> 24; - buf[2] = v >> 16; - buf[1] = v >> 8; - buf[0] = v; + *p = htonl(*p); } break; } + } } static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) { - uint32_t *pixels = pixels1; uint8_t buf[4]; - int n, i; - n = size >> 2; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->pix_bpp); + if (vs->depth == 4) { + uint32_t *pixels = pixels1; + int n, i; + n = size >> 2; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->pix_bpp); + } + } else if (vs->depth == 2) { + uint16_t *pixels = pixels1; + int n, i; + n = size >> 1; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->pix_bpp); + } + } else if (vs->depth == 1) { + uint8_t *pixels = pixels1; + int n, i; + n = size; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->pix_bpp); + } + } else { + fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n"); } } @@ -405,6 +525,18 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) #include "vnchextile.h" #undef BPP +#define GENERIC +#define BPP 8 +#include "vnchextile.h" +#undef BPP +#undef GENERIC + +#define GENERIC +#define BPP 16 +#include "vnchextile.h" +#undef BPP +#undef GENERIC + #define GENERIC #define BPP 32 #include "vnchextile.h" @@ -415,18 +547,22 @@ static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, i { int i, j; int has_fg, has_bg; - uint32_t last_fg32, last_bg32; + void *last_fg, *last_bg; vnc_framebuffer_update(vs, x, y, w, h, 5); + last_fg = (void *) malloc(vs->depth); + last_bg = (void *) malloc(vs->depth); has_fg = has_bg = 0; for (j = y; j < (y + h); j += 16) { for (i = x; i < (x + w); i += 16) { - vs->send_hextile_tile(vs, i, j, + vs->send_hextile_tile(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), - &last_bg32, &last_fg32, &has_bg, &has_fg); + last_bg, last_fg, &has_bg, &has_fg); } } + free(last_fg); + free(last_bg); } static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) @@ -442,12 +578,28 @@ static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_ int src, dst; uint8_t *src_row; uint8_t *dst_row; - char *old_row; + uint8_t *old_row; int y = 0; int pitch = ds->linesize; VncState *vs = ds->opaque; + int updating_client = 1; + + if (ds->shared_buf) { + framebuffer_set_updated(vs, dst_x, dst_y, w, h); + return; + } + + if (!vs->update_requested || + src_x < vs->visible_x || src_y < vs->visible_y || + dst_x < vs->visible_x || dst_y < vs->visible_y || + (src_x + w) > (vs->visible_x + vs->visible_w) || + (src_y + h) > (vs->visible_y + vs->visible_h) || + (dst_x + w) > (vs->visible_x + vs->visible_w) || + (dst_y + h) > (vs->visible_y + vs->visible_h)) + updating_client = 0; - vnc_update_client(vs); + if (updating_client) + _vnc_update_client(vs); if (dst_y > src_y) { y = h - 1; @@ -469,124 +621,219 @@ static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_ old_row += pitch; } - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1); - vnc_write_u16(vs, src_x); - vnc_write_u16(vs, src_y); - vnc_flush(vs); + if (updating_client && vs->csock != -1 && !vs->has_update) { + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1); + vnc_write_u16(vs, src_x); + vnc_write_u16(vs, src_y); + vnc_flush(vs); + vs->update_requested--; + } else + framebuffer_set_updated(vs, dst_x, dst_y, w, h); } -static int find_dirty_height(VncState *vs, int y, int last_x, int x) +static int find_update_height(VncState *vs, int y, int maxy, int last_x, int x) { int h; - for (h = 1; h < (vs->height - y); h++) { + for (h = 1; y + h < maxy; h++) { int tmp_x; - if (!vnc_get_bit(vs->dirty_row[y + h], last_x)) + if (!(vs->update_row[y + h] & (1ULL << last_x))) break; for (tmp_x = last_x; tmp_x < x; tmp_x++) - vnc_clear_bit(vs->dirty_row[y + h], tmp_x); + vs->update_row[y + h] &= ~(1ULL << tmp_x); } return h; } -static void vnc_update_client(void *opaque) +static void _vnc_update_client(void *opaque) { VncState *vs = opaque; + int64_t now; + int y; + uint8_t *row; + uint8_t *old_row; + uint64_t width_mask; + int n_rectangles; + int saved_offset; + int maxx, maxy; + int tile_bytes = vs->depth * DP2X(vs, 1); + + if (!vs->update_requested || vs->csock == -1) + return; + while (!is_empty_queue(vs) && vs->update_requested) { + int enc = vs->upqueue.queue_end->enc; + dequeue_framebuffer_update(vs); + switch (enc) { + case 0x574D5669: + pixel_format_message(vs); + break; + default: + break; + } + vs->update_requested--; + } + if (!vs->update_requested) return; - if (vs->need_update && vs->csock != -1) { - int y; - uint8_t *row; - char *old_row; - uint32_t width_mask[VNC_DIRTY_WORDS]; - int n_rectangles; - int saved_offset; - int has_dirty = 0; - - vga_hw_update(); - - vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS); - - /* Walk through the dirty map and eliminate tiles that - really aren't dirty */ - row = vs->ds->data; - old_row = vs->old_data; - - for (y = 0; y < vs->height; y++) { - if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) { - int x; - uint8_t *ptr; - char *old_ptr; - - ptr = row; - old_ptr = (char*)old_row; - - for (x = 0; x < vs->ds->width; x += 16) { - if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) { - vnc_clear_bit(vs->dirty_row[y], (x / 16)); - } else { - has_dirty = 1; - memcpy(old_ptr, ptr, 16 * vs->depth); - } + now = qemu_get_clock(rt_clock); - ptr += 16 * vs->depth; - old_ptr += 16 * vs->depth; - } - } + if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) + width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; + else + width_mask = ~(0ULL); - row += vs->ds->linesize; - old_row += vs->ds->linesize; - } + /* Walk through the dirty map and eliminate tiles that really + aren't dirty */ + row = vs->ds->data; + old_row = vs->old_data; - if (!has_dirty) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); - return; - } + for (y = 0; y < vs->ds->height; y++) { + if (vs->dirty_row[y] & width_mask) { + int x; + uint8_t *ptr, *old_ptr; - /* Count rectangles */ - n_rectangles = 0; - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - saved_offset = vs->output.offset; - vnc_write_u16(vs, 0); + ptr = row; + old_ptr = old_row; - for (y = 0; y < vs->height; y++) { - int x; - int last_x = -1; - for (x = 0; x < vs->width / 16; x++) { - if (vnc_get_bit(vs->dirty_row[y], x)) { - if (last_x == -1) { - last_x = x; + for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { + if (vs->dirty_row[y] & (1ULL << x)) { + if (memcmp(old_ptr, ptr, tile_bytes)) { + vs->has_update = 1; + vs->update_row[y] |= (1ULL << x); + memcpy(old_ptr, ptr, tile_bytes); } - vnc_clear_bit(vs->dirty_row[y], x); - } else { - if (last_x != -1) { - int h = find_dirty_height(vs, y, last_x, x); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); + vs->dirty_row[y] &= ~(1ULL << x); + } + + ptr += tile_bytes; + old_ptr += tile_bytes; + } + } + + row += vs->ds->linesize; + old_row += vs->ds->linesize; + } + + if (!vs->has_update || vs->visible_y >= vs->ds->height || + vs->visible_x >= vs->ds->width) + goto backoff; + + /* Count rectangles */ + n_rectangles = 0; + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + saved_offset = vs->output.offset; + vnc_write_u16(vs, 0); + + maxy = vs->visible_y + vs->visible_h; + if (maxy > vs->ds->height) + maxy = vs->ds->height; + maxx = vs->visible_x + vs->visible_w; + if (maxx > vs->ds->width) + maxx = vs->ds->width; + + for (y = vs->visible_y; y < maxy; y++) { + int x; + int last_x = -1; + for (x = X2DP_DOWN(vs, vs->visible_x); + x < X2DP_UP(vs, maxx); x++) { + if (vs->update_row[y] & (1ULL << x)) { + if (last_x == -1) + last_x = x; + vs->update_row[y] &= ~(1ULL << x); + } else { + if (last_x != -1) { + int h = find_update_height(vs, y, maxy, last_x, x); + if (h != 0) { + send_framebuffer_update(vs, DP2X(vs, last_x), y, + DP2X(vs, (x - last_x)), h); n_rectangles++; } - last_x = -1; } + last_x = -1; } - if (last_x != -1) { - int h = find_dirty_height(vs, y, last_x, x); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); + } + if (last_x != -1) { + int h = find_update_height(vs, y, maxy, last_x, x); + if (h != 0) { + send_framebuffer_update(vs, DP2X(vs, last_x), y, + DP2X(vs, (x - last_x)), h); n_rectangles++; } } - vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; - vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; - vnc_flush(vs); + } + vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; + vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; + + if (n_rectangles == 0) { + vs->output.offset = saved_offset - 2; + goto backoff; + } else + vs->update_requested--; + vs->has_update = 0; + vnc_flush(vs); + vs->last_update_time = now; + + vs->timer_interval /= 2; + if (vs->timer_interval < VNC_REFRESH_INTERVAL_BASE) + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; + + return; + + backoff: + /* No update -> back off a bit */ + vs->timer_interval += VNC_REFRESH_INTERVAL_INC; + if (vs->timer_interval > VNC_REFRESH_INTERVAL_MAX) { + vs->timer_interval = VNC_REFRESH_INTERVAL_MAX; + if (now - vs->last_update_time >= VNC_MAX_UPDATE_INTERVAL && + vs->update_requested) { + /* Send a null update. If the client is no longer + interested (e.g. minimised) it'll ignore this, and we + can stop scanning the buffer until it sends another + update request. */ + /* It turns out that there's a bug in realvncviewer 4.1.2 + which means that if you send a proper null update (with + no update rectangles), it gets a bit out of sync and + never sends any further requests, regardless of whether + it needs one or not. Fix this by sending a single 1x1 + update rectangle instead. */ + vnc_write_u8(vs, 0); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + send_framebuffer_update(vs, 0, 0, 1, 1); + vnc_flush(vs); + vs->last_update_time = now; + vs->update_requested--; + return; + } } + qemu_mod_timer(vs->timer, now + vs->timer_interval); + return; +} - if (vs->csock != -1) { - qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); +static void vnc_update_client(void *opaque) +{ + VncState *vs = opaque; + + vs->ds->dpy_refresh(vs->ds); + _vnc_update_client(vs); +} + +static void vnc_timer_init(VncState *vs) +{ + if (vs->timer == NULL) { + vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; } +} +static void vnc_dpy_refresh(DisplayState *ds) +{ + vga_hw_update(); } static int vnc_listen_poll(void *opaque) @@ -621,7 +868,7 @@ static uint8_t *buffer_end(Buffer *buffer) static void buffer_reset(Buffer *buffer) { - buffer->offset = 0; + buffer->offset = 0; } static void buffer_append(Buffer *buffer, const void *data, size_t len) @@ -630,6 +877,88 @@ static void buffer_append(Buffer *buffer, const void *data, size_t len) buffer->offset += len; } +static void enqueue_framebuffer_update(VncState *vs, int x, int y, int w, int h, + int32_t encoding) +{ + Queue *q = &vs->upqueue; + if (q->queue_end != NULL) { + if (q->queue_end != q->queue_start || q->start_count != q->end_count) { + if (q->queue_end->next == NULL) { + q->queue_end->next = (QueueItem *) qemu_mallocz (sizeof(QueueItem) * QUEUE_ALLOC_UNIT); + q->end_count = QUEUE_ALLOC_UNIT; + } + q->queue_end = q->queue_end->next; + } + } else { + q->queue_end = (QueueItem *) qemu_mallocz (sizeof(QueueItem) * QUEUE_ALLOC_UNIT); + q->queue_start = q->queue_end; + q->start_count = QUEUE_ALLOC_UNIT; + q->end_count = QUEUE_ALLOC_UNIT; + } + q->end_count--; + + q->queue_end->x = x; + q->queue_end->y = y; + q->queue_end->w = w; + q->queue_end->h = h; + q->queue_end->enc = encoding; + q->queue_end->next = (q->end_count > 0) ? (q->queue_end + 1) : NULL; +} + +static void dequeue_framebuffer_update(VncState *vs) +{ + Queue *q = &vs->upqueue; + if (q->queue_start == NULL || + (q->queue_end == q->queue_start && q->start_count == q->end_count)) + return; + + vnc_write_u8(vs, 0); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + vnc_framebuffer_update(vs, q->queue_start->x, q->queue_start->y, + q->queue_start->w, q->queue_start->h, q->queue_start->enc); + + q->start_count--; + if (q->queue_end != q->queue_start) { + if (!q->start_count) { + QueueItem *i = q->queue_start; + q->queue_start = q->queue_start->next; + q->start_count = QUEUE_ALLOC_UNIT; + free (i - QUEUE_ALLOC_UNIT + 1); + } else + q->queue_start = q->queue_start->next; + } else { + q->queue_end = q->queue_end - QUEUE_ALLOC_UNIT + q->end_count + 1; + q->queue_start = q->queue_end; + q->end_count = QUEUE_ALLOC_UNIT; + q->start_count = QUEUE_ALLOC_UNIT; + } +} + +static int is_empty_queue(VncState *vs) +{ + Queue *q = &vs->upqueue; + if (q->queue_end == NULL) return 1; + if (q->queue_end == q->queue_start && q->start_count == q->end_count) return 1; + return 0; +} + +static void free_queue(VncState *vs) +{ + Queue *q = &vs->upqueue; + while (q->queue_start != NULL) { + QueueItem *i; + q->queue_start = q->queue_start + q->start_count - 1; + i = q->queue_start; + q->queue_start = q->queue_start->next; + free(i - QUEUE_ALLOC_UNIT + 1); + q->start_count = QUEUE_ALLOC_UNIT; + } + q->queue_end = NULL; + q->start_count = 0; + q->end_count = 0; +} + static int vnc_client_io_error(VncState *vs, int ret, int last_errno) { if (ret == 0 || ret == -1) { @@ -642,7 +971,8 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno) vs->csock = -1; buffer_reset(&vs->input); buffer_reset(&vs->output); - vs->need_update = 0; + free_queue(vs); + vs->update_requested = 0; #if CONFIG_VNC_TLS if (vs->tls_session) { gnutls_deinit(vs->tls_session); @@ -682,12 +1012,12 @@ static void vnc_client_write(void *opaque) if (!ret) return; - memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); + memmove(vs->output.buffer, vs->output.buffer + ret, + vs->output.offset - ret); vs->output.offset -= ret; - if (vs->output.offset == 0) { + if (vs->output.offset == 0) qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); - } } static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) @@ -731,9 +1061,11 @@ static void vnc_client_read(void *opaque) return; if (!ret) { - memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); + memmove(vs->input.buffer, vs->input.buffer + len, + vs->input.offset - len); vs->input.offset -= len; } else { + assert(ret > vs->read_handler_expect); vs->read_handler_expect = ret; } } @@ -743,9 +1075,9 @@ static void vnc_write(VncState *vs, const void *data, size_t len) { buffer_reserve(&vs->output, len); - if (buffer_empty(&vs->output)) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); - } + if (buffer_empty(&vs->output)) + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, + vnc_client_write, vs); buffer_append(&vs->output, data, len); } @@ -779,7 +1111,7 @@ static void vnc_write_u16(VncState *vs, uint16_t value) static void vnc_write_u8(VncState *vs, uint8_t value) { - vnc_write(vs, (char *)&value, 1); + vnc_write(vs, &value, 1); } static void vnc_flush(VncState *vs) @@ -811,9 +1143,9 @@ static uint32_t read_u32(uint8_t *data, size_t offset) } #if CONFIG_VNC_TLS -static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, - const void *data, - size_t len) { +ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, + const void *data, + size_t len) { struct VncState *vs = (struct VncState *)transport; int ret; @@ -828,9 +1160,9 @@ static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, } -static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, - void *data, - size_t len) { +ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, + void *data, + size_t len) { struct VncState *vs = (struct VncState *)transport; int ret; @@ -845,19 +1177,25 @@ static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, } #endif /* CONFIG_VNC_TLS */ -static void client_cut_text(VncState *vs, size_t len, uint8_t *text) +static void client_cut_text(VncState *vs, size_t len, char *text) { } static void check_pointer_type_change(VncState *vs, int absolute) { if (vs->has_pointer_type_change && vs->absolute != absolute) { - vnc_write_u8(vs, 0); - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); - vnc_framebuffer_update(vs, absolute, 0, + if (vs->update_requested) { + vnc_write_u8(vs, 0); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + vnc_framebuffer_update(vs, absolute, 0, vs->ds->width, vs->ds->height, -257); - vnc_flush(vs); + vnc_flush(vs); + vs->update_requested--; + } else { + enqueue_framebuffer_update(vs, absolute, 0, + vs->ds->width, vs->ds->height, -257); + } } vs->absolute = absolute; } @@ -879,8 +1217,8 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) dz = 1; if (vs->absolute) { - kbd_mouse_event(x * 0x7FFF / (vs->ds->width - 1), - y * 0x7FFF / (vs->ds->height - 1), + kbd_mouse_event(x * 0x7FFF / (vs->ds->width - 1), + y * 0x7FFF / (vs->ds->height - 1), dz, buttons); } else if (vs->has_pointer_type_change) { x -= 0x7FFF; @@ -918,11 +1256,68 @@ static void press_key(VncState *vs, int keysym) kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80); } +static void press_key_shift_down(VncState *vs, int down, int keycode) +{ + if (down) + kbd_put_keycode(0x2a & 0x7f); + + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (down) + kbd_put_keycode(keycode & 0x7f); + else + kbd_put_keycode(keycode | 0x80); + + if (!down) + kbd_put_keycode(0x2a | 0x80); +} + +static void press_key_shift_up(VncState *vs, int down, int keycode) +{ + if (down) { + if (vs->modifiers_state[0x2a]) + kbd_put_keycode(0x2a | 0x80); + if (vs->modifiers_state[0x36]) + kbd_put_keycode(0x36 | 0x80); + } + + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (down) + kbd_put_keycode(keycode & 0x7f); + else + kbd_put_keycode(keycode | 0x80); + + if (!down) { + if (vs->modifiers_state[0x2a]) + kbd_put_keycode(0x2a & 0x7f); + if (vs->modifiers_state[0x36]) + kbd_put_keycode(0x36 & 0x7f); + } +} + static void do_key_event(VncState *vs, int down, uint32_t sym) { int keycode; + int shift_keys = 0; + int shift = 0; + + if (is_graphic_console()) { + if (sym >= 'A' && sym <= 'Z') { + sym = sym - 'A' + 'a'; + shift = 1; + } + else { + shift = keysymIsShift(vs->kbd_layout, sym & 0xFFFF); + } + } + shift_keys = vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]; keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF); + if (keycode == 0) { + fprintf(stderr, "Key lost : keysym=0x%x(%d)\n", sym, sym); + return; + } /* QEMU console switch */ switch(keycode) { @@ -932,12 +1327,16 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) case 0x9d: /* Right CTRL */ case 0x38: /* Left ALT */ case 0xb8: /* Right ALT */ - if (down) + if (down) { vs->modifiers_state[keycode] = 1; - else + kbd_put_keycode(keycode & 0x7f); + } + else { vs->modifiers_state[keycode] = 0; - break; - case 0x02 ... 0x0a: /* '1' to '9' keys */ + kbd_put_keycode(keycode | 0x80); + } + return; + case 0x02 ... 0x0a: /* '1' to '9' keys */ if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { /* Reset the modifiers sent to the current console */ reset_keys(vs); @@ -945,32 +1344,48 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) return; } break; - case 0x3a: /* CapsLock */ case 0x45: /* NumLock */ - if (!down) - vs->modifiers_state[keycode] ^= 1; - break; + if (down) { + kbd_put_keycode(keycode & 0x7f); + } + else { + vs->modifiers_state[keycode] ^= 1; + kbd_put_keycode(keycode | 0x80); + } + return; } - if (keycode_is_keypad(vs->kbd_layout, keycode)) { + if (keycodeIsKeypad(vs->kbd_layout, keycode)) { /* If the numlock state needs to change then simulate an additional keypress before sending this one. This will happen if the user toggles numlock away from the VNC window. */ - if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) { - if (!vs->modifiers_state[0x45]) { - vs->modifiers_state[0x45] = 1; - press_key(vs, 0xff7f); - } - } else { - if (vs->modifiers_state[0x45]) { - vs->modifiers_state[0x45] = 0; - press_key(vs, 0xff7f); - } + if (keysymIsNumlock(vs->kbd_layout, sym & 0xFFFF)) { + if (!vs->modifiers_state[0x45]) { + vs->modifiers_state[0x45] = 1; + press_key(vs, 0xff7f); + } + } else { + if (vs->modifiers_state[0x45]) { + vs->modifiers_state[0x45] = 0; + press_key(vs, 0xff7f); + } } } if (is_graphic_console()) { + /* If the shift state needs to change then simulate an additional + keypress before sending this one. + */ + if (shift && !shift_keys) { + press_key_shift_down(vs, down, keycode); + return; + } + else if (!shift && shift_keys) { + press_key_shift_up(vs, down, keycode); + return; + } + if (keycode & 0x80) kbd_put_keycode(0xe0); if (down) @@ -1025,36 +1440,30 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) static void key_event(VncState *vs, int down, uint32_t sym) { - if (sym >= 'A' && sym <= 'Z' && is_graphic_console()) - sym = sym - 'A' + 'a'; do_key_event(vs, down, sym); } +static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h) +{ + + set_bits_in_row(vs, vs->update_row, x, y, w, h); + + vs->has_update = 1; +} + static void framebuffer_update_request(VncState *vs, int incremental, int x_position, int y_position, int w, int h) { - if (x_position > vs->ds->width) - x_position = vs->ds->width; - if (y_position > vs->ds->height) - y_position = vs->ds->height; - if (x_position + w >= vs->ds->width) - w = vs->ds->width - x_position; - if (y_position + h >= vs->ds->height) - h = vs->ds->height - y_position; + if (!incremental) + framebuffer_set_updated(vs, x_position, y_position, w, h); + vs->visible_x = x_position; + vs->visible_y = y_position; + vs->visible_w = w; + vs->visible_h = h; - int i; - vs->need_update = 1; - if (!incremental) { - char *old_row = vs->old_data + y_position * vs->ds->linesize; - - for (i = 0; i < h; i++) { - vnc_set_bits(vs->dirty_row[y_position + i], - (vs->ds->width / 16), VNC_DIRTY_WORDS); - memset(old_row, 42, vs->ds->width * vs->depth); - old_row += vs->ds->linesize; - } - } + vs->update_requested++; + qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock)); } static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) @@ -1064,6 +1473,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->has_hextile = 0; vs->has_resize = 0; vs->has_pointer_type_change = 0; + vs->has_WMVi = 0; vs->absolute = -1; vs->ds->dpy_copy = NULL; @@ -1084,6 +1494,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) case -257: vs->has_pointer_type_change = 1; break; + case 0x574D5669: + vs->has_WMVi = 1; default: break; } @@ -1092,17 +1504,6 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) check_pointer_type_change(vs, kbd_mouse_is_absolute()); } -static int compute_nbits(unsigned int val) -{ - int n; - n = 0; - while (val != 0) { - n++; - val >>= 1; - } - return n; -} - static void set_pixel_format(VncState *vs, int bits_per_pixel, int depth, int big_endian_flag, int true_color_flag, @@ -1122,56 +1523,215 @@ static void set_pixel_format(VncState *vs, return; } if (bits_per_pixel == 32 && + bits_per_pixel == vs->depth * 8 && host_big_endian_flag == big_endian_flag && red_max == 0xff && green_max == 0xff && blue_max == 0xff && red_shift == 16 && green_shift == 8 && blue_shift == 0) { - vs->depth = 4; vs->write_pixels = vnc_write_pixels_copy; vs->send_hextile_tile = send_hextile_tile_32; - } else + } else if (bits_per_pixel == 16 && + bits_per_pixel == vs->depth * 8 && host_big_endian_flag == big_endian_flag && red_max == 31 && green_max == 63 && blue_max == 31 && red_shift == 11 && green_shift == 5 && blue_shift == 0) { - vs->depth = 2; vs->write_pixels = vnc_write_pixels_copy; vs->send_hextile_tile = send_hextile_tile_16; - } else - if (bits_per_pixel == 8 && + } else + if (bits_per_pixel == 8 && + bits_per_pixel == vs->depth * 8 && red_max == 7 && green_max == 7 && blue_max == 3 && red_shift == 5 && green_shift == 2 && blue_shift == 0) { vs->depth = 1; vs->write_pixels = vnc_write_pixels_copy; vs->send_hextile_tile = send_hextile_tile_8; - } else + } else { /* generic and slower case */ if (bits_per_pixel != 8 && bits_per_pixel != 16 && bits_per_pixel != 32) goto fail; - vs->depth = 4; - vs->red_shift = red_shift; - vs->red_max = red_max; - vs->red_shift1 = 24 - compute_nbits(red_max); - vs->green_shift = green_shift; - vs->green_max = green_max; - vs->green_shift1 = 16 - compute_nbits(green_max); - vs->blue_shift = blue_shift; - vs->blue_max = blue_max; - vs->blue_shift1 = 8 - compute_nbits(blue_max); - vs->pix_bpp = bits_per_pixel / 8; + if (vs->depth == 4) { + vs->send_hextile_tile = send_hextile_tile_generic_32; + } else if (vs->depth == 2) { + vs->send_hextile_tile = send_hextile_tile_generic_16; + } else { + vs->send_hextile_tile = send_hextile_tile_generic_8; + } + vs->pix_big_endian = big_endian_flag; vs->write_pixels = vnc_write_pixels_generic; - vs->send_hextile_tile = send_hextile_tile_generic; } + + vs->red_shift = red_shift; + vs->red_max = red_max; + vs->green_shift = green_shift; + vs->green_max = green_max; + vs->blue_shift = blue_shift; + vs->blue_max = blue_max; + vs->pix_bpp = bits_per_pixel / 8; +} - vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height); - memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); - memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height); +static void pixel_format_message (VncState *vs) { + char pad[3] = { 0, 0, 0 }; - vga_hw_invalidate(); - vga_hw_update(); + vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */ + if (vs->depth == 4) vnc_write_u8(vs, 24); /* depth */ + else vnc_write_u8(vs, vs->depth * 8); /* depth */ + +#ifdef WORDS_BIGENDIAN + vnc_write_u8(vs, 1); /* big-endian-flag */ +#else + vnc_write_u8(vs, 0); /* big-endian-flag */ +#endif + vnc_write_u8(vs, 1); /* true-color-flag */ + if (vs->depth == 4) { + vnc_write_u16(vs, 0xFF); /* red-max */ + vnc_write_u16(vs, 0xFF); /* green-max */ + vnc_write_u16(vs, 0xFF); /* blue-max */ + vnc_write_u8(vs, 16); /* red-shift */ + vnc_write_u8(vs, 8); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + vs->send_hextile_tile = send_hextile_tile_32; + } else if (vs->depth == 2) { + vnc_write_u16(vs, 31); /* red-max */ + vnc_write_u16(vs, 63); /* green-max */ + vnc_write_u16(vs, 31); /* blue-max */ + vnc_write_u8(vs, 11); /* red-shift */ + vnc_write_u8(vs, 5); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + vs->send_hextile_tile = send_hextile_tile_16; + } else if (vs->depth == 1) { + /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */ + vnc_write_u16(vs, 7); /* red-max */ + vnc_write_u16(vs, 7); /* green-max */ + vnc_write_u16(vs, 3); /* blue-max */ + vnc_write_u8(vs, 5); /* red-shift */ + vnc_write_u8(vs, 2); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + vs->send_hextile_tile = send_hextile_tile_8; + } + vs->red_max = vs->red_max1; + vs->green_max = vs->green_max1; + vs->blue_max = vs->blue_max1; + vs->red_shift = vs->red_shift1; + vs->green_shift = vs->green_shift1; + vs->blue_shift = vs->blue_shift1; + vs->pix_bpp = vs->depth * 8; + vs->write_pixels = vnc_write_pixels_copy; + + vnc_write(vs, pad, 3); /* padding */ +} + +static void vnc_dpy_setdata(DisplayState *ds, void *pixels) +{ + ds->data = pixels; +} + +static void vnc_dpy_colourdepth(DisplayState *ds, int depth) +{ + int host_big_endian_flag; + struct VncState *vs = ds->opaque; + + switch (depth) { + case 24: + ds->shared_buf = 0; + if (ds->depth == 32) return; + depth = 32; + break; + case 0: + ds->shared_buf = 0; + return; + default: + ds->shared_buf = 1; + break; + } + +#ifdef WORDS_BIGENDIAN + host_big_endian_flag = 1; +#else + host_big_endian_flag = 0; +#endif + + switch (depth) { + case 8: + vs->depth = depth / 8; + vs->red_max1 = 7; + vs->green_max1 = 7; + vs->blue_max1 = 3; + vs->red_shift1 = 5; + vs->green_shift1 = 2; + vs->blue_shift1 = 0; + break; + case 16: + vs->depth = depth / 8; + vs->red_max1 = 31; + vs->green_max1 = 63; + vs->blue_max1 = 31; + vs->red_shift1 = 11; + vs->green_shift1 = 5; + vs->blue_shift1 = 0; + break; + case 32: + vs->depth = 4; + vs->red_max1 = 255; + vs->green_max1 = 255; + vs->blue_max1 = 255; + vs->red_shift1 = 16; + vs->green_shift1 = 8; + vs->blue_shift1 = 0; + break; + default: + return; + } + if (ds->switchbpp) { + vnc_client_error(vs); + } else if (vs->csock != -1 && vs->has_WMVi) { + /* Sending a WMVi message to notify the client*/ + if (vs->update_requested) { + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669); + pixel_format_message(vs); + vnc_flush(vs); + vs->update_requested--; + } else { + enqueue_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669); + } + } else { + if (vs->pix_bpp == 4 && vs->depth == 4 && + host_big_endian_flag == vs->pix_big_endian && + vs->red_max == 0xff && vs->green_max == 0xff && vs->blue_max == 0xff && + vs->red_shift == 16 && vs->green_shift == 8 && vs->blue_shift == 0) { + vs->write_pixels = vnc_write_pixels_copy; + vs->send_hextile_tile = send_hextile_tile_32; + } else if (vs->pix_bpp == 2 && vs->depth == 2 && + host_big_endian_flag == vs->pix_big_endian && + vs->red_max == 31 && vs->green_max == 63 && vs->blue_max == 31 && + vs->red_shift == 11 && vs->green_shift == 5 && vs->blue_shift == 0) { + vs->write_pixels = vnc_write_pixels_copy; + vs->send_hextile_tile = send_hextile_tile_16; + } else if (vs->pix_bpp == 1 && vs->depth == 1 && + host_big_endian_flag == vs->pix_big_endian && + vs->red_max == 7 && vs->green_max == 7 && vs->blue_max == 3 && + vs->red_shift == 5 && vs->green_shift == 2 && vs->blue_shift == 0) { + vs->write_pixels = vnc_write_pixels_copy; + vs->send_hextile_tile = send_hextile_tile_8; + } else { + if (vs->depth == 4) { + vs->send_hextile_tile = send_hextile_tile_generic_32; + } else if (vs->depth == 2) { + vs->send_hextile_tile = send_hextile_tile_generic_16; + } else { + vs->send_hextile_tile = send_hextile_tile_generic_8; + } + vs->write_pixels = vnc_write_pixels_generic; + } + } + + vnc_dpy_resize(ds, ds->width, ds->height, ds->linesize); } static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) @@ -1194,8 +1754,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 4; - if (len == 4) - return 4 + (read_u16(data, 2) * 4); + if (len == 4) { + uint16_t v; + v = read_u16(data, 2); + if (v) + return 4 + v * 4; + } limit = read_u16(data, 2); for (i = 0; i < limit; i++) { @@ -1217,12 +1781,18 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 8; + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; + qemu_advance_timer(vs->timer, + qemu_get_clock(rt_clock) + vs->timer_interval); key_event(vs, read_u8(data, 1), read_u32(data, 4)); break; case 5: if (len == 1) return 6; + vs->timer_interval = VNC_REFRESH_INTERVAL_BASE; + qemu_advance_timer(vs->timer, + qemu_get_clock(rt_clock) + vs->timer_interval); pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); break; case 6: @@ -1230,79 +1800,41 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) return 8; if (len == 8) { - uint32_t dlen = read_u32(data, 4); - if (dlen > 0) - return 8 + dlen; - } + uint32_t v; + v = read_u32(data, 4); + if (v) + return 8 + v; + } - client_cut_text(vs, read_u32(data, 4), data + 8); + client_cut_text(vs, read_u32(data, 4), (char *)(data + 8)); break; default: printf("Msg: %d\n", data[0]); vnc_client_error(vs); break; } - + vnc_read_when(vs, protocol_client_msg, 1); return 0; } static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) { - char pad[3] = { 0, 0, 0 }; - char buf[1024]; - int size; + size_t l; + + vga_hw_update(); vs->width = vs->ds->width; vs->height = vs->ds->height; vnc_write_u16(vs, vs->ds->width); vnc_write_u16(vs, vs->ds->height); - vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */ - vnc_write_u8(vs, vs->depth * 8); /* depth */ -#ifdef WORDS_BIGENDIAN - vnc_write_u8(vs, 1); /* big-endian-flag */ -#else - vnc_write_u8(vs, 0); /* big-endian-flag */ -#endif - vnc_write_u8(vs, 1); /* true-color-flag */ - if (vs->depth == 4) { - vnc_write_u16(vs, 0xFF); /* red-max */ - vnc_write_u16(vs, 0xFF); /* green-max */ - vnc_write_u16(vs, 0xFF); /* blue-max */ - vnc_write_u8(vs, 16); /* red-shift */ - vnc_write_u8(vs, 8); /* green-shift */ - vnc_write_u8(vs, 0); /* blue-shift */ - vs->send_hextile_tile = send_hextile_tile_32; - } else if (vs->depth == 2) { - vnc_write_u16(vs, 31); /* red-max */ - vnc_write_u16(vs, 63); /* green-max */ - vnc_write_u16(vs, 31); /* blue-max */ - vnc_write_u8(vs, 11); /* red-shift */ - vnc_write_u8(vs, 5); /* green-shift */ - vnc_write_u8(vs, 0); /* blue-shift */ - vs->send_hextile_tile = send_hextile_tile_16; - } else if (vs->depth == 1) { - /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */ - vnc_write_u16(vs, 7); /* red-max */ - vnc_write_u16(vs, 7); /* green-max */ - vnc_write_u16(vs, 3); /* blue-max */ - vnc_write_u8(vs, 5); /* red-shift */ - vnc_write_u8(vs, 2); /* green-shift */ - vnc_write_u8(vs, 0); /* blue-shift */ - vs->send_hextile_tile = send_hextile_tile_8; - } - vs->write_pixels = vnc_write_pixels_copy; - - vnc_write(vs, pad, 3); /* padding */ + pixel_format_message(vs); - if (qemu_name) - size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name); - else - size = snprintf(buf, sizeof(buf), "QEMU"); + l = strlen(domain_name); + vnc_write_u32(vs, l); + vnc_write(vs, domain_name, l); - vnc_write_u32(vs, size); - vnc_write(vs, buf, size); vnc_flush(vs); vnc_read_when(vs, protocol_client_msg, 1); @@ -1310,6 +1842,7 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) return 0; } + static void make_challenge(VncState *vs) { int i; @@ -1320,11 +1853,11 @@ static void make_challenge(VncState *vs) vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0)); } -static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) +static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len) { - unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; + char response[VNC_AUTH_CHALLENGE_SIZE]; int i, j, pwlen; - unsigned char key[8]; + char key[8]; if (!vs->password || !vs->password[0]) { VNC_DEBUG("No password configured on server"); @@ -1731,7 +2264,7 @@ static int vnc_start_tls(struct VncState *vs) { return vnc_continue_handshake(vs); } -static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) +static int protocol_client_vencrypt_auth(VncState *vs, char *data, size_t len) { int auth = read_u32(data, 0); @@ -1761,7 +2294,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len return 0; } -static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) +static int protocol_client_vencrypt_init(VncState *vs, char *data, size_t len) { if (data[0] != 0 || data[1] != 2) { @@ -1791,7 +2324,7 @@ static int start_auth_vencrypt(VncState *vs) } #endif /* CONFIG_VNC_TLS */ -static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) +static int protocol_client_auth(VncState *vs, char *data, size_t len) { /* We only advertise 1 auth scheme at a time, so client * must pick the one we sent. Verify this */ @@ -1840,7 +2373,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) return 0; } -static int protocol_version(VncState *vs, uint8_t *version, size_t len) +static int protocol_version(VncState *vs, char *version, size_t len) { char local[13]; @@ -1899,34 +2432,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) return 0; } -static void vnc_connect(VncState *vs) -{ - VNC_DEBUG("New client on socket %d\n", vs->csock); - socket_set_nonblock(vs->csock); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); - vnc_write(vs, "RFB 003.008\n", 12); - vnc_flush(vs); - vnc_read_when(vs, protocol_version, 12); - memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height); - memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); - vs->has_resize = 0; - vs->has_hextile = 0; - vs->ds->dpy_copy = NULL; - vnc_update_client(vs); -} - static void vnc_listen_read(void *opaque) { VncState *vs = opaque; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); - /* Catch-up */ - vga_hw_update(); - vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (vs->csock != -1) { - vnc_connect(vs); + VNC_DEBUG("New client on socket %d\n", vs->csock); + socket_set_nonblock(vs->csock); + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); + vnc_write(vs, "RFB 003.008\n", 12); + vnc_flush(vs); + vnc_read_when(vs, protocol_version, 12); + framebuffer_set_updated(vs, 0, 0, vs->ds->width, vs->ds->height); + vs->has_resize = 0; + vs->has_hextile = 0; + vs->update_requested = 0; + vs->ds->dpy_copy = NULL; + vnc_timer_init(vs); } } @@ -1947,7 +2472,6 @@ void vnc_display_init(DisplayState *ds) vs->lsock = -1; vs->csock = -1; - vs->depth = 4; vs->last_x = -1; vs->last_y = -1; @@ -1959,17 +2483,19 @@ void vnc_display_init(DisplayState *ds) vs->kbd_layout = init_keyboard_layout(keyboard_layout); if (!vs->kbd_layout) exit(1); - - vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); + vs->modifiers_state[0x45] = 1; /* NumLock on - on boot */ vs->ds->data = NULL; vs->ds->dpy_update = vnc_dpy_update; vs->ds->dpy_resize = vnc_dpy_resize; - vs->ds->dpy_refresh = NULL; + vs->ds->dpy_colourdepth = vnc_dpy_colourdepth; + vs->ds->dpy_setdata = vnc_dpy_setdata; + vs->ds->dpy_refresh = vnc_dpy_refresh; - memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); - - vnc_dpy_resize(vs->ds, 640, 400); + vs->ds->width = 640; + vs->ds->height = 400; + vs->ds->linesize = 640 * 4; + vnc_dpy_colourdepth(vs->ds, 24); } #if CONFIG_VNC_TLS @@ -2048,7 +2574,8 @@ void vnc_display_close(DisplayState *ds) vs->csock = -1; buffer_reset(&vs->input); buffer_reset(&vs->output); - vs->need_update = 0; + free_queue(vs); + vs->update_requested = 0; #if CONFIG_VNC_TLS if (vs->tls_session) { gnutls_deinit(vs->tls_session); @@ -2064,6 +2591,10 @@ void vnc_display_close(DisplayState *ds) #endif } +int parse_host_port(struct sockaddr_in *saddr, const char *str); + + + int vnc_display_password(DisplayState *ds, const char *password) { VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; @@ -2080,38 +2611,42 @@ int vnc_display_password(DisplayState *ds, const char *password) return 0; } -int vnc_display_open(DisplayState *ds, const char *display) +int vnc_display_open(DisplayState *ds, const char *display, int find_unused) { struct sockaddr *addr; struct sockaddr_in iaddr; -#ifndef _WIN32 +#ifndef NO_UNIX_SOCKETS struct sockaddr_un uaddr; #endif +#ifndef CONFIG_STUBDOM int reuse_addr, ret; +#endif socklen_t addrlen; const char *p; VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; const char *options; int password = 0; - int reverse = 0; #if CONFIG_VNC_TLS int tls = 0, x509 = 0; #endif + if (display == NULL) + display = "localhost:0"; + vnc_display_close(ds); if (strcmp(display, "none") == 0) - return 0; + return 0; if (!(vs->display = strdup(display))) - return -1; + return -1; options = display; while ((options = strchr(options, ','))) { options++; if (strncmp(options, "password", 8) == 0) { password = 1; /* Require password auth */ - } else if (strncmp(options, "reverse", 7) == 0) { - reverse = 1; + } else if (strncmp(options, "switchbpp", 9) == 0) { + ds->switchbpp = 1; #if CONFIG_VNC_TLS } else if (strncmp(options, "tls", 3) == 0) { tls = 1; /* Require TLS */ @@ -2205,9 +2740,7 @@ int vnc_display_open(DisplayState *ds, const char *display) memset(uaddr.sun_path, 0, 108); snprintf(uaddr.sun_path, 108, "%s", p); - if (!reverse) { - unlink(uaddr.sun_path); - } + unlink(uaddr.sun_path); } else #endif { @@ -2221,7 +2754,16 @@ int vnc_display_open(DisplayState *ds, const char *display) return -1; } - iaddr.sin_port = htons(ntohs(iaddr.sin_port) + (reverse ? 0 : 5900)); +#ifdef CONFIG_STUBDOM + { + struct ip_addr ipaddr = { iaddr.sin_addr.s_addr }; + struct ip_addr netmask = { 0 }; + struct ip_addr gw = { 0 }; + networking_set_addr(&ipaddr, &netmask, &gw); + } +#endif + + iaddr.sin_port = htons(ntohs(iaddr.sin_port) + 5900); vs->lsock = socket(PF_INET, SOCK_STREAM, 0); if (vs->lsock == -1) { @@ -2231,6 +2773,7 @@ int vnc_display_open(DisplayState *ds, const char *display) return -1; } +#ifndef CONFIG_STUBDOM reuse_addr = 1; ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr, sizeof(reuse_addr)); @@ -2242,25 +2785,14 @@ int vnc_display_open(DisplayState *ds, const char *display) vs->display = NULL; return -1; } +#endif } - if (reverse) { - if (connect(vs->lsock, addr, addrlen) == -1) { - fprintf(stderr, "Connection to VNC client failed\n"); - close(vs->lsock); - vs->lsock = -1; - free(vs->display); - vs->display = NULL; - return -1; - } else { - vs->csock = vs->lsock; - vs->lsock = -1; - vnc_connect(vs); - return 0; - } - } - - if (bind(vs->lsock, addr, addrlen) == -1) { + while (bind(vs->lsock, addr, addrlen) == -1) { + if (find_unused && errno == EADDRINUSE) { + iaddr.sin_port = htons(ntohs(iaddr.sin_port) + 1); + continue; + } fprintf(stderr, "bind() failed\n"); close(vs->lsock); vs->lsock = -1; @@ -2278,5 +2810,39 @@ int vnc_display_open(DisplayState *ds, const char *display) return -1; } - return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs); + if (qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs) < 0) + return -1; + + return ntohs(iaddr.sin_port); } + +#ifndef CONFIG_STUBDOM +int vnc_start_viewer(int port) +{ + int pid, i, open_max; + char s[16]; + + sprintf(s, ":%d", port); + + switch (pid = fork()) { + case -1: + fprintf(stderr, "vncviewer failed fork\n"); + exit(1); + + case 0: /* child */ + open_max = sysconf(_SC_OPEN_MAX); + for (i = 0; i < open_max; i++) + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO) + close(i); + execlp("vncviewer", "vncviewer", s, NULL); + fprintf(stderr, "vncviewer execlp failed\n"); + exit(1); + + default: + return pid; + } +} +#endif + diff --git a/vnc_keysym.h b/vnc_keysym.h index 5c471044..af99ce0e 100644 --- a/vnc_keysym.h +++ b/vnc_keysym.h @@ -203,6 +203,65 @@ static name2keysym_t name2keysym[]={ { "ydiaeresis", 0x0ff}, {"EuroSign", 0x20ac}, /* XK_EuroSign */ +/* latin 2 extensions */ +{ "Aogonek", 0x01a1}, +{ "breve", 0x01a2}, +{ "Lstroke", 0x01a3}, +{ "Lcaron", 0x01a5}, +{ "Sacute", 0x01a6}, +{ "Scaron", 0x01a9}, +{ "Scedilla", 0x01aa}, +{ "Tcaron", 0x01ab}, +{ "Zacute", 0x01ac}, +{ "Zcaron", 0x01ae}, +{ "Zabovedot", 0x01af}, +{ "aogonek", 0x01b1}, +{ "ogonek", 0x01b2}, +{ "lstroke", 0x01b3}, +{ "lcaron", 0x01b5}, +{ "sacute", 0x01b6}, +{ "caron", 0x01b7}, +{ "scaron", 0x01b9}, +{ "scedilla", 0x01ba}, +{ "tcaron", 0x01bb}, +{ "zacute", 0x01bc}, +{ "doubleacute", 0x01bd}, +{ "zcaron", 0x01be}, +{ "zabovedot", 0x01bf}, +{ "Racute", 0x01c0}, +{ "Abreve", 0x01c3}, +{ "Lacute", 0x01c5}, +{ "Cacute", 0x01c6}, +{ "Ccaron", 0x01c8}, +{ "Eogonek", 0x01ca}, +{ "Ecaron", 0x01cc}, +{ "Dcaron", 0x01cf}, +{ "Dstroke", 0x01d0}, +{ "Nacute", 0x01d1}, +{ "Ncaron", 0x01d2}, +{ "Odoubleacute", 0x01d5}, +{ "Rcaron", 0x01d8}, +{ "Uring", 0x01d9}, +{ "Udoubleacute", 0x01db}, +{ "Tcedilla", 0x01de}, +{ "racute", 0x01e0}, +{ "abreve", 0x01e3}, +{ "lacute", 0x01e5}, +{ "cacute", 0x01e6}, +{ "ccaron", 0x01e8}, +{ "eogonek", 0x01ea}, +{ "ecaron", 0x01ec}, +{ "dcaron", 0x01ef}, +{ "dstroke", 0x01f0}, +{ "nacute", 0x01f1}, +{ "ncaron", 0x01f2}, +{ "odoubleacute", 0x01f5}, +{ "udoubleacute", 0x01fb}, +{ "rcaron", 0x01f8}, +{ "uring", 0x01f9}, +{ "tcedilla", 0x01fe}, +{ "abovedot", 0x01ff}, + /* modifiers */ {"Control_L", 0xffe3}, /* XK_Control_L */ {"Control_R", 0xffe4}, /* XK_Control_R */ @@ -215,6 +274,7 @@ static name2keysym_t name2keysym[]={ {"Shift_R", 0xffe2}, /* XK_Shift_R */ {"Super_L", 0xffeb}, /* XK_Super_L */ {"Super_R", 0xffec}, /* XK_Super_R */ +{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ /* special keys */ {"BackSpace", 0xff08}, /* XK_BackSpace */ @@ -289,11 +349,37 @@ static name2keysym_t name2keysym[]={ /* localized keys */ {"BackApostrophe", 0xff21}, {"Muhenkan", 0xff22}, -{"Katakana", 0xff25}, -{"Zenkaku_Hankaku", 0xff29}, +{"Katakana", 0xff27}, +{"Hankaku", 0xff29}, +{"Zenkaku_Hankaku", 0xff2a}, {"Henkan_Mode_Real", 0xff23}, {"Henkan_Mode_Ultra", 0xff3e}, {"backslash_ja", 0xffa5}, +{"Katakana_Real", 0xff25}, +{"Eisu_toggle", 0xff30}, + + /* dead keys */ +{"dead_grave", 0xfe50}, +{"dead_acute", 0xfe51}, +{"dead_circumflex", 0xfe52}, +{"dead_tilde", 0xfe53}, +{"dead_macron", 0xfe54}, +{"dead_brev", 0xfe55}, +{"dead_abovedot", 0xfe56}, +{"dead_diaeresis", 0xfe57}, +{"dead_abovering", 0xfe58}, +{"dead_doubleacute", 0xfe59}, +{"dead_caron", 0xfe5a}, +{"dead_cedilla", 0xfe5b}, +{"dead_ogonek", 0xfe5c}, +{"dead_iota", 0xfe5d}, +{"dead_voiced_sound", 0xfe5e}, +{"dead_semivoiced_sound", 0xfe5f}, +{"dead_belowdot", 0xfe60}, +{"dead_hook", 0xfe61}, +{"dead_horn", 0xfe62}, + {0,0}, }; + diff --git a/vnchextile.h b/vnchextile.h index 09c1b278..29b74840 100644 --- a/vnchextile.h +++ b/vnchextile.h @@ -2,29 +2,29 @@ #define CONCAT(a, b) CONCAT_I(a, b) #define pixel_t CONCAT(uint, CONCAT(BPP, _t)) #ifdef GENERIC -#define NAME generic +#define NAME CONCAT(generic_, BPP) #else #define NAME BPP #endif static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int x, int y, int w, int h, - uint32_t *last_bg32, - uint32_t *last_fg32, + void *last_bg_, + void *last_fg_, int *has_bg, int *has_fg) { uint8_t *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth); pixel_t *irow = (pixel_t *)row; int j, i; - pixel_t *last_bg = (pixel_t *)last_bg32; - pixel_t *last_fg = (pixel_t *)last_fg32; + pixel_t *last_bg = (pixel_t *)last_bg_; + pixel_t *last_fg = (pixel_t *)last_fg_; pixel_t bg = 0; pixel_t fg = 0; int n_colors = 0; int bg_count = 0; int fg_count = 0; int flags = 0; - uint8_t data[(sizeof(pixel_t) + 2) * 16 * 16]; + uint8_t data[(vs->pix_bpp + 2) * 16 * 16]; int n_data = 0; int n_subtiles = 0; @@ -86,7 +86,7 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, flags |= 0x08; irow = (pixel_t *)row; - + for (j = 0; j < h; j++) { int min_x = -1; for (i = 0; i < w; i++) {