#define RDP_DEFAULT_PORT 3389
-typedef struct guac_rdp_color {
- int red;
- int green;
- int blue;
-} guac_rdp_color;
-
typedef struct rdp_guac_client_data {
freerdp* rdp_inst;
int mouse_button_mask;
- guac_rdp_color foreground;
- guac_rdp_color background;
+ /**
+ * Cairo surface which will receive all TRANSPARENT glyphs.
+ */
+ cairo_surface_t* trans_glyph_surface;
+
+ /**
+ * Cairo surface which will receive all OPAQUE glyphs.
+ */
+ cairo_surface_t* opaque_glyph_surface;
+
+ /**
+ * The current Cairo surface which will receive all drawn glyphs,
+ * depending on whether we are currently drawing transparent or
+ * opaque glyphs.
+ */
+ cairo_surface_t* glyph_surface;
+
+ /**
+ * Cairo instance for drawing to the current glyph surface.
+ */
+ cairo_t* glyph_cairo;
const guac_layer* current_surface;
*/
guac_layer* layer;
+ /**
+ * The number of times a bitmap has been used.
+ */
+ int used;
+
} guac_rdp_bitmap;
+void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, uint8* data, int width, int height, int bpp, int length, boolean compressed);
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect);
void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
+void guac_rdp_gdi_end_paint(rdpContext* context);
#endif
rdpGlyph glyph;
/**
- * Guacamole layer containing cached image data.
+ * Cairo surface layer containing cached image data.
*/
- guac_layer* layer;
+ cairo_surface_t* surface;
} guac_rdp_glyph;
xfree(pointer);
/* Set up GDI */
+ instance->update->EndPaint = guac_rdp_gdi_end_paint;
instance->update->Palette = guac_rdp_gdi_palette_update;
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
client->mouse_handler = rdp_guac_client_mouse_handler;
client->key_handler = rdp_guac_client_key_handler;
- /* Send size */
- guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER,
- instance->settings->width, instance->settings->height);
-
return true;
}
/* Send connection name */
guac_protocol_send_name(client->socket, settings->window_title);
+ /* Send size */
+ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER,
+ settings->width, settings->height);
+
+ /* Create glyph surfaces */
+ guac_client_data->opaque_glyph_surface = cairo_image_surface_create(
+ CAIRO_FORMAT_RGB24, settings->width, settings->height);
+
+ guac_client_data->trans_glyph_surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32, settings->width, settings->height);
+
/* Success */
return 0;
#include "client.h"
#include "rdp_bitmap.h"
-void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
+void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_socket* socket = client->socket;
/* Allocate buffer */
guac_layer* buffer = guac_client_alloc_buffer(client);
- /* Store buffer reference in bitmap */
- ((guac_rdp_bitmap*) bitmap)->layer = buffer;
-
- /* Convert image data if present */
+ /* Cache image data if present */
if (bitmap->data != NULL) {
- /* Convert image data to 32-bit RGB */
- unsigned char* image_buffer = freerdp_image_convert(bitmap->data, NULL,
- bitmap->width, bitmap->height,
- context->instance->settings->color_depth,
- 32, ((rdp_freerdp_context*) context)->clrconv);
-
/* Create surface from image data */
cairo_surface_t* surface = cairo_image_surface_create_for_data(
- image_buffer, CAIRO_FORMAT_RGB24,
+ bitmap->data, CAIRO_FORMAT_RGB24,
bitmap->width, bitmap->height, 4*bitmap->width);
/* Send surface to buffer */
- guac_protocol_send_png(socket, GUAC_COMP_SRC, buffer, 0, 0, surface);
+ guac_protocol_send_png(socket,
+ GUAC_COMP_SRC, buffer, 0, 0, surface);
/* Free surface */
cairo_surface_destroy(surface);
- /* If ephemeral, just free image data */
- if (!bitmap->ephemeral) {
+ }
+
+ /* Store buffer reference in bitmap */
+ ((guac_rdp_bitmap*) bitmap)->layer = buffer;
+
+}
- /* Free image data if actually alloated */
- if (image_buffer != bitmap->data)
- free(image_buffer);
- }
+void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
- /* Otherwise, store converted image in bitmap, free any existing */
- else {
+ /* Convert image data if present */
+ if (bitmap->data != NULL) {
- /* Free existing image, if any */
- if (image_buffer != bitmap->data)
- free(bitmap->data);
+ /* Convert image data to 32-bit RGB */
+ unsigned char* image_buffer = freerdp_image_convert(bitmap->data, NULL,
+ bitmap->width, bitmap->height,
+ context->instance->settings->color_depth,
+ 32, ((rdp_freerdp_context*) context)->clrconv);
- /* Store converted image in bitmap */
- bitmap->data = image_buffer;
+ /* Free existing image, if any */
+ if (image_buffer != bitmap->data)
+ free(bitmap->data);
- }
+ /* Store converted image in bitmap */
+ bitmap->data = image_buffer;
}
+ /* No corresponding layer yet - caching is deferred. */
+ ((guac_rdp_bitmap*) bitmap)->layer = NULL;
+
+ /* Start at zero usage */
+ ((guac_rdp_bitmap*) bitmap)->used = 0;
+
}
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
int width = bitmap->right - bitmap->left + 1;
int height = bitmap->bottom - bitmap->top + 1;
- guac_protocol_send_copy(socket,
- ((guac_rdp_bitmap*) bitmap)->layer,
- 0, 0, width, height,
- GUAC_COMP_OVER,
- GUAC_DEFAULT_LAYER, bitmap->left, bitmap->top);
+ /* If not cached, cache if necessary */
+ if (((guac_rdp_bitmap*) bitmap)->layer == NULL
+ && ((guac_rdp_bitmap*) bitmap)->used >= 1)
+ guac_rdp_cache_bitmap(context, bitmap);
+
+ /* If cached, retrieve from cache */
+ if (((guac_rdp_bitmap*) bitmap)->layer != NULL)
+ guac_protocol_send_copy(socket,
+ ((guac_rdp_bitmap*) bitmap)->layer,
+ 0, 0, width, height,
+ GUAC_COMP_OVER,
+ GUAC_DEFAULT_LAYER, bitmap->left, bitmap->top);
+
+ /* Otherwise, draw with stored image data */
+ else if (bitmap->data != NULL) {
+
+ /* Create surface from image data */
+ cairo_surface_t* surface = cairo_image_surface_create_for_data(
+ bitmap->data, CAIRO_FORMAT_RGB24,
+ width, height, 4*bitmap->width);
+
+ /* Send surface to buffer */
+ guac_protocol_send_png(socket,
+ GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
+ bitmap->left, bitmap->top, surface);
+
+ /* Free surface */
+ cairo_surface_destroy(surface);
+
+ }
+
+ /* Increment usage counter */
+ ((guac_rdp_bitmap*) bitmap)->used++;
}
void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
- /* Free layer, if any */
+ /* If cached, free buffer */
if (((guac_rdp_bitmap*) bitmap)->layer != NULL)
guac_client_free_buffer(client, ((guac_rdp_bitmap*) bitmap)->layer);
+
}
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean primary) {
if (primary)
((rdp_guac_client_data*) client->data)->current_surface
= GUAC_DEFAULT_LAYER;
- else
+
+ else {
+
+ /* If not available as a surface, make available. */
+ if (((guac_rdp_bitmap*) bitmap)->layer == NULL)
+ guac_rdp_cache_bitmap(context, bitmap);
+
((rdp_guac_client_data*) client->data)->current_surface
= ((guac_rdp_bitmap*) bitmap)->layer;
+ }
+
}
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, uint8* data, int width, int height, int bpp, int length, boolean compressed) {
guac_socket* socket = client->socket;
guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap;
- if (bitmap->layer != NULL) {
-
- switch (memblt->bRop) {
+ switch (memblt->bRop) {
/* If blackness, send black rectangle */
case 0x00:
/* If operation is just SRC, simply copy */
case 0xCC:
- guac_protocol_send_copy(socket,
- bitmap->layer,
- memblt->nXSrc, memblt->nYSrc,
- memblt->nWidth, memblt->nHeight,
- GUAC_COMP_OVER,
- current_layer, memblt->nLeftRect, memblt->nTopRect);
+
+ /* If not cached, cache if necessary */
+ if (((guac_rdp_bitmap*) bitmap)->layer == NULL
+ && ((guac_rdp_bitmap*) bitmap)->used >= 1)
+ guac_rdp_cache_bitmap(context, memblt->bitmap);
+
+ /* If not cached, send as PNG */
+ if (bitmap->layer == NULL) {
+ if (memblt->bitmap->data != NULL) {
+
+ /* Create surface from image data */
+ cairo_surface_t* surface = cairo_image_surface_create_for_data(
+ memblt->bitmap->data + 4*(memblt->nXSrc + memblt->nYSrc*memblt->bitmap->width),
+ CAIRO_FORMAT_RGB24,
+ memblt->nWidth, memblt->nHeight,
+ 4*memblt->bitmap->width);
+
+ /* Send surface to buffer */
+ guac_protocol_send_png(socket,
+ GUAC_COMP_OVER, current_layer,
+ memblt->nLeftRect, memblt->nTopRect, surface);
+
+ /* Free surface */
+ cairo_surface_destroy(surface);
+
+ }
+ }
+
+ /* Otherwise, copy */
+ else
+ guac_protocol_send_copy(socket,
+ bitmap->layer,
+ memblt->nXSrc, memblt->nYSrc,
+ memblt->nWidth, memblt->nHeight,
+ GUAC_COMP_OVER,
+ current_layer, memblt->nLeftRect, memblt->nTopRect);
+
+ /* Increment usage counter */
+ ((guac_rdp_bitmap*) bitmap)->used++;
+
break;
/* If whiteness, send white rectangle */
/* Otherwise, use transfer */
default:
+
+ /* If not available as a surface, make available. */
+ if (bitmap->layer == NULL)
+ guac_rdp_cache_bitmap(context, memblt->bitmap);
+
guac_protocol_send_transfer(socket,
bitmap->layer,
memblt->nXSrc, memblt->nYSrc,
guac_rdp_rop3_transfer_function(client, memblt->bRop),
current_layer, memblt->nLeftRect, memblt->nTopRect);
- }
+ /* Increment usage counter */
+ ((guac_rdp_bitmap*) bitmap)->used++;
- } /* end if layer not NULL */
+ }
}
}
+void guac_rdp_gdi_end_paint(rdpContext* context) {
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ guac_socket_flush(client->socket);
+}
+
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
+#include <guacamole/error.h>
#include "client.h"
#include "rdp_glyph.h"
void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
- /* Allocate buffer */
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_socket* socket = client->socket;
- guac_layer* layer = guac_client_alloc_buffer(client);
-
int x, y, i;
int stride;
unsigned char* image_buffer;
int width = glyph->cx;
int height = glyph->cy;
- cairo_surface_t* surface;
-
/* Init Cairo buffer */
stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
image_buffer = malloc(height*stride);
}
}
- surface = cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
- guac_protocol_send_png(socket, GUAC_COMP_SRC, layer, 0, 0, surface);
- guac_socket_flush(socket);
-
- /* Free surface */
- cairo_surface_destroy(surface);
- free(image_buffer);
-
- /* Store layer */
- ((guac_rdp_glyph*) glyph)->layer = layer;
+ /* Store glyph surface */
+ ((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
+ image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
}
guac_client* client = ((rdp_freerdp_context*) context)->client;
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
- const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
-
- /* Colorize glyph */
- guac_protocol_send_rect(client->socket, ((guac_rdp_glyph*) glyph)->layer,
- 0, 0, glyph->cx, glyph->cy);
-
- guac_protocol_send_cfill(client->socket,
- GUAC_COMP_ATOP, ((guac_rdp_glyph*) glyph)->layer,
- guac_client_data->foreground.red,
- guac_client_data->foreground.green,
- guac_client_data->foreground.blue,
- 255);
-
- /* Draw glyph */
- guac_protocol_send_copy(client->socket,
- ((guac_rdp_glyph*) glyph)->layer, 0, 0, glyph->cx, glyph->cy,
- GUAC_COMP_OVER, current_layer, x, y);
+
+ /* Do not attempt to draw glyphs if glyph drawing is not begun */
+ if (guac_client_data->glyph_cairo == NULL)
+ return;
+
+ /* Use glyph as mask */
+ cairo_mask_surface(
+ guac_client_data->glyph_cairo,
+ ((guac_rdp_glyph*) glyph)->surface, x, y);
}
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) {
- guac_client* client = ((rdp_freerdp_context*) context)->client;
- guac_client_free_buffer(client, ((guac_rdp_glyph*) glyph)->layer);
+
+ unsigned char* image_buffer = cairo_image_surface_get_data(
+ ((guac_rdp_glyph*) glyph)->surface);
+
+ /* Free surface */
+ cairo_surface_destroy(((guac_rdp_glyph*) glyph)->surface);
+ free(image_buffer);
+
}
void guac_rdp_glyph_begindraw(rdpContext* context,
int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
- rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
- const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
-
- bgcolor = freerdp_color_convert_var(bgcolor,
- context->instance->settings->color_depth, 32,
- ((rdp_freerdp_context*) context)->clrconv);
+ rdp_guac_client_data* guac_client_data =
+ (rdp_guac_client_data*) client->data;
+ /* Convert foreground color */
fgcolor = freerdp_color_convert_var(fgcolor,
context->instance->settings->color_depth, 32,
((rdp_freerdp_context*) context)->clrconv);
- guac_client_data->foreground.blue = fgcolor & 0x0000FF;
- guac_client_data->foreground.green = (fgcolor & 0x00FF00) >> 8;
- guac_client_data->foreground.red = (fgcolor & 0xFF0000) >> 16;
+ /* Fill background with color if specified */
+ if (width != 0 && height != 0) {
+
+ /* Prepare for opaque glyphs */
+ guac_client_data->glyph_surface =
+ guac_client_data->opaque_glyph_surface;
+
+ /* Create cairo instance */
+ guac_client_data->glyph_cairo = cairo_create(
+ guac_client_data->glyph_surface);
+
+ /* Convert background color */
+ bgcolor = freerdp_color_convert_var(bgcolor,
+ context->instance->settings->color_depth, 32,
+ ((rdp_freerdp_context*) context)->clrconv);
- guac_client_data->background.blue = bgcolor & 0x0000FF;
- guac_client_data->background.green = (bgcolor & 0x00FF00) >> 8;
- guac_client_data->background.red = (bgcolor & 0xFF0000) >> 16;
+ /* Fill background */
+ cairo_rectangle(guac_client_data->glyph_cairo,
+ x, y, width, height);
- /* Paint background on destination */
- guac_protocol_send_rect(client->socket, current_layer,
- x, y, width, height);
+ cairo_set_source_rgb(guac_client_data->glyph_cairo,
+ ((bgcolor & 0xFF0000) >> 16) / 255.0,
+ ((bgcolor & 0x00FF00) >> 8 ) / 255.0,
+ ( bgcolor & 0x0000FF ) / 255.0);
- guac_protocol_send_cfill(client->socket,
- GUAC_COMP_OVER, current_layer,
- guac_client_data->background.red,
- guac_client_data->background.green,
- guac_client_data->background.blue,
- 255);
+ cairo_fill(guac_client_data->glyph_cairo);
+
+ }
+
+ /* Otherwise, prepare for transparent glyphs */
+ else {
+
+ /* Select transparent glyph surface */
+ guac_client_data->glyph_surface =
+ guac_client_data->trans_glyph_surface;
+
+ guac_client_data->glyph_cairo = cairo_create(
+ guac_client_data->glyph_surface);
+
+ /* Clear surface */
+ cairo_set_operator(guac_client_data->glyph_cairo,
+ CAIRO_OPERATOR_SOURCE);
+
+ cairo_set_source_rgba(guac_client_data->glyph_cairo, 0, 0, 0, 0);
+ cairo_paint(guac_client_data->glyph_cairo);
+
+ /* Restore operator */
+ cairo_set_operator(guac_client_data->glyph_cairo,
+ CAIRO_OPERATOR_OVER);
+
+ }
+
+ /* Prepare for glyph drawing */
+ cairo_set_source_rgb(guac_client_data->glyph_cairo,
+ ((fgcolor & 0xFF0000) >> 16) / 255.0,
+ ((fgcolor & 0x00FF00) >> 8 ) / 255.0,
+ ( fgcolor & 0x0000FF ) / 255.0);
}
void guac_rdp_glyph_enddraw(rdpContext* context,
int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) {
- /* UNUSED */
+
+ guac_client* client = ((rdp_freerdp_context*) context)->client;
+ rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
+ const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface;
+
+ /* Use glyph surface to provide image data for glyph rectangle */
+ cairo_surface_t* glyph_surface = guac_client_data->glyph_surface;
+ int stride = cairo_image_surface_get_stride(glyph_surface);
+
+ /* Calculate bounds */
+ int max_width = cairo_image_surface_get_width(glyph_surface) - x;
+ int max_height = cairo_image_surface_get_height(glyph_surface) - y;
+
+ /* Ensure dimensions of glyph do not exceed bounds */
+ if (width > max_width) width = max_width;
+ if (height > max_height) height = max_height;
+
+ /* Ensure data is ready */
+ cairo_surface_flush(glyph_surface);
+
+ /* Create surface for subsection with text */
+ cairo_surface_t* surface = cairo_image_surface_create_for_data(
+ cairo_image_surface_get_data(glyph_surface) + 4*x + y*stride,
+ cairo_image_surface_get_format(glyph_surface),
+ width, height, stride);
+
+ /* Send surface with all glyphs to layer */
+ guac_protocol_send_png(client->socket,
+ GUAC_COMP_OVER, current_layer, x, y,
+ surface);
+
+ /* Destroy surface */
+ cairo_surface_destroy(surface);
+
+ /* Destroy cairo instance */
+ cairo_destroy(guac_client_data->glyph_cairo);
+
}