]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Improve refresh queue
authorDaniel Imms <daimms@microsoft.com>
Sat, 31 Dec 2016 22:06:26 +0000 (14:06 -0800)
committerDaniel Imms <daimms@microsoft.com>
Sat, 31 Dec 2016 22:14:02 +0000 (14:14 -0800)
Use requestAnimationFrame in addition to a queue to refresh every animation
frame but only when a refresh is needed.

Fixes #280
Fixes #290

src/xterm.js

index 1d2206529efba38c3f7f4e767759a2ee8f3cfa29..79ca633b37810358d135ce8139478b3b535fc595 100644 (file)
@@ -137,14 +137,8 @@ function Terminal(options) {
    */
   this.y = 0;
 
-  /**
-   * Used to debounce the refresh function
-   */
-  this.isRefreshing = false;
-
-  /**
-   * Whether there is a full terminal refresh queued
-   */
+  /** A queue of the rows to be refreshed */
+  this.refreshRowsQueue = [];
 
   this.cursorState = 0;
   this.cursorHidden = false;
@@ -407,7 +401,7 @@ Terminal.prototype.blur = function() {
  */
 Terminal.bindBlur = function (term) {
   on(term.textarea, 'blur', function (ev) {
-    term.refresh(term.y, term.y);
+    term.queueRefresh(term.y, term.y);
     if (term.sendFocus) {
       term.send('\x1b[O');
     }
@@ -585,8 +579,13 @@ Terminal.prototype.open = function(parent) {
 
   this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasureElement);
 
-  // Draw the screen.
-  this.refresh(0, this.rows - 1);
+  // Setup loop that draws to screen
+  this.queueRefresh(0, this.rows - 1);
+  function refreshLoop() {
+    self.refresh();
+    window.requestAnimationFrame(refreshLoop);
+  }
+  window.requestAnimationFrame(refreshLoop);
 
   // Initialize global actions that
   // need to be taken on the document.
@@ -997,6 +996,16 @@ Terminal.flags = {
   INVISIBLE: 16
 }
 
+/**
+ * Queues a refresh between two rows (inclusive), to be done on next animation
+ * frame.
+ * @param {number} start The start row.
+ * @param {number} end The end row.
+ */
+Terminal.prototype.queueRefresh = function(start, end) {
+  this.refreshRowsQueue.push({ start: start, end: end });
+}
+
 /**
  * Refreshes (re-renders) terminal content within two rows (inclusive)
  *
@@ -1019,44 +1028,33 @@ Terminal.flags = {
  * @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
  * @param {boolean} queue Whether the refresh should ran right now or be queued
  */
-Terminal.prototype.refresh = function(start, end, queue) {
-  var self = this;
-
-  // queue defaults to true
-  queue = (typeof queue == 'undefined') ? true : queue;
-
-  /**
-   * The refresh queue allows refresh to execute only approximately 30 times a second. For
-   * commands that pass a significant amount of output to the write function, this prevents the
-   * terminal from maxing out the CPU and making the UI unresponsive. While commands can still
-   * run beyond what they do on the terminal, it is far better with a debounce in place as
-   * every single terminal manipulation does not need to be constructed in the DOM.
-   *
-   * A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
-   */
-  if (queue) {
-    // If refresh should be queued, order the refresh and return.
-    if (this._refreshIsQueued) {
-      // If a refresh has already been queued, just order a full refresh next
-      this._fullRefreshNext = true;
-    } else {
-      setTimeout(function () {
-        self.refresh(start, end, false);
-      }, 34)
-      this._refreshIsQueued = true;
-    }
+Terminal.prototype.refresh = function() {
+  if (this.refreshRowsQueue.length === 0) {
+    // Don't refresh if there were no row changes
     return;
   }
 
-  // If refresh should be run right now (not be queued), release the lock
-  this._refreshIsQueued = false;
-
-  // If multiple refreshes were requested, make a full refresh.
-  if (this._fullRefreshNext) {
+  var start;
+  var end;
+  if (this.refreshRowsQueue.length > 4) {
+    // Just do a full refresh when 5+ refreshes are queued
     start = 0;
     end = this.rows - 1;
-    this._fullRefreshNext = false // reset lock
+  } else {
+    // Get start and end rows that need refreshing
+    start = this.refreshRowsQueue[0].start;
+    end = this.refreshRowsQueue[0].end;
+    for (var i = 1; i < this.refreshRowsQueue.length; i++) {
+      if (this.refreshRowsQueue[i].start < start) {
+        start = this.refreshRowsQueue[i].start;
+      }
+      if (this.refreshRowsQueue[i].end > end) {
+        end = this.refreshRowsQueue[i].end;
+      }
+    }
   }
+  this.refreshRowsQueue = [];
+  var self = this;
 
   var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
 
@@ -1224,7 +1222,7 @@ Terminal.prototype.refresh = function(start, end, queue) {
 Terminal.prototype.showCursor = function() {
   if (!this.cursorState) {
     this.cursorState = 1;
-    this.refresh(this.y, this.y);
+    this.queueRefresh(this.y, this.y);
   }
 };
 
@@ -1311,7 +1309,7 @@ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) {
     this.emit('scroll', this.ydisp);
   }
 
-  this.refresh(0, this.rows - 1);
+  this.queueRefresh(0, this.rows - 1);
 };
 
 /**
@@ -2379,7 +2377,7 @@ Terminal.prototype.write = function(data) {
   }
 
   this.updateRange(this.y);
-  this.refresh(this.refreshStart, this.refreshEnd);
+  this.queueRefresh(this.refreshStart, this.refreshEnd);
 };
 
 /**
@@ -2945,7 +2943,7 @@ Terminal.prototype.resize = function(x, y) {
   this.scrollTop = 0;
   this.scrollBottom = y - 1;
 
-  this.refresh(0, this.rows - 1);
+  this.queueRefresh(0, this.rows - 1);
 
   this.normal = null;
 
@@ -3074,7 +3072,7 @@ Terminal.prototype.clear = function() {
   for (var i = 1; i < this.rows; i++) {
     this.lines.push(this.blankLine());
   }
-  this.refresh(0, this.rows - 1);
+  this.queueRefresh(0, this.rows - 1);
   this.emit('scroll', this.ydisp);
 };
 
@@ -3205,7 +3203,7 @@ Terminal.prototype.reset = function() {
   var customKeydownHandler = this.customKeydownHandler;
   Terminal.call(this, this.options);
   this.customKeydownHandler = customKeydownHandler;
-  this.refresh(0, this.rows - 1);
+  this.queueRefresh(0, this.rows - 1);
   this.viewport.syncScrollArea();
 };
 
@@ -4324,7 +4322,7 @@ Terminal.prototype.resetMode = function(params) {
           //   this.x = this.savedX;
           //   this.y = this.savedY;
           // }
-          this.refresh(0, this.rows - 1);
+          this.queueRefresh(0, this.rows - 1);
           this.viewport.syncScrollArea();
           this.showCursor();
         }