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 on RDP. #1061

Draft
wants to merge 10 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
221 changes: 147 additions & 74 deletions guacamole-common-js/src/main/webapp/modules/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ Guacamole.Client = function(tunnel) {
|| currentState == Guacamole.Client.State.WAITING;
}

/**
* Add optional X offset on defaut layer draw actions.
*/
this.offsetX = 0;

/**
* Produces an opaque representation of Guacamole.Client state which can be
* later imported through a call to importState(). This object is
Expand Down Expand Up @@ -325,14 +330,17 @@ Guacamole.Client = function(tunnel) {
*
* @param {!number} height
* The height of the screen.
*
* @param {!number} monitors
* The count of monitors.
*/
this.sendSize = function(width, height) {
this.sendSize = function sendSize(width, height, monitors) {

// Do not send requests if not connected
if (!isConnected())
return;

tunnel.sendMessage("size", width, height);
tunnel.sendMessage("size", width, height, monitors);

};

Expand Down Expand Up @@ -735,6 +743,25 @@ Guacamole.Client = function(tunnel) {
*/
this.onmsg = null;

/**
* Fired when the client is disconnected to close all secondary monitor
* windows.
*/
this.ondisconnect = null;

/**
* Fired when guacd send instructions to transfer them on additional
* monitors windows.
*
* @event
* @param {!string} opcode
* The current operation code.
*
* @param {*} parameters
* Operation parameters.
*/
this.ondisplayupdate = null;

/**
* Fired when a user joins a shared connection.
*
Expand Down Expand Up @@ -1068,13 +1095,15 @@ Guacamole.Client = function(tunnel) {

"arc": function(parameters) {

var layer = getLayer(parseInt(parameters[0]));
var x = parseInt(parameters[1]);
var y = parseInt(parameters[2]);
var radius = parseInt(parameters[3]);
var startAngle = parseFloat(parameters[4]);
var endAngle = parseFloat(parameters[5]);
var negative = parseInt(parameters[6]);
const offsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;

const layer = getLayer(parseInt(parameters[0]));
const x = parseInt(parameters[1]) - offsetX;
const y = parseInt(parameters[2]);
const radius = parseInt(parameters[3]);
const startAngle = parseFloat(parameters[4]);
const endAngle = parseFloat(parameters[5]);
const negative = parseInt(parameters[6]);

display.arc(layer, x, y, radius, startAngle, endAngle, negative != 0);

Expand Down Expand Up @@ -1212,15 +1241,18 @@ Guacamole.Client = function(tunnel) {

"copy": function(parameters) {

var srcL = getLayer(parseInt(parameters[0]));
var srcX = parseInt(parameters[1]);
var srcY = parseInt(parameters[2]);
var srcWidth = parseInt(parameters[3]);
var srcHeight = parseInt(parameters[4]);
var channelMask = parseInt(parameters[5]);
var dstL = getLayer(parseInt(parameters[6]));
var dstX = parseInt(parameters[7]);
var dstY = parseInt(parameters[8]);
const srcOffsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;
const dstOffsetX = parseInt(parameters[6]) === 0 ? guac_client.offsetX : 0;

const srcL = getLayer(parseInt(parameters[0]));
const srcX = parseInt(parameters[1]) - srcOffsetX;
const srcY = parseInt(parameters[2]);
const srcWidth = parseInt(parameters[3]);
const srcHeight = parseInt(parameters[4]);
const channelMask = parseInt(parameters[5]);
const dstL = getLayer(parseInt(parameters[6]));
const dstX = parseInt(parameters[7]) - dstOffsetX;
const dstY = parseInt(parameters[8]);

display.setChannelMask(dstL, channelMask);
display.copy(srcL, srcX, srcY, srcWidth, srcHeight,
Expand All @@ -1247,13 +1279,15 @@ Guacamole.Client = function(tunnel) {

"cursor": function(parameters) {

var cursorHotspotX = parseInt(parameters[0]);
var cursorHotspotY = parseInt(parameters[1]);
var srcL = getLayer(parseInt(parameters[2]));
var srcX = parseInt(parameters[3]);
var srcY = parseInt(parameters[4]);
var srcWidth = parseInt(parameters[5]);
var srcHeight = parseInt(parameters[6]);
const offsetX = parseInt(parameters[2]) === 0 ? guac_client.offsetX : 0;

const cursorHotspotX = parseInt(parameters[0]);
const cursorHotspotY = parseInt(parameters[1]);
const srcL = getLayer(parseInt(parameters[2]));
const srcX = parseInt(parameters[3]) - offsetX;
const srcY = parseInt(parameters[4]);
const srcWidth = parseInt(parameters[5]);
const srcHeight = parseInt(parameters[6]);

display.setCursor(cursorHotspotX, cursorHotspotY,
srcL, srcX, srcY, srcWidth, srcHeight);
Expand All @@ -1262,13 +1296,15 @@ Guacamole.Client = function(tunnel) {

"curve": function(parameters) {

var layer = getLayer(parseInt(parameters[0]));
var cp1x = parseInt(parameters[1]);
var cp1y = parseInt(parameters[2]);
var cp2x = parseInt(parameters[3]);
var cp2y = parseInt(parameters[4]);
var x = parseInt(parameters[5]);
var y = parseInt(parameters[6]);
const offsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;

const layer = getLayer(parseInt(parameters[0]));
const cp1x = parseInt(parameters[1]);
const cp1y = parseInt(parameters[2]);
const cp2x = parseInt(parameters[3]);
const cp2y = parseInt(parameters[4]);
const x = parseInt(parameters[5]) - offsetX;
const y = parseInt(parameters[6]);

display.curveTo(layer, cp1x, cp1y, cp2x, cp2y, x, y);

Expand Down Expand Up @@ -1398,15 +1434,17 @@ Guacamole.Client = function(tunnel) {

"img": function(parameters) {

var stream_index = parseInt(parameters[0]);
var channelMask = parseInt(parameters[1]);
var layer = getLayer(parseInt(parameters[2]));
var mimetype = parameters[3];
var x = parseInt(parameters[4]);
var y = parseInt(parameters[5]);
const offsetX = parseInt(parameters[2]) === 0 ? guac_client.offsetX : 0;

const stream_index = parseInt(parameters[0]);
const channelMask = parseInt(parameters[1]);
const layer = getLayer(parseInt(parameters[2]));
const mimetype = parameters[3];
const x = parseInt(parameters[4]) - offsetX;
const y = parseInt(parameters[5]);

// Create stream
var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
const stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);

// Draw received contents once decoded
display.setChannelMask(layer, channelMask);
Expand All @@ -1416,11 +1454,13 @@ Guacamole.Client = function(tunnel) {

"jpeg": function(parameters) {

var channelMask = parseInt(parameters[0]);
var layer = getLayer(parseInt(parameters[1]));
var x = parseInt(parameters[2]);
var y = parseInt(parameters[3]);
var data = parameters[4];
const offsetX = parseInt(parameters[1]) === 0 ? guac_client.offsetX : 0;

const channelMask = parseInt(parameters[0]);
const layer = getLayer(parseInt(parameters[1]));
const x = parseInt(parameters[2]) - offsetX;
const y = parseInt(parameters[3]);
const data = parameters[4];

display.setChannelMask(layer, channelMask);
display.draw(layer, x, y, "data:image/jpeg;base64," + data);
Expand All @@ -1440,9 +1480,11 @@ Guacamole.Client = function(tunnel) {

"line": function(parameters) {

var layer = getLayer(parseInt(parameters[0]));
var x = parseInt(parameters[1]);
var y = parseInt(parameters[2]);
const offsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;

const layer = getLayer(parseInt(parameters[0]));
const x = parseInt(parameters[1]) - offsetX;
const y = parseInt(parameters[2]);

display.lineTo(layer, x, y);

Expand All @@ -1461,8 +1503,8 @@ Guacamole.Client = function(tunnel) {

"mouse" : function handleMouse(parameters) {

var x = parseInt(parameters[0]);
var y = parseInt(parameters[1]);
const x = parseInt(parameters[0]) - guac_client.offsetX;
const y = parseInt(parameters[1]);

// Display and move software cursor to received coordinates
display.showCursor(true);
Expand Down Expand Up @@ -1554,11 +1596,13 @@ Guacamole.Client = function(tunnel) {

"png": function(parameters) {

var channelMask = parseInt(parameters[0]);
var layer = getLayer(parseInt(parameters[1]));
var x = parseInt(parameters[2]);
var y = parseInt(parameters[3]);
var data = parameters[4];
const offsetX = parseInt(parameters[1]) === 0 ? guac_client.offsetX : 0;

const channelMask = parseInt(parameters[0]);
const layer = getLayer(parseInt(parameters[1]));
const x = parseInt(parameters[2]) - offsetX;
const y = parseInt(parameters[3]);
const data = parameters[4];

display.setChannelMask(layer, channelMask);
display.draw(layer, x, y, "data:image/png;base64," + data);
Expand All @@ -1583,11 +1627,13 @@ Guacamole.Client = function(tunnel) {

"rect": function(parameters) {

var layer = getLayer(parseInt(parameters[0]));
var x = parseInt(parameters[1]);
var y = parseInt(parameters[2]);
var w = parseInt(parameters[3]);
var h = parseInt(parameters[4]);
const offsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;

const layer = getLayer(parseInt(parameters[0]));
const x = parseInt(parameters[1]) - offsetX;
const y = parseInt(parameters[2]);
const w = parseInt(parameters[3]);
const h = parseInt(parameters[4]);

display.rect(layer, x, y, w, h);

Expand Down Expand Up @@ -1644,9 +1690,11 @@ Guacamole.Client = function(tunnel) {

"start": function(parameters) {

var layer = getLayer(parseInt(parameters[0]));
var x = parseInt(parameters[1]);
var y = parseInt(parameters[2]);
const offsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;

const layer = getLayer(parseInt(parameters[0]));
const x = parseInt(parameters[0]) - offsetX;
const y = parseInt(parameters[2]);

display.moveTo(layer, x, y);

Expand Down Expand Up @@ -1687,15 +1735,18 @@ Guacamole.Client = function(tunnel) {

"transfer": function(parameters) {

var srcL = getLayer(parseInt(parameters[0]));
var srcX = parseInt(parameters[1]);
var srcY = parseInt(parameters[2]);
var srcWidth = parseInt(parameters[3]);
var srcHeight = parseInt(parameters[4]);
var function_index = parseInt(parameters[5]);
var dstL = getLayer(parseInt(parameters[6]));
var dstX = parseInt(parameters[7]);
var dstY = parseInt(parameters[8]);
const srcOffsetX = parseInt(parameters[0]) === 0 ? guac_client.offsetX : 0;
const dstOffsetX = parseInt(parameters[6]) === 0 ? guac_client.offsetX : 0;

const srcL = getLayer(parseInt(parameters[0]));
const srcX = parseInt(parameters[1]) - srcOffsetX;
const srcY = parseInt(parameters[2]);
const srcWidth = parseInt(parameters[3]);
const srcHeight = parseInt(parameters[4]);
const function_index = parseInt(parameters[5]);
const dstL = getLayer(parseInt(parameters[6]));
const dstX = parseInt(parameters[7]) - dstOffsetX;
const dstY = parseInt(parameters[8]);

/* SRC */
if (function_index === 0x3)
Expand Down Expand Up @@ -1817,16 +1868,36 @@ Guacamole.Client = function(tunnel) {

tunnel.oninstruction = function(opcode, parameters) {

var handler = instructionHandlers[opcode];
if (handler)
handler(parameters);
// Send instruction to other monitors windows
if (guac_client.ondisplayupdate) guac_client.ondisplayupdate(opcode, parameters);

// Run requested handler
guac_client.runHandler(opcode, parameters);

// Leverage network activity to ensure the next keep-alive ping is
// sent, even if the browser is currently throttling timers
scheduleKeepAlive();

};

/**
* Run operations requested by guacd.
*
* @param {!string} opcode
* The current operation code.
*
* @param {*} parameters
* Operation parameters.
*/
this.runHandler = function runHandler(opcode, parameters) {

const handler = instructionHandlers[opcode];

if (handler)
handler(parameters);

};

/**
* Sends a disconnect instruction to the server and closes the tunnel.
*/
Expand All @@ -1846,6 +1917,8 @@ Guacamole.Client = function(tunnel) {
tunnel.disconnect();
setState(Guacamole.Client.State.DISCONNECTED);

if (guac_client.ondisconnect) guac_client.ondisconnect();

}

};
Expand Down
Loading