*/
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;
*/
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');
}
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.
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)
*
* @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;
Terminal.prototype.showCursor = function() {
if (!this.cursorState) {
this.cursorState = 1;
- this.refresh(this.y, this.y);
+ this.queueRefresh(this.y, this.y);
}
};
this.emit('scroll', this.ydisp);
}
- this.refresh(0, this.rows - 1);
+ this.queueRefresh(0, this.rows - 1);
};
/**
}
this.updateRange(this.y);
- this.refresh(this.refreshStart, this.refreshEnd);
+ this.queueRefresh(this.refreshStart, this.refreshEnd);
};
/**
this.scrollTop = 0;
this.scrollBottom = y - 1;
- this.refresh(0, this.rows - 1);
+ this.queueRefresh(0, this.rows - 1);
this.normal = null;
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);
};
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();
};
// 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();
}