Merge branch 'unstable' into clipboard

This commit is contained in:
Michael Jumper 2012-04-12 01:19:56 -07:00
commit e691bfb51b
8 changed files with 287 additions and 119 deletions

View File

@ -47,12 +47,6 @@
#define RDP_DEFAULT_PORT 3389 #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 { typedef struct rdp_guac_client_data {
freerdp* rdp_inst; freerdp* rdp_inst;
@ -60,8 +54,27 @@ typedef struct rdp_guac_client_data {
int mouse_button_mask; 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; const guac_layer* current_surface;

View File

@ -54,8 +54,14 @@ typedef struct guac_rdp_bitmap {
*/ */
guac_layer* layer; guac_layer* layer;
/**
* The number of times a bitmap has been used.
*/
int used;
} guac_rdp_bitmap; } 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_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_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_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);

View File

@ -51,5 +51,6 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect); 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_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds); void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
void guac_rdp_gdi_end_paint(rdpContext* context);
#endif #endif

View File

@ -50,9 +50,9 @@ typedef struct guac_rdp_glyph {
rdpGlyph glyph; 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; } guac_rdp_glyph;

View File

@ -152,6 +152,7 @@ boolean rdp_freerdp_pre_connect(freerdp* instance) {
xfree(pointer); xfree(pointer);
/* Set up GDI */ /* Set up GDI */
instance->update->EndPaint = guac_rdp_gdi_end_paint;
instance->update->Palette = guac_rdp_gdi_palette_update; instance->update->Palette = guac_rdp_gdi_palette_update;
instance->update->SetBounds = guac_rdp_gdi_set_bounds; instance->update->SetBounds = guac_rdp_gdi_set_bounds;
@ -199,10 +200,6 @@ boolean rdp_freerdp_post_connect(freerdp* instance) {
client->mouse_handler = rdp_guac_client_mouse_handler; client->mouse_handler = rdp_guac_client_mouse_handler;
client->key_handler = rdp_guac_client_key_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; return true;
} }
@ -400,6 +397,17 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
/* Send connection name */ /* Send connection name */
guac_protocol_send_name(client->socket, settings->window_title); 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 */ /* Success */
return 0; return 0;

View File

