Flush frames asynchronously with requestAnimationFrame() if possible.
@@ -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}
*/
- function __flush_frames() {
+ 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
+ */
+ 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();
+
}
/**
