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

This commit is contained in:
Michael Jumper
2017-01-17 10:52:00 -08:00
parent d8f9d26bdb
commit fd1c652a84
2 changed files with 149 additions and 49 deletions

View File

@@ -28,6 +28,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
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');
@@ -47,6 +48,14 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
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
@@ -98,6 +107,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.
*
@@ -227,45 +245,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,
@@ -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;
}]);

View File

@@ -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;
}]);