Merge pull request #110 from glyptodon/fake-merge-screen-sharing-002-common

GUAC-1389: Bring common up to date with screen sharing changes.
This commit is contained in:
James Muehlner 2016-03-01 17:27:45 -08:00
commit 0d984f3886
20 changed files with 1596 additions and 84 deletions

View File

@ -37,6 +37,7 @@ DIST_SUBDIRS = \
SUBDIRS = \ SUBDIRS = \
src/libguac \ src/libguac \
src/common \
tests tests
if ENABLE_COMMON_SSH if ENABLE_COMMON_SSH

View File

@ -27,8 +27,12 @@ noinst_LTLIBRARIES = libguac_common.la
noinst_HEADERS = \ noinst_HEADERS = \
guac_io.h \ guac_io.h \
guac_blank_cursor.h \
guac_clipboard.h \ guac_clipboard.h \
guac_cursor.h \
guac_display.h \
guac_dot_cursor.h \ guac_dot_cursor.h \
guac_ibar_cursor.h \
guac_iconv.h \ guac_iconv.h \
guac_json.h \ guac_json.h \
guac_list.h \ guac_list.h \
@ -39,8 +43,12 @@ noinst_HEADERS = \
libguac_common_la_SOURCES = \ libguac_common_la_SOURCES = \
guac_io.c \ guac_io.c \
guac_blank_cursor.c \
guac_clipboard.c \ guac_clipboard.c \
guac_cursor.c \
guac_display.c \
guac_dot_cursor.c \ guac_dot_cursor.c \
guac_ibar_cursor.c \
guac_iconv.c \ guac_iconv.c \
guac_json.c \ guac_json.c \
guac_list.c \ guac_list.c \

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
/* Dimensions */
const int guac_common_blank_cursor_width = 1;
const int guac_common_blank_cursor_height = 1;
/* Format */
const cairo_format_t guac_common_blank_cursor_format = CAIRO_FORMAT_ARGB32;
const int guac_common_blank_cursor_stride = 4;
/* Embedded blank cursor graphic */
unsigned char guac_common_blank_cursor[] = {
0x00,0x00,0x00,0x00
};
void guac_common_set_blank_cursor(guac_user* user) {
guac_client* client = user->client;
guac_socket* socket = user->socket;
/* Draw to buffer */
guac_layer* cursor = guac_client_alloc_buffer(client);
cairo_surface_t* graphic = cairo_image_surface_create_for_data(
guac_common_blank_cursor,
guac_common_blank_cursor_format,
guac_common_blank_cursor_width,
guac_common_blank_cursor_height,
guac_common_blank_cursor_stride);
guac_user_stream_png(user, socket, GUAC_COMP_SRC, cursor,
0, 0, graphic);
cairo_surface_destroy(graphic);
/* Set cursor */
guac_protocol_send_cursor(socket, 0, 0, cursor, 0, 0,
guac_common_blank_cursor_width,
guac_common_blank_cursor_height);
/* Free buffer */
guac_client_free_buffer(client, cursor);
guac_client_log(client, GUAC_LOG_DEBUG,
"Client cursor image set to generic transparent (blank) cursor.");
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_COMMON_BLANK_CURSOR_H
#define GUAC_COMMON_BLANK_CURSOR_H
#include "config.h"
#include <cairo/cairo.h>
#include <guacamole/user.h>
/**
* Width of the embedded transparent (blank) mouse cursor graphic.
*/
extern const int guac_common_blank_cursor_width;
/**
* Height of the embedded transparent (blank) mouse cursor graphic.
*/
extern const int guac_common_blank_cursor_height;
/**
* Number of bytes in each row of the embedded transparent (blank) mouse cursor
* graphic.
*/
extern const int guac_common_blank_cursor_stride;
/**
* The Cairo grapic format of the transparent (blank) mouse cursor graphic.
*/
extern const cairo_format_t guac_common_blank_cursor_format;
/**
* Embedded transparent (blank) mouse cursor graphic.
*/
extern unsigned char guac_common_blank_cursor[];
/**
* Sets the cursor of the remote display to the embedded transparent (blank)
* cursor graphic.
*
* @param user
* The guac_user to send the cursor to.
*/
void guac_common_set_blank_cursor(guac_user* user);
#endif

View File

@ -26,6 +26,7 @@
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
#include <guacamole/user.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -48,16 +49,32 @@ void guac_common_clipboard_free(guac_common_clipboard* clipboard) {
free(clipboard); free(clipboard);
} }
void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) { /**
* Callback for guac_client_foreach_user() which sends clipboard data to each
* connected client.
*
* @param user
* The user to send the clipboard data to.
*
* @param
* A pointer to the guac_common_clipboard structure containing the
* clipboard data that should be sent to the given user.
*
* @return
* Always NULL.
*/
static void* __send_user_clipboard(guac_user* user, void* data) {
guac_common_clipboard* clipboard = (guac_common_clipboard*) data;
char* current = clipboard->buffer; char* current = clipboard->buffer;
int remaining = clipboard->length; int remaining = clipboard->length;
/* Begin stream */ /* Begin stream */
guac_stream* stream = guac_client_alloc_stream(client); guac_stream* stream = guac_user_alloc_stream(user);
guac_protocol_send_clipboard(client->socket, stream, clipboard->mimetype); guac_protocol_send_clipboard(user->socket, stream, clipboard->mimetype);
guac_client_log(client, GUAC_LOG_DEBUG, guac_user_log(user, GUAC_LOG_DEBUG,
"Created stream %i for %s clipboard data.", "Created stream %i for %s clipboard data.",
stream->index, clipboard->mimetype); stream->index, clipboard->mimetype);
@ -70,8 +87,8 @@ void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* c
block_size = remaining; block_size = remaining;
/* Send block */ /* Send block */
guac_protocol_send_blob(client->socket, stream, current, block_size); guac_protocol_send_blob(user->socket, stream, current, block_size);
guac_client_log(client, GUAC_LOG_DEBUG, guac_user_log(user, GUAC_LOG_DEBUG,
"Sent %i bytes of clipboard data on stream %i.", "Sent %i bytes of clipboard data on stream %i.",
block_size, stream->index); block_size, stream->index);
@ -81,14 +98,22 @@ void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* c
} }
guac_client_log(client, GUAC_LOG_DEBUG, guac_user_log(user, GUAC_LOG_DEBUG,
"Clipboard stream %i complete.", "Clipboard stream %i complete.",
stream->index); stream->index);
/* End stream */ /* End stream */
guac_protocol_send_end(client->socket, stream); guac_protocol_send_end(user->socket, stream);
guac_client_free_stream(client, stream); guac_user_free_stream(user, stream);
return NULL;
}
void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) {
guac_client_log(client, GUAC_LOG_DEBUG, "Broadcasting clipboard to all connected users.");
guac_client_foreach_user(client, __send_user_clipboard, clipboard);
guac_client_log(client, GUAC_LOG_DEBUG, "Broadcast of clipboard complete.");
} }
void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype) { void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype) {

314
src/common/guac_cursor.c Normal file
View File

@ -0,0 +1,314 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "guac_blank_cursor.h"
#include "guac_dot_cursor.h"
#include "guac_cursor.h"
#include "guac_ibar_cursor.h"
#include "guac_pointer_cursor.h"
#include "guac_surface.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <stdlib.h>
#include <string.h>
guac_common_cursor* guac_common_cursor_alloc(guac_client* client) {
guac_common_cursor* cursor = malloc(sizeof(guac_common_cursor));
if (cursor == NULL)
return NULL;
/* Associate cursor with client and allocate cursor layer */
cursor->client = client;
cursor->layer= guac_client_alloc_layer(client);
/* Allocate initial image buffer */
cursor->image_buffer_size = GUAC_COMMON_CURSOR_DEFAULT_SIZE;
cursor->image_buffer = malloc(cursor->image_buffer_size);
/* No cursor image yet */
cursor->width = 0;
cursor->height = 0;
cursor->surface = NULL;
cursor->hotspot_x = 0;
cursor->hotspot_y = 0;
/* No user has moved the mouse yet */
cursor->user = NULL;
/* Start cursor in upper-left */
cursor->x = 0;
cursor->y = 0;
return cursor;
}
void guac_common_cursor_free(guac_common_cursor* cursor) {
/* Free image buffer and surface */
free(cursor->image_buffer);
if (cursor->surface != NULL)
cairo_surface_destroy(cursor->surface);
/* Return layer to pool */
guac_client_free_layer(cursor->client, cursor->layer);
free(cursor);
}
void guac_common_cursor_dup(guac_common_cursor* cursor, guac_user* user,
guac_socket* socket) {
/* Synchronize location */
guac_protocol_send_move(socket, cursor->layer, GUAC_DEFAULT_LAYER,
cursor->x - cursor->hotspot_x,
cursor->y - cursor->hotspot_y,
0);
/* Synchronize cursor image */
if (cursor->surface != NULL) {
guac_protocol_send_size(socket, cursor->layer,
cursor->width, cursor->height);
guac_user_stream_png(user, socket, GUAC_COMP_SRC,
cursor->layer, 0, 0, cursor->surface);
}
guac_socket_flush(socket);
}
void guac_common_cursor_move(guac_common_cursor* cursor, guac_user* user,
int x, int y) {
guac_user* last_user = cursor->user;
/* Update current user of cursor */
if (last_user != user) {
cursor->user = user;
/* Make cursor layer visible to previous user */
if (last_user != NULL) {
guac_protocol_send_shade(last_user->socket, cursor->layer, 255);
guac_socket_flush(last_user->socket);
}
/* Show hardware cursor */
guac_protocol_send_cursor(user->socket,
cursor->hotspot_x, cursor->hotspot_y,
cursor->layer, 0, 0, cursor->width, cursor->height);
/* Hide cursor layer from new user */
guac_protocol_send_shade(user->socket, cursor->layer, 0);
guac_socket_flush(user->socket);
}
/* Update cursor position */
cursor->x = x;
cursor->y = y;
guac_protocol_send_move(cursor->client->socket, cursor->layer,
GUAC_DEFAULT_LAYER,
x - cursor->hotspot_x,
y - cursor->hotspot_y,
0);
guac_socket_flush(cursor->client->socket);
}
/**
* Ensures the cursor image buffer has enough room to fit an image with the
* given characteristics. Existing image buffer data may be destroyed.
*
* @param cursor
* The cursor whose buffer size should be checked. If this cursor lacks
* sufficient space to contain a cursor image of the specified width,
* height, and stride, the current contents of this cursor will be
* destroyed and replaced with an new buffer having sufficient space.
*
* @param width
* The required cursor width, in pixels.
*
* @param height
* The required cursor height, in pixels.
*
* @param stride
* The number of bytes in each row of image data.
*/
static void guac_common_cursor_resize(guac_common_cursor* cursor,
int width, int height, int stride) {
int minimum_size = height * stride;
/* Grow image buffer if necessary */
if (cursor->image_buffer_size < minimum_size) {
/* Calculate new size */
cursor->image_buffer_size = minimum_size*2;
/* Destructively reallocate image buffer */
free(cursor->image_buffer);
cursor->image_buffer = malloc(cursor->image_buffer_size);
}
}
/**
* Callback for guac_client_foreach_user() which sends the current cursor image
* as PNG data to each connected client.
*
* @param user
* The user to send the cursor image to.
*
* @param data
* A pointer to the guac_common_cursor structure containing the cursor
* image that should be sent to the given user.
*
* @return
* Always NULL.
*/
static void* __send_user_cursor_image(guac_user* user, void* data) {
guac_common_cursor* cursor = (guac_common_cursor*) data;
guac_user_stream_png(user, user->socket, GUAC_COMP_SRC,
cursor->layer, 0, 0, cursor->surface);
return NULL;
}
void guac_common_cursor_set_argb(guac_common_cursor* cursor, int hx, int hy,
unsigned const char* data, int width, int height, int stride) {
/* Copy image data */
guac_common_cursor_resize(cursor, width, height, stride);
memcpy(cursor->image_buffer, data, height * stride);
if (cursor->surface != NULL)
cairo_surface_destroy(cursor->surface);
cursor->surface = cairo_image_surface_create_for_data(cursor->image_buffer,
CAIRO_FORMAT_ARGB32, width, height, stride);
/* Set new cursor parameters */
cursor->width = width;
cursor->height = height;
cursor->hotspot_x = hx;
cursor->hotspot_y = hy;
/* Update location based on new hotspot */
guac_protocol_send_move(cursor->client->socket, cursor->layer,
GUAC_DEFAULT_LAYER,
cursor->x - hx,
cursor->y - hy,
0);
/* Broadcast new cursor image to all users */
guac_protocol_send_size(cursor->client->socket, cursor->layer,
width, height);
guac_client_foreach_user(cursor->client, __send_user_cursor_image, cursor);
guac_socket_flush(cursor->client->socket);
/* Update hardware cursor of current user */
if (cursor->user != NULL) {
guac_protocol_send_cursor(cursor->user->socket, hx, hy,
cursor->layer, 0, 0, width, height);
guac_socket_flush(cursor->user->socket);
}
}
void guac_common_cursor_set_surface(guac_common_cursor* cursor, int hx, int hy,
guac_common_surface* surface) {
/* Set cursor to surface contents */
guac_common_cursor_set_argb(cursor, hx, hy, surface->buffer,
surface->width, surface->height, surface->stride);
}
void guac_common_cursor_set_pointer(guac_common_cursor* cursor) {
guac_common_cursor_set_argb(cursor, 0, 0,
guac_common_pointer_cursor,
guac_common_pointer_cursor_width,
guac_common_pointer_cursor_height,
guac_common_pointer_cursor_stride);
}
void guac_common_cursor_set_dot(guac_common_cursor* cursor) {
guac_common_cursor_set_argb(cursor, 2, 2,
guac_common_dot_cursor,
guac_common_dot_cursor_width,
guac_common_dot_cursor_height,
guac_common_dot_cursor_stride);
}
void guac_common_cursor_set_ibar(guac_common_cursor* cursor) {
guac_common_cursor_set_argb(cursor,
guac_common_ibar_cursor_width / 2,
guac_common_ibar_cursor_height / 2,
guac_common_ibar_cursor,
guac_common_ibar_cursor_width,
guac_common_ibar_cursor_height,
guac_common_ibar_cursor_stride);
}
void guac_common_cursor_set_blank(guac_common_cursor* cursor) {
guac_common_cursor_set_argb(cursor, 0, 0,
guac_common_blank_cursor,
guac_common_blank_cursor_width,
guac_common_blank_cursor_height,
guac_common_blank_cursor_stride);
}
void guac_common_cursor_remove_user(guac_common_cursor* cursor,
guac_user* user) {
/* Disassociate from given user */
if (cursor->user == user)
cursor->user = NULL;
}

270
src/common/guac_cursor.h Normal file
View File

@ -0,0 +1,270 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_COMMON_CURSOR_H
#define GUAC_COMMON_CURSOR_H
#include "guac_surface.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
/**
* The default size of the cursor image buffer.
*/
#define GUAC_COMMON_CURSOR_DEFAULT_SIZE 64*64*4
/**
* Cursor object which maintains and synchronizes the current mouse cursor
* state across all users of a specific client.
*/
typedef struct guac_common_cursor {
/**
* The client to maintain the mouse cursor for.
*/
guac_client* client;
/**
* The cursor layer. This layer will be available to all connected users,
* but will be visible only to those users who are not moving the mouse.
*/
guac_layer* layer;
/**
* The width of the cursor image, in pixels.
*/
int width;
/**
* The height of the cursor image, in pixels.
*/
int height;
/**
* Arbitrary image data buffer, backing the Cairo surface used to store
* the cursor image.
*/
unsigned char* image_buffer;
/**
* The size of the image data buffer, in bytes.
*/
int image_buffer_size;
/**
* The current cursor image, if any. If the mouse cursor has not yet been
* set, this will be NULL.
*/
cairo_surface_t* surface;
/**
* The X coordinate of the hotspot of the mouse cursor.
*/
int hotspot_x;
/**
* The Y coordinate of the hotspot of the mouse cursor.
*/
int hotspot_y;
/**
* The last user to move the mouse, or NULL if no user has moved the
* mouse yet.
*/
guac_user* user;
/**
* The X coordinate of the current mouse cursor location.
*/
int x;
/**
* The Y coordinate of the current mouse cursor location.
*/
int y;
} guac_common_cursor;
/**
* Allocates a new cursor object which maintains and synchronizes the current
* mouse cursor state across all users of the given client.
*
* @param client
* The client for which this object shall maintain the mouse cursor.
*
* @return
* The newly-allocated mouse cursor.
*/
guac_common_cursor* guac_common_cursor_alloc(guac_client* client);
/**
* Frees the given cursor.
*
* @param cursor
* The cursor to free.
*/
void guac_common_cursor_free(guac_common_cursor* cursor);
/**
* Sends the current state of this cursor across the given socket, including
* the current cursor image. The resulting cursor on the remote display will
* be visible.
*
* @param cursor
* The cursor to send.
*
* @param user
* The user receiving the updated cursor.
*
* @param socket
* The socket over which the updated cursor should be sent.
*/
void guac_common_cursor_dup(guac_common_cursor* cursor, guac_user* user,
guac_socket* socket);
/**
* Moves the mouse cursor, marking the given user as the most recent user of
* the mouse. The remote mouse cursor will be hidden for this user and shown
* for all others.
*
* @param cursor
* The cursor being moved.
*
* @param user
* The user that moved the cursor.
*
* @param x
* The new X coordinate of the cursor.
*
* @param y
* The new Y coordinate of the cursor.
*/
void guac_common_cursor_move(guac_common_cursor* cursor, guac_user* user,
int x, int y);
/**
* Sets the cursor image to the given raw image data. This raw image data must
* be in 32-bit ARGB format, having 8 bits per color component, where the
* alpha component is stored in the high-order 8 bits, and blue is stored
* in the low-order 8 bits.
*
* @param cursor
* The cursor to set the image of.
*
* @param hx
* The X coordinate of the hotspot of the new cursor image.
*
* @param hy
* The Y coordinate of the hotspot of the new cursor image.
*
* @param data
* A pointer to raw 32-bit ARGB image data.
*
* @param width
* The width of the given image data, in pixels.
*
* @param height
* The height of the given image data, in pixels.
*
* @param stride
* The number of bytes in a single row of image data.
*/
void guac_common_cursor_set_argb(guac_common_cursor* cursor, int hx, int hy,
unsigned const char* data, int width, int height, int stride);
/**
* Sets the cursor image to the contents of the given surface. The entire
* contents of the surface are used, and the dimensions of the resulting
* cursor will be the dimensions of the given surface.
*
* @param cursor
* The cursor to set the image of.
*
* @param hx
* The X coordinate of the hotspot of the new cursor image.
*
* @param hy
* The Y coordinate of the hotspot of the new cursor image.
*
* @param surface
* The surface containing the cursor image.
*/
void guac_common_cursor_set_surface(guac_common_cursor* cursor, int hx, int hy,
guac_common_surface* surface);
/**
* Set the cursor of the remote display to the embedded "pointer" graphic. The
* pointer graphic is a black arrow with white border.
*
* @param cursor
* The cursor to set the image of.
*/
void guac_common_cursor_set_pointer(guac_common_cursor* cursor);
/**
* Set the cursor of the remote display to the embedded "dot" graphic. The dot
* graphic is a small black square with white border.
*
* @param cursor
* The cursor to set the image of.
*/
void guac_common_cursor_set_dot(guac_common_cursor* cursor);
/**
* Sets the cursor of the remote display to the embedded "I-bar" graphic. The
* I-bar graphic is a small black "I" shape with white border, used to indicate
* the presence of selectable or editable text.
*
* @param cursor
* The cursor to set the image of.
*/
void guac_common_cursor_set_ibar(guac_common_cursor* cursor);
/**
* Sets the cursor of the remote display to the embedded transparent (blank)
* graphic, effectively hiding the mouse cursor.
*
* @param cursor
* The cursor to set the image of.
*/
void guac_common_cursor_set_blank(guac_common_cursor* cursor);
/**
* Removes the given user, such that future synchronization will not occur.
* This is necessary when a user leaves the connection. If a user leaves the
* connection and this is not called, the corresponding guac_user and socket
* may cease to be valid, and future synchronization attempts will segfault.
*
* @param cursor
* The cursor to remove the user from.
*
* @param user
* The user to remove.
*/
void guac_common_cursor_remove_user(guac_common_cursor* cursor,
guac_user* user);
#endif

301
src/common/guac_display.c Normal file
View File

@ -0,0 +1,301 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "guac_cursor.h"
#include "guac_display.h"
#include "guac_surface.h"
#include <guacamole/client.h>
#include <guacamole/socket.h>
#include <stdlib.h>
#include <string.h>
/**
* 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);
/* 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;
}
}
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;
/* Associate display with given client */
display->client = client;
/* Allocate shared cursor */
display->cursor = guac_common_cursor_alloc(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);
free(display);
}
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) {
/* 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);
}
void guac_common_display_flush(guac_common_display* display) {
guac_common_surface_flush(display->default_surface);
}
/**
* 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) {
guac_layer* layer;
guac_common_surface* surface;
/* Allocate Guacamole layer */
layer = guac_client_alloc_layer(display->client);
/* Allocate corresponding surface */
surface = guac_common_surface_alloc(display->client,
display->client->socket, layer, width, height);
/* Add layer and surface to list */
return guac_common_display_add_layer(&display->layers, layer, surface);
}
guac_common_display_layer* guac_common_display_alloc_buffer(
guac_common_display* display, int width, int height) {
guac_layer* buffer;
guac_common_surface* surface;
/* Allocate Guacamole buffer */
buffer = guac_client_alloc_buffer(display->client);
/* Allocate corresponding surface */
surface = guac_common_surface_alloc(display->client,
display->client->socket, buffer, width, height);
/* Add buffer and surface to list */
return guac_common_display_add_layer(&display->buffers, buffer, surface);
}
void guac_common_display_free_layer(guac_common_display* display,
guac_common_display_layer* display_layer) {
/* 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);
}
void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer) {
/* 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);
}

226
src/common/guac_display.h Normal file
View File

@ -0,0 +1,226 @@
/*
* Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_COMMON_DISPLAY_H
#define GUAC_COMMON_DISPLAY_H
#include "guac_cursor.h"
#include "guac_surface.h"
#include <guacamole/client.h>
#include <guacamole/socket.h>
/**
* A list element representing a pairing of a Guacamole layer with a
* corresponding guac_common_surface which wraps that layer. Adjacent layers
* within the same list are pointed to with traditional prev/next pointers. The
* order of layers in lists need not correspond in any way to the natural
* ordering of those layers' indexes nor their stacking order (Z-order) within
* the display.
*/
typedef struct guac_common_display_layer guac_common_display_layer;
struct guac_common_display_layer {
/**
* A Guacamole layer.
*/
guac_layer* layer;
/**
* The surface which wraps the associated layer.
*/
guac_common_surface* surface;
/**
* The layer immediately prior to this layer within the list containing
* this layer, or NULL if this is the first layer/buffer in the list.
*/
guac_common_display_layer* prev;
/**
* The layer immediately following this layer within the list containing
* this layer, or NULL if this is the last layer/buffer in the list.
*/
guac_common_display_layer* next;
};
/**
* Abstracts a remote Guacamole display, having an associated client,
* default surface, mouse cursor, and various allocated buffers and layers.
*/
typedef struct guac_common_display {
/**
* The client associate with this display.
*/
guac_client* client;
/**
* The default surface of the client display.
*/
guac_common_surface* default_surface;
/**
* Client-wide cursor, synchronized across all users.
*/
guac_common_cursor* cursor;
/**
* The first element within a linked list of all currently-allocated
* layers, or NULL if no layers are currently allocated. The default layer,
* layer #0, is stored within default_surface and will not have a
* corresponding element within this list.
*/
guac_common_display_layer* layers;
/**
* The first element within a linked list of all currently-allocated
* buffers, or NULL if no buffers are currently allocated.
*/
guac_common_display_layer* buffers;
} guac_common_display;
/**
* Allocates a new display, abstracting the cursor and buffer/layer allocation
* operations of the given guac_client such that client state can be easily
* synchronized to joining users.
*
* @param client
* The guac_client to associate with this display.
*
* @param width
* The initial width of the display, in pixels.
*
* @param height
* The initial height of the display, in pixels.
*
* @return
* The newly-allocated display.
*/
guac_common_display* guac_common_display_alloc(guac_client* client,
int width, int height);
/**
* Frees the given display, and any associated resources, including any
* allocated buffers/layers.
*
* @param display
* The display to free.
*/
void guac_common_display_free(guac_common_display* display);
/**
* Duplicates the state of the given display to the given socket. Any pending
* changes to buffers, layers, or the default layer are not flushed.
*
* @param display
* The display whose state should be sent along the given socket.
*
* @param user
* The user receiving the display state.
*
* @param socket
* The socket over which the display state should be sent.
*/
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket);
/**
* Flushes pending changes to the given display. All pending operations will
* become visible to any connected users.
*
* @param display
* The display to flush.
*/
void guac_common_display_flush(guac_common_display* display);
/**
* Allocates a new layer, returning a new wrapped layer and corresponding
* surface. The layer may be reused from a previous allocation, if that layer
* has since been freed.
*
* @param display
* The display to allocate a new layer from.
*
* @param width
* The width of the layer to allocate, in pixels.
*
* @param height
* The height of the layer to allocate, in pixels.
*
* @return
* A newly-allocated layer.
*/
guac_common_display_layer* guac_common_display_alloc_layer(
guac_common_display* display, int width, int height);
/**
* Allocates a new buffer, returning a new wrapped buffer and corresponding
* surface. The buffer may be reused from a previous allocation, if that buffer
* has since been freed.
*
* @param display
* The display to allocate a new buffer from.
*
* @param width
* The width of the buffer to allocate, in pixels.
*
* @param height
* The height of the buffer to allocate, in pixels.
*
* @return
* A newly-allocated buffer.
*/
guac_common_display_layer* guac_common_display_alloc_buffer(
guac_common_display* display, int width, int height);
/**
* Frees the given surface and associated layer, returning the layer to the
* given display for future use.
*
* @param display
* The display originally allocating the layer.
*
* @param display_layer
* The layer to free.
*/
void guac_common_display_free_layer(guac_common_display* display,
guac_common_display_layer* display_layer);
/**
* Frees the given surface and associated buffer, returning the buffer to the
* given display for future use.
*
* @param display
* The display originally allocating the buffer.
*
* @param display_buffer
* The buffer to free.
*/
void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer);
#endif

