Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUACAMOLE-288: Add support for multi-monitor connections. #560

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/libguac/guacamole/user-fntypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,15 @@ typedef int guac_user_clipboard_handler(guac_user* user, guac_stream* stream,
* @param height
* The desired height of the display, in pixels.
*
* @param monitors
* The count of monitors.
*
* @return
* Zero if the size event has been successfully handled, non-zero
* otherwise.
*/
typedef int guac_user_size_handler(guac_user* user,
int width, int height);
int width, int height, int monitors);

/**
* Handler for Guacamole file streams received from a user. Each such file
Expand Down
2 changes: 1 addition & 1 deletion src/libguac/guacamole/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ struct guac_user {
*
* Example:
* @code
* int size_handler(guac_user* user, int width, int height);
* int size_handler(guac_user* user, int width, int height, int monitor);
*
* int guac_user_init(guac_user* user, int argc, char** argv) {
* user->size_handler = size_handler;
Expand Down
3 changes: 2 additions & 1 deletion src/libguac/user-handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ int __guac_handle_size(guac_user* user, int argc, char** argv) {
return user->size_handler(
user,
atoi(argv[0]), /* width */
atoi(argv[1]) /* height */
atoi(argv[1]), /* height */
(argc == 3 ? atoi(argv[2]) : 1) /* Monitors count */
);
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/kubernetes/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ int guac_kubernetes_user_key_handler(guac_user* user, int keysym, int pressed) {

}

