Skip to content

Commit d8bcb3a

Browse files
GUACAMOLE-288: Add directive that handle client display element.
1 parent 754e5e5 commit d8bcb3a

File tree

5 files changed

+434
-317
lines changed

5 files changed

+434
-317
lines changed

guacamole/src/main/frontend/src/app/client/controllers/clientController.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
788788
return;
789789

790790
// Init guacManageMonitor
791-
guacManageMonitor.init($scope.menu);
791+
guacManageMonitor.init();
792+
guacManageMonitor.menuShown = function menuShown() {
793+
$scope.menu.shown = !$scope.menu.shown;
794+
$scope.$apply();
795+
}
792796

793797
// Add or remove additional monitor
794798
guacManageMonitor.addMonitor();

guacamole/src/main/frontend/src/app/client/controllers/secondaryMonitorController.js

+19-269
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,18 @@
2323
angular.module('client').controller('secondaryMonitorController', ['$scope', '$injector', '$routeParams',
2424
function clientController($scope, $injector, $routeParams) {
2525

26-
// Required types
27-
const ClipboardData = $injector.get('ClipboardData');
28-
2926
// Required services
30-
const $window = $injector.get('$window');
31-
const clipboardService = $injector.get('clipboardService');
32-
const guacFullscreen = $injector.get('guacFullscreen');
27+
const $window = $injector.get('$window');
28+
const guacFullscreen = $injector.get('guacFullscreen');
29+
const guacManageMonitor = $injector.get('guacManageMonitor');
3330

34-
// ID of this monitor
31+
/**
32+
* ID of this monitor.
33+
*
34+
* @type {!String}
35+
*/
3536
const monitorId = $routeParams.id;
3637

37-
// Broadcast channel
38-
const broadcast = new BroadcastChannel('guac_monitors');
39-
40-
// Latest mouse state
41-
const mouseState = {};
42-
43-
// Display size in pixels and position of the monitor
44-
let displayWidth = 0;
45-
let displayHeight = 0;
46-
let monitorPosition = 0;
47-
let monitorsCount = 0;
48-
let currentScaling = 1;
49-
5038
/**
5139
* In order to open the guacamole menu, we need to hit ctrl-alt-shift. There are
5240
* several possible keysysms for each key.
@@ -57,262 +45,19 @@ angular.module('client').controller('secondaryMonitorController', ['$scope', '$i
5745
CTRL_KEYS = {0xFFE3 : true, 0xFFE4 : true},
5846
MENU_KEYS = angular.extend({}, SHIFT_KEYS, ALT_KEYS, CTRL_KEYS);
5947

60-
// Instantiate client, using an HTTP tunnel for communications.
61-
const client = new Guacamole.Client(
62-
new Guacamole.HTTPTunnel("tunnel")
63-
);
64-
65-
const display = client.getDisplay();
66-
let displayContainer;
67-
68-
setTimeout(function() {
69-
displayContainer = document.querySelector('.display')
70-
71-
// Remove any existing display
72-
displayContainer.innerHTML = "";
73-
74-
// Add display element
75-
displayContainer.appendChild(display.getElement());
76-
77-
// Ready for resize
78-
pushBroadcastMessage('resize', true);
79-
}, 1000);
80-
81-
/**
82-
* Adjust the display scaling according to the window size.
83-
*/
84-
$scope.scaleDisplay = function scaleDisplay() {
85-
86-
// Calculate required scaling factor
87-
const scaleX = $window.innerWidth / displayWidth;
88-
const scaleY = $window.innerHeight / displayHeight;
89-
90-
// Use the lowest scaling to avoid acreen overflow
91-
if (scaleX <= scaleY)
92-
currentScaling = scaleX;
93-
else
94-
currentScaling = scaleY;
95-
96-
display.scale(currentScaling);
97-
};
98-
99-
// Send monitor-close event to broadcast channel on window unload
100-
$window.addEventListener('unload', function unloadWindow() {
101-
pushBroadcastMessage('monitorClose', monitorId);
102-
});
103-
104-
// Mouse and keyboard
105-
const mouse = new Guacamole.Mouse(client.getDisplay().getElement());
106-
const keyboard = new Guacamole.Keyboard(document);
107-
108-
// Move mouse on screen and send mouse events to main window
109-
mouse.onEach(['mousedown', 'mouseup', 'mousemove'], function sendMouseEvent(e) {
110-
111-
// Ensure software cursor is shown
112-
display.showCursor(true);
113-
114-
// Update client-side cursor
115-
display.moveCursor(
116-
Math.floor(e.state.x / currentScaling),
117-
Math.floor(e.state.y / currentScaling)
118-
);
119-
120-
// Limit mouse move events to reduce latency
121-
if (mouseState.lastPush && Date.now() - mouseState.lastPush < 100
122-
&& mouseState.down === e.state.down
123-
&& mouseState.up === e.state.up
124-
&& mouseState.left === e.state.left
125-
&& mouseState.middle === e.state.middle
126-
&& mouseState.right === e.state.right)
127-
return;
128-
129-
// Click on actual display instead of the first
130-
const displayOffset = displayWidth * monitorPosition;
131-
132-
// Convert mouse state to serializable object
133-
mouseState.down = e.state.down;
134-
mouseState.up = e.state.up;
135-
mouseState.left = e.state.left;
136-
mouseState.middle = e.state.middle;
137-
mouseState.right = e.state.right;
138-
mouseState.x = e.state.x / currentScaling + displayOffset;
139-
mouseState.y = e.state.y / currentScaling;
140-
mouseState.lastPush = Date.now();
141-
142-
// Send mouse state to main window
143-
pushBroadcastMessage('mouseState', mouseState);
144-
});
145-
146-
// Hide software cursor when mouse leaves display
147-
mouse.on('mouseout', function() {
148-
if (!display) return;
149-
display.showCursor(false);
150-
});
151-
152-
// Handle any received clipboard data
153-
client.onclipboard = function clientClipboardReceived(stream, mimetype) {
154-
155-
let reader;
156-
157-
// If the received data is text, read it as a simple string
158-
if (/^text\//.exec(mimetype)) {
159-
160-
reader = new Guacamole.StringReader(stream);
161-
162-
// Assemble received data into a single string
163-
let data = '';
164-
reader.ontext = function textReceived(text) {
165-
data += text;
166-
};
167-
168-
// Set clipboard contents once stream is finished
169-
reader.onend = function textComplete() {
170-
clipboardService.setClipboard(new ClipboardData({
171-
source : 'secondaryMonitor',
172-
type : mimetype,
173-
data : data
174-
}))['catch'](angular.noop);
175-
};
176-
177-
}
178-
179-
// Otherwise read the clipboard data as a Blob
180-
else {
181-
reader = new Guacamole.BlobReader(stream, mimetype);
182-
reader.onend = function blobComplete() {
183-
clipboardService.setClipboard(new ClipboardData({
184-
source : 'secondaryMonitor',
185-
type : mimetype,
186-
data : reader.getBlob()
187-
}))['catch'](angular.noop);
188-
};
189-
}
190-
191-
};
192-
193-
// Send keydown events to main window
194-
keyboard.onkeydown = function (keysym) {
195-
pushBroadcastMessage('keydown', keysym);
196-
};
197-
198-
// Send keyup events to main window
199-
keyboard.onkeyup = function (keysym) {
200-
pushBroadcastMessage('keyup', keysym);
201-
};
202-
203-
/**
204-
* Push broadcast message containing instructions that allows additional
205-
* monitor windows to draw display, resize window and more.
206-
*
207-
* @param {!string} type
208-
* The type of message (ex: handler, fullscreen, resize)
209-
*
210-
* @param {*} content
211-
* The content of the message, can contain any type of serializable
212-
* content.
213-
*/
214-
function pushBroadcastMessage(type, content) {
215-
const message = {
216-
[type]: content
217-
};
218-
219-
broadcast.postMessage(message);
220-
};
221-
222-
/**
223-
* Handle messages sent by main window in guac_monitors channel. These
224-
* messages contain instructions to draw the screen, resize window, or
225-
* request full screen mode.
226-
*
227-
* @param {Event} e
228-
* Received message event from guac_monitors channel.
229-
*/
230-
broadcast.onmessage = function broadcastMessage(message) {
231-
232-
// Run the client handler to draw display
233-
if (message.data.handler)
234-
client.runHandler(message.data.handler.opcode,
235-
message.data.handler.parameters);
236-
237-
if (message.data.monitorsInfos) {
238-
239-
const monitorsInfos = message.data.monitorsInfos;
240-
241-
// Store new monitor count and position
242-
monitorsCount = monitorsInfos.count;
243-
monitorPosition = monitorsInfos.map[monitorId];
244-
245-
// Set the monitor count in display
246-
display.updateMonitors(monitorsCount);
247-
248-
}
249-
250-
// Resize display and window with parameters sent by guacd in the size handler
251-
if (message.data.handler && message.data.handler.opcode === 'size') {
48+
guacManageMonitor.init("secondary");
49+
guacManageMonitor.monitorAttributes.monitorId = monitorId;
25250

253-
const parameters = message.data.handler.parameters;
254-
const default_layer = 0;
255-
const layer = parseInt(parameters[0]);
256-
257-
// Ignore other layers (ex: mouse) that can have other size
258-
if (layer !== default_layer)
259-
return;
260-
261-
// Set the new display size
262-
displayWidth = parseInt(parameters[1]) / monitorsCount;
263-
displayHeight = parseInt(parameters[2]);
264-
265-
// Translate all draw actions on X to draw the current display
266-
// instead of the first
267-
client.offsetX = displayWidth * monitorPosition;
268-
269-
// Get unusable window height and width (ex: titlebar)
270-
const windowUnusableHeight = $window.outerHeight - $window.innerHeight;
271-
const windowUnusableWidth = $window.outerWidth - $window.innerWidth;
272-
273-
// Remove scrollbars
274-
document.querySelector('.client-main').style.overflow = 'hidden';
275-
276-
// Resize window to the display size
277-
$window.resizeTo(
278-
displayWidth + windowUnusableWidth,
279-
displayHeight + windowUnusableHeight
280-
);
281-
282-
// Adjust scaling to new size
283-
$scope.scaleDisplay();
284-
285-
}
286-
287-
// Full screen mode instructions
288-
if (message.data.fullscreen) {
289-
290-
// setFullscreenMode require explicit user action
291-
if (message.data.fullscreen !== false)
292-
openConsentButton();
293-
294-
// Close fullscreen mode instantly
295-
else
296-
guacFullscreen.setFullscreenMode(message.data.fullscreen);
297-
298-
}
299-
300-
};
301-
302-
/**
303-
* Add button to request user consent before enabling fullscreen mode to
304-
* comply with the setFullscreenMode requirements that require explicit
305-
* user action. The button is removed after a few seconds if the user does
306-
* not click on it.
307-
*/
308-
function openConsentButton() {
51+
guacManageMonitor.openConsentButton = function openConsentButton() {
30952

31053
// Show button
31154
$scope.showFullscreenConsent = true;
55+
$scope.$apply();
31256

31357
// Auto hide button after delay
31458
setTimeout(function() {
31559
$scope.showFullscreenConsent = false;
60+
$scope.$apply();
31661
}, 10000);
31762

31863
};
@@ -367,11 +112,16 @@ angular.module('client').controller('secondaryMonitorController', ['$scope', '$i
367112

368113
// Toggle the menu
369114
$scope.$apply(function() {
370-
pushBroadcastMessage('guacMenu', true);
115+
guacManageMonitor.pushBroadcastMessage('guacMenu', true);
371116
});
372117

373118
}
374119

375120
});
376121

122+
// Send monitor-close event to broadcast channel on window unload
123+
$window.addEventListener('unload', function unloadWindow() {
124+
guacManageMonitor.pushBroadcastMessage('monitorClose', monitorId);
125+
});
126+
377127
}]);

0 commit comments

Comments
 (0)