From 185d23fb5efef05586b75c02f2a4f5619db45a98 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 25 Oct 2011 22:01:53 -0700 Subject: [PATCH] Migrated queue from layer to client, restored GUAC_DEFAULT_LAYER. --- libguac/include/client.h | 215 +++++++++++++++++++- libguac/include/protocol.h | 123 ------------ libguac/src/client.c | 391 +++++++++++++++++++++++++++++++++---- libguac/src/protocol.c | 329 ------------------------------- 4 files changed, 569 insertions(+), 489 deletions(-) diff --git a/libguac/include/client.h b/libguac/include/client.h index 4ab1c0e5..e29aca2d 100644 --- a/libguac/include/client.h +++ b/libguac/include/client.h @@ -103,6 +103,120 @@ typedef int guac_client_clipboard_handler(guac_client* client, char* copied); */ typedef int guac_client_free_handler(guac_client* client); +typedef struct guac_client_update guac_client_update; + +/** + * The type of a layer update (png, copy, or rect, for + * example). + */ +typedef enum guac_client_update_type_t { + + GUAC_CLIENT_UPDATE_PNG, + GUAC_CLIENT_UPDATE_COPY, + GUAC_CLIENT_UPDATE_CLIP, + GUAC_CLIENT_UPDATE_RECT + +} guac_client_update_type_t; + +/** + * Represents an abstract graphical update or state change + * of a client, including dirty rectangle information. + */ +struct guac_client_update { + + /** + * The type of this update. Update type corresponds + * directly to the instruction that would be sent + * if the queue is flushed. + */ + guac_client_update_type_t type; + + /** + * The composite mode to use in this update. + */ + guac_composite_mode_t mode; + + /** + * The layer to retrieve image data from. + */ + const guac_layer* src_layer; + + /** + * The surface to retrieve image data from. + */ + cairo_surface_t* src_image; + + /** + * The red component of the source color. + */ + int src_red; + + /** + * The green component of the source color. + */ + int src_green; + + /** + * The blue component of the source color. + */ + int src_blue; + + /** + * The alpha component of the source color. + */ + int src_alpha; + + /** + * The X coordinage of the upper-left corner of the + * source rectangle. + */ + int src_x; + + /** + * The Y coordinage of the upper-left corner of the + * source rectangle. + */ + int src_y; + + /** + * The layer this update should affect. + */ + const guac_layer* dst_layer; + + /** + * The X coordinate of the upper-left corner of the + * destination rectangle. + */ + int dst_x; + + /** + * The Y coordinate of the upper-left corner of the + * destination rectangle. + */ + int dst_y; + + /** + * The width of the destination or source rectangle. + * The dimensions of the destination and source + * rectangles are always identical. + */ + int width; + + /** + * The height of the destination or source rectangle. + * The dimensions of the destination and source + * rectangles are always identical. + */ + int height; + + /** + * The next update in the update queue, if any. + */ + guac_client_update* next; + +}; + + /** * Possible current states of the Guacamole client. Currently, the only * two states are RUNNING and STOPPING. @@ -165,9 +279,14 @@ struct guac_client { guac_layer* all_layers; /** - * Pointer to a pre-allocated default layer (layer 0) + * The first element in this client's update queue. */ - guac_layer* default_layer; + guac_client_update* update_queue_head; + + /** + * The last element in this client's update queue. + */ + guac_client_update* update_queue_tail; /** * The time (in milliseconds) of receipt of the last sync message from @@ -378,4 +497,96 @@ guac_layer* guac_client_alloc_layer(guac_client* client, int index); */ void guac_client_free_buffer(guac_client* client, guac_layer* layer); +/** + * Queues a png instruction in the given client. The client may combine + * and re-order multiple instructions into more efficient instructions + * providing the end result is the same image. + * + * @param client The proxy client which will queue the instruction. + * @param layer The destination layer. + * @param mode The composite mode to use. + * @param x The destination X coordinate. + * @param y The destination Y coordinate. + * @param surface A cairo surface containing the image data to send. + */ +void guac_client_queue_png(guac_client* client, guac_composite_mode_t mode, + const guac_layer* layer, int x, int y, cairo_surface_t* surface); + +/** + * Queues a copy instruction in a given client. The client may combine + * and re-order multiple instructions into more efficient instructions + * providing the end result is the same image. + * + * @param client The proxy client which will queue the instruction. + * @param srcl The source layer. + * @param srcx The X coordinate of the source rectangle. + * @param srcy The Y coordinate of the source rectangle. + * @param w The width of the source rectangle. + * @param h The height of the source rectangle. + * @param mode The composite mode to use. + * @param dstl The destination layer. + * @param dstx The X coordinate of the destination, where the source rectangle + * should be copied. + * @param dsty The Y coordinate of the destination, where the source rectangle + * should be copied. + */ +void guac_client_queue_copy(guac_client* client, + const guac_layer* srcl, int srcx, int srcy, int w, int h, + guac_composite_mode_t mode, const guac_layer* dstl, int dstx, int dsty); + +/** + * Queues a clip instruction in a given client. The client may combine + * and re-order multiple instructions into more efficient instructions + * providing the end result is the same image. + * + * @param client The proxy client which will queue the instruction. + * @param layer The layer to set the clipping region of. + * @param x The X coordinate of the clipping rectangle. + * @param y The Y coordinate of the clipping rectangle. + * @param width The width of the clipping rectangle. + * @param height The height of the clipping rectangle. + */ +void guac_client_queue_clip(guac_client* client, const guac_layer* layer, + int x, int y, int width, int height); + +/** + * Queues a rect instruction in a given layer. The layer may combine + * and re-order multiple instructions into more efficient instructions + * providing the end result is the same image. + * + * @param client The proxy client which will queue the instruction. + * @param mode The composite mode to use. + * @param layer The destination layer. + * @param x The X coordinate of the rectangle. + * @param y The Y coordinate of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param r The red component of the color of the rectangle. + * @param g The green component of the color of the rectangle. + * @param b The blue component of the color of the rectangle. + * @param a The alpha (transparency) component of the color of the rectangle. + */ +void guac_client_queue_rect(guac_client* client, + const guac_layer* layer, guac_composite_mode_t mode, + int x, int y, int width, int height, + int r, int g, int b, int a); + +/** + * Flushes any queued instructions in the given client, sending those + * instructions over the given GUACIO connection. These instructions + * may not be identical to the instructions originally queued; they + * may be re-ordered or combined providing the end result of executing + * all instructions sent by guac_client_queue_flush() is identical to + * executing all previously queued instructions as specified and in order. + * + * @param client The client to flush. + * @return Zero on success, non-zero on error. + */ +int guac_client_queue_flush(guac_client* client); + +/** + * The default Guacamole client layer, layer 0. + */ +extern const guac_layer* GUAC_DEFAULT_LAYER; + #endif diff --git a/libguac/include/protocol.h b/libguac/include/protocol.h index 128a9b2f..b680fa5e 100644 --- a/libguac/include/protocol.h +++ b/libguac/include/protocol.h @@ -104,121 +104,8 @@ typedef enum guac_composite_mode_t { } guac_composite_mode_t; -typedef struct guac_layer_update guac_layer_update; typedef struct guac_layer guac_layer; - -/** - * The type of a layer update (png, copy, or rect, for - * example). - */ -typedef enum guac_layer_update_type_t { - - GUAC_LAYER_UPDATE_PNG, - GUAC_LAYER_UPDATE_COPY, - GUAC_LAYER_UPDATE_CLIP, - GUAC_LAYER_UPDATE_RECT - -} guac_layer_update_type_t; - -/** - * Represents an abstract graphical update or state change - * of a layer, including dirty rectangle information. - */ -struct guac_layer_update { - - /** - * The type of this update. Update type corresponds - * directly to the instruction that would be sent - * if the queue is flushed. - */ - guac_layer_update_type_t type; - - /** - * The composite mode to use in this update. - */ - guac_composite_mode_t mode; - - /** - * The layer to retrieve image data from. - */ - const guac_layer* src_layer; - - /** - * The surface to retrieve image data from. - */ - cairo_surface_t* src_image; - - /** - * The red component of the source color. - */ - int src_red; - - /** - * The green component of the source color. - */ - int src_green; - - /** - * The blue component of the source color. - */ - int src_blue; - - /** - * The alpha component of the source color. - */ - int src_alpha; - - /** - * The X coordinage of the upper-left corner of the - * source rectangle. - */ - int src_x; - - /** - * The Y coordinage of the upper-left corner of the - * source rectangle. - */ - int src_y; - - /** - * The layer this update should affect. - */ - guac_layer* dst_layer; - - /** - * The X coordinate of the upper-left corner of the - * destination rectangle. - */ - int dst_x; - - /** - * The Y coordinate of the upper-left corner of the - * destination rectangle. - */ - int dst_y; - - /** - * The width of the destination or source rectangle. - * The dimensions of the destination and source - * rectangles are always identical. - */ - int width; - - /** - * The height of the destination or source rectangle. - * The dimensions of the destination and source - * rectangles are always identical. - */ - int height; - - /** - * The next update in the update queue, if any. - */ - guac_layer_update* next; - -}; - /** * Represents a single layer within the Guacamole protocol. */ @@ -240,16 +127,6 @@ struct guac_layer { */ guac_layer* next_available; - /** - * The first element in this layer's update queue. - */ - guac_layer_update* update_queue_head; - - /** - * The last element in this layer's update queue. - */ - guac_layer_update* update_queue_tail; - }; /** diff --git a/libguac/src/client.c b/libguac/src/client.c index f914e3d7..6cd81028 100644 --- a/libguac/src/client.c +++ b/libguac/src/client.c @@ -47,6 +47,17 @@ #include "client.h" #include "client-handlers.h" +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +guac_layer __GUAC_DEFAULT_LAYER = { + .index = 0, + .next = NULL, + .next_available = NULL +}; + +const guac_layer* GUAC_DEFAULT_LAYER = &__GUAC_DEFAULT_LAYER; + guac_client* __guac_alloc_client(GUACIO* io) { /* Allocate new client (not handoff) */ @@ -58,13 +69,13 @@ guac_client* __guac_alloc_client(GUACIO* io) { client->last_received_timestamp = client->last_sent_timestamp = guac_current_timestamp(); client->state = RUNNING; - client->all_layers = NULL; + client->all_layers = NULL; client->available_buffers = NULL; - client->next_buffer_index = -1; + client->update_queue_head = NULL; + client->update_queue_tail = NULL; - /* Allocate default layer */ - client->default_layer = guac_client_alloc_layer(client, 0); + client->next_buffer_index = -1; return client; } @@ -75,8 +86,6 @@ guac_layer* guac_client_alloc_layer(guac_client* client, int index) { /* Init new layer */ allocd_layer = malloc(sizeof(guac_layer)); - allocd_layer->update_queue_head = NULL; - allocd_layer->update_queue_tail = NULL; /* Add to all_layers list */ allocd_layer->next = client->all_layers; @@ -104,8 +113,6 @@ guac_layer* guac_client_alloc_buffer(guac_client* client) { /* Init new layer */ allocd_layer = malloc(sizeof(guac_layer)); allocd_layer->index = client->next_buffer_index--; - allocd_layer->update_queue_head = NULL; - allocd_layer->update_queue_tail = NULL; /* Add to all_layers list */ allocd_layer->next = client->all_layers; @@ -123,21 +130,6 @@ void guac_client_free_buffer(guac_client* client, guac_layer* layer) { layer->next_available = client->available_buffers; client->available_buffers = layer; - /* Free all queued updates */ - while (layer->update_queue_head != NULL) { - - /* Get unflushed update, update queue head */ - guac_layer_update* unflushed_update = layer->update_queue_head; - layer->update_queue_head = unflushed_update->next; - - /* Free update */ - free(unflushed_update); - - } - - /* Queue is now empty */ - layer->update_queue_tail = NULL; - } guac_client* guac_get_client(int client_fd) { @@ -339,23 +331,23 @@ void guac_free_client(guac_client* client) { guac_layer* layer = client->all_layers; client->all_layers = layer->next; - /* Free all queued updates */ - while (layer->update_queue_head != NULL) { - - /* Get unflushed update, update queue head */ - guac_layer_update* unflushed_update = layer->update_queue_head; - layer->update_queue_head = unflushed_update->next; - - /* Free update */ - free(unflushed_update); - - } - /* Free layer */ free(layer); } + /* Free all queued updates */ + while (client->update_queue_head != NULL) { + + /* Get unflushed update, update queue head */ + guac_client_update* unflushed_update = client->update_queue_head; + client->update_queue_head = unflushed_update->next; + + /* Free update */ + free(unflushed_update); + + } + free(client); } @@ -506,3 +498,332 @@ int guac_client_handle_instruction(guac_client* client, guac_instruction* instru } +void __guac_client_queue_add_update(guac_client* client, guac_client_update* update) { + + /* Add update to queue tail */ + if (client->update_queue_tail != NULL) + client->update_queue_tail = client->update_queue_tail->next = update; + + /* Set both head and tail if necessary */ + else + client->update_queue_tail = client->update_queue_head = update; + + update->next = NULL; + +} + +void guac_client_queue_png(guac_client* client, guac_composite_mode_t mode, + const guac_layer* layer, int x, int y, cairo_surface_t* surface) { + + /* Allocate update */ + guac_client_update* update = (guac_client_update*) malloc(sizeof(guac_client_update)); + + /* Set parameters of update */ + update->type = GUAC_CLIENT_UPDATE_PNG; + update->mode = mode; + update->src_image = surface; + update->dst_layer = layer; + update->dst_x = x; + update->dst_y = y; + update->width = cairo_image_surface_get_width(surface); + update->height = cairo_image_surface_get_height(surface); + + /* Add to client queue */ + __guac_client_queue_add_update(client, update); + +} + +void guac_client_queue_copy(guac_client* client, + const guac_layer* srcl, int srcx, int srcy, int w, int h, + guac_composite_mode_t mode, const guac_layer* dstl, int dstx, int dsty) { + + /* Allocate update */ + guac_client_update* update = (guac_client_update*) malloc(sizeof(guac_client_update)); + + /* Set parameters of update */ + update->type = GUAC_CLIENT_UPDATE_COPY; + update->mode = mode; + update->src_layer = srcl; + update->src_x = srcx; + update->src_y = srcy; + update->width = w; + update->height = h; + update->dst_layer = dstl; + update->dst_x = dstx; + update->dst_y = dsty; + + /* Add to client queue */ + __guac_client_queue_add_update(client, update); + +} + +void guac_client_queue_clip(guac_client* client, const guac_layer* layer, + int x, int y, int width, int height) { + + /* Allocate update */ + guac_client_update* update = (guac_client_update*) malloc(sizeof(guac_client_update)); + + /* Set parameters of update */ + update->type = GUAC_CLIENT_UPDATE_CLIP; + update->dst_x = x; + update->dst_y = y; + update->width = width; + update->height = height; + + /* Add to client queue */ + __guac_client_queue_add_update(client, update); + +} + +void guac_client_queue_rect(guac_client* client, + const guac_layer* layer, guac_composite_mode_t mode, + int x, int y, int width, int height, + int r, int g, int b, int a) { + + /* Allocate update */ + guac_client_update* update = (guac_client_update*) malloc(sizeof(guac_client_update)); + + /* Set parameters of update */ + update->type = GUAC_CLIENT_UPDATE_RECT; + update->mode = mode; + update->src_red = r; + update->src_green = g; + update->src_blue = b; + update->src_alpha = a; + update->dst_x = x; + update->dst_y = y; + update->width = width; + update->height = height; + + /* Add to client queue */ + __guac_client_queue_add_update(client, update); + +} + +int __guac_client_update_intersects( + const guac_client_update* update1, const guac_client_update* update2) { + + /* If update1 X is within width of update2 + or update2 X is within width of update1 */ + if ((update1->dst_x >= update2->dst_x && update1->dst_x < update2->dst_x + update2->width) + || (update2->dst_x >= update1->dst_x && update2->dst_x < update1->dst_x + update2->width)) { + + /* If update1 Y is within height of update2 + or update2 Y is within height of update1 */ + if ((update1->dst_y >= update2->dst_y && update1->dst_y < update2->dst_y + update2->height) + || (update2->dst_y >= update1->dst_y && update2->dst_y < update1->dst_y + update2->height)) { + + /* Register instersection */ + return 1; + + } + + } + + /* Otherwise, no intersection */ + return 0; + +} + +int guac_client_queue_flush(guac_client* client) { + + GUACIO* io = client->io; + + while (client->update_queue_head != NULL) { + + guac_client_update* later_update; + int update_merged = 0; + + /* Get next update, update queue head. */ + guac_client_update* update = client->update_queue_head; + client->update_queue_head = update->next; + + /* Send instruction appropriate for update type */ + switch (update->type) { + + /* "png" instruction */ + case GUAC_CLIENT_UPDATE_PNG: + + /* Decide whether or not to send */ + later_update = update->next; + while (later_update != NULL) { + + /* If destination rectangles intersect */ + if (__guac_client_update_intersects(update, later_update)) { + + cairo_surface_t* merged_surface; + cairo_t* cairo; + int merged_x, merged_y, merged_width, merged_height; + + /* Cannot combine anything if intersection with non-PNG */ + if (later_update->type != GUAC_CLIENT_UPDATE_PNG) + break; + + /* Cannot combine if modes differ */ + if (later_update->mode != update->mode) + break; + + /* For now, only combine for GUAC_COMP_OVER */ + if (later_update->mode != GUAC_COMP_OVER) + break; + + /* Calculate merged dimensions */ + merged_x = MIN(update->dst_x, later_update->dst_x); + merged_y = MIN(update->dst_y, later_update->dst_y); + + merged_width = MAX( + update->dst_x + update->width, + later_update->dst_x + later_update->width + ) - merged_x; + + merged_height = MAX( + update->dst_y + update->height, + later_update->dst_y + later_update->height + ) - merged_y; + + /* Create surface for merging */ + merged_surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, merged_width, merged_height); + + /* Get drawing context */ + cairo = cairo_create(merged_surface); + + /* Draw first update within merged surface */ + cairo_set_source_surface(cairo, + update->src_image, + update->dst_x - merged_x, + update->dst_y - merged_y); + + cairo_rectangle(cairo, + update->dst_x - merged_x, + update->dst_y - merged_y, + update->width, + update->height); + + cairo_fill(cairo); + + /* Draw second update within merged surface */ + cairo_set_source_surface(cairo, + later_update->src_image, + later_update->dst_x - merged_x, + later_update->dst_y - merged_y); + + cairo_rectangle(cairo, + later_update->dst_x - merged_x, + later_update->dst_y - merged_y, + later_update->width, + later_update->height); + + cairo_fill(cairo); + + /* Done drawing */ + cairo_destroy(cairo); + + /* Alter update dimensions */ + later_update->dst_x = merged_x; + later_update->dst_y = merged_y; + later_update->width = merged_width; + later_update->height = merged_height; + + /* Alter update to include merged data*/ + cairo_surface_destroy(later_update->src_image); + later_update->src_image = merged_surface; + + update_merged = 1; + break; + + } + + /* Get next update */ + later_update = later_update->next; + + } + + /* Send instruction if update was not merged */ + if (!update_merged) { + if (guac_send_png(io, + update->mode, + update->dst_layer, + update->dst_x, + update->dst_y, + update->src_image + )) { + cairo_surface_destroy(update->src_image); + free(update); + return -1; + } + } + + cairo_surface_destroy(update->src_image); + break; + + /* "copy" instruction */ + case GUAC_CLIENT_UPDATE_COPY: + + if (guac_send_copy(io, + update->src_layer, + update->src_x, + update->src_y, + update->width, + update->height, + update->mode, + update->dst_layer, + update->dst_x, + update->dst_y + )) { + free(update); + return -1; + } + + break; + + /* "clip" instruction */ + case GUAC_CLIENT_UPDATE_CLIP: + + if (guac_send_clip(io, + update->dst_layer, + update->dst_x, + update->dst_y, + update->width, + update->height + )) { + free(update); + return -1; + } + + break; + + /* "rect" instruction */ + case GUAC_CLIENT_UPDATE_RECT: + + if (guac_send_rect(io, + update->mode, + update->dst_layer, + update->dst_x, + update->dst_y, + update->width, + update->height, + update->src_red, + update->src_green, + update->src_blue, + update->src_alpha + )) { + free(update); + return -1; + } + + break; + + } /* end of switch update type */ + + /* Free update */ + free(update); + + } + + /* Queue is now empty */ + client->update_queue_tail = NULL; + + return 0; +} + diff --git a/libguac/src/protocol.c b/libguac/src/protocol.c index 739162d8..9dfe74f7 100644 --- a/libguac/src/protocol.c +++ b/libguac/src/protocol.c @@ -63,9 +63,6 @@ #include "guacio.h" #include "protocol.h" -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - ssize_t __guac_write_length_string(GUACIO* io, const char* str) { return @@ -521,329 +518,3 @@ void guac_sleep(int millis) { } -void __guac_layer_add_update(guac_layer* layer, guac_layer_update* update) { - - /* Add update to queue tail */ - if (layer->update_queue_tail != NULL) - layer->update_queue_tail = layer->update_queue_tail->next = update; - - /* Set both head and tail if necessary */ - else - layer->update_queue_tail = layer->update_queue_head = update; - - update->next = NULL; - -} - -void guac_layer_png(guac_layer* layer, guac_composite_mode_t mode, - int x, int y, cairo_surface_t* surface) { - - /* Allocate update */ - guac_layer_update* update = (guac_layer_update*) malloc(sizeof(guac_layer_update)); - - /* Set parameters of update */ - update->type = GUAC_LAYER_UPDATE_PNG; - update->mode = mode; - update->src_image = surface; - update->dst_layer = layer; - update->dst_x = x; - update->dst_y = y; - update->width = cairo_image_surface_get_width(surface); - update->height = cairo_image_surface_get_height(surface); - - /* Add to layer queue */ - __guac_layer_add_update(layer, update); - -} - -void guac_layer_copy(guac_layer* layer, guac_composite_mode_t mode, - const guac_layer* srcl, int srcx, int srcy, int w, int h, - int dstx, int dsty) { - - /* Allocate update */ - guac_layer_update* update = (guac_layer_update*) malloc(sizeof(guac_layer_update)); - - /* Set parameters of update */ - update->type = GUAC_LAYER_UPDATE_COPY; - update->mode = mode; - update->src_layer = srcl; - update->src_x = srcx; - update->src_y = srcy; - update->width = w; - update->height = h; - update->dst_layer = layer; - update->dst_x = dstx; - update->dst_y = dsty; - - /* Add to layer queue */ - __guac_layer_add_update(layer, update); - -} - -void guac_layer_clip(guac_layer* layer, - int x, int y, int width, int height) { - - /* Allocate update */ - guac_layer_update* update = (guac_layer_update*) malloc(sizeof(guac_layer_update)); - - /* Set parameters of update */ - update->type = GUAC_LAYER_UPDATE_CLIP; - update->dst_x = x; - update->dst_y = y; - update->width = width; - update->height = height; - - /* Add to layer queue */ - __guac_layer_add_update(layer, update); - -} - -void guac_layer_rect(guac_layer* layer, guac_composite_mode_t mode, - int x, int y, int width, int height, - int r, int g, int b, int a) { - - /* Allocate update */ - guac_layer_update* update = (guac_layer_update*) malloc(sizeof(guac_layer_update)); - - /* Set parameters of update */ - update->type = GUAC_LAYER_UPDATE_RECT; - update->mode = mode; - update->src_red = r; - update->src_green = g; - update->src_blue = b; - update->src_alpha = a; - update->dst_x = x; - update->dst_y = y; - update->width = width; - update->height = height; - - /* Add to layer queue */ - __guac_layer_add_update(layer, update); - -} - -int __guac_layer_update_intersects( - const guac_layer_update* update1, const guac_layer_update* update2) { - - /* If update1 X is within width of update2 - or update2 X is within width of update1 */ - if ((update1->dst_x >= update2->dst_x && update1->dst_x < update2->dst_x + update2->width) - || (update2->dst_x >= update1->dst_x && update2->dst_x < update1->dst_x + update2->width)) { - - /* If update1 Y is within height of update2 - or update2 Y is within height of update1 */ - if ((update1->dst_y >= update2->dst_y && update1->dst_y < update2->dst_y + update2->height) - || (update2->dst_y >= update1->dst_y && update2->dst_y < update1->dst_y + update2->height)) { - - /* Register instersection */ - return 1; - - } - - } - - /* Otherwise, no intersection */ - return 0; - -} - -int guac_layer_flush(guac_layer* layer, GUACIO* io) { - - while (layer->update_queue_head != NULL) { - - guac_layer_update* later_update; - int update_merged = 0; - - /* Get next update, update queue head. */ - guac_layer_update* update = layer->update_queue_head; - layer->update_queue_head = update->next; - - /* Send instruction appropriate for update type */ - switch (update->type) { - - /* "png" instruction */ - case GUAC_LAYER_UPDATE_PNG: - - /* Decide whether or not to send */ - later_update = update->next; - while (later_update != NULL) { - - /* If destination rectangles intersect */ - if (__guac_layer_update_intersects(update, later_update)) { - - cairo_surface_t* merged_surface; - cairo_t* cairo; - int merged_x, merged_y, merged_width, merged_height; - - /* Cannot combine anything if intersection with non-PNG */ - if (later_update->type != GUAC_LAYER_UPDATE_PNG) - break; - - /* Cannot combine if modes differ */ - if (later_update->mode != update->mode) - break; - - /* For now, only combine for GUAC_COMP_OVER */ - if (later_update->mode != GUAC_COMP_OVER) - break; - - /* Calculate merged dimensions */ - merged_x = MIN(update->dst_x, later_update->dst_x); - merged_y = MIN(update->dst_y, later_update->dst_y); - - merged_width = MAX( - update->dst_x + update->width, - later_update->dst_x + later_update->width - ) - merged_x; - - merged_height = MAX( - update->dst_y + update->height, - later_update->dst_y + later_update->height - ) - merged_y; - - /* Create surface for merging */ - merged_surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, merged_width, merged_height); - - /* Get drawing context */ - cairo = cairo_create(merged_surface); - - /* Draw first update within merged surface */ - cairo_set_source_surface(cairo, - update->src_image, - update->dst_x - merged_x, - update->dst_y - merged_y); - - cairo_rectangle(cairo, - update->dst_x - merged_x, - update->dst_y - merged_y, - update->width, - update->height); - - cairo_fill(cairo); - - /* Draw second update within merged surface */ - cairo_set_source_surface(cairo, - later_update->src_image, - later_update->dst_x - merged_x, - later_update->dst_y - merged_y); - - cairo_rectangle(cairo, - later_update->dst_x - merged_x, - later_update->dst_y - merged_y, - later_update->width, - later_update->height); - - cairo_fill(cairo); - - /* Done drawing */ - cairo_destroy(cairo); - - /* Alter update dimensions */ - later_update->dst_x = merged_x; - later_update->dst_y = merged_y; - later_update->width = merged_width; - later_update->height = merged_height; - - /* Alter update to include merged data*/ - cairo_surface_destroy(later_update->src_image); - later_update->src_image = merged_surface; - - update_merged = 1; - break; - - } - - /* Get next update */ - later_update = later_update->next; - - } - - /* Send instruction if update was not merged */ - if (!update_merged) { - if (guac_send_png(io, - update->mode, - update->dst_layer, - update->dst_x, - update->dst_y, - update->src_image - )) { - cairo_surface_destroy(update->src_image); - free(update); - return -1; - } - } - - cairo_surface_destroy(update->src_image); - break; - - /* "copy" instruction */ - case GUAC_LAYER_UPDATE_COPY: - - if (guac_send_copy(io, - update->src_layer, - update->src_x, - update->src_y, - update->width, - update->height, - update->mode, - update->dst_layer, - update->dst_x, - update->dst_y - )) { - free(update); - return -1; - } - - break; - - /* "clip" instruction */ - case GUAC_LAYER_UPDATE_CLIP: - - if (guac_send_clip(io, - update->dst_layer, - update->dst_x, - update->dst_y, - update->width, - update->height - )) { - free(update); - return -1; - } - - break; - - /* "rect" instruction */ - case GUAC_LAYER_UPDATE_RECT: - - if (guac_send_rect(io, - update->mode, - update->dst_layer, - update->dst_x, - update->dst_y, - update->width, - update->height, - update->src_red, - update->src_green, - update->src_blue, - update->src_alpha - )) { - free(update); - return -1; - } - - break; - - } /* end of switch update type */ - - /* Free update */ - free(update); - - } - - /* Queue is now empty */ - layer->update_queue_tail = NULL; - - return 0; -} -