View File

@ -27,6 +27,7 @@
#include <guacamole/layer.h> #include <guacamole/layer.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/socket.h> #include <guacamole/socket.h>
#include <guacamole/user.h>
/* Macros for prettying up the embedded image. */ /* Macros for prettying up the embedded image. */
#define X 0x00,0x00,0x00,0xFF #define X 0x00,0x00,0x00,0xFF
@ -52,9 +53,10 @@ unsigned char guac_common_dot_cursor[] = {
}; };
void guac_common_set_dot_cursor(guac_client* client) { void guac_common_set_dot_cursor(guac_user* user) {
guac_socket* socket = client->socket; guac_client* client = user->client;
guac_socket* socket = user->socket;
/* Draw to buffer */ /* Draw to buffer */
guac_layer* cursor = guac_client_alloc_buffer(client); guac_layer* cursor = guac_client_alloc_buffer(client);
@ -66,7 +68,7 @@ void guac_common_set_dot_cursor(guac_client* client) {
guac_common_dot_cursor_height, guac_common_dot_cursor_height,
guac_common_dot_cursor_stride); guac_common_dot_cursor_stride);
guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor, guac_user_stream_png(user, socket, GUAC_COMP_SRC, cursor,
0, 0, graphic); 0, 0, graphic);
cairo_surface_destroy(graphic); cairo_surface_destroy(graphic);

