Browse Source
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1869 c046a42c-6fe2-441c-8c8c-71466251a162stable-0.10
10 changed files with 1477 additions and 4 deletions
@ -0,0 +1,894 @@ |
|||
#include "vl.h" |
|||
|
|||
#include <sys/socket.h> |
|||
#include <netinet/in.h> |
|||
#include <netinet/tcp.h> |
|||
#include <fcntl.h> |
|||
|
|||
#define VNC_REFRESH_INTERVAL (1000 / 30) |
|||
|
|||
#include "vnc_keysym.h" |
|||
#include "keymaps.c" |
|||
|
|||
typedef struct Buffer |
|||
{ |
|||
size_t capacity; |
|||
size_t offset; |
|||
char *buffer; |
|||
} Buffer; |
|||
|
|||
typedef struct VncState VncState; |
|||
|
|||
typedef int VncReadEvent(VncState *vs, char *data, size_t len); |
|||
|
|||
struct VncState |
|||
{ |
|||
QEMUTimer *timer; |
|||
int lsock; |
|||
int csock; |
|||
DisplayState *ds; |
|||
int need_update; |
|||
int width; |
|||
int height; |
|||
uint64_t dirty_row[768]; |
|||
char *old_data; |
|||
int depth; |
|||
int has_resize; |
|||
int has_hextile; |
|||
Buffer output; |
|||
Buffer input; |
|||
kbd_layout_t *kbd_layout; |
|||
|
|||
VncReadEvent *read_handler; |
|||
size_t read_handler_expect; |
|||
}; |
|||
|
|||
/* TODO
|
|||
1) Get the queue working for IO. |
|||
2) there is some weirdness when using the -S option (the screen is grey |
|||
and not totally invalidated |
|||
3) resolutions > 1024 |
|||
*/ |
|||
|
|||
static void vnc_write(VncState *vs, const void *data, size_t len); |
|||
static void vnc_write_u32(VncState *vs, uint32_t value); |
|||
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_client_read(void *opaque); |
|||
|
|||
static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) |
|||
{ |
|||
VncState *vs = ds->opaque; |
|||
int i; |
|||
|
|||
h += y; |
|||
|
|||
for (; y < h; y++) |
|||
for (i = 0; i < w; i += 16) |
|||
vs->dirty_row[y] |= (1ULL << ((x + i) / 16)); |
|||
} |
|||
|
|||
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, |
|||
int32_t encoding) |
|||
{ |
|||
vnc_write_u16(vs, x); |
|||
vnc_write_u16(vs, y); |
|||
vnc_write_u16(vs, w); |
|||
vnc_write_u16(vs, h); |
|||
|
|||
vnc_write_s32(vs, encoding); |
|||
} |
|||
|
|||
static void vnc_dpy_resize(DisplayState *ds, int w, int h) |
|||
{ |
|||
VncState *vs = ds->opaque; |
|||
|
|||
ds->data = realloc(ds->data, w * h * vs->depth); |
|||
vs->old_data = realloc(vs->old_data, w * h * vs->depth); |
|||
|
|||
if (ds->data == NULL || vs->old_data == NULL) { |
|||
fprintf(stderr, "vnc: memory allocation failed\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
ds->depth = vs->depth * 8; |
|||
ds->width = w; |
|||
ds->height = h; |
|||
ds->linesize = w * vs->depth; |
|||
if (vs->csock != -1 && vs->has_resize) { |
|||
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; |
|||
} |
|||
} |
|||
|
|||
static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h) |
|||
{ |
|||
int i; |
|||
char *row; |
|||
|
|||
vnc_framebuffer_update(vs, x, y, w, h, 0); |
|||
|
|||
row = vs->ds->data + y * vs->ds->linesize + x * vs->depth; |
|||
for (i = 0; i < h; i++) { |
|||
vnc_write(vs, row, w * vs->depth); |
|||
row += vs->ds->linesize; |
|||
} |
|||
} |
|||
|
|||
static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) |
|||
{ |
|||
ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F); |
|||
ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F); |
|||
} |
|||
|
|||
#define BPP 8 |
|||
#include "vnchextile.h" |
|||
#undef BPP |
|||
|
|||
#define BPP 16 |
|||
#include "vnchextile.h" |
|||
#undef BPP |
|||
|
|||
#define BPP 32 |
|||
#include "vnchextile.h" |
|||
#undef BPP |
|||
|
|||
static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h) |
|||
{ |
|||
int i, j; |
|||
int has_fg, has_bg; |
|||
uint32_t last_fg32, last_bg32; |
|||
uint16_t last_fg16, last_bg16; |
|||
uint8_t last_fg8, last_bg8; |
|||
|
|||
vnc_framebuffer_update(vs, x, y, w, h, 5); |
|||
|
|||
has_fg = has_bg = 0; |
|||
for (j = y; j < (y + h); j += 16) { |
|||
for (i = x; i < (x + w); i += 16) { |
|||
switch (vs->depth) { |
|||
case 1: |
|||
send_hextile_tile_8(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), |
|||
&last_bg8, &last_fg8, &has_bg, &has_fg); |
|||
break; |
|||
case 2: |
|||
send_hextile_tile_16(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), |
|||
&last_bg16, &last_fg16, &has_bg, &has_fg); |
|||
break; |
|||
case 4: |
|||
send_hextile_tile_32(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), |
|||
&last_bg32, &last_fg32, &has_bg, &has_fg); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) |
|||
{ |
|||
if (vs->has_hextile) |
|||
send_framebuffer_update_hextile(vs, x, y, w, h); |
|||
else |
|||
send_framebuffer_update_raw(vs, x, y, w, h); |
|||
} |
|||
|
|||
static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) |
|||
{ |
|||
int src, dst; |
|||
char *src_row; |
|||
char *dst_row; |
|||
char *old_row; |
|||
int y = 0; |
|||
int pitch = ds->linesize; |
|||
VncState *vs = ds->opaque; |
|||
|
|||
vnc_update_client(vs); |
|||
|
|||
if (dst_y > src_y) { |
|||
y = h - 1; |
|||
pitch = -pitch; |
|||
} |
|||
|
|||
src = (ds->linesize * (src_y + y) + vs->depth * src_x); |
|||
dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x); |
|||
|
|||
src_row = ds->data + src; |
|||
dst_row = ds->data + dst; |
|||
old_row = vs->old_data + dst; |
|||
|
|||
for (y = 0; y < h; y++) { |
|||
memmove(old_row, src_row, w * vs->depth); |
|||
memmove(dst_row, src_row, w * vs->depth); |
|||
src_row += pitch; |
|||
dst_row += pitch; |
|||
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); |
|||
} |
|||
|
|||
static int find_dirty_height(VncState *vs, int y, int last_x, int x) |
|||
{ |
|||
int h; |
|||
|
|||
for (h = 1; h < (vs->height - y); h++) { |
|||
int tmp_x; |
|||
if (!(vs->dirty_row[y + h] & (1ULL << last_x))) |
|||
break; |
|||
for (tmp_x = last_x; tmp_x < x; tmp_x++) |
|||
vs->dirty_row[y + h] &= ~(1ULL << tmp_x); |
|||
} |
|||
|
|||
return h; |
|||
} |
|||
|
|||
static void vnc_update_client(void *opaque) |
|||
{ |
|||
VncState *vs = opaque; |
|||
|
|||
if (vs->need_update && vs->csock != -1) { |
|||
int y; |
|||
char *row; |
|||
char *old_row; |
|||
uint64_t width_mask; |
|||
int n_rectangles; |
|||
int saved_offset; |
|||
int has_dirty = 0; |
|||
|
|||
width_mask = (1ULL << (vs->width / 16)) - 1; |
|||
|
|||
if (vs->width == 1024) |
|||
width_mask = ~(0ULL); |
|||
|
|||
/* 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 (vs->dirty_row[y] & width_mask) { |
|||
int x; |
|||
char *ptr, *old_ptr; |
|||
|
|||
ptr = row; |
|||
old_ptr = old_row; |
|||
|
|||
for (x = 0; x < vs->ds->width; x += 16) { |
|||
if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) { |
|||
vs->dirty_row[y] &= ~(1ULL << (x / 16)); |
|||
} else { |
|||
has_dirty = 1; |
|||
memcpy(old_ptr, ptr, 16 * vs->depth); |
|||
} |
|||
|
|||
ptr += 16 * vs->depth; |
|||
old_ptr += 16 * vs->depth; |
|||
} |
|||
} |
|||
|
|||
row += vs->ds->linesize; |
|||
old_row += vs->ds->linesize; |
|||
} |
|||
|
|||
if (!has_dirty) { |
|||
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); |
|||
return; |
|||
} |
|||
|
|||
/* 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); |
|||
|
|||
for (y = 0; y < vs->height; y++) { |
|||
int x; |
|||
int last_x = -1; |
|||
for (x = 0; x < vs->width / 16; x++) { |
|||
if (vs->dirty_row[y] & (1ULL << x)) { |
|||
if (last_x == -1) { |
|||
last_x = x; |
|||
} |
|||
vs->dirty_row[y] &= ~(1ULL << 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); |
|||
n_rectangles++; |
|||
} |
|||
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); |
|||
n_rectangles++; |
|||
} |
|||
} |
|||
vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; |
|||
vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; |
|||
vnc_flush(vs); |
|||
|
|||
} |
|||
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); |
|||
} |
|||
|
|||
static void vnc_timer_init(VncState *vs) |
|||
{ |
|||
if (vs->timer == NULL) { |
|||
vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); |
|||
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock)); |
|||
} |
|||
} |
|||
|
|||
static void vnc_dpy_refresh(DisplayState *ds) |
|||
{ |
|||
VncState *vs = ds->opaque; |
|||
vnc_timer_init(vs); |
|||
vga_hw_update(); |
|||
} |
|||
|
|||
static int vnc_listen_poll(void *opaque) |
|||
{ |
|||
VncState *vs = opaque; |
|||
if (vs->csock == -1) |
|||
return 1; |
|||
return 0; |
|||
} |
|||
|
|||
static void buffer_reserve(Buffer *buffer, size_t len) |
|||
{ |
|||
if ((buffer->capacity - buffer->offset) < len) { |
|||
buffer->capacity += (len + 1024); |
|||
buffer->buffer = realloc(buffer->buffer, buffer->capacity); |
|||
if (buffer->buffer == NULL) { |
|||
fprintf(stderr, "vnc: out of memory\n"); |
|||
exit(1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static int buffer_empty(Buffer *buffer) |
|||
{ |
|||
return buffer->offset == 0; |
|||
} |
|||
|
|||
static char *buffer_end(Buffer *buffer) |
|||
{ |
|||
return buffer->buffer + buffer->offset; |
|||
} |
|||
|
|||
static void buffer_reset(Buffer *buffer) |
|||
{ |
|||
buffer->offset = 0; |
|||
} |
|||
|
|||
static void buffer_append(Buffer *buffer, const void *data, size_t len) |
|||
{ |
|||
memcpy(buffer->buffer + buffer->offset, data, len); |
|||
buffer->offset += len; |
|||
} |
|||
|
|||
static int vnc_client_io_error(VncState *vs, int ret) |
|||
{ |
|||
if (ret == 0 || ret == -1) { |
|||
if (ret == -1 && (errno == EINTR || errno == EAGAIN)) |
|||
return 0; |
|||
|
|||
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); |
|||
close(vs->csock); |
|||
vs->csock = -1; |
|||
buffer_reset(&vs->input); |
|||
buffer_reset(&vs->output); |
|||
vs->need_update = 0; |
|||
return 0; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
static void vnc_client_error(VncState *vs) |
|||
{ |
|||
errno = EINVAL; |
|||
vnc_client_io_error(vs, -1); |
|||
} |
|||
|
|||
static void vnc_client_write(void *opaque) |
|||
{ |
|||
ssize_t ret; |
|||
VncState *vs = opaque; |
|||
|
|||
ret = write(vs->csock, vs->output.buffer, vs->output.offset); |
|||
ret = vnc_client_io_error(vs, ret); |
|||
if (!ret) |
|||
return; |
|||
|
|||
memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); |
|||
vs->output.offset -= ret; |
|||
|
|||
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) |
|||
{ |
|||
vs->read_handler = func; |
|||
vs->read_handler_expect = expecting; |
|||
} |
|||
|
|||
static void vnc_client_read(void *opaque) |
|||
{ |
|||
VncState *vs = opaque; |
|||
ssize_t ret; |
|||
|
|||
buffer_reserve(&vs->input, 4096); |
|||
|
|||
ret = read(vs->csock, buffer_end(&vs->input), 4096); |
|||
ret = vnc_client_io_error(vs, ret); |
|||
if (!ret) |
|||
return; |
|||
|
|||
vs->input.offset += ret; |
|||
|
|||
while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { |
|||
size_t len = vs->read_handler_expect; |
|||
int ret; |
|||
|
|||
ret = vs->read_handler(vs, vs->input.buffer, len); |
|||
if (vs->csock == -1) |
|||
return; |
|||
|
|||
if (!ret) { |
|||
memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); |
|||
vs->input.offset -= len; |
|||
} else { |
|||
vs->read_handler_expect = ret; |
|||
} |
|||
} |
|||
} |
|||
|
|||
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); |
|||
} |
|||
|
|||
buffer_append(&vs->output, data, len); |
|||
} |
|||
|
|||
static void vnc_write_s32(VncState *vs, int32_t value) |
|||
{ |
|||
vnc_write_u32(vs, *(uint32_t *)&value); |
|||
} |
|||
|
|||
static void vnc_write_u32(VncState *vs, uint32_t value) |
|||
{ |
|||
uint8_t buf[4]; |
|||
|
|||
buf[0] = (value >> 24) & 0xFF; |
|||
buf[1] = (value >> 16) & 0xFF; |
|||
buf[2] = (value >> 8) & 0xFF; |
|||
buf[3] = value & 0xFF; |
|||
|
|||
vnc_write(vs, buf, 4); |
|||
} |
|||
|
|||
static void vnc_write_u16(VncState *vs, uint16_t value) |
|||
{ |
|||
char buf[2]; |
|||
|
|||
buf[0] = (value >> 8) & 0xFF; |
|||
buf[1] = value & 0xFF; |
|||
|
|||
vnc_write(vs, buf, 2); |
|||
} |
|||
|
|||
static void vnc_write_u8(VncState *vs, uint8_t value) |
|||
{ |
|||
vnc_write(vs, (char *)&value, 1); |
|||
} |
|||
|
|||
static void vnc_flush(VncState *vs) |
|||
{ |
|||
if (vs->output.offset) |
|||
vnc_client_write(vs); |
|||
} |
|||
|
|||
static uint8_t read_u8(char *data, size_t offset) |
|||
{ |
|||
return data[offset]; |
|||
} |
|||
|
|||
static uint16_t read_u16(char *data, size_t offset) |
|||
{ |
|||
return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); |
|||
} |
|||
|
|||
static int32_t read_s32(char *data, size_t offset) |
|||
{ |
|||
return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | |
|||
(data[offset + 2] << 8) | data[offset + 3]); |
|||
} |
|||
|
|||
static uint32_t read_u32(char *data, size_t offset) |
|||
{ |
|||
return ((data[offset] << 24) | (data[offset + 1] << 16) | |
|||
(data[offset + 2] << 8) | data[offset + 3]); |
|||
} |
|||
|
|||
static void client_cut_text(VncState *vs, size_t len, char *text) |
|||
{ |
|||
} |
|||
|
|||
static void pointer_event(VncState *vs, int button_mask, int x, int y) |
|||
{ |
|||
int buttons = 0; |
|||
int dz = 0; |
|||
|
|||
if (button_mask & 0x01) |
|||
buttons |= MOUSE_EVENT_LBUTTON; |
|||
if (button_mask & 0x02) |
|||
buttons |= MOUSE_EVENT_MBUTTON; |
|||
if (button_mask & 0x04) |
|||
buttons |= MOUSE_EVENT_RBUTTON; |
|||
if (button_mask & 0x08) |
|||
dz = -1; |
|||
if (button_mask & 0x10) |
|||
dz = 1; |
|||
|
|||
if (kbd_mouse_is_absolute()) { |
|||
kbd_mouse_event(x * 0x7FFF / vs->ds->width, |
|||
y * 0x7FFF / vs->ds->height, |
|||
dz, buttons); |
|||
} else { |
|||
static int last_x = -1; |
|||
static int last_y = -1; |
|||
|
|||
if (last_x != -1) |
|||
kbd_mouse_event(x - last_x, y - last_y, dz, buttons); |
|||
|
|||
last_x = x; |
|||
last_y = y; |
|||
} |
|||
} |
|||
|
|||
static void key_event(VncState *vs, int down, uint32_t sym) |
|||
{ |
|||
int keycode; |
|||
|
|||
keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF); |
|||
|
|||
if (keycode & 0x80) |
|||
kbd_put_keycode(0xe0); |
|||
if (down) |
|||
kbd_put_keycode(keycode & 0x7f); |
|||
else |
|||
kbd_put_keycode(keycode | 0x80); |
|||
} |
|||
|
|||
static void framebuffer_update_request(VncState *vs, int incremental, |
|||
int x_position, int y_position, |
|||
int w, int 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++) { |
|||
vs->dirty_row[y_position + i] = (1ULL << (vs->ds->width / 16)) - 1; |
|||
if (vs->ds->width == 1024) { |
|||
vs->dirty_row[y_position + i] = ~(0ULL); |
|||
} |
|||
memset(old_row, 42, vs->ds->width * vs->depth); |
|||
old_row += vs->ds->linesize; |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) |
|||
{ |
|||
int i; |
|||
|
|||
vs->has_hextile = 0; |
|||
vs->has_resize = 0; |
|||
vs->ds->dpy_copy = NULL; |
|||
|
|||
for (i = n_encodings - 1; i >= 0; i--) { |
|||
switch (encodings[i]) { |
|||
case 0: /* Raw */ |
|||
vs->has_hextile = 0; |
|||
break; |
|||
case 1: /* CopyRect */ |
|||
vs->ds->dpy_copy = vnc_copy; |
|||
break; |
|||
case 5: /* Hextile */ |
|||
vs->has_hextile = 1; |
|||
break; |
|||
case -223: /* DesktopResize */ |
|||
vs->has_resize = 1; |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void set_pixel_format(VncState *vs, |
|||
int bits_per_pixel, int depth, |
|||
int big_endian_flag, int true_color_flag, |
|||
int red_max, int green_max, int blue_max, |
|||
int red_shift, int green_shift, int blue_shift) |
|||
{ |
|||
switch (bits_per_pixel) { |
|||
case 32: |
|||
case 24: |
|||
vs->depth = 4; |
|||
break; |
|||
case 16: |
|||
vs->depth = 2; |
|||
break; |
|||
case 8: |
|||
vs->depth = 1; |
|||
break; |
|||
default: |
|||
vnc_client_error(vs); |
|||
break; |
|||
} |
|||
|
|||
if (!true_color_flag) |
|||
vnc_client_error(vs); |
|||
|
|||
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); |
|||
|
|||
vga_hw_invalidate(); |
|||
vga_hw_update(); |
|||
} |
|||
|
|||
static int protocol_client_msg(VncState *vs, char *data, size_t len) |
|||
{ |
|||
int i; |
|||
uint16_t limit; |
|||
|
|||
switch (data[0]) { |
|||
case 0: |
|||
if (len == 1) |
|||
return 20; |
|||
|
|||
set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5), |
|||
read_u8(data, 6), read_u8(data, 7), |
|||
read_u16(data, 8), read_u16(data, 10), |
|||
read_u16(data, 12), read_u8(data, 14), |
|||
read_u8(data, 15), read_u8(data, 16)); |
|||
break; |
|||
case 2: |
|||
if (len == 1) |
|||
return 4; |
|||
|
|||
if (len == 4) |
|||
return 4 + (read_u16(data, 2) * 4); |
|||
|
|||
limit = read_u16(data, 2); |
|||
for (i = 0; i < limit; i++) { |
|||
int32_t val = read_s32(data, 4 + (i * 4)); |
|||
memcpy(data + 4 + (i * 4), &val, sizeof(val)); |
|||
} |
|||
|
|||
set_encodings(vs, (int32_t *)(data + 4), limit); |
|||
break; |
|||
case 3: |
|||
if (len == 1) |
|||
return 10; |
|||
|
|||
framebuffer_update_request(vs, |
|||
read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), |
|||
read_u16(data, 6), read_u16(data, 8)); |
|||
break; |
|||
case 4: |
|||
if (len == 1) |
|||
return 8; |
|||
|
|||
key_event(vs, read_u8(data, 1), read_u32(data, 4)); |
|||
break; |
|||
case 5: |
|||
if (len == 1) |
|||
return 6; |
|||
|
|||
pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); |
|||
break; |
|||
case 6: |
|||
if (len == 1) |
|||
return 8; |
|||
|
|||
if (len == 8) |
|||
return 8 + read_u32(data, 4); |
|||
|
|||
client_cut_text(vs, read_u32(data, 4), 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, char *data, size_t len) |
|||
{ |
|||
char pad[3] = { 0, 0, 0 }; |
|||
|
|||
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 */ |
|||
vnc_write_u8(vs, 0); /* big-endian-flag */ |
|||
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 */ |
|||
} 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 */ |
|||
} else if (vs->depth == 1) { |
|||
vnc_write_u16(vs, 3); /* 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 */ |
|||
} |
|||
|
|||
vnc_write(vs, pad, 3); /* padding */ |
|||
|
|||
vnc_write_u32(vs, 4); |
|||
vnc_write(vs, "QEMU", 4); |
|||
vnc_flush(vs); |
|||
|
|||
vnc_read_when(vs, protocol_client_msg, 1); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int protocol_version(VncState *vs, char *version, size_t len) |
|||
{ |
|||
char local[13]; |
|||
int maj, min; |
|||
|
|||
memcpy(local, version, 12); |
|||
local[12] = 0; |
|||
|
|||
if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) { |
|||
vnc_client_error(vs); |
|||
return 0; |
|||
} |
|||
|
|||
vnc_write_u32(vs, 1); /* None */ |
|||
vnc_flush(vs); |
|||
|
|||
vnc_read_when(vs, protocol_client_init, 1); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static void vnc_listen_read(void *opaque) |
|||
{ |
|||
VncState *vs = opaque; |
|||
struct sockaddr_in addr; |
|||
socklen_t addrlen = sizeof(addr); |
|||
|
|||
vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); |
|||
if (vs->csock != -1) { |
|||
fcntl(vs->csock, F_SETFL, O_NONBLOCK); |
|||
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); |
|||
vnc_write(vs, "RFB 003.003\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; |
|||
} |
|||
} |
|||
|
|||
void vnc_display_init(DisplayState *ds, int display) |
|||
{ |
|||
struct sockaddr_in addr; |
|||
int reuse_addr, ret; |
|||
VncState *vs; |
|||
|
|||
vs = qemu_mallocz(sizeof(VncState)); |
|||
if (!vs) |
|||
exit(1); |
|||
|
|||
ds->opaque = vs; |
|||
|
|||
vs->lsock = -1; |
|||
vs->csock = -1; |
|||
vs->depth = 4; |
|||
|
|||
vs->ds = ds; |
|||
|
|||
if (!keyboard_layout) |
|||
keyboard_layout = "en-us"; |
|||
|
|||
vs->kbd_layout = init_keyboard_layout(keyboard_layout); |
|||
if (!vs->kbd_layout) |
|||
exit(1); |
|||
|
|||
vs->lsock = socket(PF_INET, SOCK_STREAM, 0); |
|||
if (vs->lsock == -1) { |
|||
fprintf(stderr, "Could not create socket\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
addr.sin_family = AF_INET; |
|||
addr.sin_port = htons(5900 + display); |
|||
memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); |
|||
|
|||
reuse_addr = 1; |
|||
ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR, |
|||
&reuse_addr, sizeof(reuse_addr)); |
|||
if (ret == -1) { |
|||
fprintf(stderr, "setsockopt() failed\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (bind(vs->lsock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { |
|||
fprintf(stderr, "bind() failed\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (listen(vs->lsock, 1) == -1) { |
|||
fprintf(stderr, "listen() failed\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs); |
|||
if (ret == -1) { |
|||
exit(1); |
|||
} |
|||
|
|||
vs->ds->data = NULL; |
|||
vs->ds->dpy_update = vnc_dpy_update; |
|||
vs->ds->dpy_resize = vnc_dpy_resize; |
|||
vs->ds->dpy_refresh = vnc_dpy_refresh; |
|||
|
|||
memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); |
|||
|
|||
vnc_dpy_resize(vs->ds, 640, 400); |
|||
} |
|||
@ -0,0 +1,275 @@ |
|||
typedef struct { |
|||
const char* name; |
|||
int keysym; |
|||
} name2keysym_t; |
|||
static name2keysym_t name2keysym[]={ |
|||
/* ascii */ |
|||
{ "space", 0x020}, |
|||
{ "exclam", 0x021}, |
|||
{ "quotedbl", 0x022}, |
|||
{ "numbersign", 0x023}, |
|||
{ "dollar", 0x024}, |
|||
{ "percent", 0x025}, |
|||
{ "ampersand", 0x026}, |
|||
{ "apostrophe", 0x027}, |
|||
{ "parenleft", 0x028}, |
|||
{ "parenright", 0x029}, |
|||
{ "asterisk", 0x02a}, |
|||
{ "plus", 0x02b}, |
|||
{ "comma", 0x02c}, |
|||
{ "minus", 0x02d}, |
|||
{ "period", 0x02e}, |
|||
{ "slash", 0x02f}, |
|||
{ "0", 0x030}, |
|||
{ "1", 0x031}, |
|||
{ "2", 0x032}, |
|||
{ "3", 0x033}, |
|||
{ "4", 0x034}, |
|||
{ "5", 0x035}, |
|||
{ "6", 0x036}, |
|||
{ "7", 0x037}, |
|||
{ "8", 0x038}, |
|||
{ "9", 0x039}, |
|||
{ "colon", 0x03a}, |
|||
{ "semicolon", 0x03b}, |
|||
{ "less", 0x03c}, |
|||
{ "equal", 0x03d}, |
|||
{ "greater", 0x03e}, |
|||
{ "question", 0x03f}, |
|||
{ "at", 0x040}, |
|||
{ "A", 0x041}, |
|||
{ "B", 0x042}, |
|||
{ "C", 0x043}, |
|||
{ "D", 0x044}, |
|||
{ "E", 0x045}, |
|||
{ "F", 0x046}, |
|||
{ "G", 0x047}, |
|||
{ "H", 0x048}, |
|||
{ "I", 0x049}, |
|||
{ "J", 0x04a}, |
|||
{ "K", 0x04b}, |
|||
{ "L", 0x04c}, |
|||
{ "M", 0x04d}, |
|||
{ "N", 0x04e}, |
|||
{ "O", 0x04f}, |
|||
{ "P", 0x050}, |
|||
{ "Q", 0x051}, |
|||
{ "R", 0x052}, |
|||
{ "S", 0x053}, |
|||
{ "T", 0x054}, |
|||
{ "U", 0x055}, |
|||
{ "V", 0x056}, |
|||
{ "W", 0x057}, |
|||
{ "X", 0x058}, |
|||
{ "Y", 0x059}, |
|||
{ "Z", 0x05a}, |
|||
{ "bracketleft", 0x05b}, |
|||
{ "backslash", 0x05c}, |
|||
{ "bracketright", 0x05d}, |
|||
{ "asciicircum", 0x05e}, |
|||
{ "underscore", 0x05f}, |
|||
{ "grave", 0x060}, |
|||
{ "a", 0x061}, |
|||
{ "b", 0x062}, |
|||
{ "c", 0x063}, |
|||
{ "d", 0x064}, |
|||
{ "e", 0x065}, |
|||
{ "f", 0x066}, |
|||
{ "g", 0x067}, |
|||
{ "h", 0x068}, |
|||
{ "i", 0x069}, |
|||
{ "j", 0x06a}, |
|||
{ "k", 0x06b}, |
|||
{ "l", 0x06c}, |
|||
{ "m", 0x06d}, |
|||
{ "n", 0x06e}, |
|||
{ "o", 0x06f}, |
|||
{ "p", 0x070}, |
|||
{ "q", 0x071}, |
|||
{ "r", 0x072}, |
|||
{ "s", 0x073}, |
|||
{ "t", 0x074}, |
|||
{ "u", 0x075}, |
|||
{ "v", 0x076}, |
|||
{ "w", 0x077}, |
|||
{ "x", 0x078}, |
|||
{ "y", 0x079}, |
|||
{ "z", 0x07a}, |
|||
{ "braceleft", 0x07b}, |
|||
{ "bar", 0x07c}, |
|||
{ "braceright", 0x07d}, |
|||
{ "asciitilde", 0x07e}, |
|||
|
|||
/* latin 1 extensions */ |
|||
{ "nobreakspace", 0x0a0}, |
|||
{ "exclamdown", 0x0a1}, |
|||
{ "cent", 0x0a2}, |
|||
{ "sterling", 0x0a3}, |
|||
{ "currency", 0x0a4}, |
|||
{ "yen", 0x0a5}, |
|||
{ "brokenbar", 0x0a6}, |
|||
{ "section", 0x0a7}, |
|||
{ "diaeresis", 0x0a8}, |
|||
{ "copyright", 0x0a9}, |
|||
{ "ordfeminine", 0x0aa}, |
|||
{ "guillemotleft", 0x0ab}, |
|||
{ "notsign", 0x0ac}, |
|||
{ "hyphen", 0x0ad}, |
|||
{ "registered", 0x0ae}, |
|||
{ "macron", 0x0af}, |
|||
{ "degree", 0x0b0}, |
|||
{ "plusminus", 0x0b1}, |
|||
{ "twosuperior", 0x0b2}, |
|||
{ "threesuperior", 0x0b3}, |
|||
{ "acute", 0x0b4}, |
|||
{ "mu", 0x0b5}, |
|||
{ "paragraph", 0x0b6}, |
|||
{ "periodcentered", 0x0b7}, |
|||
{ "cedilla", 0x0b8}, |
|||
{ "onesuperior", 0x0b9}, |
|||
{ "masculine", 0x0ba}, |
|||
{ "guillemotright", 0x0bb}, |
|||
{ "onequarter", 0x0bc}, |
|||
{ "onehalf", 0x0bd}, |
|||
{ "threequarters", 0x0be}, |
|||
{ "questiondown", 0x0bf}, |
|||
{ "Agrave", 0x0c0}, |
|||
{ "Aacute", 0x0c1}, |
|||
{ "Acircumflex", 0x0c2}, |
|||
{ "Atilde", 0x0c3}, |
|||
{ "Adiaeresis", 0x0c4}, |
|||
{ "Aring", 0x0c5}, |
|||
{ "AE", 0x0c6}, |
|||
{ "Ccedilla", 0x0c7}, |
|||
{ "Egrave", 0x0c8}, |
|||
{ "Eacute", 0x0c9}, |
|||
{ "Ecircumflex", 0x0ca}, |
|||
{ "Ediaeresis", 0x0cb}, |
|||
{ "Igrave", 0x0cc}, |
|||
{ "Iacute", 0x0cd}, |
|||
{ "Icircumflex", 0x0ce}, |
|||
{ "Idiaeresis", 0x0cf}, |
|||
{ "ETH", 0x0d0}, |
|||
{ "Eth", 0x0d0}, |
|||
{ "Ntilde", 0x0d1}, |
|||
{ "Ograve", 0x0d2}, |
|||
{ "Oacute", 0x0d3}, |
|||
{ "Ocircumflex", 0x0d4}, |
|||
{ "Otilde", 0x0d5}, |
|||
{ "Odiaeresis", 0x0d6}, |
|||
{ "multiply", 0x0d7}, |
|||
{ "Ooblique", 0x0d8}, |
|||
{ "Oslash", 0x0d8}, |
|||
{ "Ugrave", 0x0d9}, |
|||
{ "Uacute", 0x0da}, |
|||
{ "Ucircumflex", 0x0db}, |
|||
{ "Udiaeresis", 0x0dc}, |
|||
{ "Yacute", 0x0dd}, |
|||
{ "THORN", 0x0de}, |
|||
{ "Thorn", 0x0de}, |
|||
{ "ssharp", 0x0df}, |
|||
{ "agrave", 0x0e0}, |
|||
{ "aacute", 0x0e1}, |
|||
{ "acircumflex", 0x0e2}, |
|||
{ "atilde", 0x0e3}, |
|||
{ "adiaeresis", 0x0e4}, |
|||
{ "aring", 0x0e5}, |
|||
{ "ae", 0x0e6}, |
|||
{ "ccedilla", 0x0e7}, |
|||
{ "egrave", 0x0e8}, |
|||
{ "eacute", 0x0e9}, |
|||
{ "ecircumflex", 0x0ea}, |
|||
{ "ediaeresis", 0x0eb}, |
|||
{ "igrave", 0x0ec}, |
|||
{ "iacute", 0x0ed}, |
|||
{ "icircumflex", 0x0ee}, |
|||
{ "idiaeresis", 0x0ef}, |
|||
{ "eth", 0x0f0}, |
|||
{ "ntilde", 0x0f1}, |
|||
{ "ograve", 0x0f2}, |
|||
{ "oacute", 0x0f3}, |
|||
{ "ocircumflex", 0x0f4}, |
|||
{ "otilde", 0x0f5}, |
|||
{ "odiaeresis", 0x0f6}, |
|||
{ "division", 0x0f7}, |
|||
{ "oslash", 0x0f8}, |
|||
{ "ooblique", 0x0f8}, |
|||
{ "ugrave", 0x0f9}, |
|||
{ "uacute", 0x0fa}, |
|||
{ "ucircumflex", 0x0fb}, |
|||
{ "udiaeresis", 0x0fc}, |
|||
{ "yacute", 0x0fd}, |
|||
{ "thorn", 0x0fe}, |
|||
{ "ydiaeresis", 0x0ff}, |
|||
{"EuroSign", 0x20ac}, /* XK_EuroSign */ |
|||
|
|||
/* modifiers */ |
|||
{"Control_L", 0xffe3}, /* XK_Control_L */ |
|||
{"Control_R", 0xffe4}, /* XK_Control_R */ |
|||
{"Alt_L", 0xffe9}, /* XK_Alt_L */ |
|||
{"Alt_R", 0xffea}, /* XK_Alt_R */ |
|||
{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */ |
|||
{"Meta_L", 0xffe7}, /* XK_Meta_L */ |
|||
{"Meta_R", 0xffe8}, /* XK_Meta_R */ |
|||
{"Shift_L", 0xffe1}, /* XK_Shift_L */ |
|||
{"Shift_R", 0xffe2}, /* XK_Shift_R */ |
|||
{"Super_L", 0xffeb}, /* XK_Super_L */ |
|||
{"Super_R", 0xffec}, /* XK_Super_R */ |
|||
|
|||
/* special keys */ |
|||
{"BackSpace", 0xff08}, /* XK_BackSpace */ |
|||
{"Tab", 0xff09}, /* XK_Tab */ |
|||
{"Return", 0xff0d}, /* XK_Return */ |
|||
{"Right", 0xff53}, /* XK_Right */ |
|||
{"Left", 0xff51}, /* XK_Left */ |
|||
{"Up", 0xff52}, /* XK_Up */ |
|||
{"Down", 0xff54}, /* XK_Down */ |
|||
{"Page_Down", 0xff56}, /* XK_Page_Down */ |
|||
{"Page_Up", 0xff55}, /* XK_Page_Up */ |
|||
{"Insert", 0xff63}, /* XK_Insert */ |
|||
{"Delete", 0xffff}, /* XK_Delete */ |
|||
{"Home", 0xff50}, /* XK_Home */ |
|||
{"End", 0xff57}, /* XK_End */ |
|||
{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ |
|||
{"F1", 0xffbe}, /* XK_F1 */ |
|||
{"F2", 0xffbf}, /* XK_F2 */ |
|||
{"F3", 0xffc0}, /* XK_F3 */ |
|||
{"F4", 0xffc1}, /* XK_F4 */ |
|||
{"F5", 0xffc2}, /* XK_F5 */ |
|||
{"F6", 0xffc3}, /* XK_F6 */ |
|||
{"F7", 0xffc4}, /* XK_F7 */ |
|||
{"F8", 0xffc5}, /* XK_F8 */ |
|||
{"F9", 0xffc6}, /* XK_F9 */ |
|||
{"F10", 0xffc7}, /* XK_F10 */ |
|||
{"F11", 0xffc8}, /* XK_F11 */ |
|||
{"F12", 0xffc9}, /* XK_F12 */ |
|||
{"F13", 0xffca}, /* XK_F13 */ |
|||
{"F14", 0xffcb}, /* XK_F14 */ |
|||
{"F15", 0xffcc}, /* XK_F15 */ |
|||
{"Sys_Req", 0xff15}, /* XK_Sys_Req */ |
|||
{"KP_0", 0xffb0}, /* XK_KP_0 */ |
|||
{"KP_1", 0xffb1}, /* XK_KP_1 */ |
|||
{"KP_2", 0xffb2}, /* XK_KP_2 */ |
|||
{"KP_3", 0xffb3}, /* XK_KP_3 */ |
|||
{"KP_4", 0xffb4}, /* XK_KP_4 */ |
|||
{"KP_5", 0xffb5}, /* XK_KP_5 */ |
|||
{"KP_6", 0xffb6}, /* XK_KP_6 */ |
|||
{"KP_7", 0xffb7}, /* XK_KP_7 */ |
|||
{"KP_8", 0xffb8}, /* XK_KP_8 */ |
|||
{"KP_9", 0xffb9}, /* XK_KP_9 */ |
|||
{"KP_Add", 0xffab}, /* XK_KP_Add */ |
|||
{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ |
|||
{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ |
|||
{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ |
|||
{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ |
|||
{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ |
|||
{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ |
|||
{"help", 0xff6a}, /* XK_Help */ |
|||
{"Menu", 0xff67}, /* XK_Menu */ |
|||
{"Print", 0xff61}, /* XK_Print */ |
|||
{"Mode_switch", 0xff7e}, /* XK_Mode_switch */ |
|||
{"Num_Lock", 0xff7f}, /* XK_Num_Lock */ |
|||
{"Pause", 0xff13}, /* XK_Pause */ |
|||
{"Escape", 0xff1b}, /* XK_Escape */ |
|||
{0,0}, |
|||
}; |
|||
@ -0,0 +1,189 @@ |
|||
#define CONCAT_I(a, b) a ## b |
|||
#define CONCAT(a, b) CONCAT_I(a, b) |
|||
#define pixel_t CONCAT(uint, CONCAT(BPP, _t)) |
|||
|
|||
static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, |
|||
int x, int y, int w, int h, |
|||
pixel_t *last_bg, pixel_t *last_fg, |
|||
int *has_bg, int *has_fg) |
|||
{ |
|||
char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth); |
|||
pixel_t *irow = (pixel_t *)row; |
|||
int j, i; |
|||
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]; |
|||
int n_data = 0; |
|||
int n_subtiles = 0; |
|||
|
|||
for (j = 0; j < h; j++) { |
|||
for (i = 0; i < w; i++) { |
|||
switch (n_colors) { |
|||
case 0: |
|||
bg = irow[i]; |
|||
n_colors = 1; |
|||
break; |
|||
case 1: |
|||
if (irow[i] != bg) { |
|||
fg = irow[i]; |
|||
n_colors = 2; |
|||
} |
|||
break; |
|||
case 2: |
|||
if (irow[i] != bg && irow[i] != fg) { |
|||
n_colors = 3; |
|||
} else { |
|||
if (irow[i] == bg) |
|||
bg_count++; |
|||
else if (irow[i] == fg) |
|||
fg_count++; |
|||
} |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
if (n_colors > 2) |
|||
break; |
|||
irow += vs->ds->linesize / sizeof(pixel_t); |
|||
} |
|||
|
|||
if (n_colors > 1 && fg_count > bg_count) { |
|||
pixel_t tmp = fg; |
|||
fg = bg; |
|||
bg = tmp; |
|||
} |
|||
|
|||
if (!*has_bg || *last_bg != bg) { |
|||
flags |= 0x02; |
|||
*has_bg = 1; |
|||
*last_bg = bg; |
|||
} |
|||
|
|||
if (!*has_fg || *last_fg != fg) { |
|||
flags |= 0x04; |
|||
*has_fg = 1; |
|||
*last_fg = fg; |
|||
} |
|||
|
|||
switch (n_colors) { |
|||
case 1: |
|||
n_data = 0; |
|||
break; |
|||
case 2: |
|||
flags |= 0x08; |
|||
|
|||
irow = (pixel_t *)row; |
|||
|
|||
for (j = 0; j < h; j++) { |
|||
int min_x = -1; |
|||
for (i = 0; i < w; i++) { |
|||
if (irow[i] == fg) { |
|||
if (min_x == -1) |
|||
min_x = i; |
|||
} else if (min_x != -1) { |
|||
hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); |
|||
n_data += 2; |
|||
n_subtiles++; |
|||
min_x = -1; |
|||
} |
|||
} |
|||
if (min_x != -1) { |
|||
hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); |
|||
n_data += 2; |
|||
n_subtiles++; |
|||
} |
|||
irow += vs->ds->linesize / sizeof(pixel_t); |
|||
} |
|||
break; |
|||
case 3: |
|||
flags |= 0x18; |
|||
|
|||
irow = (pixel_t *)row; |
|||
|
|||
if (!*has_bg || *last_bg != bg) |
|||
flags |= 0x02; |
|||
|
|||
for (j = 0; j < h; j++) { |
|||
int has_color = 0; |
|||
int min_x = -1; |
|||
pixel_t color; |
|||
|
|||
for (i = 0; i < w; i++) { |
|||
if (!has_color) { |
|||
if (irow[i] == bg) |
|||
continue; |
|||
color = irow[i]; |
|||
min_x = i; |
|||
has_color = 1; |
|||
} else if (irow[i] != color) { |
|||
has_color = 0; |
|||
|
|||
memcpy(data + n_data, &color, sizeof(color)); |
|||
hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); |
|||
n_data += 2 + sizeof(pixel_t); |
|||
n_subtiles++; |
|||
|
|||
min_x = -1; |
|||
if (irow[i] != bg) { |
|||
color = irow[i]; |
|||
min_x = i; |
|||
has_color = 1; |
|||
} |
|||
} |
|||
} |
|||
if (has_color) { |
|||
memcpy(data + n_data, &color, sizeof(color)); |
|||
hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); |
|||
n_data += 2 + sizeof(pixel_t); |
|||
n_subtiles++; |
|||
} |
|||
irow += vs->ds->linesize / sizeof(pixel_t); |
|||
} |
|||
|
|||
/* A SubrectsColoured subtile invalidates the foreground color */ |
|||
*has_fg = 0; |
|||
if (n_data > (w * h * sizeof(pixel_t))) { |
|||
n_colors = 4; |
|||
flags = 0x01; |
|||
*has_bg = 0; |
|||
|
|||
/* we really don't have to invalidate either the bg or fg
|
|||
but we've lost the old values. oh well. */ |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
if (n_colors > 3) { |
|||
flags = 0x01; |
|||
*has_fg = 0; |
|||
*has_bg = 0; |
|||
n_colors = 4; |
|||
} |
|||
|
|||
vnc_write_u8(vs, flags); |
|||
if (n_colors < 4) { |
|||
if (flags & 0x02) |
|||
vnc_write(vs, last_bg, sizeof(pixel_t)); |
|||
if (flags & 0x04) |
|||
vnc_write(vs, last_fg, sizeof(pixel_t)); |
|||
if (n_subtiles) { |
|||
vnc_write_u8(vs, n_subtiles); |
|||
vnc_write(vs, data, n_data); |
|||
} |
|||
} else { |
|||
for (j = 0; j < h; j++) { |
|||
vnc_write(vs, row, w * vs->depth); |
|||
row += vs->ds->linesize; |
|||
} |
|||
} |
|||
} |
|||
|
|||
#undef pixel_t |
|||
#undef CONCAT_I |
|||
#undef CONCAT |
|||
Loading…
Reference in new issue