diff --git a/src/common/common/surface.h b/src/common/common/surface.h index 3da3f175..edc7af8e 100644 --- a/src/common/common/surface.h +++ b/src/common/common/surface.h @@ -118,6 +118,40 @@ typedef struct guac_common_surface { */ guac_socket* socket; + /** + * The X coordinate of the upper-left corner of this layer, in pixels, + * relative to its parent layer. This is only applicable to visible + * (non-buffer) layers which are not the default layer. + */ + int x; + + /** + * The Y coordinate of the upper-left corner of this layer, in pixels, + * relative to its parent layer. This is only applicable to visible + * (non-buffer) layers which are not the default layer. + */ + int y; + + /** + * The Z-order of this layer, relative to sibling layers. This is only + * applicable to visible (non-buffer) layers which are not the default + * layer. + */ + int z; + + /** + * The level of opacity applied to this layer. Fully opaque is 255, while + * fully transparent is 0. This is only applicable to visible (non-buffer) + * layers which are not the default layer. + */ + int opacity; + + /** + * The layer which contains this layer. This is only applicable to visible + * (non-buffer) layers which are not the default layer. + */ + const guac_layer* parent; + /** * The width of this layer, in pixels. */ @@ -138,6 +172,18 @@ typedef struct guac_common_surface { */ unsigned char* buffer; + /** + * Non-zero if the location or parent layer of this surface has been + * changed and needs to be flushed, 0 otherwise. + */ + int location_dirty; + + /** + * Non-zero if the opacity of this surface has been changed and needs to be + * flushed, 0 otherwise. + */ + int opacity_dirty; + /** * Non-zero if this surface is dirty and needs to be flushed, 0 otherwise. */ @@ -317,10 +363,77 @@ void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, void guac_common_surface_reset_clip(guac_common_surface* surface); /** - * Flushes the given surface, drawing any pending operations on the remote - * display. + * Changes the location of the surface relative to its parent layer. If the + * surface does not represent a non-default visible layer, then this function + * has no effect. * - * @param surface The surface to flush. + * @param surface + * The surface to move relative to its parent layer. + * + * @param x + * The new X coordinate for the upper-left corner of the layer, in pixels. + * + * @param y + * The new Y coordinate for the upper-left corner of the layer, in pixels. + */ +void guac_common_surface_move(guac_common_surface* surface, int x, int y); + +/** + * Changes the stacking order of the surface relative to other surfaces within + * the same parent layer. If the surface does not represent a non-default + * visible layer, then this function has no effect. + * + * @param surface + * The surface to reorder relative to sibling layers. + * + * @param z + * The new Z-order for this layer, relative to sibling layers. + */ +void guac_common_surface_stack(guac_common_surface* surface, int z); + +/** + * Changes the parent layer of ths given surface. By default, layers will be + * children of the default layer. If the surface does not represent a + * non-default visible layer, then this function has no effect. + * + * @param surface + * The surface whose parent layer should be changed. + * + * @param parent + * The layer which should be set as the new parent of the given surface. + */ +void guac_common_surface_set_parent(guac_common_surface* surface, + const guac_layer* parent); + +/** + * Sets the opacity of the surface. If the surface does not represent a + * non-default visible layer, then this function has no effect. + * + * @param surface + * The surface whose opacity should be changed. + * + * @param opacity + * The level of opacity applied to this surface, where fully opaque is 255, + * and fully transparent is 0. + */ +void guac_common_surface_set_opacity(guac_common_surface* surface, int opacity); + +/** + * Flushes only the properties of the given surface, such as layer location or + * opacity. Image state is not flushed. If the surface represents a buffer or + * the default layer, this function has no effect. + * + * @param surface + * The surface to flush. + */ +void guac_common_surface_flush_properties(guac_common_surface* surface); + +/** + * Flushes the given surface, including any applicable properties, drawing any + * pending operations on the remote display. + * + * @param surface + * The surface to flush. */ void guac_common_surface_flush(guac_common_surface* surface); diff --git a/src/common/display.c b/src/common/display.c index 38a2ad42..03c8da43 100644 --- a/src/common/display.c +++ b/src/common/display.c @@ -155,7 +155,17 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user, } void guac_common_display_flush(guac_common_display* display) { + + guac_common_display_layer* current = display->layers; + + /* Flush all surfaces */ + while (current != NULL) { + guac_common_surface_flush(current->surface); + current = current->next; + } + guac_common_surface_flush(display->default_surface); + } /** diff --git a/src/common/surface.c b/src/common/surface.c index 5a680937..318e7d63 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -116,6 +116,29 @@ */ #define GUAC_SURFACE_WEBP_BLOCK_SIZE 8 +void guac_common_surface_move(guac_common_surface* surface, int x, int y) { + surface->x = x; + surface->y = y; + surface->location_dirty = 1; +} + +void guac_common_surface_stack(guac_common_surface* surface, int z) { + surface->z = z; + surface->location_dirty = 1; +} + +void guac_common_surface_set_parent(guac_common_surface* surface, + const guac_layer* parent) { + surface->parent = parent; + surface->location_dirty = 1; +} + +void guac_common_surface_set_opacity(guac_common_surface* surface, + int opacity) { + surface->opacity = opacity; + surface->opacity_dirty = 1; +} + /** * Updates the coordinates of the given rectangle to be within the bounds of * the given surface. @@ -990,6 +1013,7 @@ guac_common_surface* guac_common_surface_alloc(guac_client* client, surface->client = client; surface->socket = socket; surface->layer = layer; + surface->parent = GUAC_DEFAULT_LAYER; surface->width = w; surface->height = h; @@ -1443,6 +1467,29 @@ static int __guac_common_surface_bitmap_rect_compare(const void* a, const void* } +void guac_common_surface_flush_properties(guac_common_surface* surface) { + + guac_socket* socket = surface->socket; + + /* Only applicable to non-default visible layers */ + if (surface->layer->index <= 0) + return; + + /* Flush opacity */ + if (surface->opacity_dirty) { + guac_protocol_send_shade(socket, surface->layer, surface->opacity); + surface->opacity_dirty = 0; + } + + /* Flush location and hierarchy */ + if (surface->location_dirty) { + guac_protocol_send_move(socket, surface->layer, + surface->parent, surface->x, surface->y, surface->z); + surface->location_dirty = 0; + } + +} + void guac_common_surface_flush(guac_common_surface* surface) { /* Flush final dirty rectangle to queue. */ @@ -1523,6 +1570,9 @@ void guac_common_surface_flush(guac_common_surface* surface) { } + /* Flush any applicable layer properties */ + guac_common_surface_flush_properties(surface); + /* Flush complete */ surface->bitmap_queue_length = 0; @@ -1535,8 +1585,21 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user, if (!surface->realized) return; + /* Synchronize layer-specific properties if applicable */ + if (surface->layer->index > 0) { + + /* Synchronize opacity */ + guac_protocol_send_shade(socket, surface->layer, surface->opacity); + + /* Synchronize location and hierarchy */ + guac_protocol_send_move(socket, surface->layer, + surface->parent, surface->x, surface->y, surface->z); + + } + /* Sync size to new socket */ - guac_protocol_send_size(socket, surface->layer, surface->width, surface->height); + guac_protocol_send_size(socket, surface->layer, + surface->width, surface->height); /* Get entire surface */ cairo_surface_t* rect = cairo_image_surface_create_for_data(