View File

@ -27,7 +27,7 @@
#include "config.h" #include "config.h"
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <guacamole/client.h> #include <guacamole/user.h>
/** /**
* Width of the embedded mouse cursor graphic. * Width of the embedded mouse cursor graphic.
@ -57,8 +57,8 @@ extern unsigned char guac_common_dot_cursor[];
/** /**
* Set the cursor of the remote display to the embedded cursor graphic. * Set the cursor of the remote display to the embedded cursor graphic.
* *
* @param client The guac_client to send the cursor to. * @param user The guac_user to send the cursor to.
*/ */
void guac_common_set_dot_cursor(guac_client* client); void guac_common_set_dot_cursor(guac_user* user);
#endif #endif

View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <cairo/cairo.h>
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
/* Macros for prettying up the embedded image. */
#define X 0x00,0x00,0x00,0xFF
#define U 0x80,0x80,0x80,0xFF
#define O 0xFF,0xFF,0xFF,0xFF
#define _ 0x00,0x00,0x00,0x00
/* Dimensions */
const int guac_common_ibar_cursor_width = 7;
const int guac_common_ibar_cursor_height = 16;
/* Format */
const cairo_format_t guac_common_ibar_cursor_format = CAIRO_FORMAT_ARGB32;
const int guac_common_ibar_cursor_stride = 28;
/* Embedded I-bar graphic */
unsigned char guac_common_ibar_cursor[] = {
X,X,X,X,X,X,X,
X,O,O,U,O,O,X,
X,X,X,O,X,X,X,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
_,_,X,O,X,_,_,
X,X,X,O,X,X,X,
X,O,O,U,O,O,X,
X,X,X,X,X,X,X
};
void guac_common_set_ibar_cursor(guac_user* user) {
guac_client* client = user->client;
guac_socket* socket = user->socket;
/* Draw to buffer */
guac_layer* cursor = guac_client_alloc_buffer(client);
cairo_surface_t* graphic = cairo_image_surface_create_for_data(
guac_common_ibar_cursor,
guac_common_ibar_cursor_format,
guac_common_ibar_cursor_width,
guac_common_ibar_cursor_height,
guac_common_ibar_cursor_stride);
guac_user_stream_png(user, socket, GUAC_COMP_SRC, cursor,
0, 0, graphic);
cairo_surface_destroy(graphic);
/* Set cursor */
guac_protocol_send_cursor(socket, 0, 0, cursor,
guac_common_ibar_cursor_width / 2,
guac_common_ibar_cursor_height / 2,
guac_common_ibar_cursor_width,
guac_common_ibar_cursor_height);
/* Free buffer */
guac_client_free_buffer(client, cursor);
guac_client_log(client, GUAC_LOG_DEBUG,
"Client cursor image set to generic built-in I-bar.");
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GUAC_COMMON_IBAR_CURSOR_H
#define GUAC_COMMON_IBAR_CURSOR_H
#include "config.h"
#include <cairo/cairo.h>
#include <guacamole/user.h>
/**
* Width of the embedded I-bar mouse cursor graphic.
*/
extern const int guac_common_ibar_cursor_width;
/**
* Height of the embedded I-bar mouse cursor graphic.
*/
extern const int guac_common_ibar_cursor_height;
/**
* Number of bytes in each row of the embedded I-bar mouse cursor graphic.
*/
extern const int guac_common_ibar_cursor_stride;
/**
* The Cairo grapic format of the I-bar mouse cursor graphic.
*/
extern const cairo_format_t guac_common_ibar_cursor_format;
/**
* Embedded I-bar mouse cursor graphic.
*/
extern unsigned char guac_common_ibar_cursor[];
/**
* Sets the cursor of the remote display to the embedded I-bar cursor graphic.
*
* @param user
* The guac_user to send the cursor to.
*/
void guac_common_set_ibar_cursor(guac_user* user);
#endif

View File

@ -28,17 +28,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/socket.h> #include <guacamole/socket.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
#include <guacamole/user.h>
void guac_common_json_flush(guac_client* client, guac_stream* stream, void guac_common_json_flush(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state) { guac_common_json_state* json_state) {
/* If JSON buffer is non-empty, write contents to blob and reset */ /* If JSON buffer is non-empty, write contents to blob and reset */
if (json_state->size > 0) { if (json_state->size > 0) {
guac_protocol_send_blob(client->socket, stream, guac_protocol_send_blob(user->socket, stream,
json_state->buffer, json_state->size); json_state->buffer, json_state->size);
/* Reset JSON buffer size */ /* Reset JSON buffer size */
@ -48,7 +48,7 @@ void guac_common_json_flush(guac_client* client, guac_stream* stream,
} }
int guac_common_json_write(guac_client* client, guac_stream* stream, int guac_common_json_write(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state, const char* buffer, int length) { guac_common_json_state* json_state, const char* buffer, int length) {
int blob_written = 0; int blob_written = 0;
@ -66,7 +66,7 @@ int guac_common_json_write(guac_client* client, guac_stream* stream,
/* Flush if more room is needed */ /* Flush if more room is needed */
if (json_state->size + blob_length > sizeof(json_state->buffer)) { if (json_state->size + blob_length > sizeof(json_state->buffer)) {
guac_common_json_flush(client, stream, json_state); guac_common_json_flush(user, stream, json_state);
blob_written = 1; blob_written = 1;
} }
@ -86,14 +86,14 @@ int guac_common_json_write(guac_client* client, guac_stream* stream,
} }
int guac_common_json_write_string(guac_client* client, int guac_common_json_write_string(guac_user* user,
guac_stream* stream, guac_common_json_state* json_state, guac_stream* stream, guac_common_json_state* json_state,
const char* str) { const char* str) {
int blob_written = 0; int blob_written = 0;
/* Write starting quote */ /* Write starting quote */
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, "\"", 1); json_state, "\"", 1);
/* Write given string, escaping as necessary */ /* Write given string, escaping as necessary */
@ -105,11 +105,11 @@ int guac_common_json_write_string(guac_client* client,
/* Write any string content up to current character */ /* Write any string content up to current character */
if (current != str) if (current != str)
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, str, current - str); json_state, str, current - str);
/* Escape the quote that was just read */ /* Escape the quote that was just read */
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, "\\", 1); json_state, "\\", 1);
/* Reset string */ /* Reset string */
@ -121,18 +121,18 @@ int guac_common_json_write_string(guac_client* client,
/* Write any remaining string content */ /* Write any remaining string content */
if (current != str) if (current != str)
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, str, current - str); json_state, str, current - str);
/* Write ending quote */ /* Write ending quote */
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, "\"", 1); json_state, "\"", 1);
return blob_written; return blob_written;
} }
int guac_common_json_write_property(guac_client* client, guac_stream* stream, int guac_common_json_write_property(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state, const char* name, guac_common_json_state* json_state, const char* name,
const char* value) { const char* value) {
@ -140,19 +140,19 @@ int guac_common_json_write_property(guac_client* client, guac_stream* stream,
/* Write leading comma if not first property */ /* Write leading comma if not first property */
if (json_state->properties_written != 0) if (json_state->properties_written != 0)
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, ",", 1); json_state, ",", 1);
/* Write property name */ /* Write property name */
blob_written |= guac_common_json_write_string(client, stream, blob_written |= guac_common_json_write_string(user, stream,
json_state, name); json_state, name);
/* Separate name from value with colon */ /* Separate name from value with colon */
blob_written |= guac_common_json_write(client, stream, blob_written |= guac_common_json_write(user, stream,
json_state, ":", 1); json_state, ":", 1);
/* Write property value */ /* Write property value */
blob_written |= guac_common_json_write_string(client, stream, blob_written |= guac_common_json_write_string(user, stream,
json_state, value); json_state, value);
json_state->properties_written++; json_state->properties_written++;
@ -161,7 +161,7 @@ int guac_common_json_write_property(guac_client* client, guac_stream* stream,
} }
void guac_common_json_begin_object(guac_client* client, guac_stream* stream, void guac_common_json_begin_object(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state) { guac_common_json_state* json_state) {
/* Init JSON state */ /* Init JSON state */
@ -169,15 +169,15 @@ void guac_common_json_begin_object(guac_client* client, guac_stream* stream,
json_state->properties_written = 0; json_state->properties_written = 0;
/* Write leading brace - no blob can possibly be written by this */ /* Write leading brace - no blob can possibly be written by this */
assert(!guac_common_json_write(client, stream, json_state, "{", 1)); assert(!guac_common_json_write(user, stream, json_state, "{", 1));
} }
int guac_common_json_end_object(guac_client* client, guac_stream* stream, int guac_common_json_end_object(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state) { guac_common_json_state* json_state) {
/* Write final brace of JSON object */ /* Write final brace of JSON object */
return guac_common_json_write(client, stream, json_state, "}", 1); return guac_common_json_write(user, stream, json_state, "}", 1);
} }

View File

@ -25,8 +25,8 @@
#include "config.h" #include "config.h"
#include <guacamole/client.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
#include <guacamole/user.h>
/** /**
* The current streaming state of an arbitrary JSON object, consisting of * The current streaming state of an arbitrary JSON object, consisting of
@ -55,13 +55,13 @@ typedef struct guac_common_json_state {
} guac_common_json_state; } guac_common_json_state;
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object, flushes the contents of the JSON buffer to a blob * of a JSON object, flushes the contents of the JSON buffer to a blob
* instruction. Note that this will flush the JSON buffer only, and will not * instruction. Note that this will flush the JSON buffer only, and will not
* necessarily flush the underlying guac_socket of the client. * necessarily flush the underlying guac_socket of the user.
* *
* @param client * @param user
* The client to which the data will be flushed. * The user to which the data will be flushed.
* *
* @param stream * @param stream
* The stream through which the flushed data should be sent as a blob. * The stream through which the flushed data should be sent as a blob.
@ -69,16 +69,16 @@ typedef struct guac_common_json_state {
* @param json_state * @param json_state
* The state object whose buffer should be flushed. * The state object whose buffer should be flushed.
*/ */
void guac_common_json_flush(guac_client* client, guac_stream* stream, void guac_common_json_flush(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state); guac_common_json_state* json_state);
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object, writes the contents of the given buffer to the JSON buffer * of a JSON object, writes the contents of the given buffer to the JSON buffer
* of the stream state, flushing as necessary. * of the stream state, flushing as necessary.
* *
* @param client * @param user
* The client to which the data will be flushed as necessary. * The user to which the data will be flushed as necessary.
* *
* @param stream * @param stream
* The stream through which the flushed data should be sent as a blob, if * The stream through which the flushed data should be sent as a blob, if
@ -97,17 +97,17 @@ void guac_common_json_flush(guac_client* client, guac_stream* stream,
* @return * @return
* Non-zero if at least one blob was written, zero otherwise. * Non-zero if at least one blob was written, zero otherwise.
*/ */
int guac_common_json_write(guac_client* client, guac_stream* stream, int guac_common_json_write(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state, const char* buffer, int length); guac_common_json_state* json_state, const char* buffer, int length);
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object state, writes the given string as a proper JSON string, * of a JSON object state, writes the given string as a proper JSON string,
* including starting and ending quotes. The contents of the string will be * including starting and ending quotes. The contents of the string will be
* escaped as necessary. * escaped as necessary.
* *
* @param client * @param user
* The client to which the data will be flushed as necessary. * The user to which the data will be flushed as necessary.
* *
* @param stream * @param stream
* The stream through which the flushed data should be sent as a blob, if * The stream through which the flushed data should be sent as a blob, if
@ -123,17 +123,17 @@ int guac_common_json_write(guac_client* client, guac_stream* stream,
* @return * @return
* Non-zero if at least one blob was written, zero otherwise. * Non-zero if at least one blob was written, zero otherwise.
*/ */
int guac_common_json_write_string(guac_client* client, int guac_common_json_write_string(guac_user* user,
guac_stream* stream, guac_common_json_state* json_state, guac_stream* stream, guac_common_json_state* json_state,
const char* str); const char* str);
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object, writes the given JSON property name/value pair. The * of a JSON object, writes the given JSON property name/value pair. The
* name and value will be written as proper JSON strings separated by a colon. * name and value will be written as proper JSON strings separated by a colon.
* *
* @param client * @param user
* The client to which the data will be flushed as necessary. * The user to which the data will be flushed as necessary.
* *
* @param stream * @param stream
* The stream through which the flushed data should be sent as a blob, if * The stream through which the flushed data should be sent as a blob, if
@ -152,18 +152,18 @@ int guac_common_json_write_string(guac_client* client,
* @return * @return
* Non-zero if at least one blob was written, zero otherwise. * Non-zero if at least one blob was written, zero otherwise.
*/ */
int guac_common_json_write_property(guac_client* client, guac_stream* stream, int guac_common_json_write_property(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state, const char* name, guac_common_json_state* json_state, const char* name,
const char* value); const char* value);
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object, initializes the state for writing a new JSON object. Note * of a JSON object, initializes the state for writing a new JSON object. Note
* that although the client and stream must be provided, no instruction or * that although the user and stream must be provided, no instruction or
* blobs will be written due to any call to this function. * blobs will be written due to any call to this function.
* *
* @param client * @param user
* The client associated with the given stream. * The user associated with the given stream.
* *
* @param stream * @param stream
* The stream associated with the JSON object being written. * The stream associated with the JSON object being written.
@ -171,17 +171,17 @@ int guac_common_json_write_property(guac_client* client, guac_stream* stream,
* @param json_state * @param json_state
* The state object to initialize. * The state object to initialize.
*/ */
void guac_common_json_begin_object(guac_client* client, guac_stream* stream, void guac_common_json_begin_object(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state); guac_common_json_state* json_state);
/** /**
* Given a stream, the client to which it belongs, and the current stream state * Given a stream, the user to which it belongs, and the current stream state
* of a JSON object, completes writing that JSON object by writing the final * of a JSON object, completes writing that JSON object by writing the final
* terminating brace. This function must only be called following a * terminating brace. This function must only be called following a
* corresponding call to guac_common_json_begin_object(). * corresponding call to guac_common_json_begin_object().
* *
* @param client * @param user
* The client associated with the given stream. * The user associated with the given stream.
* *
* @param stream * @param stream
* The stream associated with the JSON object being written. * The stream associated with the JSON object being written.
@ -192,7 +192,7 @@ void guac_common_json_begin_object(guac_client* client, guac_stream* stream,
* @return * @return
* Non-zero if at least one blob was written, zero otherwise. * Non-zero if at least one blob was written, zero otherwise.
*/ */
int guac_common_json_end_object(guac_client* client, guac_stream* stream, int guac_common_json_end_object(guac_user* user, guac_stream* stream,
guac_common_json_state* json_state); guac_common_json_state* json_state);
#endif #endif

View File

@ -27,6 +27,7 @@
#include <guacamole/layer.h> #include <guacamole/layer.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/socket.h> #include <guacamole/socket.h>
#include <guacamole/user.h>
/* Macros for prettying up the embedded image. */ /* Macros for prettying up the embedded image. */
#define X 0x00,0x00,0x00,0xFF #define X 0x00,0x00,0x00,0xFF
@ -63,9 +64,10 @@ unsigned char guac_common_pointer_cursor[] = {
}; };
void guac_common_set_pointer_cursor(guac_client* client) { void guac_common_set_pointer_cursor(guac_user* user) {
guac_socket* socket = client->socket; guac_client* client = user->client;
guac_socket* socket = user->socket;
/* Draw to buffer */ /* Draw to buffer */
guac_layer* cursor = guac_client_alloc_buffer(client); guac_layer* cursor = guac_client_alloc_buffer(client);
@ -77,7 +79,7 @@ void guac_common_set_pointer_cursor(guac_client* client) {
guac_common_pointer_cursor_height, guac_common_pointer_cursor_height,
guac_common_pointer_cursor_stride); guac_common_pointer_cursor_stride);
guac_client_stream_png(client, socket, GUAC_COMP_SRC, cursor, guac_user_stream_png(user, socket, GUAC_COMP_SRC, cursor,
0, 0, graphic); 0, 0, graphic);
cairo_surface_destroy(graphic); cairo_surface_destroy(graphic);

View File

@ -27,7 +27,7 @@
#include "config.h" #include "config.h"
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <guacamole/client.h> #include <guacamole/user.h>
/** /**
* Width of the embedded mouse cursor graphic. * Width of the embedded mouse cursor graphic.
@ -57,8 +57,8 @@ extern unsigned char guac_common_pointer_cursor[];
/** /**
* Set the cursor of the remote display to the embedded cursor graphic. * Set the cursor of the remote display to the embedded cursor graphic.
* *
* @param client The guac_client to send the cursor to. * @param user The guac_user to send the cursor to.
*/ */
void guac_common_set_pointer_cursor(guac_client* client); void guac_common_set_pointer_cursor(guac_user* user);
#endif #endif

View File

@ -30,6 +30,7 @@
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/socket.h> #include <guacamole/socket.h>
#include <guacamole/timestamp.h> #include <guacamole/timestamp.h>
#include <guacamole/user.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
@ -1303,15 +1304,18 @@ static void __guac_common_surface_flush_to_png(guac_common_surface* surface) {
const guac_layer* layer = surface->layer; const guac_layer* layer = surface->layer;
/* Get Cairo surface for specified rect */ /* Get Cairo surface for specified rect */
unsigned char* buffer = surface->buffer + surface->dirty_rect.y * surface->stride + surface->dirty_rect.x * 4; unsigned char* buffer = surface->buffer
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, + surface->dirty_rect.y * surface->stride
surface->dirty_rect.width, + surface->dirty_rect.x * 4;
surface->dirty_rect.height,
surface->stride); cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride);
/* Send PNG for rect */ /* Send PNG for rect */
guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER, guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER,
layer, surface->dirty_rect.x, surface->dirty_rect.y, rect); layer, surface->dirty_rect.x, surface->dirty_rect.y, rect);
cairo_surface_destroy(rect); cairo_surface_destroy(rect);
surface->realized = 1; surface->realized = 1;
@ -1347,16 +1351,19 @@ static void __guac_common_surface_flush_to_jpeg(guac_common_surface* surface) {
&surface->dirty_rect, &max); &surface->dirty_rect, &max);
/* Get Cairo surface for specified rect */ /* Get Cairo surface for specified rect */
unsigned char* buffer = surface->buffer + surface->dirty_rect.y * surface->stride + surface->dirty_rect.x * 4; unsigned char* buffer = surface->buffer
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, CAIRO_FORMAT_RGB24, + surface->dirty_rect.y * surface->stride
surface->dirty_rect.width, + surface->dirty_rect.x * 4;
surface->dirty_rect.height,
surface->stride); cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride);
/* Send JPEG for rect */ /* Send JPEG for rect */
guac_client_stream_jpeg(surface->client, socket, GUAC_COMP_OVER, layer, guac_client_stream_jpeg(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect, surface->dirty_rect.x, surface->dirty_rect.y, rect,
GUAC_SURFACE_JPEG_IMAGE_QUALITY); GUAC_SURFACE_JPEG_IMAGE_QUALITY);
cairo_surface_destroy(rect); cairo_surface_destroy(rect);
surface->realized = 1; surface->realized = 1;
@ -1393,18 +1400,18 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface) {
/* Get Cairo surface for specified rect */ /* Get Cairo surface for specified rect */
unsigned char* buffer = surface->buffer unsigned char* buffer = surface->buffer
+ surface->dirty_rect.y * surface->stride + surface->dirty_rect.y * surface->stride
+ surface->dirty_rect.x * 4; + surface->dirty_rect.x * 4;
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_RGB24, CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
surface->dirty_rect.width, surface->dirty_rect.height, surface->dirty_rect.height, surface->stride);
surface->stride);
/* Send WebP for rect */ /* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect, surface->dirty_rect.x, surface->dirty_rect.y, rect,
GUAC_SURFACE_WEBP_IMAGE_QUALITY, 0); GUAC_SURFACE_WEBP_IMAGE_QUALITY, 0);
cairo_surface_destroy(rect); cairo_surface_destroy(rect);
surface->realized = 1; surface->realized = 1;
@ -1524,3 +1531,25 @@ void guac_common_surface_flush(guac_common_surface* surface) {
} }
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket) {
/* Do nothing if not realized */
if (!surface->realized)
return;
/* Sync size to new socket */
guac_protocol_send_size(socket, surface->layer, surface->width, surface->height);
/* Get entire surface */
cairo_surface_t* rect = cairo_image_surface_create_for_data(
surface->buffer, CAIRO_FORMAT_RGB24,
surface->width, surface->height, surface->stride);
/* Send PNG for rect */
guac_user_stream_png(user, socket, GUAC_COMP_OVER, surface->layer,
0, 0, rect);
cairo_surface_destroy(rect);
}

