diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts
index 27e0e836d..f6907864d 100644
--- a/src/Buffer.test.ts
+++ b/src/Buffer.test.ts
@@ -5,7 +5,7 @@
import { assert, expect } from 'chai';
import { ITerminal } from './Types';
-import { Buffer, DEFAULT_ATTR } from './Buffer';
+import { Buffer, DEFAULT_ATTR_DATA } from './Buffer';
import { CircularList } from './common/CircularList';
import { MockTerminal, TestTerminal } from './ui/TestUtils.test';
import { BufferLine, CellData } from './BufferLine';
@@ -37,7 +37,7 @@ describe('Buffer', () => {
describe('fillViewportRows', () => {
it('should fill the buffer with blank lines based on the size of the viewport', () => {
- const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR).loadCell(0, new CellData()).asCharData;
+ const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR_DATA).loadCell(0, new CellData()).asCharData;
buffer.fillViewportRows();
assert.equal(buffer.lines.length, INIT_ROWS);
for (let y = 0; y < INIT_ROWS; y++) {
@@ -184,7 +184,7 @@ describe('Buffer', () => {
buffer.fillViewportRows();
// Create 10 extra blank lines
for (let i = 0; i < 10; i++) {
- buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
+ buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// Set cursor to the bottom of the buffer
buffer.y = INIT_ROWS - 1;
@@ -204,7 +204,7 @@ describe('Buffer', () => {
buffer.fillViewportRows();
// Create 10 extra blank lines
for (let i = 0; i < 10; i++) {
- buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
+ buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// Set cursor to the bottom of the buffer
buffer.y = INIT_ROWS - 1;
diff --git a/src/Buffer.ts b/src/Buffer.ts
index 6295d1757..a5a285b02 100644
--- a/src/Buffer.ts
+++ b/src/Buffer.ts
@@ -4,13 +4,16 @@
*/
import { CircularList } from './common/CircularList';
-import { ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, ICellData } from './Types';
+import { ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, ICellData, IAttributeData } from './Types';
import { EventEmitter } from './common/EventEmitter';
import { IMarker } from 'xterm';
-import { BufferLine, CellData } from './BufferLine';
+import { BufferLine, CellData, AttributeData } from './BufferLine';
import { DEFAULT_COLOR } from './renderer/atlas/Types';
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
+
+export const DEFAULT_ATTR_DATA = new AttributeData();
+
export const CHAR_DATA_ATTR_INDEX = 0;
export const CHAR_DATA_CHAR_INDEX = 1;
export const CHAR_DATA_WIDTH_INDEX = 2;
@@ -43,7 +46,7 @@ export class Buffer implements IBuffer {
public tabs: any;
public savedY: number;
public savedX: number;
- public savedCurAttr: number;
+ public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
public markers: Marker[] = [];
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
@@ -61,19 +64,29 @@ export class Buffer implements IBuffer {
this.clear();
}
- public getNullCell(fg: number = 0, bg: number = 0): ICellData {
- this._nullCell.fg = fg;
- this._nullCell.bg = bg;
+ public getNullCell(attr?: IAttributeData): ICellData {
+ if (attr) {
+ this._nullCell.fg = attr.fg;
+ this._nullCell.bg = attr.bg;
+ } else {
+ this._nullCell.fg = 0;
+ this._nullCell.bg = 0;
+ }
return this._nullCell;
}
- public getWhitespaceCell(fg: number = 0, bg: number = 0): ICellData {
- this._whitespaceCell.fg = fg;
- this._whitespaceCell.bg = bg;
+ public getWhitespaceCell(attr?: IAttributeData): ICellData {
+ if (attr) {
+ this._whitespaceCell.fg = attr.fg;
+ this._whitespaceCell.bg = attr.bg;
+ } else {
+ this._whitespaceCell.fg = 0;
+ this._whitespaceCell.bg = 0;
+ }
return this._whitespaceCell;
}
- public getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
+ public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
return new BufferLine(this._terminal.cols, this.getNullCell(attr), isWrapped);
}
@@ -105,10 +118,10 @@ export class Buffer implements IBuffer {
/**
* Fills the buffer's viewport with blank lines.
*/
- public fillViewportRows(fillAttr?: number): void {
+ public fillViewportRows(fillAttr?: IAttributeData): void {
if (this.lines.length === 0) {
if (fillAttr === undefined) {
- fillAttr = DEFAULT_ATTR;
+ fillAttr = DEFAULT_ATTR_DATA;
}
let i = this._terminal.rows;
while (i--) {
@@ -149,7 +162,7 @@ export class Buffer implements IBuffer {
if (this.lines.length > 0) {
// Deal with columns increasing (we don't do anything when columns reduce)
if (this._terminal.cols < newCols) {
- const cell = this.getNullCell(DEFAULT_ATTR); // does xterm use the default attr?
+ const cell = this.getNullCell(DEFAULT_ATTR_DATA); // does xterm use the default attr?
for (let i = 0; i < this.lines.length; i++) {
this.lines.get(i).resize(newCols, cell);
}
@@ -172,7 +185,7 @@ export class Buffer implements IBuffer {
} else {
// Add a blank line if there is no buffer left at the top to scroll to, or if there
// are blank lines after the cursor
- this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR)));
+ this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR_DATA)));
}
}
}
diff --git a/src/BufferLine.test.ts b/src/BufferLine.test.ts
index 97508ace4..ec03c064c 100644
--- a/src/BufferLine.test.ts
+++ b/src/BufferLine.test.ts
@@ -24,23 +24,23 @@ describe('CellData', () => {
// ASCII
cell.setFromCharData([123, 'a', 1, 'a'.charCodeAt(0)]);
chai.assert.deepEqual(cell.asCharData, [123, 'a', 1, 'a'.charCodeAt(0)]);
- chai.assert.equal(cell.combined, 0);
+ chai.assert.equal(cell.isCombined, 0);
// combining
cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
- chai.assert.equal(cell.combined, Content.IS_COMBINED);
+ chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
// surrogate
cell.setFromCharData([123, '𝄞', 1, 0x1D11E]);
chai.assert.deepEqual(cell.asCharData, [123, '𝄞', 1, 0x1D11E]);
- chai.assert.equal(cell.combined, 0);
+ chai.assert.equal(cell.isCombined, 0);
// surrogate + combining
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
chai.assert.deepEqual(cell.asCharData, [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
- chai.assert.equal(cell.combined, Content.IS_COMBINED);
+ chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
// wide char
cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]);
chai.assert.deepEqual(cell.asCharData, [123, '1', 2, '1'.charCodeAt(0)]);
- chai.assert.equal(cell.combined, 0);
+ chai.assert.equal(cell.isCombined, 0);
});
});
@@ -366,7 +366,7 @@ describe('BufferLine', function(): void {
// width is set to 1
chai.assert.deepEqual(cell.asCharData, [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
// do not account a single combining char as combined
- chai.assert.equal(cell.combined, 0);
+ chai.assert.equal(cell.isCombined, 0);
});
it('should add char to combining string in cell', () => {
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
@@ -379,7 +379,7 @@ describe('BufferLine', function(): void {
// width is set to 1
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301\u0301', 1, 0x0301]);
// do not account a single combining char as combined
- chai.assert.equal(cell.combined, Content.IS_COMBINED);
+ chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
});
it('should create combining string on taken cell', () => {
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
@@ -392,7 +392,7 @@ describe('BufferLine', function(): void {
// width is set to 1
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, 0x0301]);
// do not account a single combining char as combined
- chai.assert.equal(cell.combined, Content.IS_COMBINED);
+ chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
});
});
});
diff --git a/src/BufferLine.ts b/src/BufferLine.ts
index 048bcab6b..e306346fb 100644
--- a/src/BufferLine.ts
+++ b/src/BufferLine.ts
@@ -2,11 +2,67 @@
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
-import { CharData, IBufferLine, ICellData } from './Types';
+import { CharData, IBufferLine, ICellData, IColorRGB, IAttributeData } from './Types';
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, WHITESPACE_CELL_CHAR, CHAR_DATA_ATTR_INDEX } from './Buffer';
import { stringFromCodePoint } from './core/input/TextDecoder';
+import { FLAGS } from './renderer/Types';
+import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
+/**
+ * TODO:
+ * The below color-related code can be removed when true color is implemented.
+ * It's only purpose is to match true color requests with the closest matching
+ * ANSI color code.
+ */
+const matchColorCache: {[colorRGBHash: number]: number} = {};
+
+// http://stackoverflow.com/questions/1633828
+function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {
+ return Math.pow(30 * (r1 - r2), 2)
+ + Math.pow(59 * (g1 - g2), 2)
+ + Math.pow(11 * (b1 - b2), 2);
+}
+
+function matchColor(r1: number, g1: number, b1: number): number {
+ const hash = (r1 << 16) | (g1 << 8) | b1;
+
+ if (matchColorCache[hash] !== null && matchColorCache[hash] !== undefined) {
+ return matchColorCache[hash];
+ }
+
+ let ldiff = Infinity;
+ let li = -1;
+ let i = 0;
+ let c: number;
+ let r2: number;
+ let g2: number;
+ let b2: number;
+ let diff: number;
+
+ for (; i < DEFAULT_ANSI_COLORS.length; i++) {
+ c = DEFAULT_ANSI_COLORS[i].rgba;
+ r2 = c >>> 24;
+ g2 = c >>> 16 & 0xFF;
+ b2 = c >>> 8 & 0xFF;
+ // assume that alpha is 0xFF
+
+ diff = matchColorDistance(r1, g1, b1, r2, g2, b2);
+
+ if (diff === 0) {
+ li = i;
+ break;
+ }
+
+ if (diff < ldiff) {
+ ldiff = diff;
+ li = i;
+ }
+ }
+
+ return matchColorCache[hash] = li;
+}
+
/**
* buffer memory layout:
*
@@ -76,12 +132,188 @@ export const enum Content {
WIDTH_SHIFT = 22
}
+export 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 enum FgFlags {
+ /**
+ * bit 27..31 (32th bit unused)
+ */
+ INVERSE = 0x4000000,
+ BOLD = 0x8000000,
+ UNDERLINE = 0x10000000,
+ BLINK = 0x20000000,
+ INVISIBLE = 0x40000000
+}
+
+export enum BgFlags {
+ /**
+ * bit 27..32 (upper 4 unused)
+ */
+ ITALIC = 0x4000000,
+ DIM = 0x8000000
+}
+
+export class AttributeData implements IAttributeData {
+ static toRGB(value: number): IColorRGB {
+ return [
+ value >>> Attributes.RED_SHIFT & 255,
+ value >>> Attributes.GREEN_SHIFT & 255,
+ value & 255
+ ];
+ }
+ static fromRGB(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 getColormodeFg(): number { return this.fg & Attributes.CM_MASK; }
+ public getColormodeBg(): 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(channels: boolean = false): number | IColorRGB {
+ 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 (channels) ? AttributeData.toRGB(this.fg & Attributes.RGB_MASK) : this.fg & Attributes.RGB_MASK;
+ default: return -1; // CM_DEFAULT defaults to -1
+ }
+ }
+ public getBgColor(channels: boolean = false): number | IColorRGB {
+ 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 (channels) ? AttributeData.toRGB(this.bg & Attributes.RGB_MASK) : this.bg & Attributes.RGB_MASK;
+ default: return -1; // CM_DEFAULT defaults to -1
+ }
+ }
+
+ public getOldFlags(): number {
+ let flags = 0;
+ if (this.isBold()) {
+ flags |= FLAGS.BOLD;
+ }
+ if (this.isUnderline()) {
+ flags |= FLAGS.UNDERLINE;
+ }
+ if (this.isBlink()) {
+ flags |= FLAGS.BLINK;
+ }
+ if (this.isDim()) {
+ flags |= FLAGS.DIM;
+ }
+ if (this.isInvisible()) {
+ flags |= FLAGS.INVISIBLE;
+ }
+ if (this.isInverse()) {
+ flags |= FLAGS.INVERSE;
+ }
+ if (this.isItalic()) {
+ flags |= FLAGS.ITALIC;
+ }
+ return flags;
+ }
+ public getOldFgColor(): number {
+ let color = this.getFgColor() as number;
+ if (color === -1) {
+ return 256;
+ }
+ if (this.isFgRGB()) {
+ color = matchColor(
+ (this.fg & Attributes.RED_MASK) >> Attributes.RED_SHIFT,
+ (this.fg & Attributes.GREEN_MASK) >> Attributes.GREEN_SHIFT,
+ (this.fg & Attributes.BLUE_MASK) >> Attributes.BLUE_SHIFT
+ );
+ if (color === -1) {
+ color = 256;
+ }
+ }
+ return color;
+ }
+ public getOldBgColor(): number {
+ let color = this.getBgColor() as number;
+ if (color === -1) {
+ return 256;
+ }
+ if (this.isBgRGB()) {
+ color = matchColor(
+ (this.bg & Attributes.RED_MASK) >> Attributes.RED_SHIFT,
+ (this.bg & Attributes.GREEN_MASK) >> Attributes.GREEN_SHIFT,
+ (this.bg & Attributes.BLUE_MASK) >> Attributes.BLUE_SHIFT
+ );
+ if (color === -1) {
+ color = 256;
+ }
+ }
+ return color;
+ }
+}
+
/**
* CellData - represents a single Cell in the terminal buffer.
*
* TODO: attr getter
*/
-export class CellData implements ICellData {
+export class CellData extends AttributeData implements ICellData {
/** Helper to create CellData from CharData. */
public static fromCharData(value: CharData): CellData {
@@ -97,7 +329,7 @@ export class CellData implements ICellData {
public combinedData: string = '';
/** Whether cell contains a combined string. */
- public get combined(): number {
+ public get isCombined(): number {
return this.content & Content.IS_COMBINED;
}
@@ -119,7 +351,7 @@ export class CellData implements ICellData {
/** Codepoint of cell (or last charCode of combined string) */
public get code(): number {
- return ((this.combined) ? this.combinedData.charCodeAt(this.combinedData.length - 1) : this.content & Content.CODEPOINT_MASK);
+ return ((this.isCombined) ? this.combinedData.charCodeAt(this.combinedData.length - 1) : this.content & Content.CODEPOINT_MASK);
}
/** Set data from CharData */
diff --git a/src/BufferSet.ts b/src/BufferSet.ts
index f84757d19..268c2d88b 100644
--- a/src/BufferSet.ts
+++ b/src/BufferSet.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { ITerminal, IBufferSet } from './Types';
+import { ITerminal, IBufferSet, IAttributeData } from './Types';
import { Buffer } from './Buffer';
import { EventEmitter } from './common/EventEmitter';
@@ -77,7 +77,7 @@ export class BufferSet extends EventEmitter implements IBufferSet {
/**
* Sets the alt Buffer of the BufferSet as its currently active Buffer
*/
- public activateAltBuffer(fillAttr?: number): void {
+ public activateAltBuffer(fillAttr?: IAttributeData): void {
if (this._activeBuffer === this._alt) {
return;
}
diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts
index 24eb08849..df34cf049 100644
--- a/src/InputHandler.test.ts
+++ b/src/InputHandler.test.ts
@@ -5,33 +5,33 @@
import { assert, expect } from 'chai';
import { InputHandler } from './InputHandler';
-import { MockInputHandlingTerminal } from './ui/TestUtils.test';
-import { DEFAULT_ATTR } from './Buffer';
+import { MockInputHandlingTerminal, TestTerminal } from './ui/TestUtils.test';
+import { DEFAULT_ATTR_DATA } from './Buffer';
import { Terminal } from './Terminal';
import { IBufferLine } from './Types';
-import { CellData } from './BufferLine';
+import { CellData, Attributes } from './BufferLine';
describe('InputHandler', () => {
describe('save and restore cursor', () => {
const terminal = new MockInputHandlingTerminal();
terminal.buffer.x = 1;
terminal.buffer.y = 2;
- terminal.curAttr = 3;
+ terminal.curAttrData.fg = 3;
const inputHandler = new InputHandler(terminal);
// Save cursor position
inputHandler.saveCursor([]);
assert.equal(terminal.buffer.x, 1);
assert.equal(terminal.buffer.y, 2);
- assert.equal(terminal.curAttr, 3);
+ assert.equal(terminal.curAttrData.fg, 3);
// Change cursor position
terminal.buffer.x = 10;
terminal.buffer.y = 20;
- terminal.curAttr = 30;
+ terminal.curAttrData.fg = 30;
// Restore cursor position
inputHandler.restoreCursor([]);
assert.equal(terminal.buffer.x, 1);
assert.equal(terminal.buffer.y, 2);
- assert.equal(terminal.curAttr, 3);
+ assert.equal(terminal.curAttrData.fg, 3);
});
describe('setCursorStyle', () => {
it('should call Terminal.setOption with correct params', () => {
@@ -357,45 +357,149 @@ describe('InputHandler', () => {
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
// Text color of 'TEST' should be red
- expect((term.buffer.lines.get(1).loadCell(4, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
+ expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getOldFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
// Text color of 'TEST' should be red
- expect((term.buffer.lines.get(1).loadCell(4, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
+ expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getOldFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
// Text color of 'TEST' should be default
- expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
+ expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
// Text color of 'JUNK' should be red
- expect((term.buffer.lines.get(1).loadCell(0, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
+ expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getOldFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('');
// Text color of 'TEST' should be default
- expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
+ expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
});
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
// Text color of 'TEST' should be default
- expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
+ expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
handler.parse('\x1b[?1049h\x1b[uTEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
// Text color of 'TEST' should be red
- expect((term.buffer.lines.get(1).loadCell(0, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
+ expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getOldFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
handler.parse('\x1b[42m\x1b[?1049h');
// Buffer should be filled with green background
- expect(term.buffer.lines.get(20).loadCell(10, new CellData()).fg & 0x1ff).to.equal(2);
+ expect(term.buffer.lines.get(20).loadCell(10, new CellData()).getOldBgColor()).to.equal(2);
+ });
+ });
+
+ describe('text attributes', () => {
+ let term: TestTerminal;
+ beforeEach(() => {
+ term = new TestTerminal();
+ });
+ it('bold', () => {
+ term.writeSync('\x1b[1m');
+ assert.equal(!!term.curAttrData.isBold(), true);
+ term.writeSync('\x1b[22m');
+ assert.equal(!!term.curAttrData.isBold(), false);
+ });
+ it('dim', () => {
+ term.writeSync('\x1b[2m');
+ assert.equal(!!term.curAttrData.isDim(), true);
+ term.writeSync('\x1b[22m');
+ assert.equal(!!term.curAttrData.isDim(), false);
+ });
+ it('italic', () => {
+ term.writeSync('\x1b[3m');
+ assert.equal(!!term.curAttrData.isItalic(), true);
+ term.writeSync('\x1b[23m');
+ assert.equal(!!term.curAttrData.isItalic(), false);
+ });
+ it('underline', () => {
+ term.writeSync('\x1b[4m');
+ assert.equal(!!term.curAttrData.isUnderline(), true);
+ term.writeSync('\x1b[24m');
+ assert.equal(!!term.curAttrData.isUnderline(), false);
+ });
+ it('blink', () => {
+ term.writeSync('\x1b[5m');
+ assert.equal(!!term.curAttrData.isBlink(), true);
+ term.writeSync('\x1b[25m');
+ assert.equal(!!term.curAttrData.isBlink(), false);
+ });
+ it('inverse', () => {
+ term.writeSync('\x1b[7m');
+ assert.equal(!!term.curAttrData.isInverse(), true);
+ term.writeSync('\x1b[27m');
+ assert.equal(!!term.curAttrData.isInverse(), false);
+ });
+ it('invisible', () => {
+ term.writeSync('\x1b[8m');
+ assert.equal(!!term.curAttrData.isInvisible(), true);
+ term.writeSync('\x1b[28m');
+ assert.equal(!!term.curAttrData.isInvisible(), false);
+ });
+ it('colormode palette 16', () => {
+ assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
+ assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
+ // lower 8 colors
+ for (let i = 0; i < 8; ++i) {
+ term.writeSync(`\x1b[${i + 30};${i + 40}m`);
+ assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_P16);
+ assert.equal(term.curAttrData.getFgColor(), i);
+ assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_P16);
+ assert.equal(term.curAttrData.getBgColor(), i);
+ }
+ // reset to DEFAULT
+ term.writeSync(`\x1b[39;49m`);
+ assert.equal(term.curAttrData.getColormodeFg(), 0);
+ assert.equal(term.curAttrData.getColormodeBg(), 0);
+ });
+ it('colormode palette 256', () => {
+ assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
+ assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
+ // lower 8 colors
+ for (let i = 0; i < 256; ++i) {
+ term.writeSync(`\x1b[38;5;${i};48;5;${i}m`);
+ assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_P256);
+ assert.equal(term.curAttrData.getFgColor(), i);
+ assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_P256);
+ assert.equal(term.curAttrData.getBgColor(), i);
+ }
+ // reset to DEFAULT
+ term.writeSync(`\x1b[39;49m`);
+ assert.equal(term.curAttrData.getColormodeFg(), 0);
+ assert.equal(term.curAttrData.getFgColor(), -1);
+ assert.equal(term.curAttrData.getColormodeBg(), 0);
+ assert.equal(term.curAttrData.getBgColor(), -1);
+ });
+ it('colormode RGB', () => {
+ assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
+ assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
+ term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
+ assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_RGB);
+ assert.equal(term.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3);
+ assert.deepEqual(term.curAttrData.getFgColor(true), [1, 2, 3]);
+ assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_RGB);
+ assert.deepEqual(term.curAttrData.getBgColor(true), [4, 5, 6]);
+ // reset to DEFAULT
+ term.writeSync(`\x1b[39;49m`);
+ assert.equal(term.curAttrData.getColormodeFg(), 0);
+ assert.equal(term.curAttrData.getFgColor(), -1);
+ assert.equal(term.curAttrData.getColormodeBg(), 0);
+ assert.equal(term.curAttrData.getBgColor(), -1);
+ });
+ it('should zero missing RGB values', () => {
+ term.writeSync(`\x1b[38;2;1;2;3m`);
+ term.writeSync(`\x1b[38;2;5m`);
+ assert.deepEqual(term.curAttrData.getFgColor(true), [5, 0, 0]);
});
});
});
diff --git a/src/InputHandler.ts b/src/InputHandler.ts
index c41b4165c..178507fa3 100644
--- a/src/InputHandler.ts
+++ b/src/InputHandler.ts
@@ -4,19 +4,17 @@
* @license MIT
*/
-import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types';
+import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IInputHandlingTerminal } from './Types';
import { C0, C1 } from './common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets';
-import { DEFAULT_ATTR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer';
-import { FLAGS } from './renderer/Types';
+import { NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from './Buffer';
import { wcwidth } from './CharWidth';
import { EscapeSequenceParser } from './EscapeSequenceParser';
-import { ICharset } from './core/Types';
import { IDisposable } from 'xterm';
import { Disposable } from './common/Lifecycle';
import { concat, utf32ToString } from './common/TypedArrayUtils';
import { StringToUtf32, stringFromCodePoint } from './core/input/TextDecoder';
-import { CellData } from './BufferLine';
+import { CellData, Attributes, FgFlags, BgFlags } from './BufferLine';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -314,13 +312,13 @@ export class InputHandler extends Disposable implements IInputHandler {
public print(data: Uint32Array, start: number, end: number): void {
let code: number;
let chWidth: number;
- const buffer: IBuffer = this._terminal.buffer;
- const charset: ICharset = this._terminal.charset;
- const screenReaderMode: boolean = this._terminal.options.screenReaderMode;
- const cols: number = this._terminal.cols;
- const wraparoundMode: boolean = this._terminal.wraparoundMode;
- const insertMode: boolean = this._terminal.insertMode;
- const curAttr: number = this._terminal.curAttr;
+ const buffer = this._terminal.buffer;
+ const charset = this._terminal.charset;
+ const screenReaderMode = this._terminal.options.screenReaderMode;
+ const cols = this._terminal.cols;
+ const wraparoundMode = this._terminal.wraparoundMode;
+ const insertMode = this._terminal.insertMode;
+ const curAttr = this._terminal.curAttrData;
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
this._terminal.updateRange(buffer.y);
@@ -401,12 +399,12 @@ export class InputHandler extends Disposable implements IInputHandler {
// a halfwidth char any fullwidth shifted there is lost
// and will be set to empty cell
if (bufferRow.loadCell(cols - 1, this._cell).width === 2) {
- bufferRow.setDataFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr, 0);
+ bufferRow.setDataFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg);
}
}
// write current char to buffer and advance cursor
- bufferRow.setDataFromCodePoint(buffer.x++, code, chWidth, curAttr, 0);
+ bufferRow.setDataFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
// fullwidth char - also set next cell to placeholder stub and advance cursor
// for graphemes bigger than fullwidth we can simply loop to zero
@@ -414,7 +412,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (chWidth > 0) {
while (--chWidth) {
// other than a regular empty cell a cell following a wide char has no width
- bufferRow.setDataFromCodePoint(buffer.x++, 0, 0, curAttr, 0);
+ bufferRow.setDataFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg);
}
}
}
@@ -520,7 +518,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells(
this._terminal.buffer.x,
params[0] || 1,
- this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
+ this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
);
this._terminal.updateRange(this._terminal.buffer.y);
}
@@ -694,7 +692,7 @@ export class InputHandler extends Disposable implements IInputHandler {
line.replaceCells(
start,
end,
- this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
+ this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
);
if (clearWrap) {
line.isWrapped = false;
@@ -817,7 +815,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// test: echo -e '\e[44m\e[1L\e[0m'
// blankLine(true) - xterm/linux behavior
buffer.lines.splice(scrollBottomAbsolute - 1, 1);
- buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
+ buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
}
// this.maxRange();
@@ -847,7 +845,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// test: echo -e '\e[44m\e[1M\e[0m'
// blankLine(true) - xterm/linux behavior
buffer.lines.splice(row, 1);
- buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
+ buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
}
// this.maxRange();
@@ -863,7 +861,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells(
this._terminal.buffer.x,
params[0] || 1,
- this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
+ this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
);
this._terminal.updateRange(this._terminal.buffer.y);
}
@@ -879,7 +877,7 @@ export class InputHandler extends Disposable implements IInputHandler {
while (param--) {
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
- buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
+ buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// this.maxRange();
this._terminal.updateRange(buffer.scrollTop);
@@ -898,7 +896,7 @@ export class InputHandler extends Disposable implements IInputHandler {
while (param--) {
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
- buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
+ buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// this.maxRange();
this._terminal.updateRange(buffer.scrollTop);
@@ -914,7 +912,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells(
this._terminal.buffer.x,
this._terminal.buffer.x + (params[0] || 1),
- this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
+ this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
);
}
@@ -973,7 +971,7 @@ export class InputHandler extends Disposable implements IInputHandler {
line.loadCell(buffer.x - 1, this._cell);
line.replaceCells(buffer.x,
buffer.x + (params[0] || 1),
- (this._cell.content !== undefined) ? this._cell : buffer.getNullCell(DEFAULT_ATTR)
+ (this._cell.content !== undefined) ? this._cell : buffer.getNullCell(DEFAULT_ATTR_DATA)
);
// FIXME: no updateRange here?
}
@@ -1307,7 +1305,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// FALL-THROUGH
case 47: // alt screen buffer
case 1047: // alt screen buffer
- this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttr());
+ this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttrData());
this._terminal.refresh(0, this._terminal.rows - 1);
if (this._terminal.viewport) {
this._terminal.viewport.syncScrollArea();
@@ -1567,127 +1565,128 @@ export class InputHandler extends Disposable implements IInputHandler {
public charAttributes(params: number[]): void {
// Optimize a single SGR0.
if (params.length === 1 && params[0] === 0) {
- this._terminal.curAttr = DEFAULT_ATTR;
+ this._terminal.curAttrData.fg = DEFAULT_ATTR_DATA.fg;
+ this._terminal.curAttrData.bg = DEFAULT_ATTR_DATA.bg;
return;
}
const l = params.length;
- let flags = this._terminal.curAttr >> 18;
- let fg = (this._terminal.curAttr >> 9) & 0x1ff;
- let bg = this._terminal.curAttr & 0x1ff;
let p;
+ const attr = this._terminal.curAttrData;
for (let i = 0; i < l; i++) {
p = params[i];
if (p >= 30 && p <= 37) {
// fg color 8
- fg = p - 30;
+ attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
+ attr.fg |= Attributes.CM_P16 | (p - 30);
} else if (p >= 40 && p <= 47) {
// bg color 8
- bg = p - 40;
+ attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
+ attr.bg |= Attributes.CM_P16 | (p - 40);
} else if (p >= 90 && p <= 97) {
// fg color 16
- p += 8;
- fg = p - 90;
+ attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
+ attr.fg |= Attributes.CM_P16 | (p - 90) | 8;
} else if (p >= 100 && p <= 107) {
// bg color 16
- p += 8;
- bg = p - 100;
+ attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
+ attr.bg |= Attributes.CM_P16 | (p - 100) | 8;
} else if (p === 0) {
// default
- flags = DEFAULT_ATTR >> 18;
- fg = (DEFAULT_ATTR >> 9) & 0x1ff;
- bg = DEFAULT_ATTR & 0x1ff;
- // flags = 0;
- // fg = 0x1ff;
- // bg = 0x1ff;
+ attr.fg = DEFAULT_ATTR_DATA.fg;
+ attr.bg = DEFAULT_ATTR_DATA.bg;
} else if (p === 1) {
// bold text
- flags |= FLAGS.BOLD;
+ attr.fg |= FgFlags.BOLD;
} else if (p === 3) {
// italic text
- flags |= FLAGS.ITALIC;
+ attr.bg |= BgFlags.ITALIC;
} else if (p === 4) {
// underlined text
- flags |= FLAGS.UNDERLINE;
+ attr.fg |= FgFlags.UNDERLINE;
} else if (p === 5) {
// blink
- flags |= FLAGS.BLINK;
+ attr.fg |= FgFlags.BLINK;
} else if (p === 7) {
// inverse and positive
// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
- flags |= FLAGS.INVERSE;
+ attr.fg |= FgFlags.INVERSE;
} else if (p === 8) {
// invisible
- flags |= FLAGS.INVISIBLE;
+ attr.fg |= FgFlags.INVISIBLE;
} else if (p === 2) {
// dimmed text
- flags |= FLAGS.DIM;
+ attr.bg |= BgFlags.DIM;
} else if (p === 22) {
// not bold nor faint
- flags &= ~FLAGS.BOLD;
- flags &= ~FLAGS.DIM;
+ attr.fg &= ~FgFlags.BOLD;
+ attr.bg &= ~BgFlags.DIM;
} else if (p === 23) {
// not italic
- flags &= ~FLAGS.ITALIC;
+ attr.bg &= ~BgFlags.ITALIC;
} else if (p === 24) {
// not underlined
- flags &= ~FLAGS.UNDERLINE;
+ attr.fg &= ~FgFlags.UNDERLINE;
} else if (p === 25) {
// not blink
- flags &= ~FLAGS.BLINK;
+ attr.fg &= ~FgFlags.BLINK;
} else if (p === 27) {
// not inverse
- flags &= ~FLAGS.INVERSE;
+ attr.fg &= ~FgFlags.INVERSE;
} else if (p === 28) {
// not invisible
- flags &= ~FLAGS.INVISIBLE;
+ attr.fg &= ~FgFlags.INVISIBLE;
} else if (p === 39) {
// reset fg
- fg = (DEFAULT_ATTR >> 9) & 0x1ff;
+ attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
+ attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
} else if (p === 49) {
// reset bg
- bg = DEFAULT_ATTR & 0x1ff;
+ attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
+ attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
} else if (p === 38) {
// fg color 256
if (params[i + 1] === 2) {
i += 2;
- fg = this._terminal.matchColor(
- params[i] & 0xff,
- params[i + 1] & 0xff,
- params[i + 2] & 0xff);
- if (fg === -1) fg = 0x1ff;
+ attr.fg |= Attributes.CM_RGB;
+ attr.fg &= ~Attributes.RGB_MASK;
+ attr.fg |= (params[i] & 0xFF) << Attributes.RED_SHIFT;
+ attr.fg |= (params[i + 1] & 0xFF) << Attributes.GREEN_SHIFT;
+ attr.fg |= (params[i + 2] & 0xFF) << Attributes.BLUE_SHIFT;
i += 2;
} else if (params[i + 1] === 5) {
i += 2;
p = params[i] & 0xff;
- fg = p;
+ attr.fg &= ~Attributes.PCOLOR_MASK;
+ attr.fg |= Attributes.CM_P256 | p;
}
} else if (p === 48) {
// bg color 256
if (params[i + 1] === 2) {
i += 2;
- bg = this._terminal.matchColor(
- params[i] & 0xff,
- params[i + 1] & 0xff,
- params[i + 2] & 0xff);
- if (bg === -1) bg = 0x1ff;
+ attr.bg |= Attributes.CM_RGB;
+ attr.bg &= ~Attributes.RGB_MASK;
+ attr.bg |= (params[i] & 0xFF) << Attributes.RED_SHIFT;
+ attr.bg |= (params[i + 1] & 0xFF) << Attributes.GREEN_SHIFT;
+ attr.bg |= (params[i + 2] & 0xFF) << Attributes.BLUE_SHIFT;
i += 2;
} else if (params[i + 1] === 5) {
i += 2;
p = params[i] & 0xff;
- bg = p;
+ attr.bg &= ~Attributes.PCOLOR_MASK;
+ attr.bg |= Attributes.CM_P256 | p;
}
} else if (p === 100) {
// reset fg/bg
- fg = (DEFAULT_ATTR >> 9) & 0x1ff;
- bg = DEFAULT_ATTR & 0x1ff;
+ attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
+ attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
+ attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
+ attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
} else {
this._terminal.error('Unknown SGR attribute: %d.', p);
}
}
-
- this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;
}
/**
@@ -1774,7 +1773,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._terminal.applicationCursor = false;
this._terminal.buffer.scrollTop = 0;
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
- this._terminal.curAttr = DEFAULT_ATTR;
+ this._terminal.curAttrData = DEFAULT_ATTR_DATA;
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
this._terminal.charset = null;
this._terminal.glevel = 0; // ??
@@ -1837,7 +1836,8 @@ export class InputHandler extends Disposable implements IInputHandler {
public saveCursor(params: number[]): void {
this._terminal.buffer.savedX = this._terminal.buffer.x;
this._terminal.buffer.savedY = this._terminal.buffer.y;
- this._terminal.buffer.savedCurAttr = this._terminal.curAttr;
+ this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg;
+ this._terminal.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg;
}
@@ -1849,7 +1849,8 @@ export class InputHandler extends Disposable implements IInputHandler {
public restoreCursor(params: number[]): void {
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
- this._terminal.curAttr = this._terminal.buffer.savedCurAttr || DEFAULT_ATTR;
+ this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg;
+ this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg;
}
diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts
index e06ceb78c..e6ed7b98e 100644
--- a/src/Terminal.test.ts
+++ b/src/Terminal.test.ts
@@ -6,7 +6,7 @@
import { assert, expect } from 'chai';
import { Terminal } from './Terminal';
import { MockViewport, MockCompositionHelper, MockRenderer } from './ui/TestUtils.test';
-import { DEFAULT_ATTR } from './Buffer';
+import { DEFAULT_ATTR_DATA } from './Buffer';
import { CellData } from './BufferLine';
const INIT_COLS = 80;
@@ -260,7 +260,7 @@ describe('term.js addons', () => {
assert.equal(term.buffer.lines.length, term.rows);
assert.deepEqual(term.buffer.lines.get(0), promptLine);
for (let i = 1; i < term.rows; i++) {
- assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
+ assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
});
it('should clear a buffer larger than rows', () => {
@@ -277,7 +277,7 @@ describe('term.js addons', () => {
assert.equal(term.buffer.lines.length, term.rows);
assert.deepEqual(term.buffer.lines.get(0), promptLine);
for (let i = 1; i < term.rows; i++) {
- assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
+ assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
});
it('should not break the prompt when cleared twice', () => {
@@ -290,7 +290,7 @@ describe('term.js addons', () => {
assert.equal(term.buffer.lines.length, term.rows);
assert.deepEqual(term.buffer.lines.get(0), promptLine);
for (let i = 1; i < term.rows; i++) {
- assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
+ assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
});
});
diff --git a/src/Terminal.ts b/src/Terminal.ts
index 33d8e60f2..0a488ea2f 100644
--- a/src/Terminal.ts
+++ b/src/Terminal.ts
@@ -21,11 +21,11 @@
* http://linux.die.net/man/7/urxvt
*/
-import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types';
+import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharacterJoinerHandler, IBufferLine, IAttributeData } from './Types';
import { IMouseZoneManager } from './ui/Types';
import { IRenderer } from './renderer/Types';
import { BufferSet } from './BufferSet';
-import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer';
+import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR_DATA } from './Buffer';
import { CompositionHelper } from './CompositionHelper';
import { EventEmitter } from './common/EventEmitter';
import { Viewport } from './Viewport';
@@ -41,7 +41,6 @@ import { addDisposableDomListener } from './ui/Lifecycle';
import * as Strings from './Strings';
import { MouseHelper } from './ui/MouseHelper';
import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager';
-import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
import { MouseZoneManager } from './ui/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ScreenDprMonitor } from './ui/ScreenDprMonitor';
@@ -52,6 +51,7 @@ import { IKeyboardEvent } from './common/Types';
import { evaluateKeyboardEvent } from './core/input/Keyboard';
import { KeyboardResultType, ICharset } from './core/Types';
import { clone } from './common/Clone';
+import { Attributes } from './BufferLine';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
@@ -168,7 +168,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
private _refreshEnd: number;
public savedCols: number;
- public curAttr: number;
+ public curAttrData: IAttributeData;
+ private _eraseAttrData: IAttributeData;
public params: (string | number)[];
public currentParam: string | number;
@@ -288,7 +289,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
// TODO: Can this be just []?
this.charsets = [null];
- this.curAttr = DEFAULT_ATTR;
+ this.curAttrData = DEFAULT_ATTR_DATA.clone();
+ this._eraseAttrData = DEFAULT_ATTR_DATA.clone();
this.params = [];
this.currentParam = 0;
@@ -328,9 +330,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
/**
* back_color_erase feature for xterm.
*/
- public eraseAttr(): number {
- // if (this.is('screen')) return DEFAULT_ATTR;
- return (DEFAULT_ATTR & ~0x1ff) | (this.curAttr & 0x1ff);
+ public eraseAttrData(): IAttributeData {
+ this._eraseAttrData.bg &= ~(Attributes.CM_MASK | 0xFFFFFF);
+ this._eraseAttrData.bg |= this.curAttrData.bg & ~0xFC000000;
+ return this._eraseAttrData;
}
/**
@@ -1175,8 +1178,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
public scroll(isWrapped: boolean = false): void {
let newLine: IBufferLine;
newLine = this._blankLine;
- if (!newLine || newLine.length !== this.cols || newLine.getFG(0) !== this.eraseAttr()) {
- newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
+ const eraseAttr = this.eraseAttrData();
+ if (!newLine || newLine.length !== this.cols || newLine.getFG(0) !== eraseAttr.fg || newLine.getBG(0) !== eraseAttr.bg) {
+ newLine = this.buffer.getBlankLine(eraseAttr, isWrapped);
this._blankLine = newLine;
}
newLine.isWrapped = isWrapped;
@@ -1745,23 +1749,12 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this.buffer.ybase = 0;
this.buffer.y = 0;
for (let i = 1; i < this.rows; i++) {
- this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR));
+ this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
this.refresh(0, this.rows - 1);
this.emit('scroll', this.buffer.ydisp);
}
- /**
- * If cur return the back color xterm feature attribute. Else return default attribute.
- * @param cur
- */
- public ch(cur?: boolean): CharData {
- if (cur) {
- return [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
- }
- return [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
- }
-
/**
* Evaluate if the current terminal is the given argument.
* @param term The terminal name to evaluate
@@ -1837,7 +1830,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
// blankLine(true) is xterm/linux behavior
const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;
this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);
- this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttr()));
+ this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttrData()));
this.updateRange(this.buffer.scrollTop);
this.updateRange(this.buffer.scrollBottom);
} else {
@@ -1882,46 +1875,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
return false;
}
- // TODO: Remove when true color is implemented
- public matchColor(r1: number, g1: number, b1: number): number {
- const hash = (r1 << 16) | (g1 << 8) | b1;
-
- if (matchColorCache[hash] !== null && matchColorCache[hash] !== undefined) {
- return matchColorCache[hash];
- }
-
- let ldiff = Infinity;
- let li = -1;
- let i = 0;
- let c: number;
- let r2: number;
- let g2: number;
- let b2: number;
- let diff: number;
-
- for (; i < DEFAULT_ANSI_COLORS.length; i++) {
- c = DEFAULT_ANSI_COLORS[i].rgba;
- r2 = c >>> 24;
- g2 = c >>> 16 & 0xFF;
- b2 = c >>> 8 & 0xFF;
- // assume that alpha is 0xFF
-
- diff = matchColorDistance(r1, g1, b1, r2, g2, b2);
-
- if (diff === 0) {
- li = i;
- break;
- }
-
- if (diff < ldiff) {
- ldiff = diff;
- li = i;
- }
- }
-
- return matchColorCache[hash] = li;
- }
-
private _visualBell(): boolean {
return false;
// return this.options.bellStyle === 'visual' ||
@@ -1944,19 +1897,3 @@ function wasModifierKeyOnlyEvent(ev: KeyboardEvent): boolean {
ev.keyCode === 17 || // Ctrl
ev.keyCode === 18; // Alt
}
-
-/**
- * TODO:
- * The below color-related code can be removed when true color is implemented.
- * It's only purpose is to match true color requests with the closest matching
- * ANSI color code.
- */
-
-const matchColorCache: {[colorRGBHash: number]: number} = {};
-
-// http://stackoverflow.com/questions/1633828
-function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {
- return Math.pow(30 * (r1 - r2), 2)
- + Math.pow(59 * (g1 - g2), 2)
- + Math.pow(11 * (b1 - b2), 2);
-}
diff --git a/src/Types.ts b/src/Types.ts
index d176799f4..b46f858f0 100644
--- a/src/Types.ts
+++ b/src/Types.ts
@@ -48,7 +48,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
insertMode: boolean;
wraparoundMode: boolean;
bracketedPasteMode: boolean;
- curAttr: number;
+ curAttrData: IAttributeData;
savedCols: number;
x10Mouse: boolean;
vt200Mouse: boolean;
@@ -70,7 +70,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
updateRange(y: number): void;
scroll(isWrapped?: boolean): void;
setgLevel(g: number): void;
- eraseAttr(): number;
+ eraseAttrData(): IAttributeData;
is(term: string): boolean;
setgCharset(g: number, charset: ICharset): void;
resize(x: number, y: number): void;
@@ -78,7 +78,6 @@ export interface IInputHandlingTerminal extends IEventEmitter {
reset(): void;
showCursor(): void;
refresh(start: number, end: number): void;
- matchColor(r1: number, g1: number, b1: number): number;
error(text: string, data?: any): void;
setOption(key: string, value: any): void;
tabSet(): void;
@@ -289,17 +288,17 @@ export interface IBuffer {
hasScrollback: boolean;
savedY: number;
savedX: number;
- savedCurAttr: number;
+ savedCurAttrData: IAttributeData;
isCursorInViewport: boolean;
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
getWrappedRangeForLine(y: number): { first: number, last: number };
nextStop(x?: number): number;
prevStop(x?: number): number;
- getBlankLine(attr: number, isWrapped?: boolean): IBufferLine;
+ getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
- getNullCell(fg?: number, bg?: number): ICellData;
- getWhitespaceCell(fg?: number, bg?: number): ICellData;
+ getNullCell(attr?: IAttributeData): ICellData;
+ getWhitespaceCell(attr?: IAttributeData): ICellData;
}
export interface IBufferSet extends IEventEmitter {
@@ -308,7 +307,7 @@ export interface IBufferSet extends IEventEmitter {
active: IBuffer;
activateNormalBuffer(): void;
- activateAltBuffer(fillAttr?: number): void;
+ activateAltBuffer(fillAttr?: IAttributeData): void;
}
export interface ISelectionManager {
@@ -513,13 +512,50 @@ export interface IEscapeSequenceParser extends IDisposable {
clearErrorHandler(): void;
}
-/** Cell data */
-export interface ICellData {
- content: number;
+/** RGB color type */
+export type IColorRGB = [number, number, number];
+
+/** Attribute data */
+export interface IAttributeData {
fg: number;
bg: number;
+
+ clone(): IAttributeData;
+
+ // flags
+ isInverse(): number;
+ isBold(): number;
+ isUnderline(): number;
+ isBlink(): number;
+ isInvisible(): number;
+ isItalic(): number;
+ isDim(): number;
+
+ // color modes
+ getColormodeFg(): number;
+ getColormodeBg(): number;
+ isFgRGB(): boolean;
+ isBgRGB(): boolean;
+ isFgPalette(): boolean;
+ isBgPalette(): boolean;
+ isFgDefault(): boolean;
+ isBgDefault(): boolean;
+
+ // colors
+ getFgColor(channels?: boolean): number | IColorRGB;
+ getBgColor(channels?: boolean): number | IColorRGB;
+
+ // shim for old API
+ getOldFlags(): number;
+ getOldFgColor(): number;
+ getOldBgColor(): number;
+}
+
+/** Cell data */
+export interface ICellData extends IAttributeData {
+ content: number;
combinedData: string;
- combined: number;
+ isCombined: number;
width: number;
chars: string;
code: number;
diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts
index bf7c5616d..2b2ba1bb2 100644
--- a/src/renderer/TextRenderLayer.ts
+++ b/src/renderer/TextRenderLayer.ts
@@ -80,7 +80,6 @@ export class TextRenderLayer extends BaseRenderLayer {
// Can either represent character(s) for a single cell or multiple cells
// if indicated by a character joiner.
let chars = this._cell.chars || WHITESPACE_CELL_CHAR;
- const attr = this._cell.fg;
let width = this._cell.width;
// If true, indicates that the current character(s) to draw were joined.
@@ -137,9 +136,9 @@ export class TextRenderLayer extends BaseRenderLayer {
}
}
- const flags = attr >> 18;
- let bg = attr & 0x1ff;
- let fg = (attr >> 9) & 0x1ff;
+ const flags = this._cell.getOldFlags();
+ let bg = this._cell.getOldBgColor();
+ let fg = this._cell.getOldFgColor();
// If inverse flag is on, the foreground should become the background.
if (flags & FLAGS.INVERSE) {
diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts
index e06990d78..c80cea27b 100644
--- a/src/renderer/dom/DomRendererRowFactory.test.ts
+++ b/src/renderer/dom/DomRendererRowFactory.test.ts
@@ -6,11 +6,9 @@
import jsdom = require('jsdom');
import { assert } from 'chai';
import { DomRendererRowFactory } from './DomRendererRowFactory';
-import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '../../Buffer';
-import { FLAGS } from '../Types';
-import { BufferLine, CellData } from '../../BufferLine';
+import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR_DATA } from '../../Buffer';
+import { BufferLine, CellData, FgFlags, BgFlags, Attributes } from '../../BufferLine';
import { IBufferLine } from '../../Types';
-import { DEFAULT_COLOR } from '../atlas/Types';
describe('DomRendererRowFactory', () => {
let dom: jsdom.JSDOM;
@@ -61,7 +59,9 @@ describe('DomRendererRowFactory', () => {
describe('attributes', () => {
it('should add class for bold', () => {
- lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR | (FLAGS.BOLD << 18), 'a', 1, 'a'.charCodeAt(0)]));
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
'a'
@@ -69,7 +69,9 @@ describe('DomRendererRowFactory', () => {
});
it('should add class for italic', () => {
- lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR | (FLAGS.ITALIC << 18), 'a', 1, 'a'.charCodeAt(0)]));
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
'a'
@@ -77,9 +79,12 @@ describe('DomRendererRowFactory', () => {
});
it('should add classes for 256 foreground colors', () => {
- const defaultAttrNoFgColor = (0 << 9) | (DEFAULT_COLOR << 0);
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg |= Attributes.CM_P256;
for (let i = 0; i < 256; i++) {
- lineData.setCell(0, CellData.fromCharData([defaultAttrNoFgColor | (i << 9), 'a', 1, 'a'.charCodeAt(0)]));
+ cell.fg &= ~Attributes.PCOLOR_MASK;
+ cell.fg |= i;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
`a`
@@ -88,9 +93,12 @@ describe('DomRendererRowFactory', () => {
});
it('should add classes for 256 background colors', () => {
- const defaultAttrNoBgColor = (DEFAULT_ATTR << 9) | (0 << 0);
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.bg |= Attributes.CM_P256;
for (let i = 0; i < 256; i++) {
- lineData.setCell(0, CellData.fromCharData([defaultAttrNoBgColor | (i << 0), 'a', 1, 'a'.charCodeAt(0)]));
+ cell.bg &= ~Attributes.PCOLOR_MASK;
+ cell.bg |= i;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
`a`
@@ -99,7 +107,10 @@ describe('DomRendererRowFactory', () => {
});
it('should correctly invert colors', () => {
- lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (2 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]));
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE;
+ cell.bg |= Attributes.CM_P16 | 1;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
'a'
@@ -107,7 +118,10 @@ describe('DomRendererRowFactory', () => {
});
it('should correctly invert default fg color', () => {
- lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (DEFAULT_ATTR << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]));
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg |= FgFlags.INVERSE;
+ cell.bg |= Attributes.CM_P16 | 1;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
'a'
@@ -115,7 +129,9 @@ describe('DomRendererRowFactory', () => {
});
it('should correctly invert default bg color', () => {
- lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (1 << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]));
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
'a'
@@ -123,8 +139,12 @@ describe('DomRendererRowFactory', () => {
});
it('should turn bold fg text bright', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg |= FgFlags.BOLD | Attributes.CM_P16;
for (let i = 0; i < 8; i++) {
- lineData.setCell(0, CellData.fromCharData([(FLAGS.BOLD << 18) | (i << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]));
+ cell.fg &= ~Attributes.PCOLOR_MASK;
+ cell.fg |= i;
+ lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
assert.equal(getFragmentHtml(fragment),
`a`
diff --git a/src/renderer/dom/DomRendererRowFactory.ts b/src/renderer/dom/DomRendererRowFactory.ts
index 83a4651e2..41091a637 100644
--- a/src/renderer/dom/DomRendererRowFactory.ts
+++ b/src/renderer/dom/DomRendererRowFactory.ts
@@ -41,7 +41,6 @@ export class DomRendererRowFactory {
for (let x = 0; x < lineLength; x++) {
lineData.loadCell(x, this._cell);
- const attr = this._cell.fg;
const width = this._cell.width;
// The character to the left is a wide character, drawing is owned by the char at x-1
@@ -54,9 +53,9 @@ export class DomRendererRowFactory {
charElement.style.width = `${cellWidth * width}px`;
}
- const flags = attr >> 18;
- let bg = attr & 0x1ff;
- let fg = (attr >> 9) & 0x1ff;
+ const flags = this._cell.getOldFlags();
+ let bg = this._cell.getOldBgColor();
+ let fg = this._cell.getOldFgColor();
if (isCursorRow && x === cursorX) {
charElement.classList.add(CURSOR_CLASS);
diff --git a/src/ui/TestUtils.test.ts b/src/ui/TestUtils.test.ts
index 9d525fbf9..4772a9d8c 100644
--- a/src/ui/TestUtils.test.ts
+++ b/src/ui/TestUtils.test.ts
@@ -4,12 +4,13 @@
*/
import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Types';
-import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferLine, IBufferStringIterator, ICellData } from '../Types';
+import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferLine, IBufferStringIterator, ICellData, IAttributeData } from '../Types';
import { ICircularList, XtermListener } from '../common/Types';
import { Buffer } from '../Buffer';
import * as Browser from '../core/Platform';
import { ITheme, IDisposable, IMarker } from 'xterm';
import { Terminal } from '../Terminal';
+import { AttributeData } from '../BufferLine';
export class TestTerminal extends Terminal {
writeSync(data: string): void {
@@ -187,7 +188,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
insertMode: boolean;
wraparoundMode: boolean;
bracketedPasteMode: boolean;
- curAttr: number;
+ curAttrData = new AttributeData();
savedCols: number;
x10Mouse: boolean;
vt200Mouse: boolean;
@@ -222,7 +223,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
setgLevel(g: number): void {
throw new Error('Method not implemented.');
}
- eraseAttr(): number {
+ eraseAttrData(): IAttributeData {
throw new Error('Method not implemented.');
}
eraseRight(x: number, y: number): void {
@@ -309,7 +310,7 @@ export class MockBuffer implements IBuffer {
scrollTop: number;
savedY: number;
savedX: number;
- savedCurAttr: number;
+ savedCurAttrData = new AttributeData();
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
}
@@ -325,7 +326,7 @@ export class MockBuffer implements IBuffer {
setLines(lines: ICircularList): void {
this.lines = lines;
}
- getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
+ getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
return Buffer.prototype.getBlankLine.apply(this, arguments);
}
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
@@ -334,10 +335,10 @@ export class MockBuffer implements IBuffer {
iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
return Buffer.prototype.iterator.apply(this, arguments);
}
- getNullCell(fg: number = 0, bg: number = 0): ICellData {
+ getNullCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
- getWhitespaceCell(fg: number = 0, bg: number = 0): ICellData {
+ getWhitespaceCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
}