Skip to content

Commit

Permalink
Improve refresh queue
Browse files Browse the repository at this point in the history
Use requestAnimationFrame in addition to a queue to refresh every animation
frame but only when a refresh is needed.

Fixes #280
Fixes #290
  • Loading branch information
Tyriar committed Dec 31, 2016
1 parent 1fbd3c8 commit 97feb33
Showing 1 changed file with 48 additions and 50 deletions.
98 changes: 48 additions & 50 deletions src/xterm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
*
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}
};

Expand Down Expand Up @@ -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);
};

/**
Expand Down Expand Up @@ -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);
};

/**
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
};

Expand Down Expand Up @@ -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();
};

Expand Down Expand Up @@ -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();
}
Expand Down

0 comments on commit 97feb33

Please sign in to comment.