diff --git a/README.md b/README.md index aa89c700..4707f195 100644 --- a/README.md +++ b/README.md @@ -85,13 +85,9 @@ The xterm.js team maintains the following addons but they can be built by anyone ## Browser Support -Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions we aim to support: +Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox* and *Safari*. -- Chrome latest -- Edge latest -- Firefox latest -- Safari latest -- IE11 +We also partially support *Intenet Explorer 11*, meaning xterm.js should work for the most part, but we reserve the right to not provide workarounds specifically for it unless it's absolutely necessary to get the basic input/output flow working. Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers, these are the versions we strive to keep working. diff --git a/addons/xterm-addon-attach/package.json b/addons/xterm-addon-attach/package.json index e381730c..1d72b144 100644 --- a/addons/xterm-addon-attach/package.json +++ b/addons/xterm-addon-attach/package.json @@ -1,15 +1,18 @@ { "name": "xterm-addon-attach", - "version": "0.1.0-beta10", + "version": "0.1.0-beta11", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, - "main": "lib/AttachAddon.js", + "main": "lib/xterm-addon-attach.js", "types": "typings/xterm-addon-attach.d.ts", "license": "MIT", "scripts": { - "prepublishOnly": "../../node_modules/.bin/tsc -p src" + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" }, "peerDependencies": { "xterm": "^3.14.0" diff --git a/addons/xterm-addon-attach/src/tsconfig.json b/addons/xterm-addon-attach/src/tsconfig.json index 9a92ab48..d875aa53 100644 --- a/addons/xterm-addon-attach/src/tsconfig.json +++ b/addons/xterm-addon-attach/src/tsconfig.json @@ -7,7 +7,7 @@ "es2015" ], "rootDir": ".", - "outDir": "../lib", + "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true diff --git a/addons/xterm-addon-attach/webpack.config.js b/addons/xterm-addon-attach/webpack.config.js new file mode 100644 index 00000000..65996f19 --- /dev/null +++ b/addons/xterm-addon-attach/webpack.config.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'AttachAddon'; +const mainFile = 'xterm-addon-attach.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd' + }, + mode: 'production' +}; diff --git a/addons/xterm-addon-fit/package.json b/addons/xterm-addon-fit/package.json index a29f3199..4ee8e1b7 100644 --- a/addons/xterm-addon-fit/package.json +++ b/addons/xterm-addon-fit/package.json @@ -1,15 +1,18 @@ { "name": "xterm-addon-fit", - "version": "0.1.0-beta2", + "version": "0.1.0-beta3", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, - "main": "lib/FitAddon.js", + "main": "lib/xterm-addon-fit.js", "types": "typings/xterm-addon-fit.d.ts", "license": "MIT", "scripts": { - "prepublishOnly": "../../node_modules/.bin/tsc -p src" + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" }, "peerDependencies": { "xterm": "^3.14.0" diff --git a/addons/xterm-addon-fit/src/tsconfig.json b/addons/xterm-addon-fit/src/tsconfig.json index 6b914e81..5539aa56 100644 --- a/addons/xterm-addon-fit/src/tsconfig.json +++ b/addons/xterm-addon-fit/src/tsconfig.json @@ -7,7 +7,7 @@ "es2015" ], "rootDir": ".", - "outDir": "../lib", + "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true diff --git a/addons/xterm-addon-fit/webpack.config.js b/addons/xterm-addon-fit/webpack.config.js new file mode 100644 index 00000000..4b542150 --- /dev/null +++ b/addons/xterm-addon-fit/webpack.config.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'FitAddon'; +const mainFile = 'xterm-addon-fit.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd' + }, + mode: 'production' +}; diff --git a/addons/xterm-addon-search/package.json b/addons/xterm-addon-search/package.json index 38f76add..02cb6695 100644 --- a/addons/xterm-addon-search/package.json +++ b/addons/xterm-addon-search/package.json @@ -1,15 +1,18 @@ { "name": "xterm-addon-search", - "version": "0.1.0-beta5", + "version": "0.1.0-beta6", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, - "main": "lib/SearchAddon.js", + "main": "lib/xterm-addon-search.js", "types": "typings/xterm-addon-search.d.ts", "license": "MIT", "scripts": { - "prepublishOnly": "../../node_modules/.bin/tsc -p src" + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" }, "peerDependencies": { "xterm": "^3.14.0" diff --git a/addons/xterm-addon-search/src/tsconfig.json b/addons/xterm-addon-search/src/tsconfig.json index 58a4bacf..1cb6d4d7 100644 --- a/addons/xterm-addon-search/src/tsconfig.json +++ b/addons/xterm-addon-search/src/tsconfig.json @@ -7,7 +7,7 @@ "es6", ], "rootDir": ".", - "outDir": "../lib", + "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true diff --git a/addons/xterm-addon-search/webpack.config.js b/addons/xterm-addon-search/webpack.config.js new file mode 100644 index 00000000..726dceb6 --- /dev/null +++ b/addons/xterm-addon-search/webpack.config.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'SearchAddon'; +const mainFile = 'xterm-addon-search.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd' + }, + mode: 'production' +}; diff --git a/addons/xterm-addon-web-links/package.json b/addons/xterm-addon-web-links/package.json index 82edbe9b..a764a200 100644 --- a/addons/xterm-addon-web-links/package.json +++ b/addons/xterm-addon-web-links/package.json @@ -1,15 +1,18 @@ { "name": "xterm-addon-web-links", - "version": "0.1.0-beta9", + "version": "0.1.0-beta10", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, - "main": "lib/WebLinksAddon.js", + "main": "lib/xterm-addon-web-links.js", "types": "typings/xterm-addon-web-links.d.ts", "license": "MIT", "scripts": { - "prepublishOnly": "../../node_modules/.bin/tsc -p src" + "build": "../../node_modules/.bin/tsc -p src", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package" }, "peerDependencies": { "xterm": "^3.14.0" diff --git a/addons/xterm-addon-web-links/src/tsconfig.json b/addons/xterm-addon-web-links/src/tsconfig.json index 6b914e81..5539aa56 100644 --- a/addons/xterm-addon-web-links/src/tsconfig.json +++ b/addons/xterm-addon-web-links/src/tsconfig.json @@ -7,7 +7,7 @@ "es2015" ], "rootDir": ".", - "outDir": "../lib", + "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true diff --git a/addons/xterm-addon-web-links/webpack.config.js b/addons/xterm-addon-web-links/webpack.config.js new file mode 100644 index 00000000..fd87d07e --- /dev/null +++ b/addons/xterm-addon-web-links/webpack.config.js @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'WebLinksAddon'; +const mainFile = 'xterm-addon-web-links.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd' + }, + mode: 'production' +}; diff --git a/demo/client.ts b/demo/client.ts index f1626c4f..e0d55d57 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -9,13 +9,17 @@ // Use tsc version (yarn watch) import { Terminal } from '../out/public/Terminal'; +import { AttachAddon } from '../addons/xterm-addon-attach/out/AttachAddon'; +import { FitAddon } from '../addons/xterm-addon-fit/out/FitAddon'; +import { SearchAddon, ISearchOptions } from '../addons/xterm-addon-search/out/SearchAddon'; +import { WebLinksAddon } from '../addons/xterm-addon-web-links/out/WebLinksAddon'; + // Use webpacked version (yarn package) // import { Terminal } from '../lib/xterm'; - -import { AttachAddon } from 'xterm-addon-attach'; -import { FitAddon } from 'xterm-addon-fit'; -import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; -import { WebLinksAddon } from 'xterm-addon-web-links'; +// import { AttachAddon } from 'xterm-addon-attach'; +// import { FitAddon } from 'xterm-addon-fit'; +// import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; +// import { WebLinksAddon } from 'xterm-addon-web-links'; // Pulling in the module's types relies on the above, it's looks a // little weird here as we're importing "this" module diff --git a/package.json b/package.json index 16ebca92..d291f147 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "Full xterm terminal, in your browser", "version": "3.14.0", "main": "lib/xterm.js", + "style": "css/xterm.css", "types": "typings/xterm.d.ts", "repository": "https://github.com/xtermjs/xterm.js", "license": "MIT", diff --git a/src/Clipboard.ts b/src/Clipboard.ts index cbb0935e..9461afa1 100644 --- a/src/Clipboard.ts +++ b/src/Clipboard.ts @@ -5,15 +5,6 @@ import { ITerminal, ISelectionManager } from './Types'; -interface IWindow extends Window { - clipboardData?: { - getData(format: string): string; - setData(format: string, data: string): void; - }; -} - -declare var window: IWindow; - /** * Prepares text to be pasted into the terminal by normalizing the line endings * @param text The pasted text that needs processing before inserting into the terminal @@ -38,12 +29,7 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): * @param ev The original copy event to be handled */ export function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager): void { - if (term.browser.isMSIE) { - window.clipboardData.setData('Text', selectionManager.selectionText); - } else { - ev.clipboardData.setData('text/plain', selectionManager.selectionText); - } - + ev.clipboardData.setData('text/plain', selectionManager.selectionText); // Prevent or the original text will be copied. ev.preventDefault(); } @@ -66,16 +52,9 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void { term.cancel(ev); }; - if (term.browser.isMSIE) { - if (window.clipboardData) { - text = window.clipboardData.getData('Text'); - dispatchPaste(text); - } - } else { - if (ev.clipboardData) { - text = ev.clipboardData.getData('text/plain'); - dispatchPaste(text); - } + if (ev.clipboardData) { + text = ev.clipboardData.getData('text/plain'); + dispatchPaste(text); } } diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 3e5cef57..8170796f 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -8,7 +8,10 @@ import { InputHandler } from './InputHandler'; import { MockInputHandlingTerminal, TestTerminal } from './TestUtils.test'; import { Terminal } from './Terminal'; import { IBufferLine } from 'common/Types'; -import { CellData, Attributes, AttributeData, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; +import { Attributes } from 'common/buffer/Constants'; +import { AttributeData } from 'common/buffer/AttributeData'; describe('InputHandler', () => { describe('save and restore cursor', () => { diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 60693994..a2ae4e08 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -13,9 +13,12 @@ import { IDisposable } from 'xterm'; import { Disposable } from 'common/Lifecycle'; import { concat } from 'common/TypedArrayUtils'; import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder'; -import { CellData, Attributes, FgFlags, BgFlags, AttributeData, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags } from 'common/buffer/Constants'; +import { CellData } from 'common/buffer/CellData'; +import { AttributeData } from 'common/buffer/AttributeData'; /** * Map collect to glevel. Used in `selectCharset`. diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index 8b7e71a0..1ccae7fc 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -9,7 +9,8 @@ import { IBufferLine } from 'common/Types'; import { Linkifier } from './Linkifier'; import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test'; import { CircularList } from 'common/CircularList'; -import { BufferLine, CellData } from 'common/buffer/BufferLine'; +import { BufferLine } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; class TestLinkifier extends Linkifier { constructor(terminal: ITerminal) { diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index 5dc692af..f58815f0 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -12,9 +12,10 @@ import { IBuffer } from 'common/buffer/Types'; import { IBufferLine } from 'common/Types'; import { MockTerminal } from './TestUtils.test'; import { MockOptionsService, MockBufferService } from 'common/TestUtils.test'; -import { BufferLine, CellData } from 'common/buffer/BufferLine'; +import { BufferLine } from 'common/buffer/BufferLine'; import { IBufferService } from 'common/services/Services'; import { MockCharSizeService } from 'browser/TestUtils.test'; +import { CellData } from 'common/buffer/CellData'; class TestMockTerminal extends MockTerminal { emit(event: string, data: any): void {} diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 4b6a0de0..05beaeab 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -10,7 +10,7 @@ import { MouseHelper } from 'browser/input/MouseHelper'; import * as Browser from 'common/Platform'; import { SelectionModel } from './SelectionModel'; import { AltClickHandler } from './handlers/AltClickHandler'; -import { CellData } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; import { IDisposable } from 'xterm'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 36886da1..f3f7fc58 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -6,7 +6,8 @@ import { assert, expect } from 'chai'; import { Terminal } from './Terminal'; import { MockViewport, MockCompositionHelper, MockRenderer } from './TestUtils.test'; -import { CellData, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/Terminal.ts b/src/Terminal.ts index d6093316..42cacf6f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -44,7 +44,7 @@ import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent, KeyboardResultType, ICharset, IBufferLine, IAttributeData } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { Attributes, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; @@ -55,6 +55,7 @@ import { CharSizeService } from 'browser/services/CharSizeService'; import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { Disposable } from 'common/Lifecycle'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; +import { Attributes } from 'common/buffer/Constants'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -700,7 +701,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp */ private _setTheme(theme: ITheme): void { this._theme = theme; - this._colorManager.setTheme(theme); + if (this._colorManager) { + this._colorManager.setTheme(theme); + } if (this._renderService) { this._renderService.setColors(this._colorManager.colors); } @@ -890,10 +893,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp : ev.which !== null && ev.which !== undefined ? ev.which - 1 : null; - - if (Browser.isMSIE) { - button = button === 1 ? 0 : button === 4 ? 1 : button; - } break; case 'mouseup': button = 3; diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 0fc2f461..c8164c54 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -12,7 +12,8 @@ import * as path from 'path'; import * as pty from 'node-pty'; import { Terminal } from './Terminal'; import { IViewport } from './Types'; -import { CellData, WHITESPACE_CELL_CHAR } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; +import { WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; class TestTerminal extends Terminal { innerWrite(): void { this._innerWrite(); } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 6b92bf0b..257babe8 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -11,7 +11,7 @@ import { Buffer } from 'common/buffer/Buffer'; import * as Browser from 'common/Platform'; import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm'; import { Terminal } from './Terminal'; -import { AttributeData } from 'common/buffer/BufferLine'; +import { AttributeData } from 'common/buffer/AttributeData'; import { IColorManager, IColorSet, IMouseHelper } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { EventEmitter } from 'common/EventEmitter'; diff --git a/src/Types.ts b/src/Types.d.ts similarity index 98% rename from src/Types.ts rename to src/Types.d.ts index a3435614..6f587931 100644 --- a/src/Types.ts +++ b/src/Types.d.ts @@ -5,7 +5,7 @@ import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition } from 'xterm'; import { ICharset, IAttributeData, CharData } from 'common/Types'; -import { IEvent, EventEmitter } from 'common/EventEmitter'; +import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IColorSet, IMouseHelper } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; @@ -54,8 +54,8 @@ export interface IInputHandlingTerminal { viewport: IViewport; selectionManager: ISelectionManager; - onA11yCharEmitter: EventEmitter; - onA11yTabEmitter: EventEmitter; + onA11yCharEmitter: IEventEmitter; + onA11yTabEmitter: IEventEmitter; bell(): void; focus(): void; @@ -365,7 +365,6 @@ export interface IBrowser { userAgent: string; platform: string; isFirefox: boolean; - isMSIE: boolean; isMac: boolean; isIpad: boolean; isIphone: boolean; diff --git a/src/WindowsMode.ts b/src/WindowsMode.ts index cee31de1..33b4972b 100644 --- a/src/WindowsMode.ts +++ b/src/WindowsMode.ts @@ -5,7 +5,7 @@ import { IDisposable } from 'xterm'; import { ITerminal } from './Types'; -import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'common/buffer/BufferLine'; +import { CHAR_DATA_CODE_INDEX, NULL_CELL_CODE, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; export function applyWindowsMode(terminal: ITerminal): IDisposable { // Winpty does not support wraparound mode which means that lines will never diff --git a/src/browser/Types.ts b/src/browser/Types.d.ts similarity index 100% rename from src/browser/Types.ts rename to src/browser/Types.d.ts diff --git a/src/browser/renderer/Types.ts b/src/browser/renderer/Types.d.ts similarity index 100% rename from src/browser/renderer/Types.ts rename to src/browser/renderer/Types.d.ts diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 5035ed3d..00f3a769 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -43,7 +43,7 @@ export class RenderService extends Disposable implements IRenderService { this.register(this._renderDebouncer); this._screenDprMonitor = new ScreenDprMonitor(); - this._screenDprMonitor.setListener(() => this._renderer.onDevicePixelRatioChange()); + this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange()); this.register(this._screenDprMonitor); this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged())); @@ -51,7 +51,7 @@ export class RenderService extends Disposable implements IRenderService { // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. - this.register(addDisposableDomListener(window, 'resize', () => this._renderer.onDevicePixelRatioChange())); + this.register(addDisposableDomListener(window, 'resize', () => this.onDevicePixelRatioChange())); // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so @@ -122,6 +122,7 @@ export class RenderService extends Disposable implements IRenderService { public onDevicePixelRatioChange(): void { this._renderer.onDevicePixelRatioChange(); + this.refreshRows(0, this._rowCount); } public onResize(cols: number, rows: number): void { diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts index efc101ce..34ac190f 100644 --- a/src/common/EventEmitter.ts +++ b/src/common/EventEmitter.ts @@ -13,7 +13,12 @@ export interface IEvent { (listener: (e: T) => any): IDisposable; } -export class EventEmitter { +export interface IEventEmitter { + event: IEvent; + fire(data: T): void; +} + +export class EventEmitter implements IEventEmitter { private _listeners: IListener[] = []; private _event?: IEvent; diff --git a/src/common/Platform.ts b/src/common/Platform.ts index ee82cff4..87d466c0 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -19,7 +19,6 @@ const platform = (isNode) ? 'node' : navigator.platform; export const isFirefox = !!~userAgent.indexOf('Firefox'); export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent); -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. diff --git a/src/common/Types.ts b/src/common/Types.d.ts similarity index 87% rename from src/common/Types.ts rename to src/common/Types.d.ts index b29515d4..97bd6029 100644 --- a/src/common/Types.ts +++ b/src/common/Types.d.ts @@ -3,22 +3,13 @@ * @license MIT */ -import { IEvent, EventEmitter } from 'common/EventEmitter'; +import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IDeleteEvent, IInsertEvent } from 'common/CircularList'; -export const DEFAULT_COLOR = 256; - export interface IDisposable { dispose(): void; } -export interface IEventEmitter { - on(type: string, listener: (...args: any[]) => void): void; - off(type: string, listener: (...args: any[]) => void): void; - emit(type: string, data?: any): void; - addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; -} - export type XtermListener = (...args: any[]) => void; /** @@ -40,11 +31,11 @@ export interface ICircularList { maxLength: number; isFull: boolean; - onDeleteEmitter: EventEmitter; + onDeleteEmitter: IEventEmitter; onDelete: IEvent; - onInsertEmitter: EventEmitter; + onInsertEmitter: IEventEmitter; onInsert: IEvent; - onTrimEmitter: EventEmitter; + onTrimEmitter: IEventEmitter; onTrim: IEvent; get(index: number): T | undefined; diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts new file mode 100644 index 00000000..0e7e2705 --- /dev/null +++ b/src/common/buffer/AttributeData.ts @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IAttributeData, IColorRGB } from 'common/Types'; +import { Attributes, FgFlags, BgFlags } from 'common/buffer/Constants'; + +export class AttributeData implements IAttributeData { + static toColorRGB(value: number): IColorRGB { + return [ + value >>> Attributes.RED_SHIFT & 255, + value >>> Attributes.GREEN_SHIFT & 255, + value & 255 + ]; + } + static fromColorRGB(value: IColorRGB): number { + return (value[0] & 255) << Attributes.RED_SHIFT | (value[1] & 255) << Attributes.GREEN_SHIFT | value[2] & 255; + } + + public clone(): IAttributeData { + const newObj = new AttributeData(); + newObj.fg = this.fg; + newObj.bg = this.bg; + return newObj; + } + + // data + public fg: number = 0; + public bg: number = 0; + + // flags + public isInverse(): number { return this.fg & FgFlags.INVERSE; } + public isBold(): number { return this.fg & FgFlags.BOLD; } + public isUnderline(): number { return this.fg & FgFlags.UNDERLINE; } + public isBlink(): number { return this.fg & FgFlags.BLINK; } + public isInvisible(): number { return this.fg & FgFlags.INVISIBLE; } + public isItalic(): number { return this.bg & BgFlags.ITALIC; } + public isDim(): number { return this.bg & BgFlags.DIM; } + + // color modes + public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; } + public getBgColorMode(): number { return this.bg & Attributes.CM_MASK; } + public isFgRGB(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_RGB; } + public isBgRGB(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_RGB; } + public isFgPalette(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.fg & Attributes.CM_MASK) === Attributes.CM_P256; } + public isBgPalette(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.bg & Attributes.CM_MASK) === Attributes.CM_P256; } + public isFgDefault(): boolean { return (this.fg & Attributes.CM_MASK) === 0; } + public isBgDefault(): boolean { return (this.bg & Attributes.CM_MASK) === 0; } + + // colors + public getFgColor(): number { + switch (this.fg & Attributes.CM_MASK) { + case Attributes.CM_P16: + case Attributes.CM_P256: return this.fg & Attributes.PCOLOR_MASK; + case Attributes.CM_RGB: return this.fg & Attributes.RGB_MASK; + default: return -1; // CM_DEFAULT defaults to -1 + } + } + public getBgColor(): number { + switch (this.bg & Attributes.CM_MASK) { + case Attributes.CM_P16: + case Attributes.CM_P256: return this.bg & Attributes.PCOLOR_MASK; + case Attributes.CM_RGB: return this.bg & Attributes.RGB_MASK; + default: return -1; // CM_DEFAULT defaults to -1 + } + } +} diff --git a/src/common/buffer/Buffer.test.ts b/src/common/buffer/Buffer.test.ts index 01516b3c..af5abeca 100644 --- a/src/common/buffer/Buffer.test.ts +++ b/src/common/buffer/Buffer.test.ts @@ -7,7 +7,8 @@ import { assert } from 'chai'; import { Buffer } from 'common/buffer/Buffer'; import { CircularList } from 'common/CircularList'; import { MockOptionsService, MockBufferService } from 'common/TestUtils.test'; -import { BufferLine, CellData, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; const INIT_COLS = 80; const INIT_ROWS = 24; diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index a30274b6..084b1bea 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -6,7 +6,9 @@ import { CircularList, IInsertEvent } from 'common/CircularList'; import { IBuffer, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from 'common/buffer/Types'; import { IBufferLine, ICellData, IAttributeData } from 'common/Types'; -import { BufferLine, CellData, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; +import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from 'common/buffer/Constants'; import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths, getWrappedLineTrimmedLength } from 'common/buffer/BufferReflow'; import { Marker } from 'common/buffer/Marker'; import { IOptionsService, IBufferService } from 'common/services/Services'; diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index c42b372e..ae80aa16 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -3,7 +3,9 @@ * @license MIT */ import * as chai from 'chai'; -import { BufferLine, CellData, Content, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR } from './BufferLine'; +import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content } from 'common/buffer/Constants'; +import { BufferLine } from 'common/buffer//BufferLine'; +import { CellData } from 'common/buffer/CellData'; import { CharData, IBufferLine } from '../Types'; class TestBufferLine extends BufferLine { diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index eb0a5e99..8e742be3 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -2,33 +2,12 @@ * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ -import { DEFAULT_COLOR, CharData, IBufferLine, ICellData, IColorRGB, IAttributeData } from 'common/Types'; + +import { CharData, IBufferLine, ICellData } from 'common/Types'; import { stringFromCodePoint } from 'common/input/TextDecoder'; - -export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0); - -export const CHAR_DATA_ATTR_INDEX = 0; -export const CHAR_DATA_CHAR_INDEX = 1; -export const CHAR_DATA_WIDTH_INDEX = 2; -export const CHAR_DATA_CODE_INDEX = 3; - -/** - * Null cell - a real empty cell (containing nothing). - * Note that code should always be 0 for a null cell as - * several test condition of the buffer line rely on this. - */ -export const NULL_CELL_CHAR = ''; -export const NULL_CELL_WIDTH = 1; -export const NULL_CELL_CODE = 0; - -/** - * Whitespace cell. - * This is meant as a replacement for empty cells when needed - * during rendering lines to preserve correct aligment. - */ -export const WHITESPACE_CELL_CHAR = ' '; -export const WHITESPACE_CELL_WIDTH = 1; -export const WHITESPACE_CELL_CODE = 32; +import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; +import { CellData } from 'common/buffer/CellData'; +import { AttributeData } from 'common/buffer/AttributeData'; /** * buffer memory layout: @@ -56,258 +35,8 @@ const enum Cell { BG = 2 // currently unused } -/** - * Bitmasks for accessing data in `content`. - */ -export const enum Content { - /** - * bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken) - * read: `codepoint = content & Content.codepointMask;` - * write: `content |= codepoint & Content.codepointMask;` - * shortcut if precondition `codepoint <= 0x10FFFF` is met: - * `content |= codepoint;` - */ - CODEPOINT_MASK = 0x1FFFFF, - - /** - * bit 22 flag indication whether a cell contains combined content - * read: `isCombined = content & Content.isCombined;` - * set: `content |= Content.isCombined;` - * clear: `content &= ~Content.isCombined;` - */ - IS_COMBINED_MASK = 0x200000, // 1 << 21 - - /** - * bit 1..22 mask to check whether a cell contains any string data - * we need to check for codepoint and isCombined bits to see - * whether a cell contains anything - * read: `isEmpty = !(content & Content.hasContent)` - */ - HAS_CONTENT_MASK = 0x3FFFFF, - - /** - * bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2) - * read: `width = (content & Content.widthMask) >> Content.widthShift;` - * `hasWidth = content & Content.widthMask;` - * as long as wcwidth is highest value in `content`: - * `width = content >> Content.widthShift;` - * write: `content |= (width << Content.widthShift) & Content.widthMask;` - * shortcut if precondition `0 <= width <= 3` is met: - * `content |= width << Content.widthShift;` - */ - WIDTH_MASK = 0xC00000, // 3 << 22 - WIDTH_SHIFT = 22 -} - - -export const enum Attributes { - /** - * bit 1..8 blue in RGB, color in P256 and P16 - */ - BLUE_MASK = 0xFF, - BLUE_SHIFT = 0, - PCOLOR_MASK = 0xFF, - PCOLOR_SHIFT = 0, - - /** - * bit 9..16 green in RGB - */ - GREEN_MASK = 0xFF00, - GREEN_SHIFT = 8, - - /** - * bit 17..24 red in RGB - */ - RED_MASK = 0xFF0000, - RED_SHIFT = 16, - - /** - * bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) - */ - CM_MASK = 0x3000000, - CM_DEFAULT = 0, - CM_P16 = 0x1000000, - CM_P256 = 0x2000000, - CM_RGB = 0x3000000, - - /** - * bit 1..24 RGB room - */ - RGB_MASK = 0xFFFFFF -} - -export const enum FgFlags { - /** - * bit 27..31 (32th bit unused) - */ - INVERSE = 0x4000000, - BOLD = 0x8000000, - UNDERLINE = 0x10000000, - BLINK = 0x20000000, - INVISIBLE = 0x40000000 -} - -export const enum BgFlags { - /** - * bit 27..32 (upper 4 unused) - */ - ITALIC = 0x4000000, - DIM = 0x8000000 -} - -export class AttributeData implements IAttributeData { - static toColorRGB(value: number): IColorRGB { - return [ - value >>> Attributes.RED_SHIFT & 255, - value >>> Attributes.GREEN_SHIFT & 255, - value & 255 - ]; - } - static fromColorRGB(value: IColorRGB): number { - return (value[0] & 255) << Attributes.RED_SHIFT | (value[1] & 255) << Attributes.GREEN_SHIFT | value[2] & 255; - } - - public clone(): IAttributeData { - const newObj = new AttributeData(); - newObj.fg = this.fg; - newObj.bg = this.bg; - return newObj; - } - - // data - public fg: number = 0; - public bg: number = 0; - - // flags - public isInverse(): number { return this.fg & FgFlags.INVERSE; } - public isBold(): number { return this.fg & FgFlags.BOLD; } - public isUnderline(): number { return this.fg & FgFlags.UNDERLINE; } - public isBlink(): number { return this.fg & FgFlags.BLINK; } - public isInvisible(): number { return this.fg & FgFlags.INVISIBLE; } - public isItalic(): number { return this.bg & BgFlags.ITALIC; } - public isDim(): number { return this.bg & BgFlags.DIM; } - - // color modes - public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; } - public getBgColorMode(): number { return this.bg & Attributes.CM_MASK; } - public isFgRGB(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_RGB; } - public isBgRGB(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_RGB; } - public isFgPalette(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.fg & Attributes.CM_MASK) === Attributes.CM_P256; } - public isBgPalette(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.bg & Attributes.CM_MASK) === Attributes.CM_P256; } - public isFgDefault(): boolean { return (this.fg & Attributes.CM_MASK) === 0; } - public isBgDefault(): boolean { return (this.bg & Attributes.CM_MASK) === 0; } - - // colors - public getFgColor(): number { - switch (this.fg & Attributes.CM_MASK) { - case Attributes.CM_P16: - case Attributes.CM_P256: return this.fg & Attributes.PCOLOR_MASK; - case Attributes.CM_RGB: return this.fg & Attributes.RGB_MASK; - default: return -1; // CM_DEFAULT defaults to -1 - } - } - public getBgColor(): number { - switch (this.bg & Attributes.CM_MASK) { - case Attributes.CM_P16: - case Attributes.CM_P256: return this.bg & Attributes.PCOLOR_MASK; - case Attributes.CM_RGB: return this.bg & Attributes.RGB_MASK; - default: return -1; // CM_DEFAULT defaults to -1 - } - } -} - export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData()); -/** - * CellData - represents a single Cell in the terminal buffer. - */ -export class CellData extends AttributeData implements ICellData { - - /** Helper to create CellData from CharData. */ - public static fromCharData(value: CharData): CellData { - const obj = new CellData(); - obj.setFromCharData(value); - return obj; - } - - /** Primitives from terminal buffer. */ - public content: number = 0; - public fg: number = 0; - public bg: number = 0; - public combinedData: string = ''; - - /** Whether cell contains a combined string. */ - public isCombined(): number { - return this.content & Content.IS_COMBINED_MASK; - } - - /** Width of the cell. */ - public getWidth(): number { - return this.content >> Content.WIDTH_SHIFT; - } - - /** JS string of the content. */ - public getChars(): string { - if (this.content & Content.IS_COMBINED_MASK) { - return this.combinedData; - } - if (this.content & Content.CODEPOINT_MASK) { - return stringFromCodePoint(this.content & Content.CODEPOINT_MASK); - } - return ''; - } - - /** - * Codepoint of cell - * Note this returns the UTF32 codepoint of single chars, - * if content is a combined string it returns the codepoint - * of the last char in string to be in line with code in CharData. - * */ - public getCode(): number { - return (this.isCombined()) - ? this.combinedData.charCodeAt(this.combinedData.length - 1) - : this.content & Content.CODEPOINT_MASK; - } - - /** Set data from CharData */ - public setFromCharData(value: CharData): void { - this.fg = value[CHAR_DATA_ATTR_INDEX]; - this.bg = 0; - let combined = false; - - // surrogates and combined strings need special treatment - if (value[CHAR_DATA_CHAR_INDEX].length > 2) { - combined = true; - } else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { - const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); - // if the 2-char string is a surrogate create single codepoint - // everything else is combined - if (0xD800 <= code && code <= 0xDBFF) { - const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); - if (0xDC00 <= second && second <= 0xDFFF) { - this.content = ((code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); - } else { - combined = true; - } - } else { - combined = true; - } - } else { - this.content = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); - } - if (combined) { - this.combinedData = value[CHAR_DATA_CHAR_INDEX]; - this.content = Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); - } - } - - /** Get data as CharData. */ - public getAsCharData(): CharData { - return [this.fg, this.getChars(), this.getWidth(), this.getCode()]; - } -} - - /** * Typed array based bufferline implementation. * diff --git a/src/common/buffer/BufferReflow.test.ts b/src/common/buffer/BufferReflow.test.ts index af908572..b351b89c 100644 --- a/src/common/buffer/BufferReflow.test.ts +++ b/src/common/buffer/BufferReflow.test.ts @@ -3,7 +3,8 @@ * @license MIT */ import { assert } from 'chai'; -import { BufferLine, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from 'common/buffer/BufferLine'; +import { BufferLine } from 'common/buffer/BufferLine'; +import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from 'common/buffer/Constants'; import { reflowSmallerGetNewLineLengths } from 'common/buffer/BufferReflow'; describe('BufferReflow', () => { diff --git a/src/common/buffer/CellData.ts b/src/common/buffer/CellData.ts new file mode 100644 index 00000000..21ad2ee5 --- /dev/null +++ b/src/common/buffer/CellData.ts @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { CharData, ICellData } from 'common/Types'; +import { stringFromCodePoint } from 'common/input/TextDecoder'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from 'common/buffer/Constants'; +import { AttributeData } from 'common/buffer/AttributeData'; + +/** + * CellData - represents a single Cell in the terminal buffer. + */ +export class CellData extends AttributeData implements ICellData { + /** Helper to create CellData from CharData. */ + public static fromCharData(value: CharData): CellData { + const obj = new CellData(); + obj.setFromCharData(value); + return obj; + } + /** Primitives from terminal buffer. */ + public content: number = 0; + public fg: number = 0; + public bg: number = 0; + public combinedData: string = ''; + /** Whether cell contains a combined string. */ + public isCombined(): number { + return this.content & Content.IS_COMBINED_MASK; + } + /** Width of the cell. */ + public getWidth(): number { + return this.content >> Content.WIDTH_SHIFT; + } + /** JS string of the content. */ + public getChars(): string { + if (this.content & Content.IS_COMBINED_MASK) { + return this.combinedData; + } + if (this.content & Content.CODEPOINT_MASK) { + return stringFromCodePoint(this.content & Content.CODEPOINT_MASK); + } + return ''; + } + /** + * Codepoint of cell + * Note this returns the UTF32 codepoint of single chars, + * if content is a combined string it returns the codepoint + * of the last char in string to be in line with code in CharData. + * */ + public getCode(): number { + return (this.isCombined()) + ? this.combinedData.charCodeAt(this.combinedData.length - 1) + : this.content & Content.CODEPOINT_MASK; + } + /** Set data from CharData */ + public setFromCharData(value: CharData): void { + this.fg = value[CHAR_DATA_ATTR_INDEX]; + this.bg = 0; + let combined = false; + // surrogates and combined strings need special treatment + if (value[CHAR_DATA_CHAR_INDEX].length > 2) { + combined = true; + } + else if (value[CHAR_DATA_CHAR_INDEX].length === 2) { + const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0); + // if the 2-char string is a surrogate create single codepoint + // everything else is combined + if (0xD800 <= code && code <= 0xDBFF) { + const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1); + if (0xDC00 <= second && second <= 0xDFFF) { + this.content = ((code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + } + else { + combined = true; + } + } + else { + combined = true; + } + } + else { + this.content = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + } + if (combined) { + this.combinedData = value[CHAR_DATA_CHAR_INDEX]; + this.content = Content.IS_COMBINED_MASK | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT); + } + } + /** Get data as CharData. */ + public getAsCharData(): CharData { + return [this.fg, this.getChars(), this.getWidth(), this.getCode()]; + } +} diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts new file mode 100644 index 00000000..276a5c54 --- /dev/null +++ b/src/common/buffer/Constants.ts @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export const DEFAULT_COLOR = 256; +export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0); + +export const CHAR_DATA_ATTR_INDEX = 0; +export const CHAR_DATA_CHAR_INDEX = 1; +export const CHAR_DATA_WIDTH_INDEX = 2; +export const CHAR_DATA_CODE_INDEX = 3; + +/** + * Null cell - a real empty cell (containing nothing). + * Note that code should always be 0 for a null cell as + * several test condition of the buffer line rely on this. + */ +export const NULL_CELL_CHAR = ''; +export const NULL_CELL_WIDTH = 1; +export const NULL_CELL_CODE = 0; + +/** + * Whitespace cell. + * This is meant as a replacement for empty cells when needed + * during rendering lines to preserve correct aligment. + */ +export const WHITESPACE_CELL_CHAR = ' '; +export const WHITESPACE_CELL_WIDTH = 1; +export const WHITESPACE_CELL_CODE = 32; + +/** + * Bitmasks for accessing data in `content`. + */ +export const enum Content { + /** + * bit 1..21 codepoint, max allowed in UTF32 is 0x10FFFF (21 bits taken) + * read: `codepoint = content & Content.codepointMask;` + * write: `content |= codepoint & Content.codepointMask;` + * shortcut if precondition `codepoint <= 0x10FFFF` is met: + * `content |= codepoint;` + */ + CODEPOINT_MASK = 0x1FFFFF, + + /** + * bit 22 flag indication whether a cell contains combined content + * read: `isCombined = content & Content.isCombined;` + * set: `content |= Content.isCombined;` + * clear: `content &= ~Content.isCombined;` + */ + IS_COMBINED_MASK = 0x200000, // 1 << 21 + + /** + * bit 1..22 mask to check whether a cell contains any string data + * we need to check for codepoint and isCombined bits to see + * whether a cell contains anything + * read: `isEmpty = !(content & Content.hasContent)` + */ + HAS_CONTENT_MASK = 0x3FFFFF, + + /** + * bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2) + * read: `width = (content & Content.widthMask) >> Content.widthShift;` + * `hasWidth = content & Content.widthMask;` + * as long as wcwidth is highest value in `content`: + * `width = content >> Content.widthShift;` + * write: `content |= (width << Content.widthShift) & Content.widthMask;` + * shortcut if precondition `0 <= width <= 3` is met: + * `content |= width << Content.widthShift;` + */ + WIDTH_MASK = 0xC00000, // 3 << 22 + WIDTH_SHIFT = 22 +} + +export const enum Attributes { + /** + * bit 1..8 blue in RGB, color in P256 and P16 + */ + BLUE_MASK = 0xFF, + BLUE_SHIFT = 0, + PCOLOR_MASK = 0xFF, + PCOLOR_SHIFT = 0, + + /** + * bit 9..16 green in RGB + */ + GREEN_MASK = 0xFF00, + GREEN_SHIFT = 8, + + /** + * bit 17..24 red in RGB + */ + RED_MASK = 0xFF0000, + RED_SHIFT = 16, + + /** + * bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) + */ + CM_MASK = 0x3000000, + CM_DEFAULT = 0, + CM_P16 = 0x1000000, + CM_P256 = 0x2000000, + CM_RGB = 0x3000000, + + /** + * bit 1..24 RGB room + */ + RGB_MASK = 0xFFFFFF +} + +export const enum FgFlags { + /** + * bit 27..31 (32th bit unused) + */ + INVERSE = 0x4000000, + BOLD = 0x8000000, + UNDERLINE = 0x10000000, + BLINK = 0x20000000, + INVISIBLE = 0x40000000 +} + +export const enum BgFlags { + /** + * bit 27..32 (upper 4 unused) + */ + ITALIC = 0x4000000, + DIM = 0x8000000 +} diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.d.ts similarity index 100% rename from src/common/buffer/Types.ts rename to src/common/buffer/Types.d.ts diff --git a/src/common/parser/Constants.ts b/src/common/parser/Constants.ts new file mode 100644 index 00000000..55e4a005 --- /dev/null +++ b/src/common/parser/Constants.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Internal states of EscapeSequenceParser. + */ +export const enum ParserState { + GROUND = 0, + ESCAPE = 1, + ESCAPE_INTERMEDIATE = 2, + CSI_ENTRY = 3, + CSI_PARAM = 4, + CSI_INTERMEDIATE = 5, + CSI_IGNORE = 6, + SOS_PM_APC_STRING = 7, + OSC_STRING = 8, + DCS_ENTRY = 9, + DCS_PARAM = 10, + DCS_IGNORE = 11, + DCS_INTERMEDIATE = 12, + DCS_PASSTHROUGH = 13 +} + +/** +* Internal actions of EscapeSequenceParser. +*/ +export const enum ParserAction { + IGNORE = 0, + ERROR = 1, + PRINT = 2, + EXECUTE = 3, + OSC_START = 4, + OSC_PUT = 5, + OSC_END = 6, + CSI_DISPATCH = 7, + PARAM = 8, + COLLECT = 9, + ESC_DISPATCH = 10, + CLEAR = 11, + DCS_HOOK = 12, + DCS_PUT = 13, + DCS_UNHOOK = 14 +} diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 18d5c6cf..f445efc4 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -3,10 +3,11 @@ * @license MIT */ -import { ParserState, IDcsHandler, IParsingState } from 'common/parser/Types'; +import { IDcsHandler, IParsingState } from 'common/parser/Types'; import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'common/parser/EscapeSequenceParser'; import * as chai from 'chai'; import { StringToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; +import { ParserState } from 'common/parser/Constants'; function r(a: number, b: number): string[] { let c = b - a; diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index c8b631d0..d8d4e02e 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; +import { ParserState, ParserAction } from 'common/parser/Constants'; import { Disposable } from 'common/Lifecycle'; import { utf32ToString } from 'common/input/TextDecoder'; import { IDisposable } from 'common/Types'; diff --git a/src/common/parser/Types.ts b/src/common/parser/Types.d.ts similarity index 84% rename from src/common/parser/Types.ts rename to src/common/parser/Types.d.ts index 87199702..47ec98e1 100644 --- a/src/common/parser/Types.ts +++ b/src/common/parser/Types.d.ts @@ -4,47 +4,7 @@ */ import { IDisposable } from 'common/Types'; - -/** - * Internal states of EscapeSequenceParser. - */ -export const enum ParserState { - GROUND = 0, - ESCAPE = 1, - ESCAPE_INTERMEDIATE = 2, - CSI_ENTRY = 3, - CSI_PARAM = 4, - CSI_INTERMEDIATE = 5, - CSI_IGNORE = 6, - SOS_PM_APC_STRING = 7, - OSC_STRING = 8, - DCS_ENTRY = 9, - DCS_PARAM = 10, - DCS_IGNORE = 11, - DCS_INTERMEDIATE = 12, - DCS_PASSTHROUGH = 13 -} - -/** -* Internal actions of EscapeSequenceParser. -*/ -export const enum ParserAction { - IGNORE = 0, - ERROR = 1, - PRINT = 2, - EXECUTE = 3, - OSC_START = 4, - OSC_PUT = 5, - OSC_END = 6, - CSI_DISPATCH = 7, - PARAM = 8, - COLLECT = 9, - ESC_DISPATCH = 10, - CLEAR = 11, - DCS_HOOK = 12, - DCS_PUT = 13, - DCS_UNHOOK = 14 -} +import { ParserState } from 'common/parser/Constants'; /** * Internal state of EscapeSequenceParser. diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 8335f76d..5101fdd5 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -6,12 +6,15 @@ import { IRenderLayer } from './Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { ITerminal } from '../Types'; -import { ICellData, DEFAULT_COLOR } from 'common/Types'; -import { DIM_OPACITY, INVERTED_DEFAULT_COLOR, IGlyphIdentifier } from './atlas/Types'; +import { ICellData } from 'common/Types'; +import { DEFAULT_COLOR, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; +import { IGlyphIdentifier } from './atlas/Types'; +import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from './atlas/Constants'; import { BaseCharAtlas } from './atlas/BaseCharAtlas'; import { acquireCharAtlas } from './atlas/CharAtlasCache'; -import { CellData, AttributeData, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_CODE } from 'common/buffer/BufferLine'; +import { AttributeData } from 'common/buffer/AttributeData'; import { IColorSet } from 'browser/Types'; +import { CellData } from 'common/buffer/CellData'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; diff --git a/src/renderer/CharacterJoinerRegistry.test.ts b/src/renderer/CharacterJoinerRegistry.test.ts index e5ab08ea..a73672a2 100644 --- a/src/renderer/CharacterJoinerRegistry.test.ts +++ b/src/renderer/CharacterJoinerRegistry.test.ts @@ -10,8 +10,9 @@ import { CircularList } from 'common/CircularList'; import { ICharacterJoinerRegistry } from './Types'; import { CharacterJoinerRegistry } from './CharacterJoinerRegistry'; -import { BufferLine, CellData } from 'common/buffer/BufferLine'; +import { BufferLine } from 'common/buffer/BufferLine'; import { IBufferLine } from 'common/Types'; +import { CellData } from 'common/buffer/CellData'; describe('CharacterJoinerRegistry', () => { let registry: ICharacterJoinerRegistry; diff --git a/src/renderer/CharacterJoinerRegistry.ts b/src/renderer/CharacterJoinerRegistry.ts index b707d863..80fff2b1 100644 --- a/src/renderer/CharacterJoinerRegistry.ts +++ b/src/renderer/CharacterJoinerRegistry.ts @@ -6,7 +6,9 @@ import { ITerminal } from '../Types'; import { IBufferLine, ICellData, CharData } from 'common/Types'; import { ICharacterJoinerRegistry, ICharacterJoiner } from './Types'; -import { CellData, Content, AttributeData, WHITESPACE_CELL_CHAR } from 'common/buffer/BufferLine'; +import { AttributeData } from 'common/buffer/AttributeData'; +import { WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; +import { CellData } from 'common/buffer/CellData'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; diff --git a/src/renderer/Constants.ts b/src/renderer/Constants.ts new file mode 100644 index 00000000..36ce9b07 --- /dev/null +++ b/src/renderer/Constants.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * Flags used to render terminal text properly for the old CharData format + */ +export const enum FLAGS { + BOLD = 1, + UNDERLINE = 2, + BLINK = 4, + INVERSE = 8, + INVISIBLE = 16, + DIM = 32, + ITALIC = 64 +} diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index 9626f772..b2d856d1 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -7,7 +7,7 @@ import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { ITerminal } from '../Types'; import { ICellData } from 'common/Types'; -import { CellData } from 'common/buffer/BufferLine'; +import { CellData } from 'common/buffer/CellData'; import { IColorSet } from 'browser/Types'; interface ICursorState { diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index 32db3db9..99eff4a2 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -6,7 +6,7 @@ import { ILinkifierEvent, ITerminal, ILinkifierAccessor } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; -import { INVERTED_DEFAULT_COLOR } from './atlas/Types'; +import { INVERTED_DEFAULT_COLOR } from './atlas/Constants'; import { is256Color } from './atlas/CharAtlasUtils'; import { IColorSet } from 'browser/Types'; diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index 39fcb7fc..db3bec19 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -9,9 +9,11 @@ import { ITerminal } from '../Types'; import { CharData, ICellData } from 'common/Types'; import { GridCache } from './GridCache'; import { BaseRenderLayer } from './BaseRenderLayer'; -import { CellData, AttributeData, Content, NULL_CELL_CODE } from 'common/buffer/BufferLine'; +import { AttributeData } from 'common/buffer/AttributeData'; +import { NULL_CELL_CODE, Content } from 'common/buffer/Constants'; import { JoinedCellData } from './CharacterJoinerRegistry'; import { IColorSet } from 'browser/Types'; +import { CellData } from 'common/buffer/CellData'; /** * This CharData looks like a null character, which will forc a clear and render diff --git a/src/renderer/Types.ts b/src/renderer/Types.d.ts similarity index 91% rename from src/renderer/Types.ts rename to src/renderer/Types.d.ts index 153b68e2..5f7d67a8 100644 --- a/src/renderer/Types.ts +++ b/src/renderer/Types.d.ts @@ -8,19 +8,6 @@ import { IDisposable } from 'xterm'; import { IColorSet } from 'browser/Types'; import { IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -/** - * Flags used to render terminal text properly. - */ -export const enum FLAGS { - BOLD = 1, - UNDERLINE = 2, - BLINK = 4, - INVERSE = 8, - INVISIBLE = 16, - DIM = 32, - ITALIC = 64 -} - export interface IRenderLayer extends IDisposable { /** * Called when the terminal loses focus. diff --git a/src/renderer/atlas/CharAtlasUtils.ts b/src/renderer/atlas/CharAtlasUtils.ts index b68793f6..ee787a7b 100644 --- a/src/renderer/atlas/CharAtlasUtils.ts +++ b/src/renderer/atlas/CharAtlasUtils.ts @@ -5,7 +5,7 @@ import { ITerminal } from '../../Types'; import { ICharAtlasConfig } from './Types'; -import { DEFAULT_COLOR } from 'common/Types'; +import { DEFAULT_COLOR } from 'common/buffer/Constants'; import { IColorSet } from 'browser/Types'; export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig { diff --git a/src/renderer/atlas/Constants.ts b/src/renderer/atlas/Constants.ts new file mode 100644 index 00000000..150aad88 --- /dev/null +++ b/src/renderer/atlas/Constants.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export const INVERTED_DEFAULT_COLOR = 257; +export const DIM_OPACITY = 0.5; + +export const CHAR_ATLAS_CELL_SPACING = 1; diff --git a/src/renderer/atlas/DynamicCharAtlas.ts b/src/renderer/atlas/DynamicCharAtlas.ts index c8ccefcb..fdae4e80 100644 --- a/src/renderer/atlas/DynamicCharAtlas.ts +++ b/src/renderer/atlas/DynamicCharAtlas.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { DIM_OPACITY, IGlyphIdentifier, INVERTED_DEFAULT_COLOR, ICharAtlasConfig } from './Types'; +import { IGlyphIdentifier, ICharAtlasConfig } from './Types'; +import { DIM_OPACITY, INVERTED_DEFAULT_COLOR } from './Constants'; import { BaseCharAtlas } from './BaseCharAtlas'; import { DEFAULT_ANSI_COLORS } from 'browser/ColorManager'; import { LRUMap } from './LRUMap'; diff --git a/src/renderer/atlas/Types.ts b/src/renderer/atlas/Types.d.ts similarity index 83% rename from src/renderer/atlas/Types.ts rename to src/renderer/atlas/Types.d.ts index 2cb1db40..1de843e0 100644 --- a/src/renderer/atlas/Types.ts +++ b/src/renderer/atlas/Types.d.ts @@ -6,11 +6,6 @@ import { FontWeight } from 'xterm'; import { IColorSet } from 'browser/Types'; -export const INVERTED_DEFAULT_COLOR = 257; -export const DIM_OPACITY = 0.5; - -export const CHAR_ATLAS_CELL_SPACING = 1; - export interface IGlyphIdentifier { chars: string; code: number; diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 8d370336..fe6ab4d1 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -6,7 +6,7 @@ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; import { ILinkifierEvent, ITerminal } from '../../Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; -import { INVERTED_DEFAULT_COLOR } from '../atlas/Types'; +import { INVERTED_DEFAULT_COLOR } from '../atlas/Constants'; import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; @@ -167,9 +167,14 @@ export class DomRenderer extends Disposable implements IRenderer { // Blink animation styles += `@keyframes blink {` + - ` 0% { opacity: 1.0; }` + - ` 50% { opacity: 0.0; }` + - ` 100% { opacity: 1.0; }` + + ` 0% {` + + ` background-color: ${this._colors.cursor.css};` + + ` color: ${this._colors.cursorAccent.css};` + + ` }` + + ` 50% {` + + ` background-color: ${this._colors.cursorAccent.css};` + + ` color: ${this._colors.cursor.css};` + + ` }` + `}`; // Cursor styles += diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts index 28ee0a7e..6ad9641c 100644 --- a/src/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/renderer/dom/DomRendererRowFactory.test.ts @@ -6,9 +6,11 @@ import jsdom = require('jsdom'); import { assert } from 'chai'; import { DomRendererRowFactory } from './DomRendererRowFactory'; -import { BufferLine, CellData, FgFlags, BgFlags, Attributes, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; +import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes } from 'common/buffer/Constants'; +import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { ITerminalOptions } from '../../Types'; import { IBufferLine } from 'common/Types'; +import { CellData } from 'common/buffer/CellData'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts index 8c31aa44..687d207d 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -5,8 +5,10 @@ import { ITerminalOptions } from '../../Types'; import { IBufferLine } from 'common/Types'; -import { INVERTED_DEFAULT_COLOR } from '../atlas/Types'; -import { CellData, AttributeData, NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/BufferLine'; +import { INVERTED_DEFAULT_COLOR } from '../atlas/Constants'; +import { AttributeData } from 'common/buffer/AttributeData'; +import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; +import { CellData } from 'common/buffer/CellData'; export const BOLD_CLASS = 'xterm-bold'; export const DIM_CLASS = 'xterm-dim';