Skip to content

Commit 00c1551

Browse files
WIP - GUACAMOLE-288: Receive monitor layout via layer set parameter.
1 parent b6bff91 commit 00c1551

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

guacamole-common-js/src/main/webapp/modules/Client.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,15 @@ Guacamole.Client = function(tunnel) {
868868
*/
869869
this.onmultitouch = null;
870870

871+
/**
872+
* Fired when the remote client is explicitly declaring the layout of
873+
* monitors, if any.
874+
*
875+
* @param {Object} layout
876+
* An object describing the layout of monitors.
877+
*/
878+
this.onmultimonlayout = null;
879+
871880
/**
872881
* Fired when the current value of a connection parameter is being exposed
873882
* by the server.
@@ -1070,6 +1079,13 @@ Guacamole.Client = function(tunnel) {
10701079
if (guac_client.onmultitouch && layer instanceof Guacamole.Display.VisibleLayer)
10711080
guac_client.onmultitouch(layer, parseInt(value));
10721081

1082+
},
1083+
1084+
"multimon-layout": function multimonLayout(layer, value) {
1085+
1086+
if (guac_client.onmultimonlayout)
1087+
guac_client.onmultimonlayout(JSON.parse(value));
1088+
10731089
}
10741090

10751091
};

guacamole/src/main/frontend/src/app/client/services/guacManageMonitor.js

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,16 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
8585
* @property {Object.<Number, Number>} map
8686
* A map of monitor id to position.
8787
* @property {Object.<Number, Object>} details
88-
* Details of each monitor, including width, height, etc.
88+
* Details of each browser window, including width, height, etc.
89+
* @property {Object.<Number, Object>} rendered
90+
* Details of each rendered monitor, including width, height, etc.
91+
* This is used to display what is expected by guacd.
8992
*/
9093
let monitorsInfos = {
9194
count: 1,
9295
map: {},
9396
details: {},
97+
rendered: {},
9498
};
9599

96100
const service = {};
@@ -227,21 +231,8 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
227231
client.runHandler(message.data.handler.opcode,
228232
message.data.handler.parameters);
229233

230-
if (message.data.monitorsInfos) {
231-
234+
if (message.data.monitorsInfos)
232235
monitorsInfos = message.data.monitorsInfos;
233-
const monitorId = service.monitorId;
234-
235-
// Set the monitor size in the display
236-
display.setMonitorSize(
237-
monitorsInfos.details[monitorId].width,
238-
monitorsInfos.details[monitorId].height,
239-
);
240-
241-
client.offsetX = service.getOffsetX();
242-
client.offsetY = service.getOffsetY();
243-
244-
}
245236

246237
// Full screen mode instructions
247238
if (message.data.fullscreen !== undefined) {
@@ -284,6 +275,8 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
284275
client = guac_client;
285276
display = client.getDisplay();
286277

278+
client.onmultimonlayout = onmultimonlayout;
279+
287280
// Close all secondary monitors on client disconnect
288281
if (monitorType === "primary")
289282
client.ondisconnect = service.closeAllMonitors;
@@ -404,14 +397,6 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
404397
top: size.top,
405398
});
406399

407-
display.setMonitorSize(
408-
monitorsInfos.details[0].width,
409-
monitorsInfos.details[0].height,
410-
);
411-
412-
client.offsetX = service.getOffsetX();
413-
client.offsetY = service.getOffsetY();
414-
415400
// Monitor has been closed
416401
if (size.width === 0 || size.height === 0)
417402
client.sendSize(0, 0, monitorPosition, 0);
@@ -445,8 +430,8 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
445430
// are before the current monitor
446431
for (const [id, pos] of Object.entries(monitorsInfos.map)) {
447432
if (pos < thisPosition) {
448-
const details = monitorsInfos.details[id];
449-
if (details) offsetX += details.width;
433+
const rendered = monitorsInfos.rendered[id];
434+
if (rendered?.width) offsetX += rendered.width;
450435
}
451436
}
452437

@@ -465,7 +450,7 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
465450
* The Y offset of the current monitor, in pixels.
466451
*/
467452
service.getOffsetY = function getOffsetY() {
468-
const currentOffset = monitorsInfos.details[service.monitorId]?.top ?? 0;
453+
const currentOffset = monitorsInfos.rendered[service.monitorId]?.top ?? 0;
469454
return currentOffset - getLowestTopOffset();
470455
}
471456

@@ -522,12 +507,12 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
522507
* The lowest top value of all monitors, in pixels.
523508
*/
524509
function getLowestTopOffset() {
525-
let lowestTopValue = monitorsInfos.details[0]?.top ?? 0;
510+
let lowestTopValue = monitorsInfos.rendered[0]?.top ?? 0;
526511

527512
// Loop through all monitors to find the highest monitor
528-
for (const [_, details] of Object.entries(monitorsInfos.details)) {
529-
if (details?.top < lowestTopValue) {
530-
lowestTopValue = details.top;
513+
for (const [_, rendered] of Object.entries(monitorsInfos.rendered)) {
514+
if (rendered?.top < lowestTopValue) {
515+
lowestTopValue = rendered.top;
531516
}
532517
}
533518

@@ -561,6 +546,7 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
561546
// If width or height is 0, remove monitor details
562547
if (monitorDetails.width === 0 || monitorDetails.height === 0) {
563548
delete monitorsInfos.details[monitorDetails.id];
549+
delete monitorsInfos.rendered[monitorDetails.id];
564550
delete monitorsInfos.map[monitorDetails.id];
565551
}
566552
// Update or add monitor details
@@ -621,6 +607,54 @@ angular.module('client').factory('guacManageMonitor', ['$injector',
621607
service.pushBroadcastMessage('size', monitorDetails);
622608
}
623609

610+
/**
611+
* Handle the multimonitor layout event. This is used to update the
612+
* monitorsInfos object when the layout changes.
613+
*
614+
* @param {Object} layout
615+
* An object describing the layout of monitors.
616+
*/
617+
function onmultimonlayout(layout) {
618+
if (!layout)
619+
return;
620+
621+
for (const [id, pos] of Object.entries(monitorsInfos.map)) {
622+
623+
// If the monitor is not in the layout, it is not known by
624+
// guacd anymore, so we close it
625+
if (!layout[pos]) {
626+
service.closeMonitor(id);
627+
continue;
628+
}
629+
630+
if (!monitorsInfos.rendered[id])
631+
monitorsInfos.rendered[id] = {};
632+
633+
// Update the monitor details
634+
monitorsInfos.rendered[id].width = layout[pos].width;
635+
monitorsInfos.rendered[id].height = layout[pos].height;
636+
monitorsInfos.rendered[id].top = layout[pos].top;
637+
monitorsInfos.rendered[id].left = layout[pos].left;
638+
639+
// Set the monitor size in the display only if the id matches the
640+
// current monitor id
641+
if (id === String(service.monitorId)) {
642+
display.setMonitorSize(
643+
monitorsInfos.rendered[id].width,
644+
monitorsInfos.rendered[id].height,
645+
);
646+
}
647+
648+
}
649+
650+
// Update the offset of the client when monitorInfos is fully updated
651+
// This is needed to ensure that the client knows the correct offset
652+
// of each monitor
653+
client.offsetX = service.getOffsetX();
654+
client.offsetY = service.getOffsetY();
655+
656+
}
657+
624658
// Close additional monitors when window is unloaded
625659
$window.addEventListener('unload', service.closeAllMonitors);
626660

0 commit comments

Comments
 (0)