You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2017/01/29 20:03:57 UTC

[1/3] incubator-guacamole-client git commit: GUACAMOLE-190: Update client thumbnail roughly every 5 seconds.

Repository: incubator-guacamole-client
Updated Branches:
  refs/heads/master 5776104f4 -> c0e050c65


GUACAMOLE-190: Update client thumbnail roughly every 5 seconds.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/fd1c652a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/fd1c652a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/fd1c652a

Branch: refs/heads/master
Commit: fd1c652a84173ac5dc4845dcaf4e0ecc3e456072
Parents: d8f9d26
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Jan 17 10:52:00 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 27 19:32:29 2017 -0800

----------------------------------------------------------------------
 .../webapp/app/client/types/ManagedClient.js    | 140 ++++++++++++-------
 .../app/client/types/ManagedClientThumbnail.js  |  58 ++++++++
 2 files changed, 149 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/fd1c652a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index 213a42e..404b597 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -24,14 +24,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
     function defineManagedClient($rootScope, $injector) {
 
     // Required types
-    var ClientProperties     = $injector.get('ClientProperties');
-    var ClientIdentifier     = $injector.get('ClientIdentifier');
-    var ClipboardData        = $injector.get('ClipboardData');
-    var ManagedClientState   = $injector.get('ManagedClientState');
-    var ManagedDisplay       = $injector.get('ManagedDisplay');
-    var ManagedFilesystem    = $injector.get('ManagedFilesystem');
-    var ManagedFileUpload    = $injector.get('ManagedFileUpload');
-    var ManagedShareLink     = $injector.get('ManagedShareLink');
+    var ClientProperties       = $injector.get('ClientProperties');
+    var ClientIdentifier       = $injector.get('ClientIdentifier');
+    var ClipboardData          = $injector.get('ClipboardData');
+    var ManagedClientState     = $injector.get('ManagedClientState');
+    var ManagedClientThumbnail = $injector.get('ManagedClientThumbnail');
+    var ManagedDisplay         = $injector.get('ManagedDisplay');
+    var ManagedFilesystem      = $injector.get('ManagedFilesystem');
+    var ManagedFileUpload      = $injector.get('ManagedFileUpload');
+    var ManagedShareLink       = $injector.get('ManagedShareLink');
 
     // Required services
     var $document              = $injector.get('$document');
@@ -46,7 +47,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
     var guacHistory            = $injector.get('guacHistory');
     var guacImage              = $injector.get('guacImage');
     var guacVideo              = $injector.get('guacVideo');
-        
+
+    /**
+     * The minimum amount of time to wait between updates to the client
+     * thumbnail, in milliseconds.
+     *
+     * @type Number
+     */
+    var THUMBNAIL_UPDATE_FREQUENCY = 5000;
+
     /**
      * Object which serves as a surrogate interface, encapsulating a Guacamole
      * client while it is active, allowing it to be detached and reattached
@@ -99,6 +108,15 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
         this.name = template.name;
 
         /**
+         * The most recently-generated thumbnail for this connection, as
+         * stored within the local connection history. If no thumbnail is
+         * stored, this will be null.
+         *
+         * @type ManagedClientThumbnail
+         */
+        this.thumbnail = template.thumbnail;
+
+        /**
          * The current clipboard contents.
          *
          * @type ClipboardData
@@ -228,45 +246,6 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
     };
 
     /**
-     * Store the thumbnail of the given managed client within the connection
-     * history under its associated ID. If the client is not connected, this
-     * function has no effect.
-     *
-     * @param {String} managedClient
-     *     The client whose history entry should be updated.
-     */
-    var updateHistoryEntry = function updateHistoryEntry(managedClient) {
-
-        var display = managedClient.client.getDisplay();
-
-        // Update stored thumbnail of previous connection 
-        if (display && display.getWidth() > 0 && display.getHeight() > 0) {
-
-            // Get screenshot
-            var canvas = display.flatten();
-            
-            // Calculate scale of thumbnail (max 320x240, max zoom 100%)
-            var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1);
-            
-            // Create thumbnail canvas
-            var thumbnail = $document[0].createElement("canvas");
-            thumbnail.width  = canvas.width*scale;
-            thumbnail.height = canvas.height*scale;
-            
-            // Scale screenshot to thumbnail
-            var context = thumbnail.getContext("2d");
-            context.drawImage(canvas,
-                0, 0, canvas.width, canvas.height,
-                0, 0, thumbnail.width, thumbnail.height
-            );
-
-            guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png"));
-
-        }
-
-    };
-
-    /**
      * Requests the creation of a new audio stream, recorded from the user's
      * local audio input device. If audio input is supported by the connection,
      * an audio stream will be created which will remain open until the remote
@@ -403,12 +382,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
                         // Begin streaming audio input if possible
                         requestAudioStream(client);
 
+                        // Update thumbnail with initial display contents
+                        ManagedClient.updateThumbnail(managedClient);
                         break;
 
                     // Update history when disconnecting
                     case 4: // Disconnecting
                     case 5: // Disconnected
-                        updateHistoryEntry(managedClient);
+                        ManagedClient.updateThumbnail(managedClient);
                         break;
 
                 }
@@ -431,6 +412,21 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
             });
         };
 
+        // Automatically update the client thumbnail
+        client.onsync = function syncReceived() {
+
+            var thumbnail = managedClient.thumbnail;
+            var timestamp = new Date().getTime();
+
+            // Update thumbnail if it doesn't exist or is old
+            if (!thumbnail || timestamp - thumbnail.timestamp >= THUMBNAIL_UPDATE_FREQUENCY) {
+                $rootScope.$apply(function updateClientThumbnail() {
+                    ManagedClient.updateThumbnail(managedClient);
+                });
+            }
+
+        };
+
         // Handle any received clipboard data
         client.onclipboard = function clientClipboardReceived(stream, mimetype) {
 
@@ -651,6 +647,52 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
 
     };
 
+    /**
+     * Store the thumbnail of the given managed client within the connection
+     * history under its associated ID. If the client is not connected, this
+     * function has no effect.
+     *
+     * @param {ManagedClient} managedClient
+     *     The client whose history entry should be updated.
+     */
+    ManagedClient.updateThumbnail = function updateThumbnail(managedClient) {
+
+        var display = managedClient.client.getDisplay();
+
+        // Update stored thumbnail of previous connection
+        if (display && display.getWidth() > 0 && display.getHeight() > 0) {
+
+            // Get screenshot
+            var canvas = display.flatten();
+
+            // Calculate scale of thumbnail (max 320x240, max zoom 100%)
+            var scale = Math.min(320 / canvas.width, 240 / canvas.height, 1);
+
+            // Create thumbnail canvas
+            var thumbnail = $document[0].createElement("canvas");
+            thumbnail.width  = canvas.width*scale;
+            thumbnail.height = canvas.height*scale;
+
+            // Scale screenshot to thumbnail
+            var context = thumbnail.getContext("2d");
+            context.drawImage(canvas,
+                0, 0, canvas.width, canvas.height,
+                0, 0, thumbnail.width, thumbnail.height
+            );
+
+            // Store updated thumbnail within client
+            managedClient.thumbnail = new ManagedClientThumbnail({
+                timestamp : new Date().getTime(),
+                canvas    : thumbnail
+            });
+
+            // Update historical thumbnail
+            guacHistory.updateThumbnail(managedClient.id, thumbnail.toDataURL("image/png"));
+
+        }
+
+    };
+
     return ManagedClient;
 
 }]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/fd1c652a/guacamole/src/main/webapp/app/client/types/ManagedClientThumbnail.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClientThumbnail.js b/guacamole/src/main/webapp/app/client/types/ManagedClientThumbnail.js
