Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track CursorBlinkStateManager in MutableDisposable #4689

Merged
merged 3 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 12 additions & 15 deletions addons/xterm-addon-webgl/src/WebglRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { AttributeData } from 'common/buffer/AttributeData';
import { CellData } from 'common/buffer/CellData';
import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, getDisposeArrayDisposable, toDisposable } from 'common/Lifecycle';
import { Disposable, MutableDisposable, getDisposeArrayDisposable, toDisposable } from 'common/Lifecycle';
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
import { CharData, IBufferLine, ICellData } from 'common/Types';
import { IDisposable, Terminal } from 'xterm';
Expand All @@ -31,7 +31,7 @@ import { traceCall } from 'common/services/LogService';

export class WebglRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
private _cursorBlinkStateManager: CursorBlinkStateManager | undefined;
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = new MutableDisposable();
private _charAtlasDisposable: IDisposable | undefined;
private _charAtlas: ITextureAtlas | undefined;
private _devicePixelRatio: number;
Expand Down Expand Up @@ -203,7 +203,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (const l of this._renderLayers) {
l.handleBlur(this._terminal);
}
this._cursorBlinkStateManager?.pause();
this._cursorBlinkStateManager.value?.pause();
// Request a redraw for active/inactive selection background
this._requestRedrawViewport();
}
Expand All @@ -212,7 +212,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (const l of this._renderLayers) {
l.handleFocus(this._terminal);
}
this._cursorBlinkStateManager?.resume();
this._cursorBlinkStateManager.value?.resume();
// Request a redraw for active/inactive selection background
this._requestRedrawViewport();
}
Expand All @@ -229,7 +229,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (const l of this._renderLayers) {
l.handleCursorMove(this._terminal);
}
this._cursorBlinkStateManager?.restartBlinkAnimation();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
}

private _handleOptionsChanged(): void {
Expand Down Expand Up @@ -312,7 +312,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
l.reset(this._terminal);
}

this._cursorBlinkStateManager?.restartBlinkAnimation();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
this._updateCursorBlink();
}

Expand Down Expand Up @@ -359,21 +359,18 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Render
this._rectangleRenderer?.renderBackgrounds();
this._glyphRenderer?.render(this._model);
if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isCursorVisible) {
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible) {
this._rectangleRenderer?.renderCursor();
}
}

private _updateCursorBlink(): void {
if (this._terminal.options.cursorBlink) {
if (!this._cursorBlinkStateManager) {
this._cursorBlinkStateManager = this.register(new CursorBlinkStateManager(() => {
this._requestRedrawCursor();
}, this._coreBrowserService));
}
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => {
this._requestRedrawCursor();
}, this._coreBrowserService);
} else {
this._cursorBlinkStateManager?.dispose();
this._cursorBlinkStateManager = undefined;
this._cursorBlinkStateManager.clear();
}
// Request a refresh from the terminal as management of rendering is being
// moved back to the terminal
Expand Down Expand Up @@ -408,7 +405,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
const isCursorVisible =
this._coreService.isCursorInitialized &&
!this._coreService.isCursorHidden &&
(!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isCursorVisible);
(!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible);
this._model.cursor = undefined;
let modelUpdated = false;

Expand Down
52 changes: 51 additions & 1 deletion src/common/Lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*/

import { assert } from 'chai';
import { Disposable } from 'common/Lifecycle';
import { Disposable, MutableDisposable } from 'common/Lifecycle';
import { IDisposable } from 'common/Types';

class TestDisposable extends Disposable {
public get isDisposed(): boolean {
Expand Down Expand Up @@ -43,3 +44,52 @@ describe('Disposable', () => {
});
});
});

describe('MutableDisposable', () => {
const mutable = new MutableDisposable();
class TrackedDisposable extends Disposable {
public get isDisposed(): boolean { return this._isDisposed; }
}
describe('value', () => {
it('should set the value', () => {
const d1 = new TrackedDisposable();
mutable.value = d1;
assert.strictEqual(mutable.value, d1);
assert.isFalse(d1.isDisposed);
});
it('should dispose of any previous value', () => {
const d1 = new TrackedDisposable();
const d2 = new TrackedDisposable();
mutable.value = d1;
mutable.value = d2;
assert.strictEqual(mutable.value, d2);
assert.isTrue(d1.isDisposed);
assert.isFalse(d2.isDisposed);
});
});
describe('clear', () => {
it('should clear and dispose of the object', () => {
const d1 = new TrackedDisposable();
mutable.value = d1;
mutable.clear();
assert.strictEqual(mutable.value, undefined);
assert.isTrue(d1.isDisposed);
});
});
it('dispose', () => {
it('should dispose of the object', () => {
const d1 = new TrackedDisposable();
mutable.value = d1;
mutable.dispose();
assert.strictEqual(mutable.value, undefined);
assert.isTrue(d1.isDisposed);
});
it('should prevent using the MutableDisposable again', () => {
const d1 = new TrackedDisposable();
mutable.value = d1;
mutable.dispose();
mutable.value = new TrackedDisposable();
assert.strictEqual(mutable.value, undefined);
});
});
});
36 changes: 36 additions & 0 deletions src/common/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,42 @@ export abstract class Disposable implements IDisposable {
}
}

export class MutableDisposable<T extends IDisposable> implements IDisposable {
private _value?: T;
private _isDisposed = false;

/**
* Gets the value if it exists.
*/
public get value(): T | undefined {
return this._isDisposed ? undefined : this._value;
}

/**
* Sets the value, disposing of the old value if it exists.
*/
public set value(value: T | undefined) {
if (this._isDisposed || value === this._value) {
return;
}
this._value?.dispose();
this._value = value;
}

/**
* Resets the stored value and disposes of the previously stored value.
*/
public clear(): void {
this.value = undefined;
}

public dispose(): void {
this._isDisposed = true;
this._value?.dispose();
this._value = undefined;
}
}

/**
* Wrap a function in a disposable.
*/
Expand Down