@ -53,7 +53,7 @@
#include "client.h" #include "client.h"
#include "rdp_bitmap.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_client* client = ((rdp_freerdp_context*) context)->client;
guac_socket* socket = client->socket; guac_socket* socket = client->socket;
@ -61,9 +61,31 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
/* Allocate buffer */ /* Allocate buffer */
guac_layer* buffer = guac_client_alloc_buffer(client); guac_layer* buffer = guac_client_alloc_buffer(client);
/* Cache image data if present */
if (bitmap->data != NULL) {
/* Create surface from image data */
cairo_surface_t* surface = cairo_image_surface_create_for_data(
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);
/* Free surface */
cairo_surface_destroy(surface);
}
/* Store buffer reference in bitmap */ /* Store buffer reference in bitmap */
((guac_rdp_bitmap*) bitmap)->layer = buffer; ((guac_rdp_bitmap*) bitmap)->layer = buffer;
}
void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
/* Convert image data if present */ /* Convert image data if present */
if (bitmap->data != NULL) { if (bitmap->data != NULL) {
@ -73,29 +95,6 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
context->instance->settings->color_depth, context->instance->settings->color_depth,
32, ((rdp_freerdp_context*) context)->clrconv); 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->width, bitmap->height, 4*bitmap->width);
/* Send surface to buffer */
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) {
/* Free image data if actually alloated */
if (image_buffer != bitmap->data)
free(image_buffer);
}
/* Otherwise, store converted image in bitmap, free any existing */
else {
/* Free existing image, if any */ /* Free existing image, if any */
if (image_buffer != bitmap->data) if (image_buffer != bitmap->data)
free(bitmap->data); free(bitmap->data);
@ -105,7 +104,11 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
} }
} /* No corresponding layer yet - caching is deferred. */
((guac_rdp_bitmap*) bitmap)->layer = NULL;
/* Start at zero usage */
((guac_rdp_bitmap*) bitmap)->used = 0;
} }
@ -117,20 +120,49 @@ void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
int width = bitmap->right - bitmap->left + 1; int width = bitmap->right - bitmap->left + 1;
int height = bitmap->bottom - bitmap->top + 1; int height = bitmap->bottom - bitmap->top + 1;
/* 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_protocol_send_copy(socket,
((guac_rdp_bitmap*) bitmap)->layer, ((guac_rdp_bitmap*) bitmap)->layer,
0, 0, width, height, 0, 0, width, height,
GUAC_COMP_OVER, GUAC_COMP_OVER,
GUAC_DEFAULT_LAYER, bitmap->left, bitmap->top); 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) { void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Free layer, if any */ /* If cached, free buffer */
if (((guac_rdp_bitmap*) bitmap)->layer != NULL) if (((guac_rdp_bitmap*) bitmap)->layer != NULL)
guac_client_free_buffer(client, ((guac_rdp_bitmap*) bitmap)->layer); guac_client_free_buffer(client, ((guac_rdp_bitmap*) bitmap)->layer);
} }
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean primary) { void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean primary) {
@ -139,10 +171,18 @@ void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean
if (primary) if (primary)
((rdp_guac_client_data*) client->data)->current_surface ((rdp_guac_client_data*) client->data)->current_surface
= GUAC_DEFAULT_LAYER; = 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 ((rdp_guac_client_data*) client->data)->current_surface
= ((guac_rdp_bitmap*) bitmap)->layer; = ((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) { void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, uint8* data, int width, int height, int bpp, int length, boolean compressed) {

View File

@ -159,8 +159,6 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
guac_socket* socket = client->socket; guac_socket* socket = client->socket;
guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap; guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap;
if (bitmap->layer != NULL) {
switch (memblt->bRop) { switch (memblt->bRop) {
/* If blackness, send black rectangle */ /* If blackness, send black rectangle */
@ -180,12 +178,46 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
/* If operation is just SRC, simply copy */ /* If operation is just SRC, simply copy */
case 0xCC: case 0xCC:
/* 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, guac_protocol_send_copy(socket,
bitmap->layer, bitmap->layer,
memblt->nXSrc, memblt->nYSrc, memblt->nXSrc, memblt->nYSrc,
memblt->nWidth, memblt->nHeight, memblt->nWidth, memblt->nHeight,
GUAC_COMP_OVER, GUAC_COMP_OVER,
current_layer, memblt->nLeftRect, memblt->nTopRect); current_layer, memblt->nLeftRect, memblt->nTopRect);
/* Increment usage counter */
((guac_rdp_bitmap*) bitmap)->used++;
break; break;
/* If whiteness, send white rectangle */ /* If whiteness, send white rectangle */
@ -201,6 +233,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
/* Otherwise, use transfer */ /* Otherwise, use transfer */
default: 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, guac_protocol_send_transfer(socket,
bitmap->layer, bitmap->layer,
memblt->nXSrc, memblt->nYSrc, memblt->nXSrc, memblt->nYSrc,
@ -208,9 +245,10 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
guac_rdp_rop3_transfer_function(client, memblt->bRop), guac_rdp_rop3_transfer_function(client, memblt->bRop),
current_layer, memblt->nLeftRect, memblt->nTopRect); current_layer, memblt->nLeftRect, memblt->nTopRect);
} /* Increment usage counter */
((guac_rdp_bitmap*) bitmap)->used++;
} /* end if layer not NULL */ }
} }
@ -264,3 +302,8 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
} }
void guac_rdp_gdi_end_paint(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
guac_socket_flush(client->socket);
}

View File