new file mode 100644
index 0000000..fd9b3de
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClientThumbnail.js
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Provides the ManagedClientThumbnail class used by ManagedClient.
+ */
+angular.module('client').factory('ManagedClientThumbnail', [function defineManagedClientThumbnail() {
+
+    /**
+     * Object which represents a thumbnail of the Guacamole client display,
+     * along with the time that the thumbnail was generated.
+     *
+     * @constructor
+     * @param {ManagedClientThumbnail|Object} [template={}]
+     *     The object whose properties should be copied within the new
+     *     ManagedClientThumbnail.
+     */
+    var ManagedClientThumbnail = function ManagedClientThumbnail(template) {
+
+        // Use empty object by default
+        template = template || {};
+
+        /**
+         * The time that this thumbnail was generated, as the number of
+         * milliseconds elapsed since midnight of January 1, 1970 UTC.
+         *
+         * @type Number
+         */
+        this.timestamp = template.timestamp;
+
+        /**
+         * The thumbnail of the Guacamole client display.
+         *
+         * @type HTMLCanvasElement
+         */
+        this.canvas = template.canvas;
+
+    };
+
+    return ManagedClientThumbnail;
+
+}]);
\ No newline at end of file


[3/3] incubator-guacamole-client git commit: GUACAMOLE-190: Merge client thumbnail tab favicon.

