Skip to content

Commit 28b38dc

Browse files
authored
Merge pull request #438 from Tyriar/280_improve_refresh_queue
Improve refresh queue
2 parents 43d4d4f + 7234bfb commit 28b38dc

File tree

1 file changed

+56
-56
lines changed

1 file changed

+56
-56
lines changed

src/xterm.js

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,8 @@ function Terminal(options) {
137137
*/
138138
this.y = 0;
139139

140-
/**
141-
* Used to debounce the refresh function
142-
*/
143-
this.isRefreshing = false;
144-
145-
/**
146-
* Whether there is a full terminal refresh queued
147-
*/
140+
/** A queue of the rows to be refreshed */
141+
this.refreshRowsQueue = [];
148142

149143
this.cursorState = 0;
150144
this.cursorHidden = false;
@@ -407,7 +401,7 @@ Terminal.prototype.blur = function() {
407401
*/
408402
Terminal.bindBlur = function (term) {
409403
on(term.textarea, 'blur', function (ev) {
410-
term.refresh(term.y, term.y);
404+
term.queueRefresh(term.y, term.y);
411405
if (term.sendFocus) {
412406
term.send('\x1b[O');
413407
}
@@ -585,8 +579,9 @@ Terminal.prototype.open = function(parent) {
585579

586580
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasureElement);
587581

588-
// Draw the screen.
589-
this.refresh(0, this.rows - 1);
582+
// Setup loop that draws to screen
583+
this.queueRefresh(0, this.rows - 1);
584+
this.refreshLoop();
590585

591586
// Initialize global actions that
592587
// need to be taken on the document.
@@ -997,6 +992,48 @@ Terminal.flags = {
997992
INVISIBLE: 16
998993
}
999994

995+
/**
996+
* Queues a refresh between two rows (inclusive), to be done on next animation
997+
* frame.
998+
* @param {number} start The start row.
999+
* @param {number} end The end row.
1000+
*/
1001+
Terminal.prototype.queueRefresh = function(start, end) {
1002+
this.refreshRowsQueue.push({ start: start, end: end });
1003+
}
1004+
1005+
/**
1006+
* Performs the refresh loop callback, calling refresh only if a refresh is
1007+
* necessary before queueing up the next one.
1008+
*/
1009+
Terminal.prototype.refreshLoop = function() {
1010+
// Don't refresh if there were no row changes
1011+
if (this.refreshRowsQueue.length > 0) {
1012+
var start;
1013+
var end;
1014+
if (this.refreshRowsQueue.length > 4) {
1015+
// Just do a full refresh when 5+ refreshes are queued
1016+
start = 0;
1017+
end = this.rows - 1;
1018+
} else {
1019+
// Get start and end rows that need refreshing
1020+
start = this.refreshRowsQueue[0].start;
1021+
end = this.refreshRowsQueue[0].end;
1022+
for (var i = 1; i < this.refreshRowsQueue.length; i++) {
1023+
if (this.refreshRowsQueue[i].start < start) {
1024+
start = this.refreshRowsQueue[i].start;
1025+
}
1026+
if (this.refreshRowsQueue[i].end > end) {
1027+
end = this.refreshRowsQueue[i].end;
1028+
}
1029+
}
1030+
}
1031+
this.refreshRowsQueue = [];
1032+
this.refresh(start, end);
1033+
}
1034+
window.requestAnimationFrame(this.refreshLoop.bind(this));
1035+
}
1036+
10001037
/**
10011038
* Refreshes (re-renders) terminal content within two rows (inclusive)
10021039
*
@@ -1017,47 +1054,10 @@ Terminal.flags = {
10171054
*
10181055
* @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
10191056
* @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
1020-
* @param {boolean} queue Whether the refresh should ran right now or be queued
10211057
*/
1022-
Terminal.prototype.refresh = function(start, end, queue) {
1058+
Terminal.prototype.refresh = function(start, end) {
10231059
var self = this;
10241060

1025-
// queue defaults to true
1026-
queue = (typeof queue == 'undefined') ? true : queue;
1027-
1028-
/**
1029-
* The refresh queue allows refresh to execute only approximately 30 times a second. For
1030-
* commands that pass a significant amount of output to the write function, this prevents the
1031-
* terminal from maxing out the CPU and making the UI unresponsive. While commands can still
1032-
* run beyond what they do on the terminal, it is far better with a debounce in place as
1033-
* every single terminal manipulation does not need to be constructed in the DOM.
1034-
*
1035-
* A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
1036-
*/
1037-
if (queue) {
1038-
// If refresh should be queued, order the refresh and return.
1039-
if (this._refreshIsQueued) {
1040-
// If a refresh has already been queued, just order a full refresh next
1041-
this._fullRefreshNext = true;
1042-
} else {
1043-
setTimeout(function () {
1044-
self.refresh(start, end, false);
1045-
}, 34)
1046-
this._refreshIsQueued = true;
1047-
}
1048-
return;
1049-
}
1050-
1051-
// If refresh should be run right now (not be queued), release the lock
1052-
this._refreshIsQueued = false;
1053-
1054-
// If multiple refreshes were requested, make a full refresh.
1055-
if (this._fullRefreshNext) {
1056-
start = 0;
1057-
end = this.rows - 1;
1058-
this._fullRefreshNext = false // reset lock
1059-
}
1060-
10611061
var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
10621062

10631063
// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
@@ -1224,7 +1224,7 @@ Terminal.prototype.refresh = function(start, end, queue) {
12241224
Terminal.prototype.showCursor = function() {
12251225
if (!this.cursorState) {
12261226
this.cursorState = 1;
1227-
this.refresh(this.y, this.y);
1227+
this.queueRefresh(this.y, this.y);
12281228
}
12291229
};
12301230

@@ -1311,7 +1311,7 @@ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) {
13111311
this.emit('scroll', this.ydisp);
13121312
}
13131313

1314-
this.refresh(0, this.rows - 1);
1314+
this.queueRefresh(0, this.rows - 1);
13151315
};
13161316

13171317
/**
@@ -2379,7 +2379,7 @@ Terminal.prototype.write = function(data) {
23792379
}
23802380

23812381
this.updateRange(this.y);
2382-
this.refresh(this.refreshStart, this.refreshEnd);
2382+
this.queueRefresh(this.refreshStart, this.refreshEnd);
23832383
};
23842384

23852385
/**
@@ -2945,7 +2945,7 @@ Terminal.prototype.resize = function(x, y) {
29452945
this.scrollTop = 0;
29462946
this.scrollBottom = y - 1;
29472947

2948-
this.refresh(0, this.rows - 1);
2948+
this.queueRefresh(0, this.rows - 1);
29492949

29502950
this.normal = null;
29512951

@@ -3074,7 +3074,7 @@ Terminal.prototype.clear = function() {
30743074
for (var i = 1; i < this.rows; i++) {
30753075
this.lines.push(this.blankLine());
30763076
}
3077-
this.refresh(0, this.rows - 1);
3077+
this.queueRefresh(0, this.rows - 1);
30783078
this.emit('scroll', this.ydisp);
30793079
};
30803080

@@ -3205,7 +3205,7 @@ Terminal.prototype.reset = function() {
32053205
var customKeydownHandler = this.customKeydownHandler;
32063206
Terminal.call(this, this.options);
32073207
this.customKeydownHandler = customKeydownHandler;
3208-
this.refresh(0, this.rows - 1);
3208+
this.queueRefresh(0, this.rows - 1);
32093209
this.viewport.syncScrollArea();
32103210
};
32113211

@@ -4324,7 +4324,7 @@ Terminal.prototype.resetMode = function(params) {
43244324
// this.x = this.savedX;
43254325
// this.y = this.savedY;
43264326
// }
4327-
this.refresh(0, this.rows - 1);
4327+
this.queueRefresh(0, this.rows - 1);
43284328
this.viewport.syncScrollArea();
43294329
this.showCursor();
43304330
}

0 commit comments

Comments
 (0)