Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
63eb9a4
Initial bg/fg renderer decoration proof of concept
Tyriar May 9, 2022
0c6877f
Move color to common, work with IColor
Tyriar May 9, 2022
1059846
Fix color channel used by luminance functions
Tyriar May 9, 2022
13abd2a
Support min contrast ratio in decoration fg/bg
Tyriar May 9, 2022
9f1db12
bg/fg decorations mostly working in canvas renderer
Tyriar May 9, 2022
714d4b1
Fix fg color when there is a decoration override in canvas renderer
Tyriar May 10, 2022
d293883
Get Webgl rendering fg/bg overrides
Tyriar May 10, 2022
40716b8
Consolidate override logic into model/WebglRenderer
Tyriar May 10, 2022
0a26cee
Pull override color logic into a function
Tyriar May 10, 2022
7c87f5b
Start to use new API in search addon
Tyriar May 10, 2022
de574a2
Full re-render when there are decoration changes
Tyriar May 10, 2022
b85f5aa
Use correct row for override in canvas renderer
Tyriar May 10, 2022
a67e728
Use correct row for override in webgl renderer
Tyriar May 10, 2022
ba61509
Remove react to changes idea
Tyriar May 10, 2022
e9643f5
Ensure texture atlas isn't for overrides used in canvas renderer
Tyriar May 10, 2022
1628795
Fix fg/bg flags being set by negative ints
Tyriar May 10, 2022
afe1d7e
Fix webgl contrast tests since a bug in luminance was fixed
Tyriar May 10, 2022
f502f55
Merge branch 'master' into bg_decorations
Tyriar May 10, 2022
6b4df21
Remove unneeded code
Tyriar May 10, 2022
edba006
Clear all decorations on reset
Tyriar May 10, 2022
abdce87
Fix cases where only one override was with tests
Tyriar May 11, 2022
c0b4eda
Reduce duplicate with getDecorationsAtCell method
Tyriar May 11, 2022
931ee9e
Maintain decorations sorted by line
Tyriar May 11, 2022
999f255
Fix sorted list edge case
Tyriar May 11, 2022
324ea2e
Clean up/resolve todos
Tyriar May 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions addons/xterm-addon-search/src/SearchAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,26 +653,27 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whether a result was selected.
*/
private _selectResult(result: ISearchResult | undefined, decorations?: ISearchDecorationOptions, noScroll?: boolean): boolean {
private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
if (!result) {
terminal.clearSelection();
return false;
}
terminal.select(result.col, result.row, result.size);
if (decorations?.activeMatchColorOverviewRuler) {
if (options) {
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (marker) {
this._selectedDecoration = terminal.registerDecoration({
marker,
x: result.col,
width: result.size,
backgroundColor: options.activeMatchBackground,
overviewRulerOptions: {
color: decorations.activeMatchColorOverviewRuler
color: options.activeMatchColorOverviewRuler
}
});
this._selectedDecoration?.onRender((e) => this._applyStyles(e, decorations.activeMatchBackground, decorations.activeMatchBorder));
this._selectedDecoration?.onRender((e) => this._applyStyles(e, options.activeMatchBorder));
this._selectedDecoration?.onDispose(() => marker.dispose());
}
}
Expand All @@ -695,15 +696,12 @@ export class SearchAddon implements ITerminalAddon {
* @param borderColor the border color to apply
* @returns
*/
private _applyStyles(element: HTMLElement, backgroundColor: string | undefined, borderColor: string | undefined): void {
private _applyStyles(element: HTMLElement, borderColor: string | undefined): void {
if (element.clientWidth <= 0) {
return;
}
if (!element.classList.contains('xterm-find-result-decoration')) {
element.classList.add('xterm-find-result-decoration');
if (backgroundColor) {
element.style.backgroundColor = backgroundColor;
}
if (borderColor) {
element.style.outline = `1px solid ${borderColor}`;
}
Expand All @@ -719,18 +717,20 @@ export class SearchAddon implements ITerminalAddon {
private _createResultDecoration(result: ISearchResult, options: ISearchDecorationOptions): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (!marker || !options?.matchOverviewRuler) {
if (!marker) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration({
marker,
x: result.col,
width: result.size,
backgroundColor: options.matchBackground,
overviewRulerOptions: this._resultDecorations?.get(marker.line) ? undefined : {
color: options.matchOverviewRuler, position: 'center'
color: options.matchOverviewRuler,
position: 'center'
}
});
findResultDecoration?.onRender((e) => this._applyStyles(e, options.matchBackground, options.matchBorder));
findResultDecoration?.onRender((e) => this._applyStyles(e, options.matchBorder));
findResultDecoration?.onDispose(() => marker.dispose());
return findResultDecoration;
}
Expand Down
6 changes: 3 additions & 3 deletions addons/xterm-addon-search/typings/xterm-addon-search.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ declare module 'xterm-addon-search' {
*/
interface ISearchDecorationOptions {
/**
* The background color of a match.
* The background color of a match, this must use #RRGGBB format.
*/
matchBackground?: string;

/**
* The border color of a match
* The border color of a match.
*/
matchBorder?: string;

Expand All @@ -60,7 +60,7 @@ declare module 'xterm-addon-search' {
matchOverviewRuler: string;

/**
* The background color for the currently active match.
* The background color for the currently active match, this must use #RRGGBB format.
*/
activeMatchBackground?: string;

Expand Down
7 changes: 5 additions & 2 deletions addons/xterm-addon-webgl/src/GlyphRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRaster
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_BG_OFFSET } from './RenderModel';
import { fill } from 'common/TypedArrayUtils';
import { slice } from './TypedArray';
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes, FgFlags } from 'common/buffer/Constants';
import { NULL_CELL_CODE, Attributes, FgFlags } from 'common/buffer/Constants';
import { Terminal, IBufferLine } from 'xterm';
import { IColorSet, IColor } from 'browser/Types';
import { IColor } from 'common/Types';
import { IColorSet } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { AttributeData } from 'common/buffer/AttributeData';

Expand Down Expand Up @@ -187,6 +188,8 @@ export class GlyphRenderer {
if (!this._atlas) {
return;
}

// Get the glyph
if (chars && chars.length > 1) {
rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg);
} else {
Expand Down
3 changes: 2 additions & 1 deletion addons/xterm-addon-webgl/src/RectangleRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelect
import { fill } from 'common/TypedArrayUtils';
import { Attributes, FgFlags } from 'common/buffer/Constants';
import { Terminal } from 'xterm';
import { IColorSet, IColor } from 'browser/Types';
import { IColor } from 'common/Types';
import { IColorSet } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';

Expand Down
4 changes: 3 additions & 1 deletion addons/xterm-addon-webgl/src/WebglAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ICharacterJoinerService, IRenderService } from 'browser/services/Servic
import { IColorSet } from 'browser/Types';
import { EventEmitter } from 'common/EventEmitter';
import { isSafari } from 'common/Platform';
import { IDecorationService } from 'common/services/Services';

export class WebglAddon implements ITerminalAddon {
private _terminal?: Terminal;
Expand All @@ -30,8 +31,9 @@ export class WebglAddon implements ITerminalAddon {
this._terminal = terminal;
const renderService: IRenderService = (terminal as any)._core._renderService;
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, this._preserveDrawingBuffer);
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, decorationService, this._preserveDrawingBuffer);
this._renderer.onContextLoss(() => this._onContextLoss.fire());
renderService.setRenderer(this._renderer);
}
Expand Down
80 changes: 72 additions & 8 deletions addons/xterm-addon-webgl/src/WebglRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { RectangleRenderer } from './RectangleRenderer';
import { IWebGL2RenderingContext } from './Types';
import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
import { Disposable } from 'common/Lifecycle';
import { Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { Terminal, IEvent } from 'xterm';
import { IRenderLayer } from './renderLayer/Types';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
Expand All @@ -23,6 +23,7 @@ import { addDisposableDomListener } from 'browser/Lifecycle';
import { ICharacterJoinerService } from 'browser/services/Services';
import { CharData, ICellData } from 'common/Types';
import { AttributeData } from 'common/buffer/AttributeData';
import { IDecorationService } from 'common/services/Services';

export class WebglRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
Expand All @@ -31,6 +32,7 @@ export class WebglRenderer extends Disposable implements IRenderer {

private _model: RenderModel = new RenderModel();
private _workCell: CellData = new CellData();
private _workColors: { fg: number, bg: number } = { fg: 0, bg: 0 };

private _canvas: HTMLCanvasElement;
private _gl: IWebGL2RenderingContext;
Expand All @@ -52,6 +54,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _terminal: Terminal,
private _colors: IColorSet,
private readonly _characterJoinerService: ICharacterJoinerService,
private readonly _decorationService: IDecorationService,
preserveDrawingBuffer?: boolean
) {
super();
Expand Down Expand Up @@ -331,14 +334,17 @@ export class WebglRenderer extends Disposable implements IRenderer {
let code = cell.getCode();
const i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;

// Load colors/resolve overrides into work colors
this._loadColorsForCell(x, row);

if (code !== NULL_CELL_CODE) {
this._model.lineLengths[y] = x + 1;
}

// Nothing has changed, no updates needed
if (this._model.cells[i] === code &&
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === cell.bg &&
this._model.cells[i + RENDER_MODEL_FG_OFFSET] === cell.fg) {
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._workColors.bg &&
this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._workColors.fg) {
continue;
}

Expand All @@ -349,10 +355,10 @@ export class WebglRenderer extends Disposable implements IRenderer {

// Cache the results in the model
this._model.cells[i] = code;
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = cell.bg;
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = cell.fg;
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;

this._glyphRenderer.updateCell(x, y, code, cell.bg, cell.fg, chars);
this._glyphRenderer.updateCell(x, y, code, this._workColors.bg, this._workColors.fg, chars);

if (isJoined) {
// Restore work cell
Expand All @@ -363,8 +369,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
const j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, NULL_CELL_CHAR);
this._model.cells[j] = NULL_CELL_CODE;
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workCell.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._workCell.fg;
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
}
}
}
Expand All @@ -376,6 +382,64 @@ export class WebglRenderer extends Disposable implements IRenderer {
}
}

/**
* Loads colors for the cell into the work colors object. This resolves overrides/inverse if
* necessary which is why the work cell object is not used.
*/
private _loadColorsForCell(x: number, y: number): void {
this._workColors.bg = this._workCell.bg;
this._workColors.fg = this._workCell.fg;

// Get any decoration foreground/background overrides, this happens on the model to avoid
// spreading decoration override logic throughout the different sub-renderers
let bgOverride: number | undefined;
let fgOverride: number | undefined;
for (const d of this._decorationService.getDecorationsAtCell(x, y)) {
if (d.backgroundColorRGB) {
bgOverride = (d.backgroundColorRGB.rgba >> 8) >>> 0 & 0xFFFFFF;
}
if (d.foregroundColorRGB) {
fgOverride = (d.foregroundColorRGB.rgba >> 8) >>> 0 & 0xFFFFFF;
}
}

// Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
// ahead of time in order to use the correct cache key
if (bgOverride !== undefined) {
// Non-RGB attributes from model + override + force RGB color mode
bgOverride = (this._workCell.bg & ~Attributes.RGB_MASK) | bgOverride | Attributes.CM_RGB;
}
if (fgOverride !== undefined) {
// Non-RGB attributes from model + force disable inverse + override + force RGB color mode
fgOverride = (this._workCell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | fgOverride | Attributes.CM_RGB;
}

// Handle case where inverse was specified by only one of bgOverride or fgOverride was set,
// resolving the other inverse color and setting the inverse flag if needed.
if (this._workColors.fg & FgFlags.INVERSE) {
if (bgOverride !== undefined && fgOverride === undefined) {
// Resolve bg color type (default color has a different meaning in fg vs bg)
if ((this._workColors.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
fgOverride = (this._workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | ((this._colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
} else {
fgOverride = (this._workColors.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this._workColors.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
}
}
if (bgOverride === undefined && fgOverride !== undefined) {
// Resolve bg color type (default color has a different meaning in fg vs bg)
if ((this._workColors.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
bgOverride = (this._workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | ((this._colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
} else {
bgOverride = (this._workColors.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this._workColors.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
}
}
}

// Use the override if it exists
this._workColors.bg = bgOverride ?? this._workColors.bg;
this._workColors.fg = fgOverride ?? this._workColors.fg;
}

private _updateSelectionModel(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
const terminal = this._terminal;

Expand Down
3 changes: 2 additions & 1 deletion addons/xterm-addon-webgl/src/atlas/CharAtlasUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import { ICharAtlasConfig } from './Types';
import { Attributes } from 'common/buffer/Constants';
import { Terminal, FontWeight } from 'xterm';
import { IColorSet, IColor } from 'browser/Types';
import { IColorSet } from 'browser/Types';
import { IColor } from 'common/Types';

const NULL_COLOR: IColor = {
css: '',
Expand Down
4 changes: 2 additions & 2 deletions addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/atlas/Constants';
import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types';
import { DEFAULT_COLOR, Attributes } from 'common/buffer/Constants';
import { throwIfFalsy } from '../WebglUtils';
import { IColor } from 'browser/Types';
import { IColor } from 'common/Types';
import { IDisposable } from 'xterm';
import { AttributeData } from 'common/buffer/AttributeData';
import { channels, rgba } from 'browser/Color';
import { channels, rgba } from 'common/Color';
import { tryDrawCustomChar } from 'browser/renderer/CustomGlyphs';
import { isPowerlineGlyph } from 'browser/renderer/RendererUtils';

Expand Down
1 change: 1 addition & 0 deletions addons/xterm-addon-webgl/src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
]
},
"strict": true,
"downlevelIteration": true,
"types": [
"../../../node_modules/@types/mocha"
]
Expand Down
Loading