diff --git a/libguac/configure.in b/libguac/configure.in index 96b49654..193dde7e 100644 --- a/libguac/configure.in +++ b/libguac/configure.in @@ -40,8 +40,15 @@ AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_CC +AC_PROG_CC_C99 AC_PROG_LIBTOOL +# POSIX +AC_DEFINE(_POSIX_C_SOURCE, 199309L) + +# BSD +AC_DEFINE(_BSD_SOURCE) + # Checks for libraries. AC_CHECK_LIB([dl], [dlopen],, AC_MSG_ERROR("libdl is required for loading client plugins")) AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions")) diff --git a/libguac/include/client.h b/libguac/include/client.h index 1f449f23..9703733c 100644 --- a/libguac/include/client.h +++ b/libguac/include/client.h @@ -147,6 +147,29 @@ struct guac_client { */ guac_client_state state; + /** + * The head pointer of the list of all available (allocated but not used) + * layers. + */ + guac_layer* available_layers; + + /** + * The index of the next available buffer. + */ + int next_buffer_index; + + /** + * The head pointer of the list of all available (allocated but not used) + * buffers. + */ + guac_layer* available_buffers; + + /** + * The head pointer of the list of all allocated layers, regardless of use + * status. + */ + guac_layer* all_layers; + /** * The time (in milliseconds) of receipt of the last sync message from * the client. @@ -328,4 +351,43 @@ int guac_client_handle_instruction(guac_client* client, guac_instruction* instru */ void guac_client_stop(guac_client* client); +/** + * Allocates a new buffer (invisible layer). An arbitrary index is + * automatically assigned if no existing buffer is available for use. + * + * @param client The proxy client to allocate the buffer for. + * @return The next available buffer, or a newly allocated buffer. + */ +guac_layer* guac_client_alloc_buffer(guac_client* client); + +/** + * Allocates a new layer. The layer will be given the specified index, + * even if the layer returned was a previously used (and free'd) layer. + * + * @param client The proxy client to allocate the layer buffer for. + * @param index The index of the layer to allocate. + * @return The next available layer, or a newly allocated layer. + */ +guac_layer* guac_client_alloc_layer(guac_client* client, int index); + +/** + * Returns the given buffer to the pool of available buffers, such that it + * can be reused by any subsequent call to guac_client_allow_buffer(). + * + * @param client The proxy client to return the buffer to. + * @param layer The buffer to return to the pool of available buffers. + */ +void guac_client_free_buffer(guac_client* client, guac_layer* layer); + +/** + * Returns the given layer to the pool of available layers, such that it + * can be reused by any subsequent call to guac_client_allow_layer(). + * + * @param client The proxy client to return the layer to. + * @param layer The layer to return to the pool of available layers. + */ +void guac_client_free_layer(guac_client* client, guac_layer* layer); + +extern const guac_layer* GUAC_DEFAULT_LAYER; + #endif diff --git a/libguac/include/protocol.h b/libguac/include/protocol.h index d7733cac..9d87587e 100644 --- a/libguac/include/protocol.h +++ b/libguac/include/protocol.h @@ -102,6 +102,30 @@ typedef enum guac_composite_mode_t { } guac_composite_mode_t; +typedef struct guac_layer guac_layer; + +/** + * Represents a single layer within the Guacamole protocol. + */ +struct guac_layer { + + /** + * The index of this layer. + */ + int index; + + /** + * The next allocated layer in the list of all layers. + */ + guac_layer* next; + + /** + * The next available (unused) layer in the list of + * allocated but free'd layers. + */ + guac_layer* next_available; + +}; /** * Represents a single instruction within the Guacamole protocol. @@ -230,13 +254,13 @@ int guac_send_size(GUACIO* io, int w, int h); * Sends a copy instruction over the given GUACIO connection. * * @param io The GUACIO connection to use. - * @param srcl The index of the source layer. + * @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 index of the destination layer. + * @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 @@ -244,8 +268,8 @@ int guac_send_size(GUACIO* io, int w, int h); * @return Zero on success, non-zero on error. */ int guac_send_copy(GUACIO* io, - int srcl, int srcx, int srcy, int w, int h, - guac_composite_mode_t mode, int dstl, int dstx, int dsty); + 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); /** * Sends a png instruction over the given GUACIO connection. The PNG image data @@ -253,14 +277,14 @@ int guac_send_copy(GUACIO* io, * * @param io The GUACIO connection to use. * @param mode The composite mode to use. - * @param layer The index of the destination layer. + * @param layer The destination layer. * @param x The destination X coordinate. * @param y The destination Y coordinate. * @param surface A cairo surface containing the image data to send. * @return Zero on success, non-zero on error. */ int guac_send_png(GUACIO* io, guac_composite_mode_t mode, - int layer, int x, int y, cairo_surface_t* surface); + const guac_layer* layer, int x, int y, cairo_surface_t* surface); /** * Sends a cursor instruction over the given GUACIO connection. The PNG image diff --git a/libguac/src/client.c b/libguac/src/client.c index 9a48911f..beb91cf3 100644 --- a/libguac/src/client.c +++ b/libguac/src/client.c @@ -47,6 +47,15 @@ #include "client.h" #include "client-handlers.h" +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,9 +67,80 @@ 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->available_layers = NULL; + client->available_buffers = NULL; + + client->next_buffer_index = -1; + return client; } +guac_layer* guac_client_alloc_layer(guac_client* client, int index) { + + guac_layer* allocd_layer; + + /* If available layers, pop off first available layer */ + if (client->available_layers != NULL) { + allocd_layer = client->available_layers; + client->available_layers = client->available_layers->next_available; + allocd_layer->next_available = NULL; + } + + /* If no available layers, allocate new layer, add to all_layers list */ + else { + + /* Init new layer */ + allocd_layer = malloc(sizeof(guac_layer)); + + /* Add to all_layers list */ + allocd_layer->next = client->all_layers; + client->all_layers = allocd_layer; + + } + + allocd_layer->index = index; + return allocd_layer; + +} + +void guac_client_free_layer(guac_client* client, guac_layer* layer) { + layer->next = client->available_layers; + client->available_layers = layer; +} + +guac_layer* guac_client_alloc_buffer(guac_client* client) { + + guac_layer* allocd_layer; + + /* If available layers, pop off first available buffer */ + if (client->available_buffers != NULL) { + allocd_layer = client->available_buffers; + client->available_buffers = client->available_buffers->next_available; + allocd_layer->next_available = NULL; + } + + /* If no available buffer, allocate new buffer, add to all_layers list */ + else { + + /* Init new layer */ + allocd_layer = malloc(sizeof(guac_layer)); + allocd_layer->index = client->next_buffer_index--; + + /* Add to all_layers list */ + allocd_layer->next = client->all_layers; + client->all_layers = allocd_layer; + + } + + return allocd_layer; + +} + +void guac_client_free_buffer(guac_client* client, guac_layer* layer) { + layer->next = client->available_buffers; + client->available_buffers = layer; +} guac_client* guac_get_client(int client_fd) { @@ -254,6 +334,13 @@ void guac_free_client(guac_client* client) { guac_log_error("Could not close client plugin while unloading client: %s", dlerror()); } + /* Free all layers */ + while (client->all_layers != NULL) { + guac_layer* layer = client->all_layers; + client->all_layers = layer->next; + free(layer); + } + free(client); } diff --git a/libguac/src/protocol.c b/libguac/src/protocol.c index f0c9299f..487a5872 100644 --- a/libguac/src/protocol.c +++ b/libguac/src/protocol.c @@ -273,12 +273,12 @@ int guac_send_sync(GUACIO* io, guac_timestamp_t timestamp) { } int guac_send_copy(GUACIO* io, - int srcl, int srcx, int srcy, int w, int h, - guac_composite_mode_t mode, int dstl, int dstx, int dsty) { + 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) { return guac_write_string(io, "copy:") - || guac_write_int(io, srcl) + || guac_write_int(io, srcl->index) || guac_write_string(io, ",") || guac_write_int(io, srcx) || guac_write_string(io, ",") @@ -290,7 +290,7 @@ int guac_send_copy(GUACIO* io, || guac_write_string(io, ",") || guac_write_int(io, mode) || guac_write_string(io, ",") - || guac_write_int(io, dstl) + || guac_write_int(io, dstl->index) || guac_write_string(io, ",") || guac_write_int(io, dstx) || guac_write_string(io, ",") @@ -311,7 +311,7 @@ cairo_status_t __guac_write_png(void* closure, const unsigned char* data, unsign } int guac_send_png(GUACIO* io, guac_composite_mode_t mode, - int layer, int x, int y, cairo_surface_t* surface) { + const guac_layer* layer, int x, int y, cairo_surface_t* surface) { /* Write instruction and args */ @@ -319,7 +319,7 @@ int guac_send_png(GUACIO* io, guac_composite_mode_t mode, guac_write_string(io, "png:") || guac_write_int(io, mode) || guac_write_string(io, ",") - || guac_write_int(io, layer) + || guac_write_int(io, layer->index) || guac_write_string(io, ",") || guac_write_int(io, x) || guac_write_string(io, ",")