|
|
|
@@ -21,7 +21,7 @@
|
|
|
|
|
* http://linux.die.net/man/7/urxvt
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler } from '../Types';
|
|
|
|
|
import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
|
|
|
|
|
import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
|
|
|
|
|
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
|
|
|
|
import { Viewport } from 'browser/Viewport';
|
|
|
|
@@ -49,41 +49,40 @@ import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISo
|
|
|
|
|
import { CharSizeService } from 'browser/services/CharSizeService';
|
|
|
|
|
import { IBuffer } from 'common/buffer/Types';
|
|
|
|
|
import { MouseService } from 'browser/services/MouseService';
|
|
|
|
|
import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
|
|
|
|
|
import { Linkifier2 } from 'browser/Linkifier2';
|
|
|
|
|
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
|
|
|
|
import { CoreTerminal } from 'common/CoreTerminal';
|
|
|
|
|
import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services';
|
|
|
|
|
|
|
|
|
|
// Let it work inside Node.js for automated testing purposes.
|
|
|
|
|
const document = (typeof window !== 'undefined') ? window.document : null;
|
|
|
|
|
|
|
|
|
|
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
|
|
|
|
|
|
|
|
|
|
export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
public textarea: HTMLTextAreaElement;
|
|
|
|
|
public element: HTMLElement;
|
|
|
|
|
public screenElement: HTMLElement;
|
|
|
|
|
public textarea: HTMLTextAreaElement | undefined;
|
|
|
|
|
public element: HTMLElement | undefined;
|
|
|
|
|
public screenElement: HTMLElement | undefined;
|
|
|
|
|
|
|
|
|
|
private _document: Document;
|
|
|
|
|
private _viewportScrollArea: HTMLElement;
|
|
|
|
|
private _viewportElement: HTMLElement;
|
|
|
|
|
private _helperContainer: HTMLElement;
|
|
|
|
|
private _compositionView: HTMLElement;
|
|
|
|
|
private _document: Document | undefined;
|
|
|
|
|
private _viewportScrollArea: HTMLElement | undefined;
|
|
|
|
|
private _viewportElement: HTMLElement | undefined;
|
|
|
|
|
private _helperContainer: HTMLElement | undefined;
|
|
|
|
|
private _compositionView: HTMLElement | undefined;
|
|
|
|
|
|
|
|
|
|
private _visualBellTimer: number;
|
|
|
|
|
// private _visualBellTimer: number;
|
|
|
|
|
|
|
|
|
|
public browser: IBrowser = <any>Browser;
|
|
|
|
|
|
|
|
|
|
// TODO: We should remove options once components adopt optionsService
|
|
|
|
|
public get options(): ITerminalOptions { return this.optionsService.options; }
|
|
|
|
|
public get options(): IInitializedTerminalOptions { return this.optionsService.options; }
|
|
|
|
|
|
|
|
|
|
private _customKeyEventHandler: CustomKeyEventHandler;
|
|
|
|
|
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
|
|
|
|
|
|
|
|
|
|
// browser services
|
|
|
|
|
private _charSizeService: ICharSizeService;
|
|
|
|
|
private _mouseService: IMouseService;
|
|
|
|
|
private _renderService: IRenderService;
|
|
|
|
|
private _selectionService: ISelectionService;
|
|
|
|
|
private _soundService: ISoundService;
|
|
|
|
|
private _charSizeService: ICharSizeService | undefined;
|
|
|
|
|
private _mouseService: IMouseService | undefined;
|
|
|
|
|
private _renderService: IRenderService | undefined;
|
|
|
|
|
private _selectionService: ISelectionService | undefined;
|
|
|
|
|
private _soundService: ISoundService | undefined;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Records whether the keydown event has already been handled and triggered a data event, if so
|
|
|
|
@@ -94,12 +93,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
|
|
|
|
|
public linkifier: ILinkifier;
|
|
|
|
|
public linkifier2: ILinkifier2;
|
|
|
|
|
public viewport: IViewport;
|
|
|
|
|
private _compositionHelper: ICompositionHelper;
|
|
|
|
|
private _mouseZoneManager: IMouseZoneManager;
|
|
|
|
|
private _accessibilityManager: AccessibilityManager;
|
|
|
|
|
private _colorManager: ColorManager;
|
|
|
|
|
private _theme: ITheme;
|
|
|
|
|
public viewport: IViewport | undefined;
|
|
|
|
|
private _compositionHelper: ICompositionHelper | undefined;
|
|
|
|
|
private _mouseZoneManager: IMouseZoneManager | undefined;
|
|
|
|
|
private _accessibilityManager: AccessibilityManager | undefined;
|
|
|
|
|
private _colorManager: ColorManager | undefined;
|
|
|
|
|
private _theme: ITheme | undefined;
|
|
|
|
|
|
|
|
|
|
private _onCursorMove = new EventEmitter<void>();
|
|
|
|
|
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
|
|
|
|
@@ -140,6 +139,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
|
|
|
|
|
this._setup();
|
|
|
|
|
|
|
|
|
|
this.linkifier = this._instantiationService.createInstance(Linkifier);
|
|
|
|
|
this.linkifier2 = this._instantiationService.createInstance(Linkifier2);
|
|
|
|
|
|
|
|
|
|
// Setup InputHandler listeners
|
|
|
|
|
this.register(this._inputHandler.onRequestBell(() => this.bell()));
|
|
|
|
|
this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)));
|
|
|
|
|
this.register(this._inputHandler.onRequestReset(() => this.reset()));
|
|
|
|
@@ -160,7 +163,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
super.dispose();
|
|
|
|
|
this._renderService?.dispose();
|
|
|
|
|
this._customKeyEventHandler = null;
|
|
|
|
|
this._customKeyEventHandler = undefined;
|
|
|
|
|
this.write = () => { };
|
|
|
|
|
this.element?.parentNode?.removeChild(this.element);
|
|
|
|
|
}
|
|
|
|
@@ -168,14 +171,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
protected _setup(): void {
|
|
|
|
|
super._setup();
|
|
|
|
|
|
|
|
|
|
this._customKeyEventHandler = null;
|
|
|
|
|
|
|
|
|
|
if (!this.linkifier) {
|
|
|
|
|
this.linkifier = this._instantiationService.createInstance(Linkifier);
|
|
|
|
|
}
|
|
|
|
|
if (!this.linkifier2) {
|
|
|
|
|
this.linkifier2 = this._instantiationService.createInstance(Linkifier2);
|
|
|
|
|
}
|
|
|
|
|
this._customKeyEventHandler = undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -239,7 +235,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this._accessibilityManager?.dispose();
|
|
|
|
|
this._accessibilityManager = null;
|
|
|
|
|
this._accessibilityManager = undefined;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'tabStopWidth': this.buffers.setupTabStops(); break;
|
|
|
|
@@ -257,7 +253,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this._coreService.triggerDataEvent(C0.ESC + '[I');
|
|
|
|
|
}
|
|
|
|
|
this.updateCursorStyle(ev);
|
|
|
|
|
this.element.classList.add('focus');
|
|
|
|
|
this.element!.classList.add('focus');
|
|
|
|
|
this._showCursor();
|
|
|
|
|
this._onFocus.fire();
|
|
|
|
|
}
|
|
|
|
@@ -267,7 +263,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
* textarea.
|
|
|
|
|
*/
|
|
|
|
|
public blur(): void {
|
|
|
|
|
return this.textarea.blur();
|
|
|
|
|
return this.textarea?.blur();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -276,12 +272,12 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
private _onTextAreaBlur(): void {
|
|
|
|
|
// Text can safely be removed on blur. Doing it earlier could interfere with
|
|
|
|
|
// screen readers reading it out.
|
|
|
|
|
this.textarea.value = '';
|
|
|
|
|
this.textarea!.value = '';
|
|
|
|
|
this.refresh(this.buffer.y, this.buffer.y);
|
|
|
|
|
if (this._coreService.decPrivateModes.sendFocus) {
|
|
|
|
|
this._coreService.triggerDataEvent(C0.ESC + '[O');
|
|
|
|
|
}
|
|
|
|
|
this.element.classList.remove('focus');
|
|
|
|
|
this.element!.classList.remove('focus');
|
|
|
|
|
this._onBlur.fire();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -292,29 +288,29 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this._bindKeys();
|
|
|
|
|
|
|
|
|
|
// Bind clipboard functionality
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'copy', (event: ClipboardEvent) => {
|
|
|
|
|
this.register(addDisposableDomListener(this.element!, 'copy', (event: ClipboardEvent) => {
|
|
|
|
|
// If mouse events are active it means the selection manager is disabled and
|
|
|
|
|
// copy should be handled by the host program.
|
|
|
|
|
if (!this.hasSelection()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
copyHandler(event, this._selectionService);
|
|
|
|
|
copyHandler(event, this._selectionService!);
|
|
|
|
|
}));
|
|
|
|
|
const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea, this._coreService);
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'paste', pasteHandlerWrapper));
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'paste', pasteHandlerWrapper));
|
|
|
|
|
const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea!, this._coreService);
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'paste', pasteHandlerWrapper));
|
|
|
|
|
this.register(addDisposableDomListener(this.element!, 'paste', pasteHandlerWrapper));
|
|
|
|
|
|
|
|
|
|
// Handle right click context menus
|
|
|
|
|
if (Browser.isFirefox) {
|
|
|
|
|
// Firefox doesn't appear to fire the contextmenu event on right click
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'mousedown', (event: MouseEvent) => {
|
|
|
|
|
this.register(addDisposableDomListener(this.element!, 'mousedown', (event: MouseEvent) => {
|
|
|
|
|
if (event.button === 2) {
|
|
|
|
|
rightClickHandler(event, this.textarea, this.screenElement, this._selectionService, this.options.rightClickSelectsWord);
|
|
|
|
|
rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
} else {
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => {
|
|
|
|
|
rightClickHandler(event, this.textarea, this.screenElement, this._selectionService, this.options.rightClickSelectsWord);
|
|
|
|
|
this.register(addDisposableDomListener(this.element!, 'contextmenu', (event: MouseEvent) => {
|
|
|
|
|
rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -324,9 +320,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
if (Browser.isLinux) {
|
|
|
|
|
// Use auxclick event over mousedown the latter doesn't seem to work. Note
|
|
|
|
|
// that the regular click event doesn't fire for the middle mouse button.
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'auxclick', (event: MouseEvent) => {
|
|
|
|
|
this.register(addDisposableDomListener(this.element!, 'auxclick', (event: MouseEvent) => {
|
|
|
|
|
if (event.button === 1) {
|
|
|
|
|
moveTextAreaUnderMouseCursor(event, this.textarea, this.screenElement);
|
|
|
|
|
moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
@@ -336,13 +332,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
* Apply key handling to the terminal
|
|
|
|
|
*/
|
|
|
|
|
private _bindKeys(): void {
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'compositionstart', () => this._compositionHelper.compositionstart()));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper.compositionupdate(e)));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea, 'compositionend', () => this._compositionHelper.compositionend()));
|
|
|
|
|
this.register(this.onRender(() => this._compositionHelper.updateCompositionElements()));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'compositionstart', () => this._compositionHelper!.compositionstart()));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper!.compositionupdate(e)));
|
|
|
|
|
this.register(addDisposableDomListener(this.textarea!, 'compositionend', () => this._compositionHelper!.compositionend()));
|
|
|
|
|
this.register(this.onRender(() => this._compositionHelper!.updateCompositionElements()));
|
|
|
|
|
this.register(this.onRender(e => this._queueLinkification(e.start, e.end)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -360,7 +356,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._document = parent.ownerDocument;
|
|
|
|
|
this._document = parent.ownerDocument!;
|
|
|
|
|
|
|
|
|
|
// Create main element container
|
|
|
|
|
this.element = this._document.createElement('div');
|
|
|
|
@@ -416,16 +412,15 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this.element.appendChild(fragment);
|
|
|
|
|
|
|
|
|
|
this._theme = this.options.theme || this._theme;
|
|
|
|
|
this.options.theme = undefined;
|
|
|
|
|
this._colorManager = new ColorManager(document, this.options.allowTransparency);
|
|
|
|
|
this.optionsService.onOptionChange(e => this._colorManager.onOptionsChange(e));
|
|
|
|
|
this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e));
|
|
|
|
|
this._colorManager.setTheme(this._theme);
|
|
|
|
|
|
|
|
|
|
const renderer = this._createRenderer();
|
|
|
|
|
this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement);
|
|
|
|
|
this._instantiationService.setService(IRenderService, this._renderService);
|
|
|
|
|
this._renderService.onRenderedBufferChange(e => this._onRender.fire(e));
|
|
|
|
|
this.onResize(e => this._renderService.resize(e.cols, e.rows));
|
|
|
|
|
this.onResize(e => this._renderService!.resize(e.cols, e.rows));
|
|
|
|
|
|
|
|
|
|
this._soundService = this._instantiationService.createInstance(SoundService);
|
|
|
|
|
this._instantiationService.setService(ISoundService, this._soundService);
|
|
|
|
@@ -438,14 +433,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this._viewportScrollArea
|
|
|
|
|
);
|
|
|
|
|
this.viewport.onThemeChange(this._colorManager.colors);
|
|
|
|
|
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport.syncScrollArea()));
|
|
|
|
|
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
|
|
|
|
|
this.register(this.viewport);
|
|
|
|
|
|
|
|
|
|
this.register(this.onCursorMove(() => this._renderService.onCursorMove()));
|
|
|
|
|
this.register(this.onResize(() => this._renderService.onResize(this.cols, this.rows)));
|
|
|
|
|
this.register(this.onBlur(() => this._renderService.onBlur()));
|
|
|
|
|
this.register(this.onFocus(() => this._renderService.onFocus()));
|
|
|
|
|
this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea()));
|
|
|
|
|
this.register(this.onCursorMove(() => this._renderService!.onCursorMove()));
|
|
|
|
|
this.register(this.onResize(() => this._renderService!.onResize(this.cols, this.rows)));
|
|
|
|
|
this.register(this.onBlur(() => this._renderService!.onBlur()));
|
|
|
|
|
this.register(this.onFocus(() => this._renderService!.onFocus()));
|
|
|
|
|
this.register(this._renderService.onDimensionsChange(() => this.viewport!.syncScrollArea()));
|
|
|
|
|
|
|
|
|
|
this._selectionService = this._instantiationService.createInstance(SelectionService,
|
|
|
|
|
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
|
|
|
|
@@ -453,29 +448,29 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
this.screenElement);
|
|
|
|
|
this._instantiationService.setService(ISelectionService, this._selectionService);
|
|
|
|
|
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
|
|
|
|
this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
|
|
|
|
this.register(this._selectionService.onRedrawRequest(e => this._renderService!.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
|
|
|
|
this.register(this._selectionService.onLinuxMouseSelection(text => {
|
|
|
|
|
// If there's a new selection, put it into the textarea, focus and select it
|
|
|
|
|
// in order to register it as a selection on the OS. This event is fired
|
|
|
|
|
// only on Linux to enable middle click to paste selection.
|
|
|
|
|
this.textarea.value = text;
|
|
|
|
|
this.textarea.focus();
|
|
|
|
|
this.textarea.select();
|
|
|
|
|
this.textarea!.value = text;
|
|
|
|
|
this.textarea!.focus();
|
|
|
|
|
this.textarea!.select();
|
|
|
|
|
}));
|
|
|
|
|
this.register(this.onScroll(() => {
|
|
|
|
|
this.viewport.syncScrollArea();
|
|
|
|
|
this._selectionService.refresh();
|
|
|
|
|
this.viewport!.syncScrollArea();
|
|
|
|
|
this._selectionService!.refresh();
|
|
|
|
|
}));
|
|
|
|
|
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh()));
|
|
|
|
|
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService!.refresh()));
|
|
|
|
|
|
|
|
|
|
this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement);
|
|
|
|
|
this.register(this._mouseZoneManager);
|
|
|
|
|
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
|
|
|
|
|
this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));
|
|
|
|
|
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
|
|
|
|
|
this.linkifier2.attachToDom(this.element, this._mouseService, this._renderService);
|
|
|
|
|
|
|
|
|
|
// This event listener must be registered aftre MouseZoneManager is created
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
|
|
|
|
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
|
|
|
|
|
|
|
|
|
|
// apply mouse event classes set by escape codes before terminal was attached
|
|
|
|
|
if (this._coreMouseService.areMouseEventsActive) {
|
|
|
|
@@ -507,8 +502,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
|
|
|
|
|
private _createRenderer(): IRenderer {
|
|
|
|
|
switch (this.options.rendererType) {
|
|
|
|
|
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, this.linkifier, this.linkifier2);
|
|
|
|
|
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier, this.linkifier2);
|
|
|
|
|
case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2);
|
|
|
|
|
case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2);
|
|
|
|
|
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -520,8 +515,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
private _setTheme(theme: ITheme): void {
|
|
|
|
|
this._theme = theme;
|
|
|
|
|
this._colorManager?.setTheme(theme);
|
|
|
|
|
this._renderService?.setColors(this._colorManager.colors);
|
|
|
|
|
this.viewport?.onThemeChange(this._colorManager.colors);
|
|
|
|
|
this._renderService?.setColors(this._colorManager!.colors);
|
|
|
|
|
this.viewport?.onThemeChange(this._colorManager!.colors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -541,18 +536,18 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
*/
|
|
|
|
|
public bindMouse(): void {
|
|
|
|
|
const self = this;
|
|
|
|
|
const el = this.element;
|
|
|
|
|
const el = this.element!;
|
|
|
|
|
|
|
|
|
|
// send event to CoreMouseService
|
|
|
|
|
function sendEvent(ev: MouseEvent | WheelEvent): boolean {
|
|
|
|
|
// get mouse coordinates
|
|
|
|
|
const pos = self._mouseService.getRawByteCoords(ev, self.screenElement, self.cols, self.rows);
|
|
|
|
|
const pos = self._mouseService!.getRawByteCoords(ev, self.screenElement!, self.cols, self.rows);
|
|
|
|
|
if (!pos) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let but: CoreMouseButton;
|
|
|
|
|
let action: CoreMouseAction;
|
|
|
|
|
let action: CoreMouseAction | undefined;
|
|
|
|
|
switch ((<any>ev).overrideType || ev.type) {
|
|
|
|
|
case 'mousemove':
|
|
|
|
|
action = CoreMouseAction.MOVE;
|
|
|
|
@@ -621,14 +616,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
mousedrag: null,
|
|
|
|
|
mousemove: null
|
|
|
|
|
};
|
|
|
|
|
const eventListeners: { [key: string]: (ev: Event) => void } = {
|
|
|
|
|
const eventListeners: { [key: string]: (ev: any) => void | boolean } = {
|
|
|
|
|
mouseup: (ev: MouseEvent) => {
|
|
|
|
|
sendEvent(ev);
|
|
|
|
|
if (!ev.buttons) {
|
|
|
|
|
// if no other button is held remove global handlers
|
|
|
|
|
this._document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
|
|
|
this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);
|
|
|
|
|
if (requestedEvents.mousedrag) {
|
|
|
|
|
this._document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
|
|
|
this._document!.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this.cancel(ev);
|
|
|
|
@@ -657,18 +652,18 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
if (this.optionsService.options.logLevel === 'debug') {
|
|
|
|
|
this._logService.debug('Binding to mouse events:', this._coreMouseService.explainEvents(events));
|
|
|
|
|
}
|
|
|
|
|
this.element.classList.add('enable-mouse-events');
|
|
|
|
|
this._selectionService.disable();
|
|
|
|
|
this.element!.classList.add('enable-mouse-events');
|
|
|
|
|
this._selectionService!.disable();
|
|
|
|
|
} else {
|
|
|
|
|
this._logService.debug('Unbinding from mouse events.');
|
|
|
|
|
this.element.classList.remove('enable-mouse-events');
|
|
|
|
|
this._selectionService.enable();
|
|
|
|
|
this.element!.classList.remove('enable-mouse-events');
|
|
|
|
|
this._selectionService!.enable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add/remove handlers from requestedEvents
|
|
|
|
|
|
|
|
|
|
if (!(events & CoreMouseEventType.MOVE)) {
|
|
|
|
|
el.removeEventListener('mousemove', requestedEvents.mousemove);
|
|
|
|
|
el.removeEventListener('mousemove', requestedEvents.mousemove!);
|
|
|
|
|
requestedEvents.mousemove = null;
|
|
|
|
|
} else if (!requestedEvents.mousemove) {
|
|
|
|
|
el.addEventListener('mousemove', eventListeners.mousemove);
|
|
|
|
@@ -676,7 +671,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(events & CoreMouseEventType.WHEEL)) {
|
|
|
|
|
el.removeEventListener('wheel', requestedEvents.wheel);
|
|
|
|
|
el.removeEventListener('wheel', requestedEvents.wheel!);
|
|
|
|
|
requestedEvents.wheel = null;
|
|
|
|
|
} else if (!requestedEvents.wheel) {
|
|
|
|
|
el.addEventListener('wheel', eventListeners.wheel);
|
|
|
|
@@ -684,14 +679,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(events & CoreMouseEventType.UP)) {
|
|
|
|
|
this._document.removeEventListener('mouseup', requestedEvents.mouseup);
|
|
|
|
|
this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);
|
|
|
|
|
requestedEvents.mouseup = null;
|
|
|
|
|
} else if (!requestedEvents.mouseup) {
|
|
|
|
|
requestedEvents.mouseup = eventListeners.mouseup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(events & CoreMouseEventType.DRAG)) {
|
|
|
|
|
this._document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
|
|
|
|
this._document!.removeEventListener('mousemove', requestedEvents.mousedrag!);
|
|
|
|
|
requestedEvents.mousedrag = null;
|
|
|
|
|
} else if (!requestedEvents.mousedrag) {
|
|
|
|
|
requestedEvents.mousedrag = eventListeners.mousedrag;
|
|
|
|
@@ -710,7 +705,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// Don't send the mouse button to the pty if mouse events are disabled or
|
|
|
|
|
// if the selection manager is having selection forced (ie. a modifier is
|
|
|
|
|
// held).
|
|
|
|
|
if (!this._coreMouseService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) {
|
|
|
|
|
if (!this._coreMouseService.areMouseEventsActive || this._selectionService!.shouldForceSelection(ev)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -721,10 +716,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// Note: Other emulators also do this for 'mousedown' while a button
|
|
|
|
|
// is held, we currently limit 'mousedown' to the terminal only.
|
|
|
|
|
if (requestedEvents.mouseup) {
|
|
|
|
|
this._document.addEventListener('mouseup', requestedEvents.mouseup);
|
|
|
|
|
this._document!.addEventListener('mouseup', requestedEvents.mouseup);
|
|
|
|
|
}
|
|
|
|
|
if (requestedEvents.mousedrag) {
|
|
|
|
|
this._document.addEventListener('mousemove', requestedEvents.mousedrag);
|
|
|
|
|
this._document!.addEventListener('mousemove', requestedEvents.mousedrag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.cancel(ev);
|
|
|
|
@@ -735,7 +730,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// Convert wheel events into up/down events when the buffer does not have scrollback, this
|
|
|
|
|
// enables scrolling in apps hosted in the alt buffer such as vim or tmux.
|
|
|
|
|
if (!this.buffer.hasScrollback) {
|
|
|
|
|
const amount = this.viewport.getLinesScrolled(ev);
|
|
|
|
|
const amount = this.viewport!.getLinesScrolled(ev);
|
|
|
|
|
|
|
|
|
|
// Do nothing if there's no vertical scroll
|
|
|
|
|
if (amount === 0) {
|
|
|
|
@@ -758,20 +753,20 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// the shell for example
|
|
|
|
|
this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {
|
|
|
|
|
if (requestedEvents.wheel) return;
|
|
|
|
|
if (!this.viewport.onWheel(ev)) {
|
|
|
|
|
if (!this.viewport!.onWheel(ev)) {
|
|
|
|
|
return this.cancel(ev);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => {
|
|
|
|
|
if (this._coreMouseService.areMouseEventsActive) return;
|
|
|
|
|
this.viewport.onTouchStart(ev);
|
|
|
|
|
this.viewport!.onTouchStart(ev);
|
|
|
|
|
return this.cancel(ev);
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => {
|
|
|
|
|
if (this._coreMouseService.areMouseEventsActive) return;
|
|
|
|
|
if (!this.viewport.onTouchMove(ev)) {
|
|
|
|
|
if (!this.viewport!.onTouchMove(ev)) {
|
|
|
|
|
return this.cancel(ev);
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
@@ -802,9 +797,9 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
*/
|
|
|
|
|
public updateCursorStyle(ev: KeyboardEvent): void {
|
|
|
|
|
if (this._selectionService && this._selectionService.shouldColumnSelect(ev)) {
|
|
|
|
|
this.element.classList.add('column-select');
|
|
|
|
|
this.element!.classList.add('column-select');
|
|
|
|
|
} else {
|
|
|
|
|
this.element.classList.remove('column-select');
|
|
|
|
|
this.element!.classList.remove('column-select');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -824,7 +819,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public paste(data: string): void {
|
|
|
|
|
paste(data, this.textarea, this._coreService);
|
|
|
|
|
paste(data, this.textarea!, this._coreService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -871,13 +866,13 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
|
|
|
|
|
const joinerId = this._renderService.registerCharacterJoiner(handler);
|
|
|
|
|
const joinerId = this._renderService!.registerCharacterJoiner(handler);
|
|
|
|
|
this.refresh(0, this.rows - 1);
|
|
|
|
|
return joinerId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public deregisterCharacterJoiner(joinerId: number): void {
|
|
|
|
|
if (this._renderService.deregisterCharacterJoiner(joinerId)) {
|
|
|
|
|
if (this._renderService!.deregisterCharacterJoiner(joinerId)) {
|
|
|
|
|
this.refresh(0, this.rows - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -886,7 +881,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
return this.buffer.markers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public addMarker(cursorYOffset: number): IMarker {
|
|
|
|
|
public addMarker(cursorYOffset: number): IMarker | undefined {
|
|
|
|
|
// Disallow markers on the alt buffer
|
|
|
|
|
if (this.buffer !== this.buffers.normal) {
|
|
|
|
|
return;
|
|
|
|
@@ -909,7 +904,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
* @param length The length of the selection.
|
|
|
|
|
*/
|
|
|
|
|
public select(column: number, row: number, length: number): void {
|
|
|
|
|
this._selectionService.setSelection(column, row, length);
|
|
|
|
|
this._selectionService!.setSelection(column, row, length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -921,15 +916,15 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getSelectionPosition(): ISelectionPosition | undefined {
|
|
|
|
|
if (!this._selectionService.hasSelection) {
|
|
|
|
|
if (!this._selectionService || !this._selectionService.hasSelection) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
startColumn: this._selectionService.selectionStart[0],
|
|
|
|
|
startRow: this._selectionService.selectionStart[1],
|
|
|
|
|
endColumn: this._selectionService.selectionEnd[0],
|
|
|
|
|
endRow: this._selectionService.selectionEnd[1]
|
|
|
|
|
startColumn: this._selectionService.selectionStart![0],
|
|
|
|
|
startRow: this._selectionService.selectionStart![1],
|
|
|
|
|
endColumn: this._selectionService.selectionEnd![0],
|
|
|
|
|
endRow: this._selectionService.selectionEnd![1]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -957,14 +952,14 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
* - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
|
|
|
|
|
* @param ev The keydown event to be handled.
|
|
|
|
|
*/
|
|
|
|
|
protected _keyDown(event: KeyboardEvent): boolean {
|
|
|
|
|
protected _keyDown(event: KeyboardEvent): boolean | undefined {
|
|
|
|
|
this._keyDownHandled = false;
|
|
|
|
|
|
|
|
|
|
if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this._compositionHelper.keydown(event)) {
|
|
|
|
|
if (!this._compositionHelper!.keydown(event)) {
|
|
|
|
|
if (this.buffer.ybase !== this.buffer.ydisp) {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
}
|
|
|
|
@@ -1002,7 +997,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// will announce deleted characters. This will not work 100% of the time but it should cover
|
|
|
|
|
// most scenarios.
|
|
|
|
|
if (result.key === C0.ETX || result.key === C0.CR) {
|
|
|
|
|
this.textarea.value = '';
|
|
|
|
|
this.textarea!.value = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._onKey.fire({ key: result.key, domEvent: event });
|
|
|
|
@@ -1095,16 +1090,16 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
*/
|
|
|
|
|
public bell(): void {
|
|
|
|
|
if (this._soundBell()) {
|
|
|
|
|
this._soundService.playBellSound();
|
|
|
|
|
this._soundService!.playBellSound();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this._visualBell()) {
|
|
|
|
|
this.element.classList.add('visual-bell-active');
|
|
|
|
|
clearTimeout(this._visualBellTimer);
|
|
|
|
|
this._visualBellTimer = window.setTimeout(() => {
|
|
|
|
|
this.element.classList.remove('visual-bell-active');
|
|
|
|
|
}, 200);
|
|
|
|
|
}
|
|
|
|
|
// if (this._visualBell()) {
|
|
|
|
|
// this.element.classList.add('visual-bell-active');
|
|
|
|
|
// clearTimeout(this._visualBellTimer);
|
|
|
|
|
// this._visualBellTimer = window.setTimeout(() => {
|
|
|
|
|
// this.element.classList.remove('visual-bell-active');
|
|
|
|
|
// }, 200);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -1141,7 +1136,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
// Don't clear if it's already clear
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y));
|
|
|
|
|
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
|
|
|
|
|
this.buffer.lines.length = 1;
|
|
|
|
|
this.buffer.ydisp = 0;
|
|
|
|
|
this.buffer.ybase = 0;
|
|
|
|
@@ -1202,7 +1197,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Remove cancel function and cancelEvents option
|
|
|
|
|
public cancel(ev: Event, force?: boolean): boolean {
|
|
|
|
|
public cancel(ev: Event, force?: boolean): boolean | undefined {
|
|
|
|
|
if (!this.options.cancelEvents && !force) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|