in src/libguac/display.c [196:292]
void guac_display_dup(guac_display* display, guac_socket* socket) {
guac_client* client = display->client;
guac_rwlock_acquire_read_lock(&display->last_frame.lock);
/* Wait for any pending frame to finish being sent to established users of
* the connection before syncing any new users (doing otherwise could
* result in trailing instructions of that pending frame getting sent to
* new users after they finish joining, even though they are already in
* sync with that frame, and those trailing instructions may not have the
* intended meaning in context of the new users' remote displays) */
guac_flag_wait_and_lock(&display->render_state,
GUAC_DISPLAY_RENDER_STATE_FRAME_NOT_IN_PROGRESS);
/* Sync the state of all layers/buffers */
guac_display_layer* current = display->last_frame.layers;
while (current != NULL) {
const guac_layer* layer = current->layer;
guac_rect layer_bounds;
guac_display_layer_get_bounds(current, &layer_bounds);
int width = guac_rect_width(&layer_bounds);
int height = guac_rect_height(&layer_bounds);
guac_protocol_send_size(socket, layer, width, height);
if (width > 0 && height > 0) {
/* Get Cairo surface covering layer bounds */
unsigned char* buffer = GUAC_DISPLAY_LAYER_STATE_MUTABLE_BUFFER(current->last_frame, layer_bounds);
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
current->opaque ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
width, height, current->last_frame.buffer_stride);
/* Send PNG for rect */
guac_client_stream_png(client, socket, GUAC_COMP_OVER, layer, 0, 0, rect);
/* Resync copy of previous frame */
guac_protocol_send_copy(socket,
layer, 0, 0, width, height,
GUAC_COMP_OVER, current->last_frame_buffer, 0, 0);
cairo_surface_destroy(rect);
}
/* Resync any properties that are specific to non-buffer layers */
if (current->layer->index > 0) {
/* Resync layer opacity */
guac_protocol_send_shade(socket, current->layer,
current->last_frame.opacity);
/* Resync layer position/hierarchy */
guac_protocol_send_move(socket, current->layer,
current->last_frame.parent,
current->last_frame.x,
current->last_frame.y,
current->last_frame.z);
}
/* Resync multitouch support */
if (current->layer->index >= 0) {
guac_protocol_send_set_int(socket, current->layer,
GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH,
current->last_frame.touches);
}
current = current->last_frame.next;
}
/* Synchronize mouse cursor */
guac_display_layer* cursor = display->cursor_buffer;
guac_protocol_send_cursor(socket,
display->last_frame.cursor_hotspot_x,
display->last_frame.cursor_hotspot_y,
cursor->layer, 0, 0,
cursor->last_frame.width,
cursor->last_frame.height);
/* Synchronize mouse location */
guac_protocol_send_mouse(socket, display->last_frame.cursor_x, display->last_frame.cursor_y,
display->last_frame.cursor_mask, client->last_sent_timestamp);
/* The initial frame synchronizing the newly-joined users is now complete */
guac_protocol_send_sync(socket, client->last_sent_timestamp, display->last_frame.frames);
/* Further rendering for the current connection can now safely continue */
guac_flag_unlock(&display->render_state);
guac_rwlock_release_lock(&display->last_frame.lock);
guac_socket_flush(socket);
}