From d6db8fac7ea01d8a6a4a8c2b8bc84b66be4fb471 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 23 Jul 2021 23:45:12 -0700 Subject: [PATCH 1/5] GUACAMOLE-377: Flush frames asynchronously with requestAnimationFrame() if possible. --- .../src/main/webapp/modules/Display.js | 87 ++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/Display.js b/guacamole-common-js/src/main/webapp/modules/Display.js index 8baa6b7d7..be01e8353 100644 --- a/guacamole-common-js/src/main/webapp/modules/Display.js +++ b/guacamole-common-js/src/main/webapp/modules/Display.js @@ -163,10 +163,28 @@ Guacamole.Display = function() { var frames = []; /** - * Flushes all pending frames. + * The ID of the animation frame request returned by the last call to + * requestAnimationFrame(). This value will only be set if the browser + * supports requestAnimationFrame(), if a frame render is currently + * pending, and if the current browser tab is currently focused (likely to + * handle requests for animation frames). In all other cases, this will be + * null. + * + * @private + * @type {number} + */ + var inProgressFrame = null; + + /** + * Flushes all pending frames synchronously. This function will block until + * all pending frames have rendered. If a frame is currently blocked by an + * asynchronous operation like an image load, this function will return + * after reaching that operation and the flush operation will + * automamtically resume after that operation completes. + * * @private */ - function __flush_frames() { + var syncFlush = function syncFlush() { var rendered_frames = 0; @@ -185,6 +203,71 @@ Guacamole.Display = function() { // Remove rendered frames from array frames.splice(0, rendered_frames); + }; + + /** + * Flushes all pending frames asynchronously. This function returns + * immediately, relying on requestAnimationFrame() to dictate when each + * frame should be flushed. + * + * @private + */ + var asyncFlush = function asyncFlush() { + + var continueFlush = function continueFlush() { + + // We're no longer waiting to render a frame + inProgressFrame = null; + + // Nothing to do if there are no frames remaining + if (!frames.length) + return; + + // Flush the next frame only if it is ready (not awaiting + // completion of some asynchronous operation like an image load) + if (frames[0].isReady()) + frames.shift().flush(); + + // Request yet another animation frame if frames remain to be + // flushed + if (frames.length) + inProgressFrame = window.requestAnimationFrame(continueFlush); + + }; + + // Begin flushing frames if not already waiting to render a frame + if (!inProgressFrame) + inProgressFrame = window.requestAnimationFrame(continueFlush); + + }; + + // Switch from asynchronous frame handling to synchronous frame handling if + // requestAnimationFrame() is unlikely to be usable (browsers may not + // invoke the animation frame callback if the relevant tab is not focused) + window.addEventListener('blur', function switchToSyncFlush() { + if (inProgressFrame && !document.hasFocus()) { + + // Cancel pending asynchronous processing of frame ... + window.cancelAnimationFrame(inProgressFrame); + inProgressFrame = null; + + // ... and instead process it synchronously + syncFlush(); + + } + }, true); + + /** + * Flushes all pending frames. + * @private + */ + function __flush_frames() { + + if (window.requestAnimationFrame && document.hasFocus()) + asyncFlush(); + else + syncFlush(); + } /** From 93d97e8c9713bcb4f771082c2afaa50037c78ac2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 Sep 2021 17:34:07 -0700 Subject: [PATCH 2/5] GUACAMOLE-377: Add parameter definition and translation for "enable-gfx". --- .../main/resources/org/apache/guacamole/protocols/rdp.json | 5 +++++ guacamole/src/main/frontend/src/translations/en.json | 1 + 2 files changed, 6 insertions(+) diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json index e78313d79..77855f3db 100644 --- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json +++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json @@ -254,6 +254,11 @@ { "name" : "performance", "fields" : [ + { + "name" : "enable-gfx", + "type" : "BOOLEAN", + "options" : [ "true" ] + }, { "name" : "enable-wallpaper", "type" : "BOOLEAN", diff --git a/guacamole/src/main/frontend/src/translations/en.json b/guacamole/src/main/frontend/src/translations/en.json index db502311d..3ee46a313 100644 --- a/guacamole/src/main/frontend/src/translations/en.json +++ b/guacamole/src/main/frontend/src/translations/en.json @@ -490,6 +490,7 @@ "FIELD_HEADER_ENABLE_DRIVE" : "Enable drive:", "FIELD_HEADER_ENABLE_FONT_SMOOTHING" : "Enable font smoothing (ClearType):", "FIELD_HEADER_ENABLE_FULL_WINDOW_DRAG" : "Enable full-window drag:", + "FIELD_HEADER_ENABLE_GFX" : "Enable Graphics Pipeline Extension (RemoteFX):", "FIELD_HEADER_ENABLE_MENU_ANIMATIONS" : "Enable menu animations:", "FIELD_HEADER_DISABLE_BITMAP_CACHING" : "Disable bitmap caching:", "FIELD_HEADER_DISABLE_OFFSCREEN_CACHING" : "Disable off-screen caching:", From e5dccc865724efd45d871cc1bc9488cc0c9791c2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 Sep 2021 00:25:27 -0700 Subject: [PATCH 3/5] GUACAMOLE-377: Add JavaScript API support for tracking display render statistics. --- .../src/main/webapp/modules/Client.js | 11 +- .../src/main/webapp/modules/Display.js | 264 +++++++++++++++++- 2 files changed, 267 insertions(+), 8 deletions(-) diff --git a/guacamole-common-js/src/main/webapp/modules/Client.js b/guacamole-common-js/src/main/webapp/modules/Client.js index 83d89abaa..2cc9c28b1 100644 --- a/guacamole-common-js/src/main/webapp/modules/Client.js +++ b/guacamole-common-js/src/main/webapp/modules/Client.js @@ -840,6 +840,12 @@ Guacamole.Client = function(tunnel) { * @event * @param {!number} timestamp * The timestamp associated with the sync instruction. + * + * @param {!number} frames + * The number of frames that were considered or combined to produce the + * frame associated with this sync instruction, or zero if this value + * is not known or the remote desktop server provides no concept of + * frames. */ this.onsync = null; @@ -1530,6 +1536,7 @@ Guacamole.Client = function(tunnel) { "sync": function(parameters) { var timestamp = parseInt(parameters[0]); + var frames = parameters[1] ? parseInt(parameters[1]) : 0; // Flush display, send sync when done display.flush(function displaySyncComplete() { @@ -1547,7 +1554,7 @@ Guacamole.Client = function(tunnel) { currentTimestamp = timestamp; } - }); + }, timestamp, frames); // If received first update, no longer waiting. if (currentState === STATE_WAITING) @@ -1555,7 +1562,7 @@ Guacamole.Client = function(tunnel) { // Call sync handler if defined if (guac_client.onsync) - guac_client.onsync(timestamp); + guac_client.onsync(timestamp, frames); }, diff --git a/guacamole-common-js/src/main/webapp/modules/Display.js b/guacamole-common-js/src/main/webapp/modules/Display.js index be01e8353..374a7620d 100644 --- a/guacamole-common-js/src/main/webapp/modules/Display.js +++ b/guacamole-common-js/src/main/webapp/modules/Display.js @@ -112,6 +112,17 @@ Guacamole.Display = function() { */ this.cursorY = 0; + /** + * The number of milliseconds over which display rendering statistics + * should be gathered, dispatching {@link #onstatistics} events as those + * statistics are available. If set to zero, no statistics will be + * gathered. + * + * @default 0 + * @type {!number} + */ + this.statisticWindow = 0; + /** * Fired when the default layer (and thus the entire Guacamole display) * is resized. @@ -142,6 +153,18 @@ Guacamole.Display = function() { */ this.oncursor = null; + /** + * Fired whenever performance statistics are available for recently- + * rendered frames. This event will fire only if {@link #statisticWindow} + * is non-zero. + * + * @event + * @param {!Guacamole.Display.Statistics} stats + * An object containing general rendering performance statistics for + * the remote desktop, Guacamole server, and Guacamole client. + */ + this.onstatistics = null; + /** * The queue of all pending Tasks. Tasks will be run in order, with new * tasks added at the end of the queue and old tasks removed from the @@ -186,6 +209,10 @@ Guacamole.Display = function() { */ var syncFlush = function syncFlush() { + var localTimestamp = 0; + var remoteTimestamp = 0; + + var renderedLogicalFrames = 0; var rendered_frames = 0; // Draw all pending frames, if ready @@ -196,6 +223,10 @@ Guacamole.Display = function() { break; frame.flush(); + + localTimestamp = frame.localTimestamp; + remoteTimestamp = frame.remoteTimestamp; + renderedLogicalFrames += frame.logicalFrames; rendered_frames++; } @@ -203,6 +234,9 @@ Guacamole.Display = function() { // Remove rendered frames from array frames.splice(0, rendered_frames); + if (rendered_frames) + notifyFlushed(localTimestamp, remoteTimestamp, renderedLogicalFrames); + }; /** @@ -225,8 +259,11 @@ Guacamole.Display = function() { // Flush the next frame only if it is ready (not awaiting // completion of some asynchronous operation like an image load) - if (frames[0].isReady()) - frames.shift().flush(); + if (frames[0].isReady()) { + var frame = frames.shift(); + frame.flush(); + notifyFlushed(frame.localTimestamp, frame.remoteTimestamp, frame.logicalFrames); + } // Request yet another animation frame if frames remain to be // flushed @@ -241,6 +278,101 @@ Guacamole.Display = function() { }; + /** + * Recently-gathered display render statistics, as made available by calls + * to notifyFlushed(). The contents of this array will be trimmed to + * contain only up to {@link #statisticWindow} milliseconds of statistics. + * + * @private + * @type {Guacamole.Display.Statistics[]} + */ + var statistics = []; + + /** + * Notifies that one or more frames have been successfully rendered + * (flushed) to the display. + * + * @private + * @param {!number} localTimestamp + * The local timestamp of the point in time at which the most recent, + * flushed frame was received by the display, in milliseconds since the + * Unix Epoch. + * + * @param {!number} remoteTimestamp + * The remote timestamp of sync instruction associated with the most + * recent, flushed frame received by the display. This timestamp is in + * milliseconds, but is arbitrary, having meaning only relative to + * other timestamps in the same connection. + * + * @param {!number} logicalFrames + * The number of remote desktop frames that were flushed. + */ + var notifyFlushed = function notifyFlushed(localTimestamp, remoteTimestamp, logicalFrames) { + + // Ignore if statistics are not being gathered + if (!guac_display.statisticWindow) + return; + + var current = new Date().getTime(); + + // Find the first statistic that is still within the configured time + // window + for (var first = 0; first < statistics.length; first++) { + if (current - statistics[first].timestamp <= guac_display.statisticWindow) + break; + } + + // Remove all statistics except those within the time window + statistics.splice(0, first - 1); + + // Record statistics for latest frame + statistics.push({ + localTimestamp : localTimestamp, + remoteTimestamp : remoteTimestamp, + timestamp : current, + frames : logicalFrames + }); + + // Determine the actual time interval of the available statistics (this + // will not perfectly match the configured interval, which is an upper + // bound) + var statDuration = (statistics[statistics.length - 1].timestamp - statistics[0].timestamp) / 1000; + + // Determine the amount of time that elapsed remotely (within the + // remote desktop) + var remoteDuration = (statistics[statistics.length - 1].remoteTimestamp - statistics[0].remoteTimestamp) / 1000; + + // Calculate the number of frames that have been rendered locally + // within the configured time interval + var localFrames = statistics.length; + + // Calculate the number of frames actually received from the remote + // desktop by the Guacamole server + var remoteFrames = statistics.reduce(function sumFrames(prev, stat) { + return prev + stat.frames; + }, 0); + + // Calculate the number of frames that the Guacamole server had to + // drop or combine with other frames + var drops = statistics.reduce(function sumDrops(prev, stat) { + return prev + Math.max(0, stat.frames - 1); + }, 0); + + // Produce lag and FPS statistics from above raw measurements + var stats = new Guacamole.Display.Statistics({ + processingLag : current - localTimestamp, + desktopFps : (remoteDuration && remoteFrames) ? remoteFrames / remoteDuration : null, + clientFps : statDuration ? localFrames / statDuration : null, + serverFps : remoteDuration ? localFrames / remoteDuration : null, + dropRate : remoteDuration ? drops / remoteDuration : null + }); + + // Notify of availability of new statistics + if (guac_display.onstatistics) + guac_display.onstatistics(stats); + + }; + // Switch from asynchronous frame handling to synchronous frame handling if // requestAnimationFrame() is unlikely to be usable (browsers may not // invoke the animation frame callback if the relevant tab is not focused) @@ -281,8 +413,43 @@ Guacamole.Display = function() { * * @param {!Task[]} tasks * The set of tasks which must be executed to render this frame. + * + * @param {number} [timestamp] + * The remote timestamp of sync instruction associated with this frame. + * This timestamp is in milliseconds, but is arbitrary, having meaning + * only relative to other remote timestamps in the same connection. If + * omitted, a compatible but local timestamp will be used instead. + * + * @param {number} [logicalFrames=0] + * The number of remote desktop frames that were combined to produce + * this frame, or zero if this value is unknown or inapplicable. */ - function Frame(callback, tasks) { + var Frame = function Frame(callback, tasks, timestamp, logicalFrames) { + + /** + * The local timestamp of the point in time at which this frame was + * received by the display, in milliseconds since the Unix Epoch. + * + * @type {!number} + */ + this.localTimestamp = new Date().getTime(); + + /** + * The remote timestamp of sync instruction associated with this frame. + * This timestamp is in milliseconds, but is arbitrary, having meaning + * only relative to other remote timestamps in the same connection. + * + * @type {!number} + */ + this.remoteTimestamp = timestamp || this.localTimestamp; + + /** + * The number of remote desktop frames that were combined to produce + * this frame. If unknown or not applicable, this will be zero. + * + * @type {!number} + */ + this.logicalFrames = logicalFrames || 0; /** * Cancels rendering of this frame and all associated tasks. The @@ -337,7 +504,7 @@ Guacamole.Display = function() { }; - } + }; /** * A container for an task handler. Each operation which must be ordered @@ -514,11 +681,20 @@ Guacamole.Display = function() { * @param {function} [callback] * The function to call when this frame is flushed. This may happen * immediately, or later when blocked tasks become unblocked. + * + * @param {number} timestamp + * The remote timestamp of sync instruction associated with this frame. + * This timestamp is in milliseconds, but is arbitrary, having meaning + * only relative to other remote timestamps in the same connection. + * + * @param {number} logicalFrames + * The number of remote desktop frames that were combined to produce + * this frame. */ - this.flush = function(callback) { + this.flush = function(callback, timestamp, logicalFrames) { // Add frame, reset tasks - frames.push(new Frame(callback, tasks)); + frames.push(new Frame(callback, tasks, timestamp, logicalFrames)); tasks = []; // Attempt flush @@ -1938,3 +2114,79 @@ Guacamole.Display.VisibleLayer = function(width, height) { * @type {!number} */ Guacamole.Display.VisibleLayer.__next_id = 0; + +/** + * A set of Guacamole display performance statistics, describing the speed at + * which the remote desktop, Guacamole server, and Guacamole client are + * rendering frames. + * + * @constructor + * @param {Guacamole.Display.Statistics|Object} [template={}] + * The object whose properties should be copied within the new + * Guacamole.Display.Statistics. + */ +Guacamole.Display.Statistics = function Statistics(template) { + + template = template || {}; + + /** + * The amount of time that the Guacamole client is taking to render + * individual frames, in milliseconds, if known. If this value is unknown, + * such as if the there are insufficient frame statistics recorded to + * calculate this value, this will be null. + * + * @type {?number} + */ + this.processingLag = template.processingLag; + + /** + * The framerate of the remote desktop currently being viewed within the + * relevant Gucamole.Display, independent of Guacamole, in frames per + * second. This represents the speed at which the remote desktop is + * producing frame data for the Guacamole server to consume. If this + * value is unknown, such as if the remote desktop server does not actually + * define frame boundaries, this will be null. + * + * @type {?number} + */ + this.desktopFps = template.desktopFps; + + /** + * The rate at which the Guacamole server is generating frames for the + * Guacamole client to consume, in frames per second. If the Guacamole + * server is correctly adjusting for variance in client/browser processing + * power, this rate should closely match the client rate, and should remain + * independent of any network latency. If this value is unknown, such as if + * the there are insufficient frame statistics recorded to calculate this + * value, this will be null. + * + * @type {?number} + */ + this.serverFps = template.serverFps; + + /** + * The rate at which the Guacamole client is consuming frames generated by + * the Guacamole server, in frames per second. If the Guacamole server is + * correctly adjusting for variance in client/browser processing power, + * this rate should closely match the server rate, regardless of any + * latency on the network between the server and client. If this value is + * unknown, such as if the there are insufficient frame statistics recorded + * to calculate this value, this will be null. + * + * @type {?number} + */ + this.clientFps = template.clientFps; + + /** + * The rate at which the Guacamole server is dropping or combining frames + * received from the remote desktop server to compensate for variance in + * client/browser processing power, in frames per second. This value may + * also be non-zero if the server is compensating for variances in its own + * processing power, or relative slowness in image compression vs. the rate + * that inbound frames are received. If this value is unknown, such as if + * the remote desktop server does not actually define frame boundaries, + * this will be null. + */ + this.dropRate = template.dropRate; + +}; From 59ace6c49399c865ff2ca36f61ee937cd7099fc2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 Sep 2021 00:52:30 -0700 Subject: [PATCH 4/5] GUACAMOLE-377: Add extension for displaying render statistics in a toolbar. --- .../guacamole-display-statistics/.gitignore | 1 + .../guacamole-display-statistics/.ratignore | 1 + .../guacamole-display-statistics/pom.xml | 124 ++++++++++++++++++ .../src/main/assembly/dist.xml | 53 ++++++++ .../directives/guacClientStatistics.js | 108 +++++++++++++++ .../src/main/resources/guac-manifest.json | 28 ++++ .../main/resources/html/add-statistics.html | 4 + .../src/main/resources/license.txt | 18 +++ .../resources/styles/clientStatistics.css | 57 ++++++++ .../templates/guacClientStatistics.html | 39 ++++++ .../src/main/resources/translations/en.json | 12 ++ extensions/pom.xml | 3 + 12 files changed, 448 insertions(+) create mode 100644 extensions/guacamole-display-statistics/.gitignore create mode 100644 extensions/guacamole-display-statistics/.ratignore create mode 100644 extensions/guacamole-display-statistics/pom.xml create mode 100644 extensions/guacamole-display-statistics/src/main/assembly/dist.xml create mode 100644 extensions/guacamole-display-statistics/src/main/resources/directives/guacClientStatistics.js create mode 100644 extensions/guacamole-display-statistics/src/main/resources/guac-manifest.json create mode 100644 extensions/guacamole-display-statistics/src/main/resources/html/add-statistics.html create mode 100644 extensions/guacamole-display-statistics/src/main/resources/license.txt create mode 100644 extensions/guacamole-display-statistics/src/main/resources/styles/clientStatistics.css create mode 100644 extensions/guacamole-display-statistics/src/main/resources/templates/guacClientStatistics.html create mode 100644 extensions/guacamole-display-statistics/src/main/resources/translations/en.json diff --git a/extensions/guacamole-display-statistics/.gitignore b/extensions/guacamole-display-statistics/.gitignore new file mode 100644 index 000000000..e55f47f1f --- /dev/null +++ b/extensions/guacamole-display-statistics/.gitignore @@ -0,0 +1 @@ +src/main/resources/generated/ diff --git a/extensions/guacamole-display-statistics/.ratignore b/extensions/guacamole-display-statistics/.ratignore new file mode 100644 index 000000000..da318d12f --- /dev/null +++ b/extensions/guacamole-display-statistics/.ratignore @@ -0,0 +1 @@ +src/main/resources/html/*.html diff --git a/extensions/guacamole-display-statistics/pom.xml b/extensions/guacamole-display-statistics/pom.xml new file mode 100644 index 000000000..403d974cf --- /dev/null +++ b/extensions/guacamole-display-statistics/pom.xml @@ -0,0 +1,124 @@ + + + + + 4.0.0 + org.apache.guacamole + guacamole-display-statistics + jar + 1.4.0 + guacamole-display-statistics + http://guacamole.apache.org/ + + + org.apache.guacamole + extensions + 1.4.0 + ../ + + + + + + + + com.keithbranton.mojo + angular-maven-plugin + 0.3.4 + + + generate-resources + + html2js + + + + + ${basedir}/src/main/resources + **/*.html + ${basedir}/src/main/resources/generated/templates-main/templates.js + app/ext/display-stats + + + + + + com.github.buckelieg + minify-maven-plugin + + + default-cli + + UTF-8 + + ${basedir}/src/main/resources + ${project.build.directory}/classes + + / + / + display-stats.css + + + license.txt + + + + **/*.css + + + / + / + display-stats.js + + + license.txt + + + + **/*.js + + + + + **/*.test.js + + CLOSURE + + + + OFF + OFF + + + + + minify + + + + + + + + + diff --git a/extensions/guacamole-display-statistics/src/main/assembly/dist.xml b/extensions/guacamole-display-statistics/src/main/assembly/dist.xml new file mode 100644 index 000000000..0b16a7147 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/assembly/dist.xml @@ -0,0 +1,53 @@ + + + + + dist + ${project.artifactId}-${project.version} + + + + tar.gz + + + + + + + + + target/licenses + + + + + target + + + *.jar + + + + + + diff --git a/extensions/guacamole-display-statistics/src/main/resources/directives/guacClientStatistics.js b/extensions/guacamole-display-statistics/src/main/resources/directives/guacClientStatistics.js new file mode 100644 index 000000000..4dc6b8a15 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/directives/guacClientStatistics.js @@ -0,0 +1,108 @@ +/* + * 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 directive which displays frame rendering performance statistics for a + * Guacamole client. + */ +angular.module('client').directive('guacClientStatistics', [function guacClientStatistics() { + + const directive = { + restrict: 'E', + templateUrl: 'app/ext/display-stats/templates/guacClientStatistics.html', + }; + + directive.scope = { + + /** + * The Guacamole client to display frame rendering statistics for. + * + * @type ManagedClient + */ + client : '=' + + }; + + directive.controller = ['$scope', function guacClientStatisticsController($scope) { + + /** + * Updates the displayed frame rendering statistics to the values + * within the given statistics object. + * + * @param {!Guacamole.Display.Statistics} stats + * An object containing general rendering performance statistics for + * the remote desktop, Guacamole server, and Guacamole client. + */ + var updateStatistics = function updateStatistics(stats) { + $scope.$apply(function statisticsChanged() { + $scope.statistics = stats; + }); + }; + + /** + * Returns whether the given value is a defined value that should be + * rendered within the statistics toolbar. + * + * @param {number} value + * The value to test. + * + * @returns {!boolean} + * true if the given value should be rendered within the statistics + * toolbar, false otherwise. + */ + $scope.hasValue = function hasValue(value) { + return value || value === 0; + }; + + /** + * Rounds the given numeric value to the nearest hundredth (two decimal places). + * + * @param {!number} value + * The value to round. + * + * @param {!number} + * The given value, rounded to the nearest hundredth. + */ + $scope.round = function round(value) { + return Math.round(value * 100) / 100; + }; + + // Assign/remove onstatistics handlers to track the statistics of the + // current client + $scope.$watch('client', function clientChanged(client, oldClient) { + + if (oldClient) + oldClient.managedDisplay.display.onstatistics = null; + + client.managedDisplay.display.statisticWindow = 1000; + client.managedDisplay.display.onstatistics = updateStatistics; + + }); + + // Clear onstatistics handler when directive is being unloaded + $scope.$on('$destroy', function scopeDestroyed() { + if ($scope.client) + $scope.client.managedDisplay.display.onstatistics = null; + }); + + }]; + + return directive; + +}]); diff --git a/extensions/guacamole-display-statistics/src/main/resources/guac-manifest.json b/extensions/guacamole-display-statistics/src/main/resources/guac-manifest.json new file mode 100644 index 000000000..71c84b384 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/guac-manifest.json @@ -0,0 +1,28 @@ +{ + + "guacamoleVersion" : "1.4.0", + + "name" : "Display Statistic Toolbar", + "namespace" : "display-stats", + + "translations" : [ + "translations/en.json" + ], + + "js" : [ + "display-stats.min.js" + ], + + "css" : [ + "display-stats.min.css" + ], + + "html" : [ + "html/add-statistics.html" + ], + + "resources" : { + "templates/guacClientStatistics.html" : "text/html" + } + +} diff --git a/extensions/guacamole-display-statistics/src/main/resources/html/add-statistics.html b/extensions/guacamole-display-statistics/src/main/resources/html/add-statistics.html new file mode 100644 index 000000000..5978f7f58 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/html/add-statistics.html @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/guacamole-display-statistics/src/main/resources/license.txt b/extensions/guacamole-display-statistics/src/main/resources/license.txt new file mode 100644 index 000000000..042f3ce1f --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/license.txt @@ -0,0 +1,18 @@ +/* + * 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. + */ diff --git a/extensions/guacamole-display-statistics/src/main/resources/styles/clientStatistics.css b/extensions/guacamole-display-statistics/src/main/resources/styles/clientStatistics.css new file mode 100644 index 000000000..ba4661469 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/styles/clientStatistics.css @@ -0,0 +1,57 @@ +/* + * 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. + */ + +guac-client-statistics { + font-size: 13px; + color: white; + background: #111; +} + +guac-client-statistics dl.client-statistics { + display: table; + margin: 0; + padding: 0.25em; +} + +guac-client-statistics dl.client-statistics dt, +guac-client-statistics dl.client-statistics dd { + display: table-cell; + padding: 0.25em; +} + +guac-client-statistics dl.client-statistics dt { + padding-right: 0.5em; + padding-left: 1em; +} + +guac-client-statistics dl.client-statistics dt:first-child { + padding-left: 0.5em; +} + +guac-client-statistics dl.client-statistics dd { + min-width: 6em; + border: 1px solid rgba(255, 255, 255, 0.125); + border-radius: 3px; + background: black; +} + +guac-client-statistics dl.client-statistics dd.no-value::before { + color: #888; + content: '-'; +} diff --git a/extensions/guacamole-display-statistics/src/main/resources/templates/guacClientStatistics.html b/extensions/guacamole-display-statistics/src/main/resources/templates/guacClientStatistics.html new file mode 100644 index 000000000..ae7537c0f --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/templates/guacClientStatistics.html @@ -0,0 +1,39 @@ +
+ +
+ {{ 'CLIENT.FIELD_HEADER_DESKTOP_FRAMERATE' | translate }} +
+
+ +
+ +
+ {{ 'CLIENT.FIELD_HEADER_SERVER_FRAMERATE' | translate }} +
+
+ +
+ +
+ {{ 'CLIENT.FIELD_HEADER_CLIENT_FRAMERATE' | translate }} +
+
+ +
+ +
+ {{ 'CLIENT.FIELD_HEADER_DROP_FRAMERATE' | translate }} +
+
+ +
+ +
\ No newline at end of file diff --git a/extensions/guacamole-display-statistics/src/main/resources/translations/en.json b/extensions/guacamole-display-statistics/src/main/resources/translations/en.json new file mode 100644 index 000000000..ef42908e4 --- /dev/null +++ b/extensions/guacamole-display-statistics/src/main/resources/translations/en.json @@ -0,0 +1,12 @@ +{ + "CLIENT" : { + + "FIELD_HEADER_CLIENT_FRAMERATE" : "Guacamole (Client):", + "FIELD_HEADER_DESKTOP_FRAMERATE" : "Remote Desktop:", + "FIELD_HEADER_DROP_FRAMERATE" : "Drop:", + "FIELD_HEADER_SERVER_FRAMERATE" : "Guacamole (Server):", + + "INFO_FRAMERATE" : "{VALUE} fps" + + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index de2b24556..3bab33257 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -53,6 +53,9 @@ guacamole-history-recording-storage guacamole-vault + + guacamole-display-statistics + From be90de9a9dd56d4d79808fee6f5b60b525e770a3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 18 May 2022 15:40:31 -0700 Subject: [PATCH 5/5] GUACAMOLE-377: Switch over to "disable-gfx" parameter (enable RDPGFX by default). --- .../resources/org/apache/guacamole/protocols/rdp.json | 10 +++++----- guacamole/src/main/frontend/src/translations/en.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json index 77855f3db..c51f76209 100644 --- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json +++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json @@ -254,11 +254,6 @@ { "name" : "performance", "fields" : [ - { - "name" : "enable-gfx", - "type" : "BOOLEAN", - "options" : [ "true" ] - }, { "name" : "enable-wallpaper", "type" : "BOOLEAN", @@ -303,6 +298,11 @@ "name" : "disable-glyph-caching", "type" : "BOOLEAN", "options" : [ "true" ] + }, + { + "name" : "disable-gfx", + "type" : "BOOLEAN", + "options" : [ "true" ] } ] }, diff --git a/guacamole/src/main/frontend/src/translations/en.json b/guacamole/src/main/frontend/src/translations/en.json index 3ee46a313..05a9fa279 100644 --- a/guacamole/src/main/frontend/src/translations/en.json +++ b/guacamole/src/main/frontend/src/translations/en.json @@ -490,11 +490,11 @@ "FIELD_HEADER_ENABLE_DRIVE" : "Enable drive:", "FIELD_HEADER_ENABLE_FONT_SMOOTHING" : "Enable font smoothing (ClearType):", "FIELD_HEADER_ENABLE_FULL_WINDOW_DRAG" : "Enable full-window drag:", - "FIELD_HEADER_ENABLE_GFX" : "Enable Graphics Pipeline Extension (RemoteFX):", "FIELD_HEADER_ENABLE_MENU_ANIMATIONS" : "Enable menu animations:", "FIELD_HEADER_DISABLE_BITMAP_CACHING" : "Disable bitmap caching:", "FIELD_HEADER_DISABLE_OFFSCREEN_CACHING" : "Disable off-screen caching:", "FIELD_HEADER_DISABLE_GLYPH_CACHING" : "Disable glyph caching:", + "FIELD_HEADER_DISABLE_GFX" : "Disable Graphics Pipeline Extension:", "FIELD_HEADER_ENABLE_PRINTING" : "Enable printing:", "FIELD_HEADER_ENABLE_SFTP" : "Enable SFTP:", "FIELD_HEADER_ENABLE_THEMING" : "Enable theming:",