mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Make terminal rendering work in popout windows
Add support for the parent element (what is passed to Terminal.open) being in a different (same origin) window. Accesses to DOM APIs such as requestAnimationFrame and devicePixelRatio thus need to be scoped to the corrent window, instead of assuming that it's the same as the window/global scope where the code is running. This is done by inferring a parent window at the creation time, and then storing it in CoreBrowserService, which is already passed to most places that need it. To catch future regressions, an ESLint rule that checks for global accesses is added (it uses AST selectors via the no-restricted-syntax rule). This should also be applicable when the parent element is an iframe. Fixes #3758
This commit is contained in:
@@ -147,6 +147,29 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"warn",
|
||||
{
|
||||
"selector": "CallExpression[callee.name='requestAnimationFrame']",
|
||||
"message": "The global requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.name='cancelAnimationFrame']",
|
||||
"message": "The global cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='requestAnimationFrame']",
|
||||
"message": "window.requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='cancelAnimationFrame']",
|
||||
"message": "window.cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
|
||||
},
|
||||
{
|
||||
"selector": "MemberExpression[object.name='window'][property.name='devicePixelRatio']",
|
||||
"message": "window.devicePixelRatio should be avoided, get it from ICoreBrowserService."
|
||||
}
|
||||
],
|
||||
"no-trailing-spaces": "warn",
|
||||
"no-unsafe-finally": "warn",
|
||||
"no-var": "warn",
|
||||
|
||||
@@ -15,6 +15,7 @@ import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
import { ICoreBrowserService } from 'browser/services/Services';
|
||||
import { excludeFromContrastRatioDemands, throwIfFalsy } from 'browser/renderer/RendererUtils';
|
||||
import { channels, color, rgba } from 'common/Color';
|
||||
import { removeElementFromParent } from 'browser/Dom';
|
||||
@@ -60,7 +61,8 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
private _rendererId: number,
|
||||
protected readonly _bufferService: IBufferService,
|
||||
protected readonly _optionsService: IOptionsService,
|
||||
protected readonly _decorationService: IDecorationService
|
||||
protected readonly _decorationService: IDecorationService,
|
||||
protected readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._canvas.classList.add(`xterm-${id}-layer`);
|
||||
@@ -125,7 +127,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
this._charAtlas = acquireCharAtlas(this._optionsService.rawOptions, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight);
|
||||
this._charAtlas = acquireCharAtlas(this._optionsService.rawOptions, this._rendererId, colorSet, this._scaledCharWidth, this._scaledCharHeight, this._coreBrowserService.dpr);
|
||||
this._charAtlas.warmUp();
|
||||
}
|
||||
|
||||
@@ -180,9 +182,9 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
const cellOffset = Math.ceil(this._scaledCellHeight * 0.5);
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
(y + 1) * this._scaledCellHeight - cellOffset - window.devicePixelRatio,
|
||||
(y + 1) * this._scaledCellHeight - cellOffset - this._coreBrowserService.dpr,
|
||||
width * this._scaledCellWidth,
|
||||
window.devicePixelRatio);
|
||||
this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,23 +196,24 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
protected _fillBottomLineAtCells(x: number, y: number, width: number = 1, pixelOffset: number = 0): void {
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
(y + 1) * this._scaledCellHeight + pixelOffset - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
|
||||
(y + 1) * this._scaledCellHeight + pixelOffset - this._coreBrowserService.dpr - 1 /* Ensure it's drawn within the cell */,
|
||||
width * this._scaledCellWidth,
|
||||
window.devicePixelRatio);
|
||||
this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
protected _curlyUnderlineAtCell(x: number, y: number, width: number = 1): void {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.strokeStyle = this._ctx.fillStyle;
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
const lineWidth = this._coreBrowserService.dpr;
|
||||
this._ctx.lineWidth = lineWidth;
|
||||
for (let xOffset = 0; xOffset < width; xOffset++) {
|
||||
const xLeft = (x + xOffset) * this._scaledCellWidth;
|
||||
const xMid = (x + xOffset + 0.5) * this._scaledCellWidth;
|
||||
const xRight = (x + xOffset + 1) * this._scaledCellWidth;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
|
||||
const yMidBot = yMid - window.devicePixelRatio;
|
||||
const yMidTop = yMid + window.devicePixelRatio;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - lineWidth - 1;
|
||||
const yMidBot = yMid - lineWidth;
|
||||
const yMidTop = yMid + lineWidth;
|
||||
this._ctx.moveTo(xLeft, yMid);
|
||||
this._ctx.bezierCurveTo(
|
||||
xLeft, yMidBot,
|
||||
@@ -231,10 +234,11 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.strokeStyle = this._ctx.fillStyle;
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
this._ctx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
|
||||
const lineWidth = this._coreBrowserService.dpr;
|
||||
this._ctx.lineWidth = lineWidth;
|
||||
this._ctx.setLineDash([lineWidth * 2, lineWidth]);
|
||||
const xLeft = x * this._scaledCellWidth;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - lineWidth - 1;
|
||||
this._ctx.moveTo(xLeft, yMid);
|
||||
for (let xOffset = 0; xOffset < width; xOffset++) {
|
||||
// const xLeft = x * this._scaledCellWidth;
|
||||
@@ -250,11 +254,12 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.save();
|
||||
this._ctx.beginPath();
|
||||
this._ctx.strokeStyle = this._ctx.fillStyle;
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
this._ctx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
|
||||
const lineWidth = this._coreBrowserService.dpr;
|
||||
this._ctx.lineWidth = lineWidth;
|
||||
this._ctx.setLineDash([lineWidth * 4, lineWidth * 3]);
|
||||
const xLeft = x * this._scaledCellWidth;
|
||||
const xRight = (x + width) * this._scaledCellWidth;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
|
||||
const yMid = (y + 1) * this._scaledCellHeight - lineWidth - 1;
|
||||
this._ctx.moveTo(xLeft, yMid);
|
||||
this._ctx.lineTo(xRight, yMid);
|
||||
this._ctx.stroke();
|
||||
@@ -272,7 +277,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
window.devicePixelRatio * width,
|
||||
this._coreBrowserService.dpr * width,
|
||||
this._scaledCellHeight);
|
||||
}
|
||||
|
||||
@@ -283,12 +288,13 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
* @param y The row to fill.
|
||||
*/
|
||||
protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void {
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
const lineWidth = this._coreBrowserService.dpr;
|
||||
this._ctx.lineWidth = lineWidth;
|
||||
this._ctx.strokeRect(
|
||||
x * this._scaledCellWidth + window.devicePixelRatio / 2,
|
||||
y * this._scaledCellHeight + (window.devicePixelRatio / 2),
|
||||
width * this._scaledCellWidth - window.devicePixelRatio,
|
||||
(height * this._scaledCellHeight) - window.devicePixelRatio);
|
||||
x * this._scaledCellWidth + lineWidth / 2,
|
||||
y * this._scaledCellHeight + (lineWidth / 2),
|
||||
width * this._scaledCellWidth - lineWidth,
|
||||
(height * this._scaledCellHeight) - lineWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,7 +350,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
// Draw custom characters if applicable
|
||||
let drawSuccess = false;
|
||||
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize);
|
||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize, this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
// Draw the character
|
||||
@@ -472,7 +478,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
// Draw custom characters if applicable
|
||||
let drawSuccess = false;
|
||||
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize);
|
||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize, this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
// Draw the character
|
||||
@@ -509,7 +515,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
const fontWeight = isBold ? this._optionsService.rawOptions.fontWeightBold : this._optionsService.rawOptions.fontWeight;
|
||||
const fontStyle = isItalic ? 'italic' : '';
|
||||
|
||||
return `${fontStyle} ${fontWeight} ${this._optionsService.rawOptions.fontSize * window.devicePixelRatio}px ${this._optionsService.rawOptions.fontFamily}`;
|
||||
return `${fontStyle} ${fontWeight} ${this._optionsService.rawOptions.fontSize * this._coreBrowserService.dpr}px ${this._optionsService.rawOptions.fontFamily}`;
|
||||
}
|
||||
|
||||
private _getContrastColor(cell: CellData, x: number, y: number): IColor | undefined {
|
||||
|
||||
@@ -39,16 +39,16 @@ export class CanvasRenderer extends Disposable implements IRenderer {
|
||||
private readonly _optionsService: IOptionsService,
|
||||
characterJoinerService: ICharacterJoinerService,
|
||||
coreService: ICoreService,
|
||||
coreBrowserService: ICoreBrowserService,
|
||||
private readonly _coreBrowserService: ICoreBrowserService,
|
||||
decorationService: IDecorationService
|
||||
) {
|
||||
super();
|
||||
const allowTransparency = this._optionsService.rawOptions.allowTransparency;
|
||||
this._renderLayers = [
|
||||
new TextRenderLayer(this._screenElement, 0, this._colors, allowTransparency, this._id, this._bufferService, this._optionsService, characterJoinerService, decorationService),
|
||||
new SelectionRenderLayer(this._screenElement, 1, this._colors, this._id, this._bufferService, coreBrowserService, decorationService, this._optionsService),
|
||||
new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, linkifier2, this._bufferService, this._optionsService, decorationService),
|
||||
new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, coreBrowserService, decorationService)
|
||||
new TextRenderLayer(this._screenElement, 0, this._colors, allowTransparency, this._id, this._bufferService, this._optionsService, characterJoinerService, decorationService, this._coreBrowserService),
|
||||
new SelectionRenderLayer(this._screenElement, 1, this._colors, this._id, this._bufferService, this._coreBrowserService, decorationService, this._optionsService),
|
||||
new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, linkifier2, this._bufferService, this._optionsService, decorationService, this._coreBrowserService),
|
||||
new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService)
|
||||
];
|
||||
this.dimensions = {
|
||||
scaledCharWidth: 0,
|
||||
@@ -64,10 +64,10 @@ export class CanvasRenderer extends Disposable implements IRenderer {
|
||||
actualCellWidth: 0,
|
||||
actualCellHeight: 0
|
||||
};
|
||||
this._devicePixelRatio = window.devicePixelRatio;
|
||||
this._devicePixelRatio = this._coreBrowserService.dpr;
|
||||
this._updateDimensions();
|
||||
|
||||
this.register(observeDevicePixelDimensions(this._renderLayers[0].canvas, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
|
||||
this.register(observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
|
||||
|
||||
this.onOptionsChanged();
|
||||
}
|
||||
@@ -83,8 +83,8 @@ export class CanvasRenderer extends Disposable implements IRenderer {
|
||||
public onDevicePixelRatioChange(): void {
|
||||
// If the device pixel ratio changed, the char atlas needs to be regenerated
|
||||
// and the terminal needs to refreshed
|
||||
if (this._devicePixelRatio !== window.devicePixelRatio) {
|
||||
this._devicePixelRatio = window.devicePixelRatio;
|
||||
if (this._devicePixelRatio !== this._coreBrowserService.dpr) {
|
||||
this._devicePixelRatio = this._coreBrowserService.dpr;
|
||||
this.onResize(this._bufferService.cols, this._bufferService.rows);
|
||||
}
|
||||
}
|
||||
@@ -175,16 +175,17 @@ export class CanvasRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
// See the WebGL renderer for an explanation of this section.
|
||||
this.dimensions.scaledCharWidth = Math.floor(this._charSizeService.width * window.devicePixelRatio);
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * window.devicePixelRatio);
|
||||
const dpr = this._coreBrowserService.dpr;
|
||||
this.dimensions.scaledCharWidth = Math.floor(this._charSizeService.width * dpr);
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._charSizeService.height * dpr);
|
||||
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._optionsService.rawOptions.lineHeight);
|
||||
this.dimensions.scaledCharTop = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
|
||||
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._optionsService.rawOptions.letterSpacing);
|
||||
this.dimensions.scaledCharLeft = Math.floor(this._optionsService.rawOptions.letterSpacing / 2);
|
||||
this.dimensions.scaledCanvasHeight = this._bufferService.rows * this.dimensions.scaledCellHeight;
|
||||
this.dimensions.scaledCanvasWidth = this._bufferService.cols * this.dimensions.scaledCellWidth;
|
||||
this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio);
|
||||
this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio);
|
||||
this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / dpr);
|
||||
this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / dpr);
|
||||
this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows;
|
||||
this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols;
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
bufferService: IBufferService,
|
||||
optionsService: IOptionsService,
|
||||
private readonly _coreService: ICoreService,
|
||||
private readonly _coreBrowserService: ICoreBrowserService,
|
||||
coreBrowserService: ICoreBrowserService,
|
||||
decorationService: IDecorationService
|
||||
) {
|
||||
super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService);
|
||||
super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService, coreBrowserService);
|
||||
this._state = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -99,7 +99,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
if (!this._cursorBlinkStateManager) {
|
||||
this._cursorBlinkStateManager = new CursorBlinkStateManager(this._coreBrowserService.isFocused, () => {
|
||||
this._render(true);
|
||||
});
|
||||
}, this._coreBrowserService);
|
||||
}
|
||||
} else {
|
||||
this._cursorBlinkStateManager?.dispose();
|
||||
@@ -196,7 +196,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
private _clearCursor(): void {
|
||||
if (this._state) {
|
||||
// Avoid potential rounding errors when device pixel ratio is less than 1
|
||||
if (window.devicePixelRatio < 1) {
|
||||
if (this._coreBrowserService.dpr < 1) {
|
||||
this._clearAll();
|
||||
} else {
|
||||
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
|
||||
@@ -258,7 +258,8 @@ class CursorBlinkStateManager {
|
||||
|
||||
constructor(
|
||||
isFocused: boolean,
|
||||
private _renderCallback: () => void
|
||||
private _renderCallback: () => void,
|
||||
private _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
this.isCursorVisible = true;
|
||||
if (isFocused) {
|
||||
@@ -270,15 +271,15 @@ class CursorBlinkStateManager {
|
||||
|
||||
public dispose(): void {
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
if (this._blinkStartTimeout) {
|
||||
window.clearTimeout(this._blinkStartTimeout);
|
||||
this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
|
||||
this._blinkStartTimeout = undefined;
|
||||
}
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
@@ -292,7 +293,7 @@ class CursorBlinkStateManager {
|
||||
// Force a cursor render to ensure it's visible and in the correct position
|
||||
this.isCursorVisible = true;
|
||||
if (!this._animationFrame) {
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
@@ -302,7 +303,7 @@ class CursorBlinkStateManager {
|
||||
private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {
|
||||
// Clear any existing interval
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
|
||||
@@ -310,7 +311,7 @@ class CursorBlinkStateManager {
|
||||
// the regular interval is setup in order to support restarting the blink
|
||||
// animation in a lightweight way (without thrashing clearInterval and
|
||||
// setInterval).
|
||||
this._blinkStartTimeout = window.setTimeout(() => {
|
||||
this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => {
|
||||
// Check if another animation restart was requested while this was being
|
||||
// started
|
||||
if (this._animationTimeRestarted) {
|
||||
@@ -324,13 +325,13 @@ class CursorBlinkStateManager {
|
||||
|
||||
// Hide the cursor
|
||||
this.isCursorVisible = false;
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
|
||||
// Setup the blink interval
|
||||
this._blinkInterval = window.setInterval(() => {
|
||||
this._blinkInterval = this._coreBrowserService.window.setInterval(() => {
|
||||
// Adjust the animation time if it was restarted
|
||||
if (this._animationTimeRestarted) {
|
||||
// calc time diff
|
||||
@@ -343,7 +344,7 @@ class CursorBlinkStateManager {
|
||||
|
||||
// Invert visibility and render
|
||||
this.isCursorVisible = !this.isCursorVisible;
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
@@ -354,15 +355,15 @@ class CursorBlinkStateManager {
|
||||
public pause(): void {
|
||||
this.isCursorVisible = true;
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
if (this._blinkStartTimeout) {
|
||||
window.clearTimeout(this._blinkStartTimeout);
|
||||
this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
|
||||
this._blinkStartTimeout = undefined;
|
||||
}
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/Constants';
|
||||
import { ICoreBrowserService } from 'browser/services/Services';
|
||||
import { is256Color } from './atlas/CharAtlasUtils';
|
||||
import { IColorSet, ILinkifierEvent, ILinkifier2 } from 'browser/Types';
|
||||
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
||||
@@ -21,9 +22,10 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
||||
linkifier2: ILinkifier2,
|
||||
bufferService: IBufferService,
|
||||
optionsService: IOptionsService,
|
||||
decorationService: IDecorationService
|
||||
decorationService: IDecorationService,
|
||||
coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService);
|
||||
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService, coreBrowserService);
|
||||
|
||||
linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
|
||||
linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
|
||||
|
||||
@@ -25,11 +25,11 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
||||
colors: IColorSet,
|
||||
rendererId: number,
|
||||
bufferService: IBufferService,
|
||||
private readonly _coreBrowserService: ICoreBrowserService,
|
||||
coreBrowserService: ICoreBrowserService,
|
||||
decorationService: IDecorationService,
|
||||
optionsService: IOptionsService
|
||||
) {
|
||||
super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService);
|
||||
super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService, coreBrowserService);
|
||||
this._clearState();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { NULL_CELL_CODE, Content, UnderlineStyle } from 'common/buffer/Constants
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IOptionsService, IBufferService, IDecorationService } from 'common/services/Services';
|
||||
import { ICharacterJoinerService } from 'browser/services/Services';
|
||||
import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services';
|
||||
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
||||
import { color, css } from 'common/Color';
|
||||
|
||||
@@ -39,9 +39,10 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
bufferService: IBufferService,
|
||||
optionsService: IOptionsService,
|
||||
private readonly _characterJoinerService: ICharacterJoinerService,
|
||||
decorationService: IDecorationService
|
||||
decorationService: IDecorationService,
|
||||
coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService, decorationService);
|
||||
super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService, decorationService, coreBrowserService);
|
||||
this._state = new GridCache<CharData>();
|
||||
}
|
||||
|
||||
@@ -282,8 +283,8 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
switch (cell.extended.underlineStyle) {
|
||||
case UnderlineStyle.DOUBLE:
|
||||
this._fillBottomLineAtCells(x, y, cell.getWidth(), -window.devicePixelRatio);
|
||||
this._fillBottomLineAtCells(x, y, cell.getWidth(), window.devicePixelRatio);
|
||||
this._fillBottomLineAtCells(x, y, cell.getWidth(), -this._coreBrowserService.dpr);
|
||||
this._fillBottomLineAtCells(x, y, cell.getWidth(), this._coreBrowserService.dpr);
|
||||
break;
|
||||
case UnderlineStyle.CURLY:
|
||||
this._curlyUnderlineAtCell(x, y, cell.getWidth());
|
||||
|
||||
@@ -29,9 +29,10 @@ export function acquireCharAtlas(
|
||||
rendererId: number,
|
||||
colors: IColorSet,
|
||||
scaledCharWidth: number,
|
||||
scaledCharHeight: number
|
||||
scaledCharHeight: number,
|
||||
devicePixelRatio: number
|
||||
): BaseCharAtlas {
|
||||
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors);
|
||||
const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, options, colors, devicePixelRatio);
|
||||
|
||||
// Check to see if the renderer already owns this config
|
||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DEFAULT_COLOR } from 'common/buffer/Constants';
|
||||
import { IColorSet, IPartialColorSet } from 'browser/Types';
|
||||
import { ITerminalOptions } from 'xterm';
|
||||
|
||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: Required<ITerminalOptions>, colors: IColorSet): ICharAtlasConfig {
|
||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: Required<ITerminalOptions>, colors: IColorSet, devicePixelRatio: number): ICharAtlasConfig {
|
||||
// null out some fields that don't matter
|
||||
const clonedColors: IPartialColorSet = {
|
||||
foreground: colors.foreground,
|
||||
@@ -19,7 +19,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
|
||||
ansi: colors.ansi.slice()
|
||||
};
|
||||
return {
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
devicePixelRatio,
|
||||
scaledCharWidth,
|
||||
scaledCharHeight,
|
||||
fontFamily: options.fontFamily,
|
||||
|
||||
@@ -77,7 +77,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
this._core = (this._terminal as any)._core;
|
||||
|
||||
this._renderLayers = [
|
||||
new LinkRenderLayer(this._core.screenElement!, 2, this._colors, this._core),
|
||||
new LinkRenderLayer(this._core.screenElement!, 2, this._colors, this._core, this._coreBrowserService),
|
||||
new CursorRenderLayer(_terminal, this._core.screenElement!, 3, this._colors, this._onRequestRedraw, this._coreBrowserService, coreService)
|
||||
];
|
||||
this.dimensions = {
|
||||
@@ -94,7 +94,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
actualCellWidth: 0,
|
||||
actualCellHeight: 0
|
||||
};
|
||||
this._devicePixelRatio = window.devicePixelRatio;
|
||||
this._devicePixelRatio = this._coreBrowserService.dpr;
|
||||
this._updateDimensions();
|
||||
|
||||
this._canvas = document.createElement('canvas');
|
||||
@@ -132,13 +132,13 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
this._requestRedrawViewport();
|
||||
}));
|
||||
|
||||
this.register(observeDevicePixelDimensions(this._canvas, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
|
||||
this.register(observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
|
||||
|
||||
this._core.screenElement!.appendChild(this._canvas);
|
||||
|
||||
this._initializeWebGLState();
|
||||
|
||||
this._isAttached = document.body.contains(this._core.screenElement!);
|
||||
this._isAttached = this._coreBrowserService.window.document.body.contains(this._core.screenElement!);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -173,8 +173,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
public onDevicePixelRatioChange(): void {
|
||||
// If the device pixel ratio changed, the char atlas needs to be regenerated
|
||||
// and the terminal needs to refreshed
|
||||
if (this._devicePixelRatio !== window.devicePixelRatio) {
|
||||
this._devicePixelRatio = window.devicePixelRatio;
|
||||
if (this._devicePixelRatio !== this._coreBrowserService.dpr) {
|
||||
this._devicePixelRatio = this._coreBrowserService.dpr;
|
||||
this.onResize(this._terminal.cols, this._terminal.rows);
|
||||
}
|
||||
}
|
||||
@@ -281,7 +281,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
const atlas = acquireCharAtlas(this._terminal, this._colors, this.dimensions.scaledCellWidth, this.dimensions.scaledCellHeight, this.dimensions.scaledCharWidth, this.dimensions.scaledCharHeight);
|
||||
const atlas = acquireCharAtlas(this._terminal, this._colors, this.dimensions.scaledCellWidth, this.dimensions.scaledCellHeight, this.dimensions.scaledCharWidth, this.dimensions.scaledCharHeight, this._coreBrowserService.dpr);
|
||||
if (!('getRasterizedGlyph' in atlas)) {
|
||||
throw new Error('The webgl renderer only works with the webgl char atlas');
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public renderRows(start: number, end: number): void {
|
||||
if (!this._isAttached) {
|
||||
if (document.body.contains(this._core.screenElement!) && (this._core as any)._charSizeService.width && (this._core as any)._charSizeService.height) {
|
||||
if (this._coreBrowserService.window.document.body.contains(this._core.screenElement!) && (this._core as any)._charSizeService.width && (this._core as any)._charSizeService.height) {
|
||||
this._updateDimensions();
|
||||
this._refreshCharAtlas();
|
||||
this._isAttached = true;
|
||||
|
||||
@@ -31,9 +31,10 @@ export function acquireCharAtlas(
|
||||
scaledCellWidth: number,
|
||||
scaledCellHeight: number,
|
||||
scaledCharWidth: number,
|
||||
scaledCharHeight: number
|
||||
scaledCharHeight: number,
|
||||
devicePixelRatio: number
|
||||
): WebglCharAtlas {
|
||||
const newConfig = generateConfig(scaledCellWidth, scaledCellHeight, scaledCharWidth, scaledCharHeight, terminal, colors);
|
||||
const newConfig = generateConfig(scaledCellWidth, scaledCellHeight, scaledCharWidth, scaledCharHeight, terminal, colors, devicePixelRatio);
|
||||
|
||||
// Check to see if the terminal already owns this config
|
||||
for (let i = 0; i < charAtlasCache.length; i++) {
|
||||
|
||||
@@ -14,7 +14,7 @@ const NULL_COLOR: IColor = {
|
||||
rgba: 0
|
||||
};
|
||||
|
||||
export function generateConfig(scaledCellWidth: number, scaledCellHeight: number, scaledCharWidth: number, scaledCharHeight: number, terminal: Terminal, colors: IColorSet): ICharAtlasConfig {
|
||||
export function generateConfig(scaledCellWidth: number, scaledCellHeight: number, scaledCharWidth: number, scaledCharHeight: number, terminal: Terminal, colors: IColorSet, devicePixelRatio: number): ICharAtlasConfig {
|
||||
// null out some fields that don't matter
|
||||
const clonedColors: IColorSet = {
|
||||
foreground: colors.foreground,
|
||||
@@ -33,7 +33,7 @@ export function generateConfig(scaledCellWidth: number, scaledCellHeight: number
|
||||
};
|
||||
return {
|
||||
customGlyphs: terminal.options.customGlyphs,
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
devicePixelRatio,
|
||||
letterSpacing: terminal.options.letterSpacing,
|
||||
lineHeight: terminal.options.lineHeight,
|
||||
scaledCellWidth,
|
||||
|
||||
@@ -409,7 +409,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
// Draw custom characters if applicable
|
||||
let customGlyph = false;
|
||||
if (this._config.customGlyphs !== false) {
|
||||
customGlyph = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight, this._config.fontSize);
|
||||
customGlyph = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight, this._config.fontSize, this._config.devicePixelRatio);
|
||||
}
|
||||
|
||||
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
||||
@@ -427,7 +427,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
// Draw underline
|
||||
if (underline) {
|
||||
this._tmpCtx.save();
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 15));
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15));
|
||||
// When the line width is odd, draw at a 0.5 position
|
||||
const yOffset = lineWidth % 2 === 1 ? 0.5 : 0;
|
||||
this._tmpCtx.lineWidth = lineWidth;
|
||||
@@ -501,12 +501,12 @@ export class WebglCharAtlas implements IDisposable {
|
||||
);
|
||||
break;
|
||||
case UnderlineStyle.DOTTED:
|
||||
this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
|
||||
this._tmpCtx.setLineDash([this._config.devicePixelRatio * 2, this._config.devicePixelRatio]);
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
break;
|
||||
case UnderlineStyle.DASHED:
|
||||
this._tmpCtx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
|
||||
this._tmpCtx.setLineDash([this._config.devicePixelRatio * 4, this._config.devicePixelRatio * 3]);
|
||||
this._tmpCtx.moveTo(xChLeft, yTop);
|
||||
this._tmpCtx.lineTo(xChRight, yTop);
|
||||
break;
|
||||
@@ -543,7 +543,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
const clipRegion = new Path2D();
|
||||
clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.scaledCellWidth, yBot - yTop + Math.ceil(lineWidth / 2));
|
||||
this._tmpCtx.clip(clipRegion);
|
||||
this._tmpCtx.lineWidth = window.devicePixelRatio * 3;
|
||||
this._tmpCtx.lineWidth = this._config.devicePixelRatio * 3;
|
||||
this._tmpCtx.strokeStyle = backgroundColor.css;
|
||||
this._tmpCtx.strokeText(chars, padding, padding + this._config.scaledCharHeight);
|
||||
this._tmpCtx.restore();
|
||||
@@ -578,7 +578,7 @@ export class WebglCharAtlas implements IDisposable {
|
||||
|
||||
// Draw strokethrough
|
||||
if (strikethrough) {
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
|
||||
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 10));
|
||||
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
|
||||
this._tmpCtx.lineWidth = lineWidth;
|
||||
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { acquireCharAtlas } from '../atlas/CharAtlasCache';
|
||||
import { Terminal } from 'xterm';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { TEXT_BASELINE } from 'browser/renderer/Constants';
|
||||
import { ICoreBrowserService } from 'browser/services/Services';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { WebglCharAtlas } from 'atlas/WebglCharAtlas';
|
||||
@@ -30,7 +31,8 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
id: string,
|
||||
zIndex: number,
|
||||
private _alpha: boolean,
|
||||
protected _colors: IColorSet
|
||||
protected _colors: IColorSet,
|
||||
protected readonly _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
this._canvas = document.createElement('canvas');
|
||||
this._canvas.classList.add(`xterm-${id}-layer`);
|
||||
@@ -93,7 +95,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCellWidth, this._scaledCellHeight, this._scaledCharWidth, this._scaledCharHeight);
|
||||
this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCellWidth, this._scaledCellHeight, this._scaledCharWidth, this._scaledCharHeight, this._coreBrowserService.dpr);
|
||||
this._charAtlas.warmUp();
|
||||
}
|
||||
|
||||
@@ -143,9 +145,9 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void {
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
(y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
|
||||
(y + 1) * this._scaledCellHeight - this._coreBrowserService.dpr - 1 /* Ensure it's drawn within the cell */,
|
||||
width * this._scaledCellWidth,
|
||||
window.devicePixelRatio);
|
||||
this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +160,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
window.devicePixelRatio * width,
|
||||
this._coreBrowserService.dpr * width,
|
||||
this._scaledCellHeight);
|
||||
}
|
||||
|
||||
@@ -169,12 +171,12 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
* @param y The row to fill.
|
||||
*/
|
||||
protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void {
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
this._ctx.lineWidth = this._coreBrowserService.dpr;
|
||||
this._ctx.strokeRect(
|
||||
x * this._scaledCellWidth + window.devicePixelRatio / 2,
|
||||
y * this._scaledCellHeight + (window.devicePixelRatio / 2),
|
||||
width * this._scaledCellWidth - window.devicePixelRatio,
|
||||
(height * this._scaledCellHeight) - window.devicePixelRatio);
|
||||
x * this._scaledCellWidth + this._coreBrowserService.dpr / 2,
|
||||
y * this._scaledCellHeight + (this._coreBrowserService.dpr / 2),
|
||||
width * this._scaledCellWidth - this._coreBrowserService.dpr,
|
||||
(height * this._scaledCellHeight) - this._coreBrowserService.dpr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +260,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight;
|
||||
const fontStyle = isItalic ? 'italic' : '';
|
||||
|
||||
return `${fontStyle} ${fontWeight} ${terminal.options.fontSize! * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
return `${fontStyle} ${fontWeight} ${terminal.options.fontSize! * this._coreBrowserService.dpr}px ${terminal.options.fontFamily}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +38,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
zIndex: number,
|
||||
colors: IColorSet,
|
||||
private _onRequestRefreshRowsEvent: IEventEmitter<IRequestRedrawEvent>,
|
||||
private readonly _coreBrowserService: ICoreBrowserService,
|
||||
coreBrowserService: ICoreBrowserService,
|
||||
private readonly _coreService: ICoreService
|
||||
) {
|
||||
super(container, 'cursor', zIndex, true, colors);
|
||||
super(container, 'cursor', zIndex, true, colors, coreBrowserService);
|
||||
this._state = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -195,7 +195,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
private _clearCursor(): void {
|
||||
if (this._state) {
|
||||
// Avoid potential rounding errors when device pixel ratio is less than 1
|
||||
if (window.devicePixelRatio < 1) {
|
||||
if (this._coreBrowserService.dpr < 1) {
|
||||
this._clearAll();
|
||||
} else {
|
||||
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
|
||||
@@ -257,10 +257,10 @@ class CursorBlinkStateManager {
|
||||
|
||||
constructor(
|
||||
private _renderCallback: () => void,
|
||||
coreBrowserService: ICoreBrowserService
|
||||
private _coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
this.isCursorVisible = true;
|
||||
if (coreBrowserService.isFocused) {
|
||||
if (this._coreBrowserService.isFocused) {
|
||||
this._restartInterval();
|
||||
}
|
||||
}
|
||||
@@ -269,15 +269,15 @@ class CursorBlinkStateManager {
|
||||
|
||||
public dispose(): void {
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
if (this._blinkStartTimeout) {
|
||||
window.clearTimeout(this._blinkStartTimeout);
|
||||
this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
|
||||
this._blinkStartTimeout = undefined;
|
||||
}
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ class CursorBlinkStateManager {
|
||||
// Force a cursor render to ensure it's visible and in the correct position
|
||||
this.isCursorVisible = true;
|
||||
if (!this._animationFrame) {
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
@@ -301,7 +301,7 @@ class CursorBlinkStateManager {
|
||||
private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {
|
||||
// Clear any existing interval
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ class CursorBlinkStateManager {
|
||||
// the regular interval is setup in order to support restarting the blink
|
||||
// animation in a lightweight way (without thrashing clearInterval and
|
||||
// setInterval).
|
||||
this._blinkStartTimeout = window.setTimeout(() => {
|
||||
this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => {
|
||||
// Check if another animation restart was requested while this was being
|
||||
// started
|
||||
if (this._animationTimeRestarted) {
|
||||
@@ -323,13 +323,13 @@ class CursorBlinkStateManager {
|
||||
|
||||
// Hide the cursor
|
||||
this.isCursorVisible = false;
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
|
||||
// Setup the blink interval
|
||||
this._blinkInterval = window.setInterval(() => {
|
||||
this._blinkInterval = this._coreBrowserService.window.setInterval(() => {
|
||||
// Adjust the animation time if it was restarted
|
||||
if (this._animationTimeRestarted) {
|
||||
// calc time diff
|
||||
@@ -342,7 +342,7 @@ class CursorBlinkStateManager {
|
||||
|
||||
// Invert visibility and render
|
||||
this.isCursorVisible = !this.isCursorVisible;
|
||||
this._animationFrame = window.requestAnimationFrame(() => {
|
||||
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
||||
this._renderCallback();
|
||||
this._animationFrame = undefined;
|
||||
});
|
||||
@@ -353,15 +353,15 @@ class CursorBlinkStateManager {
|
||||
public pause(): void {
|
||||
this.isCursorVisible = true;
|
||||
if (this._blinkInterval) {
|
||||
window.clearInterval(this._blinkInterval);
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
if (this._blinkStartTimeout) {
|
||||
window.clearTimeout(this._blinkStartTimeout);
|
||||
this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
|
||||
this._blinkStartTimeout = undefined;
|
||||
}
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,19 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/Constants';
|
||||
import { is256Color } from '../atlas/CharAtlasUtils';
|
||||
import { ITerminal, IColorSet, ILinkifierEvent } from 'browser/Types';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
import { ICoreBrowserService } from 'browser/services/Services';
|
||||
|
||||
export class LinkRenderLayer extends BaseRenderLayer {
|
||||
private _state: ILinkifierEvent | undefined;
|
||||
|
||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) {
|
||||
super(container, 'link', zIndex, true, colors);
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
zIndex: number,
|
||||
colors: IColorSet,
|
||||
terminal: ITerminal,
|
||||
coreBrowserService: ICoreBrowserService
|
||||
) {
|
||||
super(container, 'link', zIndex, true, colors, coreBrowserService);
|
||||
|
||||
terminal.linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
|
||||
terminal.linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
|
||||
|
||||
@@ -98,7 +98,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
|
||||
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
|
||||
|
||||
this._screenDprMonitor = new ScreenDprMonitor();
|
||||
this._screenDprMonitor = new ScreenDprMonitor(window);
|
||||
this.register(this._screenDprMonitor);
|
||||
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
|
||||
// This shouldn't be needed on modern browsers but is present in case the
|
||||
|
||||
@@ -16,13 +16,14 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
private _refreshCallbacks: FrameRequestCallback[] = [];
|
||||
|
||||
constructor(
|
||||
private _parentWindow: Window,
|
||||
private _renderCallback: (start: number, end: number) => void
|
||||
) {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._animationFrame) {
|
||||
window.cancelAnimationFrame(this._animationFrame);
|
||||
this._parentWindow.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +31,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
public addRefreshCallback(callback: FrameRequestCallback): number {
|
||||
this._refreshCallbacks.push(callback);
|
||||
if (!this._animationFrame) {
|
||||
this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());
|
||||
this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh());
|
||||
}
|
||||
return this._animationFrame;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
|
||||
return;
|
||||
}
|
||||
|
||||
this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh());
|
||||
this._animationFrame = this._parentWindow.requestAnimationFrame(() => this._innerRefresh());
|
||||
}
|
||||
|
||||
private _innerRefresh(): void {
|
||||
|
||||
@@ -18,11 +18,16 @@ export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRat
|
||||
* monitor with a different DPI.
|
||||
*/
|
||||
export class ScreenDprMonitor extends Disposable {
|
||||
private _currentDevicePixelRatio: number = window.devicePixelRatio;
|
||||
private _currentDevicePixelRatio: number;
|
||||
private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined;
|
||||
private _listener: ScreenDprListener | undefined;
|
||||
private _resolutionMediaMatchList: MediaQueryList | undefined;
|
||||
|
||||
constructor(private _parentWindow: Window) {
|
||||
super();
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
}
|
||||
|
||||
public setListener(listener: ScreenDprListener): void {
|
||||
if (this._listener) {
|
||||
this.clearListener();
|
||||
@@ -32,7 +37,7 @@ export class ScreenDprMonitor extends Disposable {
|
||||
if (!this._listener) {
|
||||
return;
|
||||
}
|
||||
this._listener(window.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio);
|
||||
this._updateDpr();
|
||||
};
|
||||
this._updateDpr();
|
||||
@@ -52,8 +57,8 @@ export class ScreenDprMonitor extends Disposable {
|
||||
this._resolutionMediaMatchList?.removeListener(this._outerListener);
|
||||
|
||||
// Add listeners for new DPR
|
||||
this._currentDevicePixelRatio = window.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = window.matchMedia(`screen and (resolution: ${window.devicePixelRatio}dppx)`);
|
||||
this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio;
|
||||
this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`);
|
||||
this._resolutionMediaMatchList.addListener(this._outerListener);
|
||||
}
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));
|
||||
this._helperContainer.appendChild(this.textarea);
|
||||
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea);
|
||||
this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, this._document.defaultView ?? window);
|
||||
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
|
||||
|
||||
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user