@ -39,17 +39,13 @@
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/error.h>
#include "client.h" #include "client.h"
#include "rdp_glyph.h" #include "rdp_glyph.h"
void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) { 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 x, y, i;
int stride; int stride;
unsigned char* image_buffer; unsigned char* image_buffer;
@ -59,8 +55,6 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
int width = glyph->cx; int width = glyph->cx;
int height = glyph->cy; int height = glyph->cy;
cairo_surface_t* surface;
/* Init Cairo buffer */ /* Init Cairo buffer */
stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
image_buffer = malloc(height*stride); image_buffer = malloc(height*stride);
@ -97,16 +91,9 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
} }
} }
surface = cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride); /* Store glyph surface */
guac_protocol_send_png(socket, GUAC_COMP_SRC, layer, 0, 0, surface); ((guac_rdp_glyph*) glyph)->surface = cairo_image_surface_create_for_data(
guac_socket_flush(socket); image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
/* Free surface */
cairo_surface_destroy(surface);
free(image_buffer);
/* Store layer */
((guac_rdp_glyph*) glyph)->layer = layer;
} }
@ -114,69 +101,139 @@ void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client* client = ((rdp_freerdp_context*) context)->client;
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; 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 */ /* Do not attempt to draw glyphs if glyph drawing is not begun */
guac_protocol_send_rect(client->socket, ((guac_rdp_glyph*) glyph)->layer, if (guac_client_data->glyph_cairo == NULL)
0, 0, glyph->cx, glyph->cy); return;
guac_protocol_send_cfill(client->socket, /* Use glyph as mask */
GUAC_COMP_ATOP, ((guac_rdp_glyph*) glyph)->layer, cairo_mask_surface(
guac_client_data->foreground.red, guac_client_data->glyph_cairo,
guac_client_data->foreground.green, ((guac_rdp_glyph*) glyph)->surface, x, y);
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);
} }
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) { 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, void guac_rdp_glyph_begindraw(rdpContext* context,
int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) { int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) {
guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client* client = ((rdp_freerdp_context*) context)->client;
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; rdp_guac_client_data* guac_client_data =
const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; (rdp_guac_client_data*) client->data;
bgcolor = freerdp_color_convert_var(bgcolor,
context->instance->settings->color_depth, 32,
((rdp_freerdp_context*) context)->clrconv);
/* Convert foreground color */
fgcolor = freerdp_color_convert_var(fgcolor, fgcolor = freerdp_color_convert_var(fgcolor,
context->instance->settings->color_depth, 32, context->instance->settings->color_depth, 32,
((rdp_freerdp_context*) context)->clrconv); ((rdp_freerdp_context*) context)->clrconv);
guac_client_data->foreground.blue = fgcolor & 0x0000FF; /* Fill background with color if specified */
guac_client_data->foreground.green = (fgcolor & 0x00FF00) >> 8; if (width != 0 && height != 0) {
guac_client_data->foreground.red = (fgcolor & 0xFF0000) >> 16;
guac_client_data->background.blue = bgcolor & 0x0000FF; /* Prepare for opaque glyphs */
guac_client_data->background.green = (bgcolor & 0x00FF00) >> 8; guac_client_data->glyph_surface =
guac_client_data->background.red = (bgcolor & 0xFF0000) >> 16; guac_client_data->opaque_glyph_surface;
/* Paint background on destination */ /* Create cairo instance */
guac_protocol_send_rect(client->socket, current_layer, 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);
/* Fill background */
cairo_rectangle(guac_client_data->glyph_cairo,
x, y, width, height); x, y, width, height);
guac_protocol_send_cfill(client->socket, cairo_set_source_rgb(guac_client_data->glyph_cairo,
GUAC_COMP_OVER, current_layer, ((bgcolor & 0xFF0000) >> 16) / 255.0,
guac_client_data->background.red, ((bgcolor & 0x00FF00) >> 8 ) / 255.0,
guac_client_data->background.green, ( bgcolor & 0x0000FF ) / 255.0);
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, void guac_rdp_glyph_enddraw(rdpContext* context,
int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) { 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);
} }