Merge pull request #4920 from Tyriar/tyriar/bg_blend

Add bg+powerline background blend behavior
This commit is contained in:
Daniel Imms
2023-12-19 08:57:03 -08:00
committed by GitHub
10 changed files with 226 additions and 46 deletions
@@ -28,5 +28,10 @@ test.describe('Canvas Renderer Integration Tests', () => {
test.skip(({ browserName }) => browserName === 'webkit');
injectSharedRendererTests(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper, async () => {
await ctx.page.evaluate(`
window.addon = new window.CanvasAddon(true);
window.term.loadAddon(window.addon);
`);
});
});
@@ -29,5 +29,10 @@ test.describe('WebGL Renderer Integration Tests', async () => {
}
injectSharedRendererTests(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper, async () => {
await ctx.page.evaluate(`
window.addon = new window.WebglAddon(true);
window.term.loadAddon(window.addon);
`);
});
});
@@ -11,7 +11,7 @@ import { ICoreService, IDecorationService, IOptionsService } from 'common/servic
import { color, rgba } from 'common/Color';
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
import { excludeFromContrastRatioDemands } from 'browser/renderer/shared/RendererUtils';
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
import { AttributeData } from 'common/buffer/AttributeData';
import { WidthCache } from 'browser/renderer/dom/WidthCache';
import { IColorContrastCache } from 'browser/Types';
@@ -458,7 +458,7 @@ export class DomRendererRowFactory {
}
private _applyMinimumContrast(element: HTMLElement, bg: IColor, fg: IColor, cell: ICellData, bgOverride: IColor | undefined, fgOverride: IColor | undefined): boolean {
if (this._optionsService.rawOptions.minimumContrastRatio === 1 || excludeFromContrastRatioDemands(cell.getCode())) {
if (this._optionsService.rawOptions.minimumContrastRatio === 1 || treatGlyphAsBackgroundColor(cell.getCode())) {
return false;
}
@@ -5,6 +5,8 @@ import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle
import { IDecorationService, IOptionsService } from 'common/services/Services';
import { ICellData } from 'common/Types';
import { Terminal } from '@xterm/xterm';
import { rgba } from 'common/Color';
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
// Work variables to avoid garbage collection
let $fg = 0;
@@ -65,11 +67,11 @@ export class CellColorResolver {
// Apply decorations on the bottom layer
this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
if (d.backgroundColorRGB) {
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
$bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
$hasBg = true;
}
if (d.foregroundColorRGB) {
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
$fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
$hasFg = true;
}
});
@@ -77,10 +79,94 @@ export class CellColorResolver {
// Apply the selection color if needed
$isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y);
if ($isSelected) {
$bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
// If the cell has a bg color, retain the color by blending it with the selection color
if (
(this.result.fg & FgFlags.INVERSE) ||
(this.result.bg & Attributes.CM_MASK) !== Attributes.CM_DEFAULT
) {
// Resolve the standard bg color
if (this.result.fg & FgFlags.INVERSE) {
switch (this.result.fg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
$bg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
$bg = (this.result.fg & Attributes.RGB_MASK) << 8 | 0xFF;
break;
case Attributes.CM_DEFAULT:
default:
$bg = this._themeService.colors.foreground.rgba;
}
} else {
switch (this.result.bg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
$bg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
$bg = this.result.bg & Attributes.RGB_MASK << 8 | 0xFF;
break;
// No need to consider default bg color here as it's not possible
}
}
// Blend with selection bg color
$bg = rgba.blend(
$bg,
((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80
) >> 8 & Attributes.RGB_MASK;
} else {
$bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK;
}
$hasBg = true;
// Apply explicit selection foreground if present
if ($colors.selectionForeground) {
$fg = $colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
$fg = $colors.selectionForeground.rgba >> 8 & Attributes.RGB_MASK;
$hasFg = true;
}
// Overwrite fg as bg if it's a special decorative glyph (eg. powerline)
if (treatGlyphAsBackgroundColor(cell.getCode())) {
// Inverse default background should be treated as transparent
if (
(this.result.fg & FgFlags.INVERSE) &&
(this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT
) {
$fg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK;
} else {
if (this.result.fg & FgFlags.INVERSE) {
switch (this.result.bg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
$fg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
$fg = this.result.bg & Attributes.RGB_MASK << 8 | 0xFF;
break;
// No need to consider default bg color here as it's not possible
}
} else {
switch (this.result.fg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
$fg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
$fg = (this.result.fg & Attributes.RGB_MASK) << 8 | 0xFF;
break;
case Attributes.CM_DEFAULT:
default:
$fg = this._themeService.colors.foreground.rgba;
}
}
$fg = rgba.blend(
$fg,
((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80
) >> 8 & Attributes.RGB_MASK;
}
$hasFg = true;
}
}
@@ -88,11 +174,11 @@ export class CellColorResolver {
// Apply decorations on the top layer
this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
if (d.backgroundColorRGB) {
$bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
$bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
$hasBg = true;
}
if (d.foregroundColorRGB) {
$fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
$fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK;
$hasFg = true;
}
});
@@ -119,7 +205,7 @@ export class CellColorResolver {
if ($hasBg && !$hasFg) {
// Resolve bg color type (default color has a different meaning in fg vs bg)
if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB;
} else {
$fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
}
@@ -128,7 +214,7 @@ export class CellColorResolver {
if (!$hasBg && $hasFg) {
// Resolve bg color type (default color has a different meaning in fg vs bg)
if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB;
} else {
$bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
}
+1 -1
View File
@@ -27,7 +27,7 @@ function isBoxOrBlockGlyph(codepoint: number): boolean {
return 0x2500 <= codepoint && codepoint <= 0x259F;
}
export function excludeFromContrastRatioDemands(codepoint: number): boolean {
export function treatGlyphAsBackgroundColor(codepoint: number): boolean {
return isPowerlineGlyph(codepoint) || isBoxOrBlockGlyph(codepoint);
}
+2 -2
View File
@@ -6,7 +6,7 @@
import { IColorContrastCache } from 'browser/Types';
import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/shared/Constants';
import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
import { computeNextVariantOffset, excludeFromContrastRatioDemands, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types';
import { NULL_COLOR, color, rgba } from 'common/Color';
import { EventEmitter } from 'common/EventEmitter';
@@ -490,7 +490,7 @@ export class TextureAtlas implements ITextureAtlas {
const powerlineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
const restrictedPowerlineGlyph = chars.length === 1 && isRestrictedPowerlineGlyph(chars.charCodeAt(0));
const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, excludeFromContrastRatioDemands(chars.charCodeAt(0)));
const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, treatGlyphAsBackgroundColor(chars.charCodeAt(0)));
this._tmpCtx.fillStyle = foregroundColor.css;
// For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129)
+21
View File
@@ -271,6 +271,27 @@ describe('Color', () => {
});
describe('rgba', () => {
describe('blend', () => {
it('should blend colors based on the alpha channel', () => {
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF00), 0x000000FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF10), 0x101010FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF20), 0x202020FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF30), 0x303030FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF40), 0x404040FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF50), 0x505050FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF60), 0x606060FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF70), 0x707070FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF80), 0x808080FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFF90), 0x909090FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFA0), 0xA0A0A0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFB0), 0xB0B0B0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFC0), 0xC0C0C0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFD0), 0xD0D0D0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFE0), 0xE0E0E0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFF0), 0xF0F0F0FF);
assert.deepEqual(rgba.blend(0x000000FF, 0xFFFFFFFF), 0xFFFFFFFF);
});
});
describe('ensureContrastRatio', () => {
it('should return undefined if the color already meets the contrast ratio (black bg)', () => {
assert.equal(rgba.ensureContrastRatio(0x000000ff, 0x606060ff, 1), undefined);
+17
View File
@@ -245,6 +245,23 @@ export namespace rgb {
* Helper functions where the source type is "rgba" (number: 0xrrggbbaa).
*/
export namespace rgba {
export function blend(bg: number, fg: number): number {
$a = (fg & 0xFF) / 0xFF;
if ($a === 1) {
return fg;
}
const fgR = (fg >> 24) & 0xFF;
const fgG = (fg >> 16) & 0xFF;
const fgB = (fg >> 8) & 0xFF;
const bgR = (bg >> 24) & 0xFF;
const bgG = (bg >> 16) & 0xFF;
const bgB = (bg >> 8) & 0xFF;
$r = bgR + Math.round((fgR - bgR) * $a);
$g = bgG + Math.round((fgG - bgG) * $a);
$b = bgB + Math.round((fgB - bgB) * $a);
return channels.toRgba($r, $g, $b);
}
/**
* Given a foreground color and a background color, either increase or reduce the luminance of the
* foreground color until the specified contrast ratio is met. If pure white or black is hit
+5 -2
View File
@@ -8,7 +8,10 @@ import { ITestContext, createTestContext, openTerminal } from './TestUtils';
import { ISharedRendererTestContext, injectSharedRendererTestsStandalone, injectSharedRendererTests } from './SharedRendererTests';
let ctx: ITestContext;
const ctxWrapper: ISharedRendererTestContext = { value: undefined } as any;
const ctxWrapper: ISharedRendererTestContext = {
value: undefined,
skipDomExceptions: true
} as any;
test.beforeAll(async ({ browser }) => {
ctx = await createTestContext(browser);
ctxWrapper.value = ctx;
@@ -18,5 +21,5 @@ test.afterAll(async () => await ctx.page.close());
test.describe('DOM Renderer Integration Tests', () => {
injectSharedRendererTests(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper);
injectSharedRendererTestsStandalone(ctxWrapper, () => {});
});
+72 -29
View File
@@ -11,6 +11,7 @@ import { ITestContext, MaybeAsync, openTerminal, pollFor, pollForApproximate } f
export interface ISharedRendererTestContext {
value: ITestContext;
skipCanvasExceptions?: boolean;
skipDomExceptions?: boolean;
}
export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void {
@@ -945,7 +946,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 255, 0, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [255, 0, 0, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0, 255, 0, 255]);
await ctx.value.page.evaluate(`window.term.selectAll()`);
await ctx.value.proxy.selectAll();
frameDetails = undefined;
// Selection only cell needs to be first to ensure renderer has kicked in
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]);
@@ -965,7 +966,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
// Check both the cursor line and another line
await ctx.value.proxy.writeln('_ ');
await ctx.value.proxy.write('_ ');
await ctx.value.page.evaluate(`window.term.selectAll()`);
await ctx.value.proxy.selectAll();
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [128, 0, 0, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [128, 0, 0, 255]);
@@ -980,6 +981,44 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
});
});
(ctx.skipCanvasExceptions || ctx.skipDomExceptions ? test.describe.skip : test.describe)('selection blending', () => {
test('background', async () => {
const theme: ITheme = {
red: '#CC0000',
selectionBackground: '#FFFFFF'
};
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await ctx.value.proxy.focus();
await ctx.value.proxy.writeln('\x1b[41m red bg');
await ctx.value.proxy.writeln('\x1b[7m inverse');
await ctx.value.proxy.writeln('\x1b[31;7m red fg inverse');
await ctx.value.proxy.selectAll();
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [230,128,128,255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [255,255,255,255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 3), [230,128,128,255]);
});
test('powerline decorative symbols', async () => {
const theme: ITheme = {
red: '#CC0000',
green: '#00CC00',
selectionBackground: '#FFFFFF'
};
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await ctx.value.proxy.focus();
await ctx.value.proxy.writeln('\u{E0B4} plain\x1b[0m');
await ctx.value.proxy.writeln('\x1b[31;42m\u{E0B4} red fg green bg\x1b[0m');
await ctx.value.proxy.writeln('\x1b[32;41m\u{E0B4} green fg red bg\x1b[0m');
await ctx.value.proxy.writeln('\x1b[31;42;7m\u{E0B4} red fg green bg inverse\x1b[0m');
await ctx.value.proxy.writeln('\x1b[32;41;7m\u{E0B4} green fg red bg inverse\x1b[0m');
await ctx.value.proxy.selectAll();
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255,255,255,255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 2), [230, 128, 128, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 3), [128, 230, 128, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 4), [128, 230, 128, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 5), [230, 128, 128, 255]);
});
});
test.describe('allowTransparency', async () => {
test.beforeEach(() => ctx.value.page.evaluate(`term.options.allowTransparency = true`));
@@ -1003,7 +1042,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
const data = `\x1b[7m■\x1b[0m`;
await ctx.value.proxy.write( data);
await ctx.value.page.evaluate(`window.term.selectAll()`);
await ctx.value.proxy.selectAll();
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [255, 0, 0, 255]);
});
});
@@ -1092,7 +1131,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
});
test.describe('regression tests', () => {
test('#4736: inactive selection background should replace regular cell background color', async () => {
(ctx.skipCanvasExceptions ? test.skip : test)('#4736: inactive selection background should replace regular cell background color', async () => {
const theme: ITheme = {
selectionBackground: '#FF0000',
selectionInactiveBackground: '#0000FF'
@@ -1126,7 +1165,8 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 2, 1), [0, 0, 0, 255]);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 3, 1), [0, 0, 0, 255]);
});
test('#4759: minimum contrast ratio should be respected on inverse text', async () => {
// HACK: It's not clear why DOM is failing here
(ctx.skipDomExceptions ? test.skip : test)('#4759: minimum contrast ratio should be respected on inverse text', async () => {
const theme: ITheme = {
foreground: '#aaaaaa',
background: '#333333'
@@ -1205,31 +1245,34 @@ enum CellColorPosition {
* This is much slower than just calling `Terminal.reset` but testing some features needs this
* treatment.
*/
export function injectSharedRendererTestsStandalone(ctx: ISharedRendererTestContext): void {
test.beforeEach(async () => {
// Recreate terminal
await openTerminal(ctx.value);
ctx.value.page.evaluate(`
window.term.options.minimumContrastRatio = 1;
window.term.options.allowTransparency = false;
window.term.options.theme = undefined;
`);
// Clear the cached screenshot before each test
frameDetails = undefined;
});
test.describe('regression tests', () => {
test('#4790: cursor should not be displayed before focusing', async () => {
const theme: ITheme = {
cursor: '#0000FF'
};
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
await ctx.value.proxy.focus();
export function injectSharedRendererTestsStandalone(ctx: ISharedRendererTestContext, setupCb: () => Promise<void> | void): void {
test.describe('standalone tests', () => {
test.beforeEach(async () => {
// Recreate terminal
await openTerminal(ctx.value);
await ctx.value.page.evaluate(`
window.term.options.minimumContrastRatio = 1;
window.term.options.allowTransparency = false;
window.term.options.theme = undefined;
`);
await setupCb();
// Clear the cached screenshot before each test
frameDetails = undefined;
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]);
await ctx.value.proxy.blur();
frameDetails = undefined;
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
});
test.describe('regression tests', () => {
test('#4790: cursor should not be displayed before focusing', async () => {
const theme: ITheme = {
cursor: '#0000FF'
};
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
await ctx.value.proxy.focus();
frameDetails = undefined;
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 255, 255]);
await ctx.value.proxy.blur();
frameDetails = undefined;
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
});
});
});
}