Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into 439_wide_char_class
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyriar committed Jan 9, 2017
2 parents 07a8b8f + eb3b3fa commit a1ee8ae
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 82 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js
- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js
- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies.
- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE


Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list.
Expand Down Expand Up @@ -117,6 +118,10 @@ Visit https://lair.io/sourcelair/xterm and follow the instructions. All developm

[Download Visual Studio Code](http://code.visualstudio.com/Download), clone xterm.js and you are all set.

#### [Eclipse Che](http://www.eclipse.org/che)

You can start Eclipse Che with `docker run eclipse/che start`

## License Agreement

If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work.
Expand Down
10 changes: 10 additions & 0 deletions src/Viewport.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { assert } from 'chai';
import { Viewport } from './Viewport';

class MockWindow {
// Disable refreshLoop in test
public requestAnimationFrame() { }
}

describe('Viewport', () => {
let terminal;
let viewportElement;
Expand All @@ -11,6 +16,7 @@ describe('Viewport', () => {
const CHARACTER_HEIGHT = 10;

beforeEach(() => {
(<any>global).window = new MockWindow();
terminal = {
lines: [],
rows: 0,
Expand Down Expand Up @@ -67,10 +73,14 @@ describe('Viewport', () => {
terminal.rows = 1;
assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
viewport.syncScrollArea();
assert.ok(viewport.isRefreshQueued);
viewport.refresh();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
terminal.lines.push('');
viewport.syncScrollArea();
assert.ok(viewport.isRefreshQueued);
viewport.refresh();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
});
Expand Down
20 changes: 17 additions & 3 deletions src/Viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class Viewport {
private currentRowHeight: number;
private lastRecordedBufferLength: number;
private lastRecordedViewportHeight: number;
private isRefreshQueued: boolean;

/**
* Creates a new Viewport.
Expand All @@ -30,12 +31,25 @@ export class Viewport {
this.currentRowHeight = 0;
this.lastRecordedBufferLength = 0;
this.lastRecordedViewportHeight = 0;
this.isRefreshQueued = false;

this.terminal.on('scroll', this.syncScrollArea.bind(this));
this.terminal.on('resize', this.syncScrollArea.bind(this));
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));

this.syncScrollArea();
this.refreshLoop();
}

/**
* Queues a refresh to be done on next animation frame.
*/
private refreshLoop(): void {
if (this.isRefreshQueued) {
this.refresh();
this.isRefreshQueued = false;
}
window.requestAnimationFrame(this.refreshLoop.bind(this));
}

/**
Expand Down Expand Up @@ -68,14 +82,14 @@ export class Viewport {
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
// If buffer height changed
this.lastRecordedBufferLength = this.terminal.lines.length;
this.refresh();
this.isRefreshQueued = true;
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
// If viewport height changed
this.refresh();
this.isRefreshQueued = true;
} else {
// If size has changed, refresh viewport
if (this.charMeasure.height !== this.currentRowHeight) {
this.refresh();
this.isRefreshQueued = true;
}
}

Expand Down
22 changes: 0 additions & 22 deletions src/utils/Browser.js

This file was deleted.

22 changes: 22 additions & 0 deletions src/utils/Browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Attributes and methods to help with identifying the current browser and platform.
* @module xterm/utils/Browser
* @license MIT
*/

import { contains } from './Generic';

const isNode = (typeof navigator === 'undefined') ? true : false;
const userAgent = (isNode) ? 'node' : navigator.userAgent;
const platform = (isNode) ? 'node' : navigator.platform;

export const isFirefox = !!~userAgent.indexOf('Firefox');
export const isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');

// Find the users platform. We use this to interpret the meta key
// and ISO third level shifts.
// http://stackoverflow.com/q/19877924/577598
export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
export const isIpad = platform === 'iPad';
export const isIphone = platform === 'iPhone';
export const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
2 changes: 1 addition & 1 deletion src/utils/Generic.js → src/utils/Generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
* @param {Array} array The array to search for the given element.
* @param {Object} el The element to look for into the array
*/
export let contains = function(arr, el) {
export function contains(arr: any[], el: any) {
return arr.indexOf(el) >= 0;
};
112 changes: 56 additions & 56 deletions src/xterm.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,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 @@ -408,7 +402,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 @@ -590,8 +584,9 @@ Terminal.prototype.open = function(parent) {

this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);

// Draw the screen.
this.refresh(0, this.rows - 1);
// Setup loop that draws to screen
this.queueRefresh(0, this.rows - 1);
this.refreshLoop();

// Initialize global actions that
// need to be taken on the document.
Expand Down Expand Up @@ -1009,6 +1004,48 @@ 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 });
}

/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
Terminal.prototype.refreshLoop = function() {
// Don't refresh if there were no row changes
if (this.refreshRowsQueue.length > 0) {
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;
} 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 = [];
this.refresh(start, end);
}
window.requestAnimationFrame(this.refreshLoop.bind(this));
}

/**
* Refreshes (re-renders) terminal content within two rows (inclusive)
*
Expand All @@ -1029,47 +1066,10 @@ Terminal.flags = {
*
* @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
* @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) {
Terminal.prototype.refresh = function(start, end) {
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;
}
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) {
start = 0;
end = this.rows - 1;
this._fullRefreshNext = false // reset lock
}

var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;

// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
Expand Down Expand Up @@ -1242,7 +1242,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 @@ -1329,7 +1329,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 @@ -2397,7 +2397,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 @@ -2965,7 +2965,7 @@ Terminal.prototype.resize = function(x, y) {

this.charMeasure.measure();

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

this.normal = null;

Expand Down Expand Up @@ -3094,7 +3094,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 @@ -3225,7 +3225,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 @@ -4344,7 +4344,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 a1ee8ae

Please sign in to comment.