From a8fac571592b9405da010989d0416358f9c822c2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Sep 2010 00:15:36 -0700 Subject: [PATCH] Client-side cursor (initial) --- guacamole/proxy/protocol.c | 64 +++++++++++++++++++++++++++++++++ guacamole/proxy/protocol.h | 1 + guacamole/proxy/proxy.c | 74 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 136 insertions(+), 3 deletions(-) diff --git a/guacamole/proxy/protocol.c b/guacamole/proxy/protocol.c index ed525dc0b..6ee3e86fa 100644 --- a/guacamole/proxy/protocol.c +++ b/guacamole/proxy/protocol.c @@ -179,6 +179,70 @@ void guac_send_png(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) } +void guac_send_cursor(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h) { + + png_structp png; + png_infop png_info; + + /* Write image */ + + /* Set up PNG writer */ + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + perror("Error initializing libpng write structure"); + return; + } + + png_info = png_create_info_struct(png); + if (!png_info) { + perror("Error initializing libpng info structure"); + png_destroy_write_struct(&png, NULL); + return; + } + + /* Set error handler */ + if (setjmp(png_jmpbuf(png))) { + perror("Error setting handler"); + png_destroy_write_struct(&png, &png_info); + return; + } + + png_set_write_fn(png, io, __guac_write_png, __guac_write_flush); + + /* Set PNG IHDR */ + png_set_IHDR( + png, + png_info, + w, + h, + 8, + PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT + ); + + guac_write_string(io, "cursor:"); + guac_write_int(io, x); + guac_write_string(io, ","); + guac_write_int(io, y); + guac_write_string(io, ","); + png_set_rows(png, png_info, png_rows); + png_write_png(png, png_info, PNG_TRANSFORM_IDENTITY, NULL); + + if (guac_flush_base64(io) < 0) { + perror("Error flushing PNG"); + png_error(png, "Error flushing PNG"); + return; + } + + png_destroy_write_struct(&png, &png_info); + + guac_write_string(io, ";"); + +} + + int __guac_fill_instructionbuf(GUACIO* io) { int retval; diff --git a/guacamole/proxy/protocol.h b/guacamole/proxy/protocol.h index d9d6ed27e..a0bf755a6 100644 --- a/guacamole/proxy/protocol.h +++ b/guacamole/proxy/protocol.h @@ -22,6 +22,7 @@ void guac_send_name(GUACIO* io, const char* name); void guac_send_size(GUACIO* io, int w, int h); void guac_send_copy(GUACIO* io, int srcx, int srcy, int w, int h, int dstx, int dsty); void guac_send_png(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h); +void guac_send_cursor(GUACIO* io, int x, int y, png_byte** png_rows, int w, int h); int guac_instructions_waiting(GUACIO* io); guac_instruction* guac_read_instruction(GUACIO* io); diff --git a/guacamole/proxy/proxy.c b/guacamole/proxy/proxy.c index f29b31ca9..f12d38083 100644 --- a/guacamole/proxy/proxy.c +++ b/guacamole/proxy/proxy.c @@ -13,7 +13,67 @@ char __guac_password[] = "potato"; char* __GUAC_VNC_TAG_IO = "GUACIO"; char* __GUAC_VNC_TAG_PNG_BUFFER = "PNG_BUFFER"; +char* __GUAC_VNC_TAG_PNG_BUFFER_ALPHA = "PNG_BUFFER_ALPHA"; +void guac_vnc_cursor(rfbClient* client, int x, int y, int w, int h, int bpp) { + + int dx, dy; + + GUACIO* io = rfbClientGetClientData(client, __GUAC_VNC_TAG_IO); + png_byte** png_buffer = rfbClientGetClientData(client, __GUAC_VNC_TAG_PNG_BUFFER_ALPHA); + png_byte* row; + + png_byte** png_row_current = png_buffer; + + unsigned int bytesPerRow = bpp * w; + unsigned char* fb_row_current = client->rcSource; + unsigned char* fb_mask = client->rcMask; + unsigned char* fb_row; + unsigned int v; + + /* Copy image data from VNC client to PNG */ + for (dy = 0; dy> client->format.redShift) * 256 / (client->format.redMax+1); + *(row++) = (v >> client->format.greenShift) * 256 / (client->format.greenMax+1); + *(row++) = (v >> client->format.blueShift) * 256 / (client->format.blueMax+1); + + /* Handle mask */ + if (*(fb_mask++)) + *(row++) = 255; + else + *(row++) = 0; + + fb_row += bpp; + + } + } + + /* SEND CURSOR */ + guac_send_cursor(io, x, y, png_buffer, w, h); + guac_flush(io); + +} void guac_vnc_update(rfbClient* client, int x, int y, int w, int h) { int dx, dy; @@ -88,7 +148,7 @@ char* guac_vnc_get_password(rfbClient* client) { } -png_byte** guac_alloc_png_buffer(int w, int h) { +png_byte** guac_alloc_png_buffer(int w, int h, int bpp) { png_byte** png_buffer; png_byte* row; @@ -97,7 +157,7 @@ png_byte** guac_alloc_png_buffer(int w, int h) { /* Allocate rows for PNG */ png_buffer = (png_byte**) malloc(h * sizeof(png_byte*)); for (y=0; yGotFrameBufferUpdate = guac_vnc_update; /*rfb_client->GotCopyRect = guac_vnc_copyrect;*/ + /* Enable client-side cursor */ + rfb_client->GotCursorShape = guac_vnc_cursor; + rfb_client->appData.useRemoteCursor = TRUE; + /* Password */ rfb_client->GetPassword = guac_vnc_get_password; @@ -146,11 +211,13 @@ void proxy(int client_fd) { fprintf(stderr, "SUCCESS.\n"); } - png_buffer = guac_alloc_png_buffer(rfb_client->width, rfb_client->height); + png_buffer = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 3); /* No-alpha */ + png_buffer_alpha = guac_alloc_png_buffer(rfb_client->width, rfb_client->height, 4); /* With alpha */ /* Store Guac data in client */ rfbClientSetClientData(rfb_client, __GUAC_VNC_TAG_IO, io); rfbClientSetClientData(rfb_client, __GUAC_VNC_TAG_PNG_BUFFER, png_buffer); + rfbClientSetClientData(rfb_client, __GUAC_VNC_TAG_PNG_BUFFER_ALPHA, png_buffer_alpha); /* Send name */ guac_send_name(io, rfb_client->desktopName); @@ -210,6 +277,7 @@ void proxy(int client_fd) { /* Free PNG data */ guac_free_png_buffer(png_buffer, rfb_client->height); + guac_free_png_buffer(png_buffer_alpha, rfb_client->height); /* Clean up VNC client*/