Posted by jm...@apache.org.
GUACAMOLE-190: Merge client thumbnail tab favicon.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/c0e050c6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/c0e050c6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/c0e050c6

Branch: refs/heads/master
Commit: c0e050c65add2a4bd83089cfd4e8527214328bb1
Parents: 5776104 69a25c4
Author: James Muehlner <ja...@guac-dev.org>
Authored: Sun Jan 29 12:01:00 2017 -0800
Committer: James Muehlner <ja...@guac-dev.org>
Committed: Sun Jan 29 12:01:00 2017 -0800

----------------------------------------------------------------------
 .../app/client/controllers/clientController.js  |   6 +
 .../webapp/app/client/types/ManagedClient.js    | 140 ++++++++++++------
 .../app/client/types/ManagedClientThumbnail.js  |  58 ++++++++
 .../webapp/app/index/services/iconService.js    | 148 +++++++++++++++++++
 4 files changed, 303 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[2/3] incubator-guacamole-client git commit: GUACAMOLE-190: Synchronize page icon with client thumbnail.

Posted by jm...@apache.org.
GUACAMOLE-190: Synchronize page icon with client thumbnail.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/69a25c4e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/69a25c4e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/69a25c4e

Branch: refs/heads/master
Commit: 69a25c4e48f0c4d8fecf9cd7c95a859f30646e4f
Parents: fd1c652
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Jan 17 10:52:46 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri Jan 27 19:32:35 2017 -0800

----------------------------------------------------------------------
 .../app/client/controllers/clientController.js  |   6 +
 .../webapp/app/index/services/iconService.js    | 148 +++++++++++++++++++
 2 files changed, 154 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/69a25c4e/guacamole/src/main/webapp/app/client/controllers/clientController.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index 9827de1..a66a0ce 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -35,6 +35,7 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
     var clipboardService      = $injector.get('clipboardService');
     var guacClientManager     = $injector.get('guacClientManager');
     var guacNotification      = $injector.get('guacNotification');
+    var iconService           = $injector.get('iconService');
     var preferenceService     = $injector.get('preferenceService');
     var tunnelService         = $injector.get('tunnelService');
     var userPageService       = $injector.get('userPageService');
@@ -403,6 +404,11 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
 
     });
 