int guac_kubernetes_user_size_handler(guac_user* user, int width, int height) {
int guac_kubernetes_user_size_handler(guac_user* user, int width, int height, int monitors) {

/* Get terminal */
guac_client* client = user->client;
Expand Down
83 changes: 54 additions & 29 deletions src/protocols/rdp/channels/disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ guac_rdp_disp* guac_rdp_disp_alloc(guac_client* client) {

/* No requests have been made */
disp->last_request = guac_timestamp_current();
disp->requested_width = 0;
disp->requested_height = 0;
disp->reconnect_needed = 0;
disp->requested_width = 0;
disp->requested_height = 0;
disp->reconnect_needed = 0;
disp->requested_monitors = 1;

return disp;

Expand Down Expand Up @@ -88,7 +89,7 @@ static void guac_rdp_disp_channel_connected(rdpContext* context,
/* Init module with current display size */
guac_rdp_disp_set_size(guac_disp, rdp_client->settings,
context->instance, guac_rdp_get_width(context->instance),
guac_rdp_get_height(context->instance));
guac_rdp_get_height(context->instance), 1);

/* Store reference to the display update plugin once it's connected */
DispClientContext* disp = (DispClientContext*) args->pInterface;
Expand Down Expand Up @@ -151,7 +152,7 @@ void guac_rdp_disp_load_plugin(rdpContext* context) {
}

void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings,
freerdp* rdp_inst, int width, int height) {
freerdp* rdp_inst, int width, int height, int monitors) {

guac_rect resize = {
.left = 0,
Expand Down Expand Up @@ -180,6 +181,7 @@ void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings,
/* Store deferred size */
disp->requested_width = width;
disp->requested_height = height;
disp->requested_monitors = monitors;

/* Send display update notification if possible */
guac_rdp_disp_update_size(disp, settings, rdp_inst);
Expand All @@ -191,6 +193,15 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,

int width = disp->requested_width;
int height = disp->requested_height;
int monitors_count = disp->requested_monitors;

/* Prevent opening too many monitors than allowed */
if (settings->max_secondary_monitors + 1 < monitors_count)
monitors_count = settings->max_secondary_monitors + 1;

/* At least one monitor is required */
if (monitors_count < 1)
monitors_count = 1;

/* Do not update size if no requests have been received */
if (width == 0 || height == 0)
Expand All @@ -204,7 +215,7 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,

/* Do NOT send requests unless the size will change */
if (rdp_inst != NULL
&& width == guac_rdp_get_width(rdp_inst)
&& width * monitors_count == guac_rdp_get_width(rdp_inst)
&& height == guac_rdp_get_height(rdp_inst))
return;

Expand All @@ -221,32 +232,46 @@ void guac_rdp_disp_update_size(guac_rdp_disp* disp,

}

else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) {
DISPLAY_CONTROL_MONITOR_LAYOUT monitors[1] = {{
.Flags = 0x1, /* DISPLAYCONTROL_MONITOR_PRIMARY */
.Left = 0,
.Top = 0,
.Width = width,
.Height = height,
.PhysicalWidth = 0,
.PhysicalHeight = 0,
.Orientation = 0,
.DesktopScaleFactor = 0,
.DeviceScaleFactor = 0
}};

/* Send display update notification if display channel is connected */
if (disp->disp != NULL) {

guac_client* client = disp->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

pthread_mutex_lock(&(rdp_client->message_lock));
disp->disp->SendMonitorLayout(disp->disp, 1, monitors);
pthread_mutex_unlock(&(rdp_client->message_lock));
/* Send display update notification if display channel is connected */
else if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE
&& disp->disp != NULL) {

/* Init monitors layout */
DISPLAY_CONTROL_MONITOR_LAYOUT* monitors;
monitors = guac_mem_alloc(monitors_count * sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));

for (int i = 0; i < monitors_count; i++) {

/* First monitor is the primary */
int primary_monitor = (i == 0 ? 1 : 0);

/* Shift each monitor to the right */
int monitor_left = i * width;

/* Get current monitor */
DISPLAY_CONTROL_MONITOR_LAYOUT* monitor = &monitors[i];

/* Set current monitor properties */
monitor->Flags = primary_monitor;
monitor->Left = monitor_left;
monitor->Top = 0;
monitor->Width = width;
monitor->Height = height;
monitor->Orientation = 0;
monitor->PhysicalWidth = 0;
monitor->PhysicalHeight = 0;
}

/* Send display update notification */
guac_client* client = disp->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;

pthread_mutex_lock(&(rdp_client->message_lock));
disp->disp->SendMonitorLayout(disp->disp, monitors_count, monitors);
pthread_mutex_unlock(&(rdp_client->message_lock));

guac_mem_free(monitors);

}

}
Expand Down
10 changes: 9 additions & 1 deletion src/protocols/rdp/channels/disp.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ typedef struct guac_rdp_disp {
*/
int requested_height;

/**
* The number of monitors requested.
*/
int requested_monitors;

/**
* Whether the size has changed and the RDP connection must be closed and
* reestablished.
Expand Down Expand Up @@ -154,9 +159,12 @@ void guac_rdp_disp_load_plugin(rdpContext* context);
* The desired display height, in pixels. Due to the restrictions of the
* RDP display update channel, this will be constrained to the range of 200
* through 8192 inclusive.
*
* @param monitors
* The count of monitors.
*/
void guac_rdp_disp_set_size(guac_rdp_disp* disp, guac_rdp_settings* settings,
freerdp* rdp_inst, int width, int height);
freerdp* rdp_inst, int width, int height, int monitors);

/**
* Sends an actual display update request to the RDP server based on previous
Expand Down
11 changes: 11 additions & 0 deletions src/protocols/rdp/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <guacamole/mem.h>
#include <guacamole/recording.h>
#include <guacamole/rwlock.h>
#include <guacamole/string.h>

#include <dirent.h>
#include <errno.h>
Expand Down Expand Up @@ -130,6 +131,16 @@ static int guac_rdp_join_pending_handler(guac_client* client) {
/* Bring user up to date with any registered static channels */
guac_rdp_pipe_svc_send_pipes(client, broadcast_socket);

/* Get max secondary monitors */
char* max_monitors = guac_mem_alloc(12);
guac_itoa(max_monitors,
(unsigned int) rdp_client->settings->max_secondary_monitors);

/* Send current max allowed secondary monitors */
guac_client_stream_argv(client, broadcast_socket, "text/plain",
"secondary-monitors", max_monitors);
guac_mem_free(max_monitors);

/* Synchronize with current display */
if (rdp_client->display != NULL) {
guac_display_dup(rdp_client->display, broadcast_socket);
Expand Down
4 changes: 2 additions & 2 deletions src/protocols/rdp/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) {

}

int guac_rdp_user_size_handler(guac_user* user, int width, int height) {
int guac_rdp_user_size_handler(guac_user* user, int width, int height, int monitors) {

guac_client* client = user->client;
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
Expand All @@ -203,7 +203,7 @@ int guac_rdp_user_size_handler(guac_user* user, int width, int height) {
height = height * settings->resolution / user->info.optimal_resolution;

/* Send display update */
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height);
guac_rdp_disp_set_size(rdp_client->disp, settings, rdp_inst, width, height, monitors);

return 0;

Expand Down
1 change: 0 additions & 1 deletion src/protocols/rdp/rdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,6 @@ static int guac_rdp_handle_connection(guac_client* client) {
guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);

/* Wait for data and construct a reasonable frame */

int wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_MESSAGE_CHECK_INTERVAL);
if (wait_result < 0)
break;
Expand Down
14 changes: 14 additions & 0 deletions src/protocols/rdp/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"create-recording-path",
"recording-write-existing",
"resize-method",
"secondary-monitors",
"enable-audio-input",
"enable-touch",
"read-only",
Expand Down Expand Up @@ -598,6 +599,12 @@ enum RDP_ARGS_IDX {
*/
IDX_RESIZE_METHOD,

/**
* The maximum allowed count of secondary monitors.
* 0 to disable.
*/
IDX_SECONDARY_MONITORS,

/**
* "true" if audio input (microphone) should be enabled for the RDP
* connection, "false" or blank otherwise.
Expand Down Expand Up @@ -1234,6 +1241,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
settings->resize_method = GUAC_RESIZE_NONE;
}

/* Maximum secondary monitors (default 0 = disabled) */
settings->max_secondary_monitors =
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_SECONDARY_MONITORS, 0);

/* RDP Graphics Pipeline enable/disable */
settings->enable_gfx =
!guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
Expand Down Expand Up @@ -1729,6 +1741,7 @@ void guac_rdp_push_settings(guac_client* client,
freerdp_settings_set_uint32(rdp_settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNSPECIFIED);
freerdp_settings_set_uint32(rdp_settings, FreeRDP_OsMinorType, OSMINORTYPE_UNSPECIFIED);
freerdp_settings_set_bool(rdp_settings, FreeRDP_DesktopResize, TRUE);
freerdp_settings_set_bool(rdp_settings, FreeRDP_UseMultimon, TRUE);

/* Claim support only for specific updates, independent of FreeRDP defaults */
BYTE* order_support = freerdp_settings_get_pointer_writable(rdp_settings, FreeRDP_OrderSupport);
Expand Down Expand Up @@ -1975,6 +1988,7 @@ void guac_rdp_push_settings(guac_client* client,
rdp_settings->OsMajorType = OSMAJORTYPE_UNSPECIFIED;
rdp_settings->OsMinorType = OSMINORTYPE_UNSPECIFIED;
rdp_settings->DesktopResize = TRUE;
rdp_settings->UseMultimon = TRUE;

/* Claim support only for specific updates, independent of FreeRDP defaults */
ZeroMemory(rdp_settings->OrderSupport, GUAC_RDP_ORDER_SUPPORT_LENGTH);
Expand Down
5 changes: 5 additions & 0 deletions src/protocols/rdp/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,11 @@ typedef struct guac_rdp_settings {
*/
guac_rdp_resize_method resize_method;

/**
* The maximum allowed count of secondary monitors.
*/
int max_secondary_monitors;

/**
* Whether audio input (microphone) is enabled.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/ssh/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ int guac_ssh_user_key_handler(guac_user* user, int keysym, int pressed) {
return 0;
}

int guac_ssh_user_size_handler(guac_user* user, int width, int height) {
int guac_ssh_user_size_handler(guac_user* user, int width, int height, int monitors) {

/* Get terminal */
guac_client* client = user->client;
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/telnet/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ int guac_telnet_user_key_handler(guac_user* user, int keysym, int pressed) {

}

int guac_telnet_user_size_handler(guac_user* user, int width, int height) {
int guac_telnet_user_size_handler(guac_user* user, int width, int height, int monitors) {

/* Get terminal */
guac_client* client = user->client;
Expand Down
2 changes: 1 addition & 1 deletion src/protocols/vnc/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ int guac_vnc_user_key_handler(guac_user* user, int keysym, int pressed) {
}

#ifdef LIBVNC_HAS_SIZE_MSG
int guac_vnc_user_size_handler(guac_user* user, int width, int height) {
int guac_vnc_user_size_handler(guac_user* user, int width, int height, int monitors) {

guac_user_log(user, GUAC_LOG_TRACE, "Running user size handler.");

Expand Down