display.c (11753B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "common/cursor.h" 21#include "common/display.h" 22#include "common/surface.h" 23 24#include <guacamole/client.h> 25#include <guacamole/mem.h> 26#include <guacamole/socket.h> 27 28#include <pthread.h> 29#include <stdlib.h> 30#include <string.h> 31 32/** 33 * Synchronizes all surfaces within the given linked list to the given socket. 34 * If the provided pointer to the linked list is NULL, this function has no 35 * effect. 36 * 37 * @param layers 38 * The head element of the linked list of layers to synchronize, which may 39 * be NULL if the list is currently empty. 40 * 41 * @param client 42 * The client associated with the users receiving the layers. 43 * 44 * @param socket 45 * The socket over which each layer should be sent. 46 */ 47static void guac_common_display_dup_layers(guac_common_display_layer* layers, 48 guac_client* client, guac_socket* socket) { 49 50 guac_common_display_layer* current = layers; 51 52 /* Synchronize all surfaces in given list */ 53 while (current != NULL) { 54 guac_common_surface_dup(current->surface, client, socket); 55 current = current->next; 56 } 57 58} 59 60/** 61 * Frees all layers and associated surfaces within the given list, as well as 62 * their corresponding list elements. If the provided pointer to the linked 63 * list is NULL, this function has no effect. 64 * 65 * @param layers 66 * The head element of the linked list of layers to free, which may be NULL 67 * if the list is currently empty. 68 * 69 * @param client 70 * The client owning the layers wrapped by each of the layers in the list. 71 */ 72static void guac_common_display_free_layers(guac_common_display_layer* layers, 73 guac_client* client) { 74 75 guac_common_display_layer* current = layers; 76 77 /* Free each surface in given list */ 78 while (current != NULL) { 79 80 guac_common_display_layer* next = current->next; 81 guac_layer* layer = current->layer; 82 83 /* Free surface */ 84 guac_common_surface_free(current->surface); 85 86 /* Destroy layer within remotely-connected client */ 87 guac_protocol_send_dispose(client->socket, layer); 88 89 /* Free layer or buffer depending on index */ 90 if (layer->index < 0) 91 guac_client_free_buffer(client, layer); 92 else if (layer->index > 0) 93 guac_client_free_layer(client, layer); 94 95 /* Free current element and advance to next */ 96 guac_mem_free(current); 97 current = next; 98 99 } 100 101} 102 103/** 104 * Allocates a display and a cursor which are used to represent the remote 105 * display and cursor. 106 * 107 * @param client 108 * The client owning the cursor. 109 * 110 * @param width 111 * The desired width of the display. 112 * 113 * @param height 114 * The desired height of the display. 115 * 116 * @return 117 * The newly-allocated display or NULL if display cannot be allocated. 118 */ 119guac_common_display* guac_common_display_alloc(guac_client* client, 120 int width, int height) { 121 122 /* Allocate display */ 123 guac_common_display* display = guac_mem_alloc(sizeof(guac_common_display)); 124 if (display == NULL) 125 return NULL; 126 127 /* Allocate shared cursor */ 128 display->cursor = guac_common_cursor_alloc(client); 129 if (display->cursor == NULL) { 130 guac_mem_free(display); 131 return NULL; 132 } 133 134 pthread_mutex_init(&display->_lock, NULL); 135 136 /* Associate display with given client */ 137 display->client = client; 138 139 display->default_surface = guac_common_surface_alloc(client, 140 client->socket, GUAC_DEFAULT_LAYER, width, height); 141 142 /* No initial layers or buffers */ 143 display->layers = NULL; 144 display->buffers = NULL; 145 146 return display; 147 148} 149 150void guac_common_display_free(guac_common_display* display) { 151 152 /* Free shared cursor */ 153 guac_common_cursor_free(display->cursor); 154 155 /* Free default surface */ 156 guac_common_surface_free(display->default_surface); 157 158 /* Free all layers and buffers */ 159 guac_common_display_free_layers(display->buffers, display->client); 160 guac_common_display_free_layers(display->layers, display->client); 161 162 pthread_mutex_destroy(&display->_lock); 163 guac_mem_free(display); 164 165} 166 167void guac_common_display_dup( 168 guac_common_display* display, guac_client* client, 169 guac_socket* socket) { 170 171 pthread_mutex_lock(&display->_lock); 172 173 /* Sunchronize shared cursor */ 174 guac_common_cursor_dup(display->cursor, client, socket); 175 176 /* Synchronize default surface */ 177 guac_common_surface_dup(display->default_surface, client, socket); 178 179 /* Synchronize all layers and buffers */ 180 guac_common_display_dup_layers(display->layers, client, socket); 181 guac_common_display_dup_layers(display->buffers, client, socket); 182 183 pthread_mutex_unlock(&display->_lock); 184 185} 186 187void guac_common_display_set_lossless(guac_common_display* display, 188 int lossless) { 189 190 pthread_mutex_lock(&display->_lock); 191 192 /* Update lossless setting to be applied to all newly-allocated 193 * layers/buffers */ 194 display->lossless = lossless; 195 196 /* Update losslessness of all allocated layers/buffers */ 197 guac_common_display_layer* current = display->layers; 198 while (current != NULL) { 199 guac_common_surface_set_lossless(current->surface, lossless); 200 current = current->next; 201 } 202 203 /* Update losslessness of default display layer (not included within layers 204 * list) */ 205 guac_common_surface_set_lossless(display->default_surface, lossless); 206 207 pthread_mutex_unlock(&display->_lock); 208 209} 210 211void guac_common_display_flush(guac_common_display* display) { 212 213 pthread_mutex_lock(&display->_lock); 214 215 guac_common_display_layer* current = display->layers; 216 217 /* Flush all surfaces */ 218 while (current != NULL) { 219 guac_common_surface_flush(current->surface); 220 current = current->next; 221 } 222 223 guac_common_surface_flush(display->default_surface); 224 225 pthread_mutex_unlock(&display->_lock); 226 227} 228 229/** 230 * Allocates and inserts a new element into the given linked list of display 231 * layers, associating it with the given layer and surface. 232 * 233 * @param head 234 * A pointer to the head pointer of the list of layers. The head pointer 235 * will be updated by this function to point to the newly-allocated 236 * display layer. 237 * 238 * @param layer 239 * The Guacamole layer to associated with the new display layer. 240 * 241 * @param surface 242 * The surface associated with the given Guacamole layer and which should 243 * be associated with the new display layer. 244 * 245 * @return 246 * The newly-allocated display layer, which has been associated with the 247 * provided layer and surface. 248 */ 249static guac_common_display_layer* guac_common_display_add_layer( 250 guac_common_display_layer** head, guac_layer* layer, 251 guac_common_surface* surface) { 252 253 guac_common_display_layer* old_head = *head; 254 255 guac_common_display_layer* display_layer = 256 guac_mem_alloc(sizeof(guac_common_display_layer)); 257 258 /* Init layer/surface pair */ 259 display_layer->layer = layer; 260 display_layer->surface = surface; 261 262 /* Insert list element as the new head */ 263 display_layer->prev = NULL; 264 display_layer->next = old_head; 265 *head = display_layer; 266 267 /* Update old head to point to new element, if it existed */ 268 if (old_head != NULL) 269 old_head->prev = display_layer; 270 271 return display_layer; 272 273} 274 275/** 276 * Removes the given display layer from the linked list whose head pointer is 277 * provided. 278 * 279 * @param head 280 * A pointer to the head pointer of the list of layers. The head pointer 281 * will be updated by this function if necessary, and will be set to NULL 282 * if the display layer being removed is the only layer in the list. 283 * 284 * @param display_layer 285 * The display layer to remove from the given list. 286 */ 287static void guac_common_display_remove_layer(guac_common_display_layer** head, 288 guac_common_display_layer* display_layer) { 289 290 /* Update previous element, if it exists */ 291 if (display_layer->prev != NULL) 292 display_layer->prev->next = display_layer->next; 293 294 /* If there is no previous element, update the list head */ 295 else 296 *head = display_layer->next; 297 298 /* Update next element, if it exists */ 299 if (display_layer->next != NULL) 300 display_layer->next->prev = display_layer->prev; 301 302} 303 304guac_common_display_layer* guac_common_display_alloc_layer( 305 guac_common_display* display, int width, int height) { 306 307 pthread_mutex_lock(&display->_lock); 308 309 /* Allocate Guacamole layer */ 310 guac_layer* layer = guac_client_alloc_layer(display->client); 311 312 /* Allocate corresponding surface */ 313 guac_common_surface* surface = guac_common_surface_alloc(display->client, 314 display->client->socket, layer, width, height); 315 316 /* Apply current display losslessness */ 317 guac_common_surface_set_lossless(surface, display->lossless); 318 319 /* Add layer and surface to list */ 320 guac_common_display_layer* display_layer = 321 guac_common_display_add_layer(&display->layers, layer, surface); 322 323 pthread_mutex_unlock(&display->_lock); 324 return display_layer; 325 326} 327 328guac_common_display_layer* guac_common_display_alloc_buffer( 329 guac_common_display* display, int width, int height) { 330 331 pthread_mutex_lock(&display->_lock); 332 333 /* Allocate Guacamole buffer */ 334 guac_layer* buffer = guac_client_alloc_buffer(display->client); 335 336 /* Allocate corresponding surface */ 337 guac_common_surface* surface = guac_common_surface_alloc(display->client, 338 display->client->socket, buffer, width, height); 339 340 /* Apply current display losslessness */ 341 guac_common_surface_set_lossless(surface, display->lossless); 342 343 /* Add buffer and surface to list */ 344 guac_common_display_layer* display_layer = 345 guac_common_display_add_layer(&display->buffers, buffer, surface); 346 347 pthread_mutex_unlock(&display->_lock); 348 return display_layer; 349 350} 351 352void guac_common_display_free_layer(guac_common_display* display, 353 guac_common_display_layer* display_layer) { 354 355 pthread_mutex_lock(&display->_lock); 356 357 /* Remove list element from list */ 358 guac_common_display_remove_layer(&display->layers, display_layer); 359 360 /* Free associated layer and surface */ 361 guac_common_surface_free(display_layer->surface); 362 guac_client_free_layer(display->client, display_layer->layer); 363 364 /* Free list element */ 365 guac_mem_free(display_layer); 366 367 pthread_mutex_unlock(&display->_lock); 368 369} 370 371void guac_common_display_free_buffer(guac_common_display* display, 372 guac_common_display_layer* display_buffer) { 373 374 pthread_mutex_lock(&display->_lock); 375 376 /* Remove list element from list */ 377 guac_common_display_remove_layer(&display->buffers, display_buffer); 378 379 /* Free associated layer and surface */ 380 guac_common_surface_free(display_buffer->surface); 381 guac_client_free_buffer(display->client, display_buffer->layer); 382 383 /* Free list element */ 384 guac_mem_free(display_buffer); 385 386 pthread_mutex_unlock(&display->_lock); 387 388} 389