/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "common/cursor.h" #include "common/display.h" #include "common/surface.h" #include #include #include #include #include /** * Synchronizes all surfaces within the given linked list to the given socket. * If the provided pointer to the linked list is NULL, this function has no * effect. * * @param layers * The head element of the linked list of layers to synchronize, which may * be NULL if the list is currently empty. * * @param user * The user receiving the layers. * * @param socket * The socket over which each layer should be sent. */ static void guac_common_display_dup_layers(guac_common_display_layer* layers, guac_user* user, guac_socket* socket) { guac_common_display_layer* current = layers; /* Synchronize all surfaces in given list */ while (current != NULL) { guac_common_surface_dup(current->surface, user, socket); current = current->next; } } /** * Frees all layers and associated surfaces within the given list, as well as * their corresponding list elements. If the provided pointer to the linked * list is NULL, this function has no effect. * * @param layers * The head element of the linked list of layers to free, which may be NULL * if the list is currently empty. * * @param client * The client owning the layers wrapped by each of the layers in the list. */ static void guac_common_display_free_layers(guac_common_display_layer* layers, guac_client* client) { guac_common_display_layer* current = layers; /* Free each surface in given list */ while (current != NULL) { guac_common_display_layer* next = current->next; guac_layer* layer = current->layer; /* Free surface */ guac_common_surface_free(current->surface); /* Destroy layer within remotely-connected client */ guac_protocol_send_dispose(client->socket, layer); /* Free layer or buffer depending on index */ if (layer->index < 0) guac_client_free_buffer(client, layer); else if (layer->index > 0) guac_client_free_layer(client, layer); /* Free current element and advance to next */ free(current); current = next; } } /** * Allocates a display and a cursor which are used to represent the remote * display and cursor. * * @param client * The client owning the cursor. * * @param width * The desired width of the display. * * @param height * The desired height of the display. * * @return * The newly-allocated display or NULL if display cannot be allocated. */ guac_common_display* guac_common_display_alloc(guac_client* client, int width, int height) { /* Allocate display */ guac_common_display* display = malloc(sizeof(guac_common_display)); if (display == NULL) return NULL; /* Allocate shared cursor */ display->cursor = guac_common_cursor_alloc(client); if (display->cursor == NULL) { free(display); return NULL; } pthread_mutex_init(&display->_lock, NULL); /* Associate display with given client */ display->client = client; display->default_surface = guac_common_surface_alloc(client, client->socket, GUAC_DEFAULT_LAYER, width, height); /* No initial layers or buffers */ display->layers = NULL; display->buffers = NULL; return display; } void guac_common_display_free(guac_common_display* display) { /* Free shared cursor */ guac_common_cursor_free(display->cursor); /* Free default surface */ guac_common_surface_free(display->default_surface); /* Free all layers and buffers */ guac_common_display_free_layers(display->buffers, display->client); guac_common_display_free_layers(display->layers, display->client); pthread_mutex_destroy(&display->_lock); free(display); } void guac_common_display_dup(guac_common_display* display, guac_user* user, guac_socket* socket) { pthread_mutex_lock(&display->_lock); /* Sunchronize shared cursor */ guac_common_cursor_dup(display->cursor, user, socket); /* Synchronize default surface */ guac_common_surface_dup(display->default_surface, user, socket); /* Synchronize all layers and buffers */ guac_common_display_dup_layers(display->layers, user, socket); guac_common_display_dup_layers(display->buffers, user, socket); pthread_mutex_unlock(&display->_lock); } void guac_common_display_flush(guac_common_display* display) { pthread_mutex_lock(&display->_lock); 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); pthread_mutex_unlock(&display->_lock); } /** * Allocates and inserts a new element into the given linked list of display * layers, associating it with the given layer and surface. * * @param head * A pointer to the head pointer of the list of layers. The head pointer * will be updated by this function to point to the newly-allocated * display layer. * * @param layer * The Guacamole layer to associated with the new display layer. * * @param surface * The surface associated with the given Guacamole layer and which should * be associated with the new display layer. * * @return * The newly-allocated display layer, which has been associated with the * provided layer and surface. */ static guac_common_display_layer* guac_common_display_add_layer( guac_common_display_layer** head, guac_layer* layer, guac_common_surface* surface) { guac_common_display_layer* old_head = *head; guac_common_display_layer* display_layer = malloc(sizeof(guac_common_display_layer)); /* Init layer/surface pair */ display_layer->layer = layer; display_layer->surface = surface; /* Insert list element as the new head */ display_layer->prev = NULL; display_layer->next = old_head; *head = display_layer; /* Update old head to point to new element, if it existed */ if (old_head != NULL) old_head->prev = display_layer; return display_layer; } /** * Removes the given display layer from the linked list whose head pointer is * provided. * * @param head * A pointer to the head pointer of the list of layers. The head pointer * will be updated by this function if necessary, and will be set to NULL * if the display layer being removed is the only layer in the list. * * @param display_layer * The display layer to remove from the given list. */ static void guac_common_display_remove_layer(guac_common_display_layer** head, guac_common_display_layer* display_layer) { /* Update previous element, if it exists */ if (display_layer->prev != NULL) display_layer->prev->next = display_layer->next; /* If there is no previous element, update the list head */ else *head = display_layer->next; /* Update next element, if it exists */ if (display_layer->next != NULL) display_layer->next->prev = display_layer->prev; } guac_common_display_layer* guac_common_display_alloc_layer( guac_common_display* display, int width, int height) { pthread_mutex_lock(&display->_lock); /* Allocate Guacamole layer */ guac_layer* layer = guac_client_alloc_layer(display->client); /* Allocate corresponding surface */ guac_common_surface* surface = guac_common_surface_alloc(display->client, display->client->socket, layer, width, height); /* Add layer and surface to list */ guac_common_display_layer* display_layer = guac_common_display_add_layer(&display->layers, layer, surface); pthread_mutex_unlock(&display->_lock); return display_layer; } guac_common_display_layer* guac_common_display_alloc_buffer( guac_common_display* display, int width, int height) { pthread_mutex_lock(&display->_lock); /* Allocate Guacamole buffer */ guac_layer* buffer = guac_client_alloc_buffer(display->client); /* Allocate corresponding surface */ guac_common_surface* surface = guac_common_surface_alloc(display->client, display->client->socket, buffer, width, height); /* Add buffer and surface to list */ guac_common_display_layer* display_layer = guac_common_display_add_layer(&display->buffers, buffer, surface); pthread_mutex_unlock(&display->_lock); return display_layer; } void guac_common_display_free_layer(guac_common_display* display, guac_common_display_layer* display_layer) { pthread_mutex_lock(&display->_lock); /* Remove list element from list */ guac_common_display_remove_layer(&display->layers, display_layer); /* Free associated layer and surface */ guac_common_surface_free(display_layer->surface); guac_client_free_layer(display->client, display_layer->layer); /* Free list element */ free(display_layer); pthread_mutex_unlock(&display->_lock); } void guac_common_display_free_buffer(guac_common_display* display, guac_common_display_layer* display_buffer) { pthread_mutex_lock(&display->_lock); /* Remove list element from list */ guac_common_display_remove_layer(&display->buffers, display_buffer); /* Free associated layer and surface */ guac_common_surface_free(display_buffer->surface); guac_client_free_buffer(display->client, display_buffer->layer); /* Free list element */ free(display_buffer); pthread_mutex_unlock(&display->_lock); }