diff --git a/guacamole/src/main/webapp/app/client/directives/guacClient.js b/guacamole/src/main/webapp/app/client/directives/guacClient.js index 8cc3db40d..3abdc1944 100644 --- a/guacamole/src/main/webapp/app/client/directives/guacClient.js +++ b/guacamole/src/main/webapp/app/client/directives/guacClient.js @@ -258,7 +258,7 @@ angular.module('client').directive('guacClient', [function guacClient() { }); // Attach any given managed client - $scope.$watch('client', function(managedClient) { + $scope.$watch('client', function attachManagedClient(managedClient) { // Remove any existing display displayContainer.innerHTML = ""; @@ -274,26 +274,21 @@ angular.module('client').directive('guacClient', [function guacClient() { display = client.getDisplay(); display.scale($scope.client.clientProperties.scale); - // Update the scale of the display when the client display size changes. - display.onresize = function() { - $scope.$apply(updateDisplayScale); - }; - - // Use local cursor if possible, update localCursor flag - display.oncursor = function(canvas, x, y) { - localCursor = mouse.setCursor(canvas, x, y); - }; - // Add display element displayElement = display.getElement(); displayContainer.appendChild(displayElement); - // Do nothing when the display element is clicked on. - displayElement.onclick = function(e) { - e.preventDefault(); - return false; - }; + }); + // Update scale when display is resized + $scope.$watch('client.managedDisplay.size', function setDisplaySize() { + $scope.$evalAsync(updateDisplayScale); + }); + + // Keep local cursor up-to-date + $scope.$watch('client.managedDisplay.cursor', function setCursor(cursor) { + if (cursor) + localCursor = mouse.setCursor(cursor.canvas, cursor.x, cursor.y); }); /* diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js index 81c30ae57..0818a0636 100644 --- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js +++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js @@ -29,6 +29,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', // Required types var ClientProperties = $injector.get('ClientProperties'); var ManagedClientState = $injector.get('ManagedClientState'); + var ManagedDisplay = $injector.get('ManagedDisplay'); // Required services var $window = $injector.get('$window'); @@ -74,6 +75,13 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', */ this.tunnel = template.tunnel; + /** + * The display associated with the underlying Guacamole client. + * + * @type ManagedDisplay + */ + this.managedDisplay = template.managedDisplay; + /** * The name returned via the Guacamole protocol for this connection, if * any. @@ -89,6 +97,20 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', */ this.clipboardData = template.clipboardData; + /** + * The current width of the Guacamole display, in pixels. + * + * @type Number + */ + this.displayWidth = template.displayWidth || 0; + + /** + * The current width of the Guacamole display, in pixels. + * + * @type Number + */ + this.displayHeight = template.displayHeight || 0; + /** * The current state of the Guacamole client (idle, connecting, * connected, terminated with error, etc.). @@ -353,6 +375,9 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector', }; + // Manage the client display + managedClient.managedDisplay = ManagedDisplay.getInstance(client.getDisplay()); + /* TODO: Restore file transfer again */ /* diff --git a/guacamole/src/main/webapp/app/client/types/ManagedDisplay.js b/guacamole/src/main/webapp/app/client/types/ManagedDisplay.js new file mode 100644 index 000000000..c70423f70 --- /dev/null +++ b/guacamole/src/main/webapp/app/client/types/ManagedDisplay.js @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Provides the ManagedDisplay class used by the guacClientManager service. + */ +angular.module('client').factory('ManagedDisplay', ['$rootScope', + function defineManagedDisplay($rootScope) { + + /** + * Object which serves as a surrogate interface, encapsulating a Guacamole + * display while it is active, allowing it to be detached and reattached + * from different client views. + * + * @constructor + * @param {ManagedDisplay|Object} [template={}] + * The object whose properties should be copied within the new + * ManagedDisplay. + */ + var ManagedDisplay = function ManagedDisplay(template) { + + // Use empty object by default + template = template || {}; + + /** + * The underlying Guacamole display. + * + * @type Guacamole.Display + */ + this.display = template.display; + + /** + * The current size of the Guacamole display. + * + * @type ManagedDisplay.Dimensions + */ + this.size = new ManagedDisplay.Dimensions(template.size); + + /** + * The current mouse cursor, if any. + * + * @type ManagedDisplay.Cursor + */ + this.cursor = template.cursor; + + }; + + /** + * Object which represents the size of the Guacamole display. + * + * @constructor + * @param {ManagedDisplay.Dimensions|Object} template + * The object whose properties should be copied within the new + * ManagedDisplay.Dimensions. + */ + ManagedDisplay.Dimensions = function Dimensions(template) { + + // Use empty object by default + template = template || {}; + + /** + * The current width of the Guacamole display, in pixels. + * + * @type Number + */ + this.width = template.width || 0; + + /** + * The current width of the Guacamole display, in pixels. + * + * @type Number + */ + this.height = template.height || 0; + + }; + + /** + * Object which represents a mouse cursor used by the Guacamole display. + * + * @constructor + * @param {ManagedDisplay.Cursor|Object} template + * The object whose properties should be copied within the new + * ManagedDisplay.Cursor. + */ + ManagedDisplay.Cursor = function Cursor(template) { + + // Use empty object by default + template = template || {}; + + /** + * The actual mouse cursor image. + * + * @type HTMLCanvasElement + */ + this.canvas = template.canvas; + + /** + * The X coordinate of the cursor hotspot. + * + * @type Number + */ + this.x = template.x; + + /** + * The Y coordinate of the cursor hotspot. + * + * @type Number + */ + this.y = template.y; + + }; + + /** + * Creates a new ManagedDisplay which represents the current state of the + * given Guacamole display. + * + * @param {Guacamole.Display} display + * The Guacamole display to represent. Changes to this display will + * affect this ManagedDisplay. + * + * @returns {ManagedDisplay} + * A new ManagedDisplay which represents the current state of the + * given Guacamole display. + */ + ManagedDisplay.getInstance = function getInstance(display) { + + var managedDisplay = new ManagedDisplay({ + display : display + }); + + // Store changes to display size + display.onresize = function() { + $rootScope.$apply(function updateClientSize() { + managedDisplay.size = new ManagedDisplay.Dimensions({ + width : display.getWidth(), + height : display.getHeight() + }); + }); + }; + + // Store changes to display cursor + display.oncursor = function(canvas, x, y) { + $rootScope.$apply(function updateClientCursor() { + managedDisplay.cursor = new ManagedDisplay.Cursor({ + canvas : canvas, + x : x, + y : y + }); + }); + }; + + // Do nothing when the display element is clicked on + display.getElement().onclick = function(e) { + e.preventDefault(); + return false; + }; + + return managedDisplay; + + }; + + return ManagedDisplay; + +}]); \ No newline at end of file