+    // Update page icon when thumbnail changes
+    $scope.$watch('client.thumbnail.canvas', function thumbnailChanged(canvas) {
+        iconService.setIcons(canvas);
+    });
+
     // Watch clipboard for new data, associating it with any pressed keys
     $scope.$watch('client.clipboardData', function clipboardChanged(data) {
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/69a25c4e/guacamole/src/main/webapp/app/index/services/iconService.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/index/services/iconService.js b/guacamole/src/main/webapp/app/index/services/iconService.js
new file mode 100644
index 0000000..ed9e057
--- /dev/null
+++ b/guacamole/src/main/webapp/app/index/services/iconService.js
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A service for updating or resetting the favicon of the current page.
+ */
+angular.module('index').factory('iconService', ['$rootScope', function iconService($rootScope) {
+
+    var service = {};
+
+    /**
+     * The URL of the image used for the low-resolution (64x64) favicon. This
+     * MUST match the URL which is set statically within index.html.
+     *
+     * @constant
+     * @type String
+     */
+    var DEFAULT_SMALL_ICON_URL = 'images/logo-64.png';
+
+    /**
+     * The URL of the image used for the high-resolution (144x144) favicon. This
+     * MUST match the URL which is set statically within index.html.
+     *
+     * @constant
+     * @type String
+     */
+    var DEFAULT_LARGE_ICON_URL = 'images/logo-144.png';
+
+    /**
+     * JQuery-wrapped array of all link tags which point to the small,
+     * low-resolution page icon.
+     *
+     * @type Element[]
+     */
+    var smallIcons = $('link[rel=icon][href="' + DEFAULT_SMALL_ICON_URL + '"]');
+
+    /**
+     * JQuery-wrapped array of all link tags which point to the large,
+     * high-resolution page icon.
+     *
+     * @type Element[]
+     */
+    var largeIcons = $('link[rel=icon][href="' + DEFAULT_LARGE_ICON_URL + '"]');
+
+    /**
+     * Generates an icon by scaling the provided image to fit the given
+     * dimensions, returning a canvas containing the generated icon.
+     *
+     * @param {HTMLCanvasElement} canvas
+     *     A canvas element containing the image which should be scaled to
+     *     produce the contents of the generated icon.
+     *
+     * @param {Number} width
+     *     The width of the icon to generate, in pixels.
+     *
+     * @param {Number} height
+     *     The height of the icon to generate, in pixels.
+     *
+     * @returns {HTMLCanvasElement}
+     *     A new canvas element having the given dimensions and containing the
+     *     provided image, scaled to fit.
+     */
+    var generateIcon = function generateIcon(canvas, width, height) {
+
+        // Create icon canvas having the provided dimensions
+        var icon = document.createElement('canvas');
+        icon.width = width;
+        icon.height = height;
+
+        // Calculate the scale factor necessary to fit the provided image
+        // within the icon dimensions
+        var scale = Math.min(width / canvas.width, height / canvas.height);
+
+        // Calculate the dimensions and position of the scaled image within
+        // the icon, offsetting the image such that it is centered
+        var scaledWidth = canvas.width * scale;
+        var scaledHeight = canvas.height * scale;
+        var offsetX = (width - scaledWidth) / 2;
+        var offsetY = (height - scaledHeight) / 2;
+
+        // Draw the icon, scaling the provided image as necessary
+        var context = icon.getContext('2d');
+        context.drawImage(canvas, offsetX, offsetY, scaledWidth, scaledHeight);
+        return icon;
+
+    };
+
+    /**
+     * Temporarily sets the icon of the current page to the contents of the
+     * given canvas element. The image within the canvas element will be
+     * automatically scaled and centered to fit within the dimensions of the
+     * page icons. The page icons will be automatically reset to their original
+     * values upon navigation.
+     *
+     * @param {HTMLCanvasElement} canvas
+     *     The canvas element containing the icon. If this value is null or
+     *     undefined, this function has no effect.
+     */
+    service.setIcons = function setIcons(canvas) {
+
+        // Do nothing if no canvas provided
+        if (!canvas)
+            return;
+
+        // Assign low-resolution (64x64) icon
+        var smallIcon = generateIcon(canvas, 64, 64);
+        smallIcons.attr('href', smallIcon.toDataURL('image/png'));
+
+        // Assign high-resolution (144x144) icon
+        var largeIcon = generateIcon(canvas, 144, 144);
+        largeIcons.attr('href', largeIcon.toDataURL('image/png'));
+
+    };
+
+    /**
+     * Resets the icons of the current page to their original values, undoing
+     * any previous calls to setIcons(). This function is automatically invoked
+     * upon navigation.
+     */
+    service.setDefaultIcons = function setDefaultIcons() {
+        smallIcons.attr('href', DEFAULT_SMALL_ICON_URL);
+        largeIcons.attr('href', DEFAULT_LARGE_ICON_URL);
+    };
+
+    // Automatically reset page icons after navigation
+    $rootScope.$on('$routeChangeSuccess', function resetIcon() {
+        service.setDefaultIcons();
+    });
+
+    return service;
+
+}]);