View File

@ -337,5 +337,21 @@ void guac_common_surface_flush(guac_common_surface* surface);
*/ */
void guac_common_surface_flush_deferred(guac_common_surface* surface); void guac_common_surface_flush_deferred(guac_common_surface* surface);
/**
* Duplicates the contents of the current surface to the given socket. Pending
* changes are not flushed.
*
* @param surface
* The surface to duplicate.
*
* @param user
* The user receiving the surface.
*
* @param socket
* The socket over which the surface contents should be sent.
*/
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket);
#endif #endif

View File

@ -28,6 +28,7 @@ check_PROGRAMS = test_libguac
noinst_HEADERS = \ noinst_HEADERS = \
client/client_suite.h \ client/client_suite.h \
common/common_suite.h \
protocol/suite.h \ protocol/suite.h \
util/util_suite.h util/util_suite.h
@ -36,6 +37,10 @@ test_libguac_SOURCES = \
client/client_suite.c \ client/client_suite.c \
client/buffer_pool.c \ client/buffer_pool.c \
client/layer_pool.c \ client/layer_pool.c \
common/common_suite.c \
common/guac_iconv.c \
common/guac_string.c \
common/guac_rect.c \
protocol/suite.c \ protocol/suite.c \
protocol/base64_decode.c \ protocol/base64_decode.c \
protocol/instruction_parse.c \ protocol/instruction_parse.c \
@ -48,9 +53,11 @@ test_libguac_SOURCES = \
test_libguac_CFLAGS = \ test_libguac_CFLAGS = \
-Werror -Wall -pedantic \ -Werror -Wall -pedantic \
@COMMON_INCLUDE@ \
@LIBGUAC_INCLUDE@ @LIBGUAC_INCLUDE@
test_libguac_LDADD = \ test_libguac_LDADD = \
@COMMON_LTLIB@ \
@CUNIT_LIBS@ \ @CUNIT_LIBS@ \
@LIBGUAC_LTLIB@ @LIBGUAC_LTLIB@