mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
@@ -10,12 +10,19 @@ import { ICoreBrowserService } from 'browser/services/Services';
|
||||
*/
|
||||
const BLINK_INTERVAL = 600;
|
||||
|
||||
/**
|
||||
* The idle time after which cursor blinking stops (2 minutes).
|
||||
*/
|
||||
const IDLE_TIMEOUT = 5000; //2 * 60 * 1000;
|
||||
|
||||
export class CursorBlinkStateManager {
|
||||
public isCursorVisible: boolean;
|
||||
|
||||
private _animationFrame: number | undefined;
|
||||
private _blinkStartTimeout: number | undefined;
|
||||
private _blinkInterval: number | undefined;
|
||||
private _idleTimeout: number | undefined;
|
||||
private _isIdlePaused: boolean = false;
|
||||
|
||||
/**
|
||||
* The time at which the animation frame was restarted, this is used on the
|
||||
@@ -31,6 +38,7 @@ export class CursorBlinkStateManager {
|
||||
this.isCursorVisible = true;
|
||||
if (this._coreBrowserService.isFocused) {
|
||||
this._restartInterval();
|
||||
this._resetIdleTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +57,16 @@ export class CursorBlinkStateManager {
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
if (this._idleTimeout) {
|
||||
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
||||
this._idleTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public restartBlinkAnimation(): void {
|
||||
if (this._isIdlePaused) {
|
||||
this._resetIdleTimer();
|
||||
}
|
||||
if (this.isPaused) {
|
||||
return;
|
||||
}
|
||||
@@ -121,6 +136,7 @@ export class CursorBlinkStateManager {
|
||||
|
||||
public pause(): void {
|
||||
this.isCursorVisible = true;
|
||||
this._isIdlePaused = false;
|
||||
if (this._blinkInterval) {
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
@@ -133,6 +149,10 @@ export class CursorBlinkStateManager {
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
if (this._idleTimeout) {
|
||||
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
||||
this._idleTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public resume(): void {
|
||||
@@ -141,6 +161,47 @@ export class CursorBlinkStateManager {
|
||||
|
||||
this._animationTimeRestarted = undefined;
|
||||
this._restartInterval();
|
||||
this._resetIdleTimer();
|
||||
this.restartBlinkAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the idle timer. If the terminal is idle for IDLE_TIMEOUT, cursor
|
||||
* blinking will stop.
|
||||
*/
|
||||
private _resetIdleTimer(): void {
|
||||
this._isIdlePaused = false;
|
||||
if (this._idleTimeout) {
|
||||
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
||||
}
|
||||
this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
|
||||
this._stopBlinkingDueToIdle();
|
||||
}, IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops cursor blinking due to idle timeout.
|
||||
*/
|
||||
private _stopBlinkingDueToIdle(): void {
|
||||
// Make cursor visible and stop blinking
|
||||
this.isCursorVisible = true;
|
||||
this._isIdlePaused = true;
|
||||
if (this._blinkInterval) {
|
||||
this._coreBrowserService.window.clearInterval(this._blinkInterval);
|
||||
this._blinkInterval = undefined;
|
||||
}
|
||||
if (this._blinkStartTimeout) {
|
||||
this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
|
||||
this._blinkStartTimeout = undefined;
|
||||
}
|
||||
if (this._animationFrame) {
|
||||
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
|
||||
this._animationFrame = undefined;
|
||||
}
|
||||
// Clear the idle timeout as we've already acted on it
|
||||
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
||||
this._idleTimeout = undefined;
|
||||
// Trigger a render to show the cursor in its final visible state
|
||||
this._renderCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h));
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(this._coreBrowserService.mainDocument, 'mousedown', () => this._cursorBlinkStateManager.value?.restartBlinkAnimation()));
|
||||
|
||||
this._core.screenElement!.appendChild(this._canvas);
|
||||
|
||||
[this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
|
||||
|
||||
@@ -15,6 +15,7 @@ import { color } from 'common/Color';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
|
||||
|
||||
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
|
||||
@@ -23,6 +24,12 @@ const FG_CLASS_PREFIX = 'xterm-fg-';
|
||||
const BG_CLASS_PREFIX = 'xterm-bg-';
|
||||
const FOCUS_CLASS = 'xterm-focus';
|
||||
const SELECTION_CLASS = 'xterm-selection';
|
||||
const CURSOR_BLINK_IDLE_CLASS = 'xterm-cursor-blink-idle';
|
||||
|
||||
/**
|
||||
* The idle time after which cursor blinking stops (2 minutes).
|
||||
*/
|
||||
const IDLE_TIMEOUT = 5000; // 2 * 60 * 1000;
|
||||
|
||||
let nextTerminalId = 1;
|
||||
|
||||
@@ -42,6 +49,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
private _selectionContainer: HTMLElement;
|
||||
private _widthCache: WidthCache;
|
||||
private _selectionRenderModel: ISelectionRenderModel = createSelectionRenderModel();
|
||||
private _idleTimeout: number | undefined;
|
||||
|
||||
public dimensions: IRenderDimensions;
|
||||
|
||||
@@ -89,6 +97,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
this._register(this._linkifier2.onShowLinkUnderline(e => this._handleLinkHover(e)));
|
||||
this._register(this._linkifier2.onHideLinkUnderline(e => this._handleLinkLeave(e)));
|
||||
|
||||
this._register(addDisposableListener(this._document, 'mousedown', () => this._resetIdleTimer()));
|
||||
|
||||
this._register(toDisposable(() => {
|
||||
this._element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||
|
||||
@@ -99,6 +109,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
this._widthCache.dispose();
|
||||
this._themeStyleElement.remove();
|
||||
this._dimensionsStyleElement.remove();
|
||||
this._clearIdleTimer();
|
||||
}));
|
||||
|
||||
this._widthCache = new WidthCache(this._document, this._helperContainer);
|
||||
@@ -225,6 +236,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` animation: ${blinkAnimationBlockId} 1s step-end infinite;` +
|
||||
`}` +
|
||||
// Disable cursor blinking when idle
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${CURSOR_BLINK_IDLE_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS} {` +
|
||||
` animation: none !important;` +
|
||||
`}` +
|
||||
// !important helps fix an issue where the cursor will not render on top of the selection,
|
||||
// however it's very hard to fix this issue and retain the blink animation without the use of
|
||||
// !important. So this edge case fails when cursor blink is on.
|
||||
@@ -328,11 +343,14 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public handleBlur(): void {
|
||||
this._rowContainer.classList.remove(FOCUS_CLASS);
|
||||
this._clearIdleTimer();
|
||||
this.renderRows(0, this._bufferService.rows - 1);
|
||||
}
|
||||
|
||||
public handleFocus(): void {
|
||||
this._rowContainer.classList.add(FOCUS_CLASS);
|
||||
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
||||
this._resetIdleTimer();
|
||||
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||
}
|
||||
|
||||
@@ -406,7 +424,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
public handleCursorMove(): void {
|
||||
// No-op, the cursor is drawn when rows are drawn
|
||||
// Reset idle timer on cursor movement (which happens on input)
|
||||
this._resetIdleTimer();
|
||||
}
|
||||
|
||||
private _handleOptionsChanged(): void {
|
||||
@@ -539,4 +558,37 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the idle timer. If the terminal is idle for IDLE_TIMEOUT, cursor
|
||||
* blinking will stop.
|
||||
*/
|
||||
private _resetIdleTimer(): void {
|
||||
if (!this._coreBrowserService.isFocused) {
|
||||
return;
|
||||
}
|
||||
this._clearIdleTimer();
|
||||
this._rowContainer.classList.remove(CURSOR_BLINK_IDLE_CLASS);
|
||||
this._idleTimeout = this._coreBrowserService.window.setTimeout(() => {
|
||||
this._stopBlinkingDueToIdle();
|
||||
}, IDLE_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the idle timer.
|
||||
*/
|
||||
private _clearIdleTimer(): void {
|
||||
if (this._idleTimeout) {
|
||||
this._coreBrowserService.window.clearTimeout(this._idleTimeout);
|
||||
this._idleTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops cursor blinking due to idle timeout.
|
||||
*/
|
||||
private _stopBlinkingDueToIdle(): void {
|
||||
this._rowContainer.classList.add(CURSOR_BLINK_IDLE_CLASS);
|
||||
this._idleTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user