Skip to content

Commit

Permalink
GUACAMOLE-1846: Sync data to all pending users using broadcast socket.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmuehlner committed Aug 23, 2023
1 parent 5acb8d1 commit b1b6649
Show file tree
Hide file tree
Showing 30 changed files with 530 additions and 203 deletions.
21 changes: 21 additions & 0 deletions src/common/common/cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ void guac_common_cursor_free(guac_common_cursor* cursor);
* the current cursor image. The resulting cursor on the remote display will
* be visible.
*
* @deprecated guac_common_cursor_dup_batch should be used instead of this
* user-specific method. Unlike this function, it supports sending the cursor
* to multiple users with a broadcast socket.
*
* @param cursor
* The cursor to send.
*
Expand All @@ -162,6 +166,23 @@ void guac_common_cursor_free(guac_common_cursor* cursor);
void guac_common_cursor_dup(guac_common_cursor* cursor, guac_user* user,
guac_socket* socket);

/**
* 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 client
* The user receiving the updated cursor.
*
* @param socket
* The socket over which the updated cursor should be sent.
*/
void guac_common_cursor_dup_batch(
guac_common_cursor* cursor, guac_client* client, guac_socket* socket);

/**
* Updates the current position and button state of the mouse cursor, marking
* the given user as the most recent user of the mouse. The remote mouse cursor
Expand Down
20 changes: 20 additions & 0 deletions src/common/common/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ 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.
*
* @deprecated guac_common_display_dup_batch() should be used to duplicate
* display to a subset of users, rather than this user specific function.
*
* @param display
* The display whose state should be sent along the given socket.
*
Expand All @@ -160,6 +163,23 @@ void guac_common_display_free(guac_common_display* display);
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket);

/**
* 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 client
* The client associated with the users receiving the display state.
*
* @param socket
* The socket over which the display state should be sent.
*/
void guac_common_display_dup_batch(
guac_common_display* display, guac_client* client,
guac_socket* socket);

/**
* Flushes pending changes to the given display. All pending operations will
* become visible to any connected users.
Expand Down
20 changes: 20 additions & 0 deletions src/common/common/surface.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@ void guac_common_surface_flush(guac_common_surface* surface);
* Duplicates the contents of the current surface to the given socket. Pending
* changes are not flushed.
*
* @deprecated guac_common_surface_dup_batch() should be used for synchronizing
* new users, as it allows duplication of the surface to multiple users at once
* with a broadcast socket
*
* @param surface
* The surface to duplicate.
*
Expand All @@ -499,6 +503,22 @@ void guac_common_surface_flush(guac_common_surface* surface);
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket);

/**
* Duplicates the contents of the current surface to the given socket. Pending
* changes are not flushed.
*
* @param surface
* The surface to duplicate.
*
* @param client
* The client whos users are receiving the surface.
*
* @param socket
* The socket over which the surface contents should be sent.
*/
void guac_common_surface_dup_batch(guac_common_surface* surface,
guac_client* client, guac_socket* socket);

/**
* Declares that the given surface should receive touch events. By default,
* surfaces are assumed to not expect touch events. This value is advisory, and
Expand Down
10 changes: 9 additions & 1 deletion src/common/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ void guac_common_cursor_free(guac_common_cursor* cursor) {
void guac_common_cursor_dup(guac_common_cursor* cursor, guac_user* user,
guac_socket* socket) {

/* Duplicate along the provided socket, ignoring the user */
guac_common_cursor_dup_batch(cursor, user->client, socket);

}

void guac_common_cursor_dup_batch(
guac_common_cursor* cursor, guac_client* client, guac_socket* socket) {

/* Synchronize location */
guac_protocol_send_mouse(socket, cursor->x, cursor->y, cursor->button_mask,
cursor->timestamp);
Expand All @@ -111,7 +119,7 @@ void guac_common_cursor_dup(guac_common_cursor* cursor, guac_user* user,
guac_protocol_send_size(socket, cursor->buffer,
cursor->width, cursor->height);

guac_user_stream_png(user, socket, GUAC_COMP_SRC,
guac_client_stream_png(client, socket, GUAC_COMP_SRC,
cursor->buffer, 0, 0, cursor->surface);

guac_protocol_send_cursor(socket,
Expand Down
25 changes: 16 additions & 9 deletions src/common/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@
* 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 client
* The client associated with the users 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_client* client, 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);
guac_common_surface_dup_batch(current->surface, client, socket);
current = current->next;
}

Expand Down Expand Up @@ -166,19 +166,26 @@ void guac_common_display_free(guac_common_display* display) {
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) {

guac_client* client = user->client;
/* Defer to the batch functiong, ignoring the user */
guac_common_display_dup_batch(display, user->client, socket);

}

void guac_common_display_dup_batch(
guac_common_display* display, guac_client* client,
guac_socket* socket) {

pthread_mutex_lock(&display->_lock);

/* Sunchronize shared cursor */
guac_common_cursor_dup(display->cursor, user, socket);
guac_common_cursor_dup_batch(display->cursor, client, socket);

/* Synchronize default surface */
guac_common_surface_dup(display->default_surface, user, socket);
guac_common_surface_dup_batch(display->default_surface, client, socket);

/* Synchronize all layers and buffers */
guac_common_display_dup_layers(display->layers, user, socket);
guac_common_display_dup_layers(display->buffers, user, socket);
guac_common_display_dup_layers(display->layers, client, socket);
guac_common_display_dup_layers(display->buffers, client, socket);

/* Sends a sync instruction to mark the boundary of the first frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp, 1);
Expand Down
10 changes: 8 additions & 2 deletions src/common/surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,13 @@ void guac_common_surface_flush(guac_common_surface* surface) {
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket) {

/* Duplicate the surface along the provided socket, ignoring the user */
guac_common_surface_dup_batch(surface, user->client, socket);
}

void guac_common_surface_dup_batch(guac_common_surface* surface,
guac_client* client, guac_socket* socket) {

pthread_mutex_lock(&surface->_lock);

/* Do nothing if not realized */
Expand Down Expand Up @@ -2028,7 +2035,7 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
surface->width, surface->height, surface->stride);

/* Send PNG for rect */
guac_user_stream_png(user, socket, GUAC_COMP_OVER, surface->layer,
guac_client_stream_png(client, socket, GUAC_COMP_OVER, surface->layer,
0, 0, rect);
cairo_surface_destroy(rect);

Expand All @@ -2038,4 +2045,3 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
pthread_mutex_unlock(&surface->_lock);

}

8 changes: 5 additions & 3 deletions src/libguac/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ static void guac_client_promote_pending_users(union sigval data) {

guac_release_lock(&(client->__users_lock));

/* Release the lock (this is done AFTER updating the non-pending user list
/* Release the lock (this is done AFTER updating the connected user list
* to ensure that all users are always on exactly one of these lists) */
guac_release_lock(&(client->__pending_users_lock));

Expand Down Expand Up @@ -253,8 +253,9 @@ guac_client* guac_client_alloc() {
/* Ensure the timer is constructed only once */
pthread_mutex_init(&(client->__pending_users_timer_mutex), NULL);

/* Set up socket to broadcast to all users */
/* Set up broadcast sockets */
client->socket = guac_socket_broadcast(client);
client->pending_socket = guac_socket_broadcast_pending(client);

/* Set the timer event thread as initially inactive, since it hasn't run */
atomic_flag_clear(&(client->__pending_timer_event_active));
Expand All @@ -280,8 +281,9 @@ void guac_client_free(guac_client* client) {

}

/* Free socket */
/* Free sockets */
guac_socket_free(client->socket);
guac_socket_free(client->pending_socket);

/* Free layer pools */
guac_pool_free(client->__buffer_pool);
Expand Down
25 changes: 22 additions & 3 deletions src/libguac/guacamole/client-fntypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#include "client-types.h"
#include "object-types.h"
#include "protocol-types.h"
#include "socket.h"
#include "stream-types.h"
#include "user-fntypes.h"
#include "user-types.h"

#include <stdarg.h>
Expand All @@ -49,9 +51,9 @@
typedef int guac_client_free_handler(guac_client* client);

/**
* Handler that will run before pending users are promoted to full users.
* Any required operations for pending users should be applied using
* guac_client_foreach_pending_user().
* Handler that will run before immediately before pending users are promoted
* to full users. The pending user socket should be used to communicate to the
* pending users.
*
* @param client
* The client whose handler was invoked.
Expand Down Expand Up @@ -89,5 +91,22 @@ typedef void guac_client_log_handler(guac_client* client,
*/
typedef int guac_client_init_handler(guac_client* client);

/**
* A function that will broadcast arbitrary data to a subset of users for
* the provided client, using the provided user callback for any user-specific
* operations.
*
* @param client
* The guac_client associated with the users to broadcast to.
*
* @param callback
* A callback that should be invoked with each broadcasted user.
*
* @param data
* Arbitrary data that may be used to broadcast to the subset of users.
*/
typedef void guac_client_broadcast_handler(
guac_client* client, guac_user_callback* callback, void* data);

#endif

26 changes: 17 additions & 9 deletions src/libguac/guacamole/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,24 @@
struct guac_client {

/**
* The guac_socket structure to be used to communicate with all connected
* web-clients (users). Unlike the user-level guac_socket, this guac_socket
* will broadcast instructions to all connected users simultaneously. It
* is expected that the implementor of any Guacamole proxy client will
* provide their own mechanism of I/O for their protocol. The guac_socket
* structure is used only to communicate conveniently with the Guacamole
* web-client.
* The guac_socket structure to be used to communicate with all non-pending
* connected web-clients (users). Unlike the user-level guac_socket, this
* guac_socket will broadcast instructions to all non-pending connected users
* simultaneously. It is expected that the implementor of any Guacamole proxy
* client will provide their own mechanism of I/O for their protocol. The
* guac_socket structure is used only to communicate conveniently with the
* Guacamole web-client.
*/
guac_socket* socket;

/**
* The guac_socket structure to be used to communicate with all pending
* connected web-clients (users). Aside from operating on a different
* subset of users, this socket has all the same behavior and semantics as
* the non-pending socket.
*/
guac_socket* pending_socket;

/**
* The current state of the client. When the client is first allocated,
* this will be initialized to GUAC_CLIENT_RUNNING. It will remain at
Expand Down Expand Up @@ -248,8 +256,8 @@ struct guac_client {

/**
* A handler that will be run prior to pending users being promoted to full
* users. Any required pending user operations should be applied
* guac_client_foreach_pending_user().
* users. Any required pending user operations should be performed using
* the client's pending user socket.
*
* Example:
* @code
Expand Down
36 changes: 31 additions & 5 deletions src/libguac/guacamole/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,10 @@ guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary);

/**
* Allocates and initializes a new guac_socket which duplicates all
* instructions written across the sockets of each connected user of the given
* guac_client. The returned socket is a write-only socket. Attempts to read
* from the socket will fail. If a write occurs while no users are connected,
* that write will simply be dropped.
* instructions written across the sockets of each connected user of the
* given guac_client. The returned socket is a write-only socket. Attempts
* to read from the socket will fail. If a write occurs while no users are
* connected, that write will simply be dropped.
*
* Return values (error codes) from each user's socket will not affect the
* in-progress write, but each failing user will be forcibly stopped with
Expand All @@ -253,12 +253,38 @@ guac_socket* guac_socket_tee(guac_socket* primary, guac_socket* secondary);
*
* @return
* A write-only guac_socket object which broadcasts copies of all
* instructions written across all connected users of the given
* instructions written across all non-pending connected users of the given
* guac_client, or NULL if an error occurs while allocating the guac_socket
* object.
*/
guac_socket* guac_socket_broadcast(guac_client* client);

/**
* Allocates and initializes a new guac_socket which duplicates all
* instructions written across the sockets of each pending connected
* user of the given guac_client. The returned socket is a write-only socket.
* Attempts to read from the socket will fail. If a write occurs while no
* users are connected, that write will simply be dropped.
*
* Return values (error codes) from each user's socket will not affect the
* in-progress write, but each failing user will be forcibly stopped with
* guac_user_stop().
*
* If an error occurs while allocating the guac_socket object, NULL is returned,
* and guac_error is set appropriately.
*
* @param client
* The client associated with the group of pending users across which
* duplicates of all instructions should be written.
*
* @return
* A write-only guac_socket object which broadcasts copies of all
* instructions written across all pending connected users of the given
* guac_client, or NULL if an error occurs while allocating the guac_socket
* object.
*/
guac_socket* guac_socket_broadcast_pending(guac_client* client);

/**
* Writes the given unsigned int to the given guac_socket object. The data
* written may be buffered until the buffer is flushed automatically or
Expand Down
Loading

0 comments on commit b1b6649

Please sign in to comment.