diff --git a/src/Buffer.ts b/src/Buffer.ts index 6d2186457f..0f75bdad1a 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -8,8 +8,9 @@ import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIt import { EventEmitter } from './common/EventEmitter'; import { IMarker } from 'xterm'; import { BufferLine, BufferLineTypedArray } from './BufferLine'; +import { DEFAULT_COLOR } from './renderer/atlas/Types'; -export const DEFAULT_ATTR = (0 << 18) | (257 << 9) | (256 << 0); +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; diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 84e290e7ff..2afdebb54e 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -9,6 +9,7 @@ import { DIM_OPACITY, INVERTED_DEFAULT_COLOR, IGlyphIdentifier } from './atlas/T import BaseCharAtlas from './atlas/BaseCharAtlas'; import { acquireCharAtlas } from './atlas/CharAtlasCache'; import { CHAR_DATA_CHAR_INDEX } from '../Buffer'; +import { is256Color } from './atlas/CharAtlasUtils'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; @@ -298,7 +299,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { if (fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; - } else if (fg < 256) { + } else if (is256Color(fg)) { // 256 color support this._ctx.fillStyle = this._colors.ansi[fg].css; } else { diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index 8679939a28..855830e40b 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -7,6 +7,7 @@ import { ILinkHoverEvent, ITerminal, ILinkifierAccessor, LinkHoverEventTypes } f import { IColorSet, IRenderDimensions } from './Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from './atlas/Types'; +import { is256Color } from './atlas/CharAtlasUtils'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkHoverEvent = null; @@ -42,7 +43,7 @@ export class LinkRenderLayer extends BaseRenderLayer { private _onLinkHover(e: ILinkHoverEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; - } else if (e.fg < 256) { + } else if (is256Color(e.fg)) { // 256 color support this._ctx.fillStyle = this._colors.ansi[e.fg].css; } else { diff --git a/src/renderer/TextRenderLayer.ts b/src/renderer/TextRenderLayer.ts index 7f10e7c9e1..7b3feed7db 100644 --- a/src/renderer/TextRenderLayer.ts +++ b/src/renderer/TextRenderLayer.ts @@ -6,9 +6,10 @@ import { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, NULL_CELL_CODE } from '../Buffer'; import { FLAGS, IColorSet, IRenderDimensions, ICharacterJoinerRegistry } from './Types'; import { CharData, ITerminal } from '../Types'; -import { INVERTED_DEFAULT_COLOR } from './atlas/Types'; +import { INVERTED_DEFAULT_COLOR, DEFAULT_COLOR } from './atlas/Types'; import { GridCache } from './GridCache'; import { BaseRenderLayer } from './BaseRenderLayer'; +import { is256Color } from './atlas/CharAtlasUtils'; /** * This CharData looks like a null character, which will forc a clear and render @@ -143,10 +144,10 @@ export class TextRenderLayer extends BaseRenderLayer { const temp = bg; bg = fg; fg = temp; - if (fg === 256) { + if (fg === DEFAULT_COLOR) { fg = INVERTED_DEFAULT_COLOR; } - if (bg === 257) { + if (bg === DEFAULT_COLOR) { bg = INVERTED_DEFAULT_COLOR; } } @@ -186,7 +187,7 @@ export class TextRenderLayer extends BaseRenderLayer { let nextFillStyle = null; // null represents default background color if (bg === INVERTED_DEFAULT_COLOR) { nextFillStyle = this._colors.foreground.css; - } else if (bg < 256) { + } else if (is256Color(bg)) { nextFillStyle = this._colors.ansi[bg].css; } @@ -230,7 +231,7 @@ export class TextRenderLayer extends BaseRenderLayer { this._ctx.save(); if (fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; - } else if (fg < 256) { + } else if (is256Color(fg)) { // 256 color support this._ctx.fillStyle = this._colors.ansi[fg].css; } else { diff --git a/src/renderer/atlas/CharAtlasUtils.ts b/src/renderer/atlas/CharAtlasUtils.ts index 59ac07df63..c504f77e21 100644 --- a/src/renderer/atlas/CharAtlasUtils.ts +++ b/src/renderer/atlas/CharAtlasUtils.ts @@ -6,6 +6,7 @@ import { ITerminal } from '../../Types'; import { IColorSet } from '../Types'; import { ICharAtlasConfig } from '../../shared/atlas/Types'; +import { DEFAULT_COLOR } from './Types'; export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig { // null out some fields that don't matter @@ -51,3 +52,7 @@ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean a.colors.foreground === b.colors.foreground && a.colors.background === b.colors.background; } + +export function is256Color(colorCode: number): boolean { + return colorCode < DEFAULT_COLOR; +} diff --git a/src/renderer/atlas/DynamicCharAtlas.ts b/src/renderer/atlas/DynamicCharAtlas.ts index 633369000f..b6b323f084 100644 --- a/src/renderer/atlas/DynamicCharAtlas.ts +++ b/src/renderer/atlas/DynamicCharAtlas.ts @@ -42,7 +42,7 @@ interface IGlyphCacheValue { inBitmap: boolean; } -function getGlyphCacheKey(glyph: IGlyphIdentifier): number { +export function getGlyphCacheKey(glyph: IGlyphIdentifier): number { // Note that this only returns a valid key when code < 256 // Layout: // 0b00000000000000000000000000000001: italic (1) diff --git a/src/renderer/atlas/StaticCharAtlas.ts b/src/renderer/atlas/StaticCharAtlas.ts index c0d8a81442..8dc8be74e9 100644 --- a/src/renderer/atlas/StaticCharAtlas.ts +++ b/src/renderer/atlas/StaticCharAtlas.ts @@ -3,10 +3,11 @@ * @license MIT */ -import { DIM_OPACITY, IGlyphIdentifier } from './Types'; +import { DIM_OPACITY, IGlyphIdentifier, DEFAULT_COLOR } from './Types'; import { CHAR_ATLAS_CELL_SPACING, ICharAtlasConfig } from '../../shared/atlas/Types'; import { generateStaticCharAtlasTexture } from '../../shared/atlas/CharAtlasGenerator'; import BaseCharAtlas from './BaseCharAtlas'; +import { is256Color } from './CharAtlasUtils'; export default class StaticCharAtlas extends BaseCharAtlas { private _texture: HTMLCanvasElement | ImageBitmap; @@ -41,8 +42,8 @@ export default class StaticCharAtlas extends BaseCharAtlas { const isAscii = glyph.code < 256; // A color is basic if it is one of the 4 bit ANSI colors. const isBasicColor = glyph.fg < 16; - const isDefaultColor = glyph.fg >= 256; - const isDefaultBackground = glyph.bg >= 256; + const isDefaultColor = glyph.fg === DEFAULT_COLOR; + const isDefaultBackground = glyph.bg === DEFAULT_COLOR; return isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground && !glyph.italic; } @@ -58,9 +59,9 @@ export default class StaticCharAtlas extends BaseCharAtlas { } let colorIndex = 0; - if (glyph.fg < 256) { + if (is256Color(glyph.fg)) { colorIndex = 2 + glyph.fg + (glyph.bold ? 16 : 0); - } else { + } else if (glyph.fg === DEFAULT_COLOR) { // If default color and bold if (glyph.bold) { colorIndex = 1; diff --git a/src/renderer/atlas/Types.ts b/src/renderer/atlas/Types.ts index 6fb3c5d16c..76cfd07da3 100644 --- a/src/renderer/atlas/Types.ts +++ b/src/renderer/atlas/Types.ts @@ -3,7 +3,8 @@ * @license MIT */ -export const INVERTED_DEFAULT_COLOR = -1; +export const DEFAULT_COLOR = 256; +export const INVERTED_DEFAULT_COLOR = 257; export const DIM_OPACITY = 0.5; export interface IGlyphIdentifier { diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 73c63b4652..a0cefd677c 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -10,6 +10,7 @@ import { EventEmitter } from '../../common/EventEmitter'; import { ColorManager } from '../ColorManager'; import { RenderDebouncer } from '../../ui/RenderDebouncer'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; +import { INVERTED_DEFAULT_COLOR } from '../atlas/Types'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; @@ -199,6 +200,9 @@ export class DomRenderer extends EventEmitter implements IRenderer { `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; }); + styles += + `${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${this.colorManager.colors.background.css}; }` + + `${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this.colorManager.colors.foreground.css}; }`; this._themeStyleElement.innerHTML = styles; return this.colorManager.colors; diff --git a/src/renderer/dom/DomRendererRowFactory.test.ts b/src/renderer/dom/DomRendererRowFactory.test.ts index 03f4178445..67342da01b 100644 --- a/src/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/renderer/dom/DomRendererRowFactory.test.ts @@ -10,6 +10,7 @@ import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '. import { FLAGS } from '../Types'; import { BufferLine } from '../../BufferLine'; import { IBufferLine } from '../../Types'; +import { DEFAULT_COLOR } from '../atlas/Types'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; @@ -76,7 +77,7 @@ describe('DomRendererRowFactory', () => { }); it('should add classes for 256 foreground colors', () => { - const defaultAttrNoFgColor = (0 << 9) | (256 << 0); + const defaultAttrNoFgColor = (0 << 9) | (DEFAULT_COLOR << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoFgColor | (i << 9), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); @@ -87,7 +88,7 @@ describe('DomRendererRowFactory', () => { }); it('should add classes for 256 background colors', () => { - const defaultAttrNoBgColor = (257 << 9) | (0 << 0); + const defaultAttrNoBgColor = (DEFAULT_ATTR << 9) | (0 << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoBgColor | (i << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); @@ -106,24 +107,24 @@ describe('DomRendererRowFactory', () => { }); it('should correctly invert default fg color', () => { - lineData.set(0, [(FLAGS.INVERSE << 18) | (257 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); + lineData.set(0, [(FLAGS.INVERSE << 18) | (DEFAULT_ATTR << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), - 'a' + 'a' ); }); it('should correctly invert default bg color', () => { - lineData.set(0, [(FLAGS.INVERSE << 18) | (1 << 9) | (256 << 0), 'a', 1, 'a'.charCodeAt(0)]); + lineData.set(0, [(FLAGS.INVERSE << 18) | (1 << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), - 'a' + 'a' ); }); it('should turn bold fg text bright', () => { for (let i = 0; i < 8; i++) { - lineData.set(0, [(FLAGS.BOLD << 18) | (i << 9) | (256 << 0), 'a', 1, 'a'.charCodeAt(0)]); + lineData.set(0, [(FLAGS.BOLD << 18) | (i << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]); 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 07303e2427..54f088d453 100644 --- a/src/renderer/dom/DomRendererRowFactory.ts +++ b/src/renderer/dom/DomRendererRowFactory.ts @@ -6,6 +6,7 @@ import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from '../../Buffer'; import { FLAGS } from '../Types'; import { IBufferLine } from '../../Types'; +import { DEFAULT_COLOR, INVERTED_DEFAULT_COLOR } from '../atlas/Types'; export const BOLD_CLASS = 'xterm-bold'; export const ITALIC_CLASS = 'xterm-italic'; @@ -79,16 +80,17 @@ export class DomRendererRowFactory { const temp = bg; bg = fg; fg = temp; - if (fg === 256) { - fg = 0; + if (fg === DEFAULT_COLOR) { + fg = INVERTED_DEFAULT_COLOR; } - if (bg === 257) { - bg = 15; + if (bg === DEFAULT_COLOR) { + bg = INVERTED_DEFAULT_COLOR; } } if (flags & FLAGS.BOLD) { - // Convert the FG color to the bold variant + // Convert the FG color to the bold variant. This should not happen when + // the fg is the inverse default color as there is no bold variant. if (fg < 8) { fg += 8; } @@ -100,10 +102,10 @@ export class DomRendererRowFactory { } charElement.textContent = char; - if (fg !== 257) { + if (fg !== DEFAULT_COLOR) { charElement.classList.add(`xterm-fg-${fg}`); } - if (bg !== 256) { + if (bg !== DEFAULT_COLOR) { charElement.classList.add(`xterm-bg-${bg}`); } fragment.appendChild(charElement);