aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/xterm/src/browser/Terminal.ts
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-03-23 14:04:55 -0500
committerToby Vincent <tobyv13@gmail.com>2022-03-23 14:15:18 -0500
commit8e36499326e969c7c34ecc537e589bd5ef5598a0 (patch)
tree7943cda837324847875d472d2ad68922c5019a50 /node_modules/xterm/src/browser/Terminal.ts
parent337fa04fe3686e0f0d236eef5df89179ee68f673 (diff)
chore: removed node_modules from git
Diffstat (limited to 'node_modules/xterm/src/browser/Terminal.ts')
-rw-r--r--node_modules/xterm/src/browser/Terminal.ts1408
1 files changed, 0 insertions, 1408 deletions
diff --git a/node_modules/xterm/src/browser/Terminal.ts b/node_modules/xterm/src/browser/Terminal.ts
deleted file mode 100644
index 703c995..0000000
--- a/node_modules/xterm/src/browser/Terminal.ts
+++ /dev/null
@@ -1,1408 +0,0 @@
-/**
- * Copyright (c) 2014 The xterm.js authors. All rights reserved.
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
- * @license MIT
- *
- * Originally forked from (with the author's permission):
- * Fabrice Bellard's javascript vt100 for jslinux:
- * http://bellard.org/jslinux/
- * Copyright (c) 2011 Fabrice Bellard
- * The original design remains. The terminal itself
- * has been extended to include xterm CSI codes, among
- * other features.
- *
- * Terminal Emulation References:
- * http://vt100.net/
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
- * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- * http://invisible-island.net/vttest/
- * http://www.inwap.com/pdp10/ansicode.txt
- * http://linux.die.net/man/4/console_codes
- * http://linux.die.net/man/7/urxvt
- */
-
-import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2, CharacterJoinerHandler } from 'browser/Types';
-import { IRenderer } from 'browser/renderer/Types';
-import { CompositionHelper } from 'browser/input/CompositionHelper';
-import { Viewport } from 'browser/Viewport';
-import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard';
-import { C0 } from 'common/data/EscapeSequences';
-import { WindowsOptionsReportType } from '../common/InputHandler';
-import { Renderer } from 'browser/renderer/Renderer';
-import { Linkifier } from 'browser/Linkifier';
-import { SelectionService } from 'browser/services/SelectionService';
-import * as Browser from 'common/Platform';
-import { addDisposableDomListener } from 'browser/Lifecycle';
-import * as Strings from 'browser/LocalizableStrings';
-import { SoundService } from 'browser/services/SoundService';
-import { MouseZoneManager } from 'browser/MouseZoneManager';
-import { AccessibilityManager } from './AccessibilityManager';
-import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
-import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
-import { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';
-import { evaluateKeyboardEvent } from 'common/input/Keyboard';
-import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
-import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
-import { ColorManager } from 'browser/ColorManager';
-import { RenderService } from 'browser/services/RenderService';
-import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService, IDecorationService } from 'browser/services/Services';
-import { CharSizeService } from 'browser/services/CharSizeService';
-import { IBuffer } from 'common/buffer/Types';
-import { MouseService } from 'browser/services/MouseService';
-import { Linkifier2 } from 'browser/Linkifier2';
-import { CoreBrowserService } from 'browser/services/CoreBrowserService';
-import { CoreTerminal } from 'common/CoreTerminal';
-import { color, rgba } from 'browser/Color';
-import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
-import { toRgbString } from 'common/input/XParseColor';
-import { DecorationService } from 'browser/services/DecorationService';
-
-// Let it work inside Node.js for automated testing purposes.
-const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
-
-export class Terminal extends CoreTerminal implements ITerminal {
- public textarea: HTMLTextAreaElement | undefined;
- public element: HTMLElement | undefined;
- public screenElement: HTMLElement | undefined;
-
- private _document: Document | undefined;
- private _viewportScrollArea: HTMLElement | undefined;
- private _viewportElement: HTMLElement | undefined;
- private _helperContainer: HTMLElement | undefined;
- private _compositionView: HTMLElement | undefined;
-
- // private _visualBellTimer: number;
-
- public browser: IBrowser = Browser as any;
-
- private _customKeyEventHandler: CustomKeyEventHandler | undefined;
-
- // browser services
- private _charSizeService: ICharSizeService | undefined;
- private _mouseService: IMouseService | undefined;
- private _renderService: IRenderService | undefined;
- private _characterJoinerService: ICharacterJoinerService | 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
- * the keypress event should not trigger a data event but should still print to the textarea so
- * screen readers will announce it.
- */
- private _keyDownHandled: boolean = false;
-
- /**
- * Records whether the keypress event has already been handled and triggered a data event, if so
- * the input event should not trigger a data event but should still print to the textarea so
- * screen readers will announce it.
- */
- private _keyPressHandled: boolean = false;
-
- /**
- * Records whether there has been a keydown event for a dead key without a corresponding keydown
- * event for the composed/alternative character. If we cancel the keydown event for the dead key,
- * no events will be emitted for the final character.
- */
- private _unprocessedDeadKey: boolean = false;
-
- public linkifier: ILinkifier;
- public linkifier2: ILinkifier2;
- public viewport: IViewport | undefined;
- public decorationService: IDecorationService;
- 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; }
- private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>();
- public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; }
- private _onRender = new EventEmitter<{ start: number, end: number }>();
- public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; }
- private _onSelectionChange = new EventEmitter<void>();
- public get onSelectionChange(): IEvent<void> { return this._onSelectionChange.event; }
- private _onTitleChange = new EventEmitter<string>();
- public get onTitleChange(): IEvent<string> { return this._onTitleChange.event; }
- private _onBell = new EventEmitter<void>();
- public get onBell(): IEvent<void> { return this._onBell.event; }
-
- private _onFocus = new EventEmitter<void>();
- public get onFocus(): IEvent<void> { return this._onFocus.event; }
- private _onBlur = new EventEmitter<void>();
- public get onBlur(): IEvent<void> { return this._onBlur.event; }
- private _onA11yCharEmitter = new EventEmitter<string>();
- public get onA11yChar(): IEvent<string> { return this._onA11yCharEmitter.event; }
- private _onA11yTabEmitter = new EventEmitter<number>();
- public get onA11yTab(): IEvent<number> { return this._onA11yTabEmitter.event; }
-
- /**
- * Creates a new `Terminal` object.
- *
- * @param options An object containing a set of options, the available options are:
- * - `cursorBlink` (boolean): Whether the terminal cursor blinks
- * - `cols` (number): The number of columns of the terminal (horizontal size)
- * - `rows` (number): The number of rows of the terminal (vertical size)
- *
- * @public
- * @class Xterm Xterm
- * @alias module:xterm/src/xterm
- */
- constructor(
- options: Partial<ITerminalOptions> = {}
- ) {
- super(options);
-
- this._setup();
-
- this.linkifier = this._instantiationService.createInstance(Linkifier);
- this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));
- this.decorationService = this.register(this._instantiationService.createInstance(DecorationService));
-
- // 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.onRequestSendFocus(() => this._reportFocus()));
- this.register(this._inputHandler.onRequestReset(() => this.reset()));
- this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
- this.register(this._inputHandler.onColor((event) => this._handleColorEvent(event)));
- this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));
- this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));
- this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
- this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter));
-
- // Setup listeners
- this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)));
- }
-
- /**
- * Handle color event from inputhandler for OSC 4|104 | 10|110 | 11|111 | 12|112.
- * An event from OSC 4|104 may contain multiple set or report requests, and multiple
- * or none restore requests (resetting all),
- * while an event from OSC 10|110 | 11|111 | 12|112 always contains a single request.
- */
- private _handleColorEvent(event: IColorEvent): void {
- if (!this._colorManager) return;
- for (const req of event) {
- let acc: 'foreground' | 'background' | 'cursor' | 'ansi' | undefined = undefined;
- let ident = '';
- switch (req.index) {
- case ColorIndex.FOREGROUND: // OSC 10 | 110
- acc = 'foreground';
- ident = '10';
- break;
- case ColorIndex.BACKGROUND: // OSC 11 | 111
- acc = 'background';
- ident = '11';
- break;
- case ColorIndex.CURSOR: // OSC 12 | 112
- acc = 'cursor';
- ident = '12';
- break;
- default: // OSC 4 | 104
- // we can skip the [0..255] range check here (already done in inputhandler)
- acc = 'ansi';
- ident = '4;' + req.index;
- }
- if (acc) {
- switch (req.type) {
- case ColorRequestType.REPORT:
- const channels = color.toColorRGB(acc === 'ansi'
- ? this._colorManager.colors.ansi[req.index]
- : this._colorManager.colors[acc]);
- this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(channels)}${C0.BEL}`);
- break;
- case ColorRequestType.SET:
- if (acc === 'ansi') this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color);
- else this._colorManager.colors[acc] = rgba.toColor(...req.color);
- break;
- case ColorRequestType.RESTORE:
- this._colorManager.restoreColor(req.index);
- break;
- }
- }
- }
- this._renderService?.setColors(this._colorManager.colors);
- this.viewport?.onThemeChange(this._colorManager.colors);
- }
-
- public dispose(): void {
- if (this._isDisposed) {
- return;
- }
- super.dispose();
- this._renderService?.dispose();
- this._customKeyEventHandler = undefined;
- this.write = () => { };
- this.element?.parentNode?.removeChild(this.element);
- }
-
- protected _setup(): void {
- super._setup();
-
- this._customKeyEventHandler = undefined;
- }
-
- /**
- * Convenience property to active buffer.
- */
- public get buffer(): IBuffer {
- return this.buffers.active;
- }
-
- /**
- * Focus the terminal. Delegates focus handling to the terminal's DOM element.
- */
- public focus(): void {
- if (this.textarea) {
- this.textarea.focus({ preventScroll: true });
- }
- }
-
- protected _updateOptions(key: string): void {
- super._updateOptions(key);
-
- // TODO: These listeners should be owned by individual components
- switch (key) {
- case 'fontFamily':
- case 'fontSize':
- // When the font changes the size of the cells may change which requires a renderer clear
- this._renderService?.clear();
- this._charSizeService?.measure();
- break;
- case 'cursorBlink':
- case 'cursorStyle':
- // The DOM renderer needs a row refresh to update the cursor styles
- this.refresh(this.buffer.y, this.buffer.y);
- break;
- case 'customGlyphs':
- case 'drawBoldTextInBrightColors':
- case 'letterSpacing':
- case 'lineHeight':
- case 'fontWeight':
- case 'fontWeightBold':
- case 'minimumContrastRatio':
- // When the font changes the size of the cells may change which requires a renderer clear
- if (this._renderService) {
- this._renderService.clear();
- this._renderService.onResize(this.cols, this.rows);
- this.refresh(0, this.rows - 1);
- }
- break;
- case 'rendererType':
- if (this._renderService) {
- this._renderService.setRenderer(this._createRenderer());
- this._renderService.onResize(this.cols, this.rows);
- }
- break;
- case 'scrollback':
- this.viewport?.syncScrollArea();
- break;
- case 'screenReaderMode':
- if (this.optionsService.rawOptions.screenReaderMode) {
- if (!this._accessibilityManager && this._renderService) {
- this._accessibilityManager = new AccessibilityManager(this, this._renderService);
- }
- } else {
- this._accessibilityManager?.dispose();
- this._accessibilityManager = undefined;
- }
- break;
- case 'tabStopWidth': this.buffers.setupTabStops(); break;
- case 'theme':
- this._setTheme(this.optionsService.rawOptions.theme);
- break;
- }
- }
-
- /**
- * Binds the desired focus behavior on a given terminal object.
- */
- private _onTextAreaFocus(ev: KeyboardEvent): void {
- if (this.coreService.decPrivateModes.sendFocus) {
- this.coreService.triggerDataEvent(C0.ESC + '[I');
- }
- this.updateCursorStyle(ev);
- this.element!.classList.add('focus');
- this._showCursor();
- this._onFocus.fire();
- }
-
- /**
- * Blur the terminal, calling the blur function on the terminal's underlying
- * textarea.
- */
- public blur(): void {
- return this.textarea?.blur();
- }
-
- /**
- * Binds the desired blur behavior on a given terminal object.
- */
- 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.refresh(this.buffer.y, this.buffer.y);
- if (this.coreService.decPrivateModes.sendFocus) {
- this.coreService.triggerDataEvent(C0.ESC + '[O');
- }
- this.element!.classList.remove('focus');
- this._onBlur.fire();
- }
-
- private _syncTextArea(): void {
- if (!this.textarea || !this.buffer.isCursorInViewport || this._compositionHelper!.isComposing || !this._renderService) {
- return;
- }
- const cursorY = this.buffer.ybase + this.buffer.y;
- const bufferLine = this.buffer.lines.get(cursorY);
- if (!bufferLine) {
- return;
- }
- const cursorX = Math.min(this.buffer.x, this.cols - 1);
- const cellHeight = this._renderService.dimensions.actualCellHeight;
- const width = bufferLine.getWidth(cursorX);
- const cellWidth = this._renderService.dimensions.actualCellWidth * width;
- const cursorTop = this.buffer.y * this._renderService.dimensions.actualCellHeight;
- const cursorLeft = cursorX * this._renderService.dimensions.actualCellWidth;
-
- // Sync the textarea to the exact position of the composition view so the IME knows where the
- // text is.
- this.textarea.style.left = cursorLeft + 'px';
- this.textarea.style.top = cursorTop + 'px';
- this.textarea.style.width = cellWidth + 'px';
- this.textarea.style.height = cellHeight + 'px';
- this.textarea.style.lineHeight = cellHeight + 'px';
- this.textarea.style.zIndex = '-5';
- }
-
- /**
- * Initialize default behavior
- */
- private _initGlobal(): void {
- this._bindKeys();
-
- // Bind clipboard functionality
- 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!);
- }));
- 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) => {
- if (event.button === 2) {
- 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);
- }));
- }
-
- // Move the textarea under the cursor when middle clicking on Linux to ensure
- // middle click to paste selection works. This only appears to work in Chrome
- // at the time is writing.
- 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) => {
- if (event.button === 1) {
- moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!);
- }
- }));
- }
- }
-
- /**
- * 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(addDisposableDomListener(this.textarea!, 'input', (ev: InputEvent) => this._inputEvent(ev), true));
- this.register(this.onRender(() => this._compositionHelper!.updateCompositionElements()));
- this.register(this.onRender(e => this._queueLinkification(e.start, e.end)));
- }
-
- /**
- * Opens the terminal within an element.
- *
- * @param parent The element to create the terminal within.
- */
- public open(parent: HTMLElement): void {
- if (!parent) {
- throw new Error('Terminal requires a parent element.');
- }
-
- if (!parent.isConnected) {
- this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
- }
-
- this._document = parent.ownerDocument!;
-
- // Create main element container
- this.element = this._document.createElement('div');
- this.element.dir = 'ltr'; // xterm.css assumes LTR
- this.element.classList.add('terminal');
- this.element.classList.add('xterm');
- this.element.setAttribute('tabindex', '0');
- parent.appendChild(this.element);
-
- // Performance: Use a document fragment to build the terminal
- // viewport and helper elements detached from the DOM
- const fragment = document.createDocumentFragment();
- this._viewportElement = document.createElement('div');
- this._viewportElement.classList.add('xterm-viewport');
- fragment.appendChild(this._viewportElement);
- this._viewportScrollArea = document.createElement('div');
- this._viewportScrollArea.classList.add('xterm-scroll-area');
- this._viewportElement.appendChild(this._viewportScrollArea);
-
- this.screenElement = document.createElement('div');
- this.screenElement.classList.add('xterm-screen');
- // Create the container that will hold helpers like the textarea for
- // capturing DOM Events. Then produce the helpers.
- this._helperContainer = document.createElement('div');
- this._helperContainer.classList.add('xterm-helpers');
- this.screenElement.appendChild(this._helperContainer);
- fragment.appendChild(this.screenElement);
-
- this.textarea = document.createElement('textarea');
- this.textarea.classList.add('xterm-helper-textarea');
- this.textarea.setAttribute('aria-label', Strings.promptLabel);
- this.textarea.setAttribute('aria-multiline', 'false');
- this.textarea.setAttribute('autocorrect', 'off');
- this.textarea.setAttribute('autocapitalize', 'off');
- this.textarea.setAttribute('spellcheck', 'false');
- this.textarea.tabIndex = 0;
- this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._onTextAreaFocus(ev)));
- this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));
- this._helperContainer.appendChild(this.textarea);
-
- const coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea);
- this._instantiationService.setService(ICoreBrowserService, coreBrowserService);
-
- this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
- this._instantiationService.setService(ICharSizeService, this._charSizeService);
-
- this._theme = this.options.theme || this._theme;
- this._colorManager = new ColorManager(document, this.options.allowTransparency);
- this.register(this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e)));
- this._colorManager.setTheme(this._theme);
-
- this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService);
- this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService);
-
- const renderer = this._createRenderer();
- this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement));
- this._instantiationService.setService(IRenderService, this._renderService);
- this.register(this._renderService.onRenderedBufferChange(e => this._onRender.fire(e)));
- this.onResize(e => this._renderService!.resize(e.cols, e.rows));
-
- this._compositionView = document.createElement('div');
- this._compositionView.classList.add('composition-view');
- this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
- this._helperContainer.appendChild(this._compositionView);
-
- // Performance: Add viewport and helper elements from the fragment
- this.element.appendChild(fragment);
-
- this._soundService = this._instantiationService.createInstance(SoundService);
- this._instantiationService.setService(ISoundService, this._soundService);
- this._mouseService = this._instantiationService.createInstance(MouseService);
- this._instantiationService.setService(IMouseService, this._mouseService);
-
- this.viewport = this._instantiationService.createInstance(Viewport,
- (amount: number) => this.scrollLines(amount, true, ScrollSource.VIEWPORT),
- this._viewportElement,
- this._viewportScrollArea,
- this.element
- );
- this.viewport.onThemeChange(this._colorManager.colors);
- this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
- this.register(this.viewport);
-
- this.register(this.onCursorMove(() => {
- this._renderService!.onCursorMove();
- this._syncTextArea();
- }));
- 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.register(this._instantiationService.createInstance(SelectionService,
- this.element,
- this.screenElement,
- this.linkifier2
- ));
- this._instantiationService.setService(ISelectionService, this._selectionService);
- this.register(this._selectionService.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent)));
- this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
- this.register(this._selectionService.onRequestRedraw(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.register(this._onScroll.event(ev => {
- this.viewport!.syncScrollArea();
- 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.linkifier.attachToDom(this.element, this._mouseZoneManager);
- this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
-
- this.decorationService.attachToDom(this.screenElement, this._renderService, this._bufferService);
- // This event listener must be registered aftre MouseZoneManager is created
- 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) {
- this._selectionService.disable();
- this.element.classList.add('enable-mouse-events');
- } else {
- this._selectionService.enable();
- }
-
- if (this.options.screenReaderMode) {
- // Note that this must be done *after* the renderer is created in order to
- // ensure the correct order of the dprchange event
- this._accessibilityManager = new AccessibilityManager(this, this._renderService);
- }
-
- // Measure the character size
- this._charSizeService.measure();
-
- // Setup loop that draws to screen
- this.refresh(0, this.rows - 1);
-
- // Initialize global actions that need to be taken on the document.
- this._initGlobal();
-
- // Listen for mouse events and translate
- // them into terminal mouse protocols.
- this.bindMouse();
- }
-
- 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);
- default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
- }
- }
-
- /**
- * Sets the theme on the renderer. The renderer must have been initialized.
- * @param theme The theme to set.
- */
- private _setTheme(theme: ITheme): void {
- this._theme = theme;
- this._colorManager?.setTheme(theme);
- this._renderService?.setColors(this._colorManager!.colors);
- this.viewport?.onThemeChange(this._colorManager!.colors);
- }
-
- /**
- * Bind certain mouse events to the terminal.
- * By default only 3 button + wheel up/down is ativated. For higher buttons
- * no mouse report will be created. Typically the standard actions will be active.
- *
- * There are several reasons not to enable support for higher buttons/wheel:
- * - Button 4 and 5 are typically used for history back and forward navigation,
- * there is no straight forward way to supress/intercept those standard actions.
- * - Support for higher buttons does not work in some platform/browser combinations.
- * - Left/right wheel was not tested.
- * - Emulators vary in mouse button support, typically only 3 buttons and
- * wheel up/down work reliable.
- *
- * TODO: Move mouse event code into its own file.
- */
- public bindMouse(): void {
- const self = this;
- 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);
- if (!pos) {
- return false;
- }
-
- let but: CoreMouseButton;
- let action: CoreMouseAction | undefined;
- switch ((ev as any).overrideType || ev.type) {
- case 'mousemove':
- action = CoreMouseAction.MOVE;
- if (ev.buttons === undefined) {
- // buttons is not supported on macOS, try to get a value from button instead
- but = CoreMouseButton.NONE;
- if (ev.button !== undefined) {
- but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
- }
- } else {
- // according to MDN buttons only reports up to button 5 (AUX2)
- but = ev.buttons & 1 ? CoreMouseButton.LEFT :
- ev.buttons & 4 ? CoreMouseButton.MIDDLE :
- ev.buttons & 2 ? CoreMouseButton.RIGHT :
- CoreMouseButton.NONE; // fallback to NONE
- }
- break;
- case 'mouseup':
- action = CoreMouseAction.UP;
- but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
- break;
- case 'mousedown':
- action = CoreMouseAction.DOWN;
- but = ev.button < 3 ? ev.button : CoreMouseButton.NONE;
- break;
- case 'wheel':
- // only UP/DOWN wheel events are respected
- if ((ev as WheelEvent).deltaY !== 0) {
- action = (ev as WheelEvent).deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
- }
- but = CoreMouseButton.WHEEL;
- break;
- default:
- // dont handle other event types by accident
- return false;
- }
-
- // exit if we cannot determine valid button/action values
- // do nothing for higher buttons than wheel
- if (action === undefined || but === undefined || but > CoreMouseButton.WHEEL) {
- return false;
- }
-
- return self.coreMouseService.triggerMouseEvent({
- col: pos.x - 33, // FIXME: why -33 here?
- row: pos.y - 33,
- button: but,
- action,
- ctrl: ev.ctrlKey,
- alt: ev.altKey,
- shift: ev.shiftKey
- });
- }
-
- /**
- * Event listener state handling.
- * We listen to the onProtocolChange event of CoreMouseService and put
- * requested listeners in `requestedEvents`. With this the listeners
- * have all bits to do the event listener juggling.
- * Note: 'mousedown' currently is "always on" and not managed
- * by onProtocolChange.
- */
- const requestedEvents: { [key: string]: ((ev: Event) => void) | null } = {
- mouseup: null,
- wheel: null,
- mousedrag: null,
- mousemove: null
- };
- 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!);
- if (requestedEvents.mousedrag) {
- this._document!.removeEventListener('mousemove', requestedEvents.mousedrag);
- }
- }
- return this.cancel(ev);
- },
- wheel: (ev: WheelEvent) => {
- sendEvent(ev);
- return this.cancel(ev, true);
- },
- mousedrag: (ev: MouseEvent) => {
- // deal only with move while a button is held
- if (ev.buttons) {
- sendEvent(ev);
- }
- },
- mousemove: (ev: MouseEvent) => {
- // deal only with move without any button
- if (!ev.buttons) {
- sendEvent(ev);
- }
- }
- };
- this.register(this.coreMouseService.onProtocolChange(events => {
- // apply global changes on events
- if (events) {
- if (this.optionsService.rawOptions.logLevel === 'debug') {
- this._logService.debug('Binding to mouse events:', this.coreMouseService.explainEvents(events));
- }
- 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();
- }
-
- // add/remove handlers from requestedEvents
-
- if (!(events & CoreMouseEventType.MOVE)) {
- el.removeEventListener('mousemove', requestedEvents.mousemove!);
- requestedEvents.mousemove = null;
- } else if (!requestedEvents.mousemove) {
- el.addEventListener('mousemove', eventListeners.mousemove);
- requestedEvents.mousemove = eventListeners.mousemove;
- }
-
- if (!(events & CoreMouseEventType.WHEEL)) {
- el.removeEventListener('wheel', requestedEvents.wheel!);
- requestedEvents.wheel = null;
- } else if (!requestedEvents.wheel) {
- el.addEventListener('wheel', eventListeners.wheel, { passive: false });
- requestedEvents.wheel = eventListeners.wheel;
- }
-
- if (!(events & CoreMouseEventType.UP)) {
- 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!);
- requestedEvents.mousedrag = null;
- } else if (!requestedEvents.mousedrag) {
- requestedEvents.mousedrag = eventListeners.mousedrag;
- }
- }));
- // force initial onProtocolChange so we dont miss early mouse requests
- this.coreMouseService.activeProtocol = this.coreMouseService.activeProtocol;
-
- /**
- * "Always on" event listeners.
- */
- this.register(addDisposableDomListener(el, 'mousedown', (ev: MouseEvent) => {
- ev.preventDefault();
- this.focus();
-
- // 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)) {
- return;
- }
-
- sendEvent(ev);
-
- // Register additional global handlers which should keep reporting outside
- // of the terminal element.
- // 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);
- }
- if (requestedEvents.mousedrag) {
- this._document!.addEventListener('mousemove', requestedEvents.mousedrag);
- }
-
- return this.cancel(ev);
- }));
-
- this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {
- // do nothing, if app side handles wheel itself
- if (requestedEvents.wheel) return;
-
- if (!this.buffer.hasScrollback) {
- // 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.
- const amount = this.viewport!.getLinesScrolled(ev);
-
- // Do nothing if there's no vertical scroll
- if (amount === 0) {
- return;
- }
-
- // Construct and send sequences
- const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
- let data = '';
- for (let i = 0; i < Math.abs(amount); i++) {
- data += sequence;
- }
- this.coreService.triggerDataEvent(data, true);
- return this.cancel(ev, true);
- }
-
- // normal viewport scrolling
- // conditionally stop event, if the viewport still had rows to scroll within
- if (this.viewport!.onWheel(ev)) {
- return this.cancel(ev);
- }
- }, { passive: false }));
-
- this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => {
- if (this.coreMouseService.areMouseEventsActive) return;
- this.viewport!.onTouchStart(ev);
- return this.cancel(ev);
- }, { passive: true }));
-
- this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => {
- if (this.coreMouseService.areMouseEventsActive) return;
- if (!this.viewport!.onTouchMove(ev)) {
- return this.cancel(ev);
- }
- }, { passive: false }));
- }
-
-
- /**
- * Tells the renderer to refresh terminal content between two rows (inclusive) at the next
- * opportunity.
- * @param start The row to start from (between 0 and this.rows - 1).
- * @param end The row to end at (between start and this.rows - 1).
- */
- public refresh(start: number, end: number): void {
- this._renderService?.refreshRows(start, end);
- }
-
- /**
- * Queues linkification for the specified rows.
- * @param start The row to start from (between 0 and this.rows - 1).
- * @param end The row to end at (between start and this.rows - 1).
- */
- private _queueLinkification(start: number, end: number): void {
- this.linkifier?.linkifyRows(start, end);
- }
-
- /**
- * Change the cursor style for different selection modes
- */
- public updateCursorStyle(ev: KeyboardEvent): void {
- if (this._selectionService?.shouldColumnSelect(ev)) {
- this.element!.classList.add('column-select');
- } else {
- this.element!.classList.remove('column-select');
- }
- }
-
- /**
- * Display the cursor element
- */
- private _showCursor(): void {
- if (!this.coreService.isCursorInitialized) {
- this.coreService.isCursorInitialized = true;
- this.refresh(this.buffer.y, this.buffer.y);
- }
- }
-
- public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
- super.scrollLines(disp, suppressScrollEvent, source);
- this.refresh(0, this.rows - 1);
- }
-
- public paste(data: string): void {
- paste(data, this.textarea!, this.coreService);
- }
-
- /**
- * Attaches a custom key event handler which is run before keys are processed,
- * giving consumers of xterm.js ultimate control as to what keys should be
- * processed by the terminal and what keys should not.
- * @param customKeyEventHandler The custom KeyboardEvent handler to attach.
- * This is a function that takes a KeyboardEvent, allowing consumers to stop
- * propagation and/or prevent the default action. The function returns whether
- * the event should be processed by xterm.js.
- */
- public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {
- this._customKeyEventHandler = customKeyEventHandler;
- }
-
- /**
- * Registers a link matcher, allowing custom link patterns to be matched and
- * handled.
- * @param regex The regular expression to search for, specifically
- * this searches the textContent of the rows. You will want to use \s to match
- * a space ' ' character for example.
- * @param handler The callback when the link is called.
- * @param options Options for the link matcher.
- * @return The ID of the new matcher, this can be used to deregister.
- */
- public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number {
- const matcherId = this.linkifier.registerLinkMatcher(regex, handler, options);
- this.refresh(0, this.rows - 1);
- return matcherId;
- }
-
- /**
- * Deregisters a link matcher if it has been registered.
- * @param matcherId The link matcher's ID (returned after register)
- */
- public deregisterLinkMatcher(matcherId: number): void {
- if (this.linkifier.deregisterLinkMatcher(matcherId)) {
- this.refresh(0, this.rows - 1);
- }
- }
-
- public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
- return this.linkifier2.registerLinkProvider(linkProvider);
- }
-
- public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
- if (!this._characterJoinerService) {
- throw new Error('Terminal must be opened first');
- }
- const joinerId = this._characterJoinerService.register(handler);
- this.refresh(0, this.rows - 1);
- return joinerId;
- }
-
- public deregisterCharacterJoiner(joinerId: number): void {
- if (!this._characterJoinerService) {
- throw new Error('Terminal must be opened first');
- }
- if (this._characterJoinerService.deregister(joinerId)) {
- this.refresh(0, this.rows - 1);
- }
- }
-
- public get markers(): IMarker[] {
- return this.buffer.markers;
- }
-
- public addMarker(cursorYOffset: number): IMarker | undefined {
- // Disallow markers on the alt buffer
- if (this.buffer !== this.buffers.normal) {
- return;
- }
-
- return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
- }
-
- public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
- return this.decorationService!.registerDecoration(decorationOptions);
- }
-
- /**
- * Gets whether the terminal has an active selection.
- */
- public hasSelection(): boolean {
- return this._selectionService ? this._selectionService.hasSelection : false;
- }
-
- /**
- * Selects text within the terminal.
- * @param column The column the selection starts at..
- * @param row The row the selection starts at.
- * @param length The length of the selection.
- */
- public select(column: number, row: number, length: number): void {
- this._selectionService!.setSelection(column, row, length);
- }
-
- /**
- * Gets the terminal's current selection, this is useful for implementing copy
- * behavior outside of xterm.js.
- */
- public getSelection(): string {
- return this._selectionService ? this._selectionService.selectionText : '';
- }
-
- public getSelectionPosition(): ISelectionPosition | undefined {
- 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]
- };
- }
-
- /**
- * Clears the current terminal selection.
- */
- public clearSelection(): void {
- this._selectionService?.clearSelection();
- }
-
- /**
- * Selects all text within the terminal.
- */
- public selectAll(): void {
- this._selectionService?.selectAll();
- }
-
- public selectLines(start: number, end: number): void {
- this._selectionService?.selectLines(start, end);
- }
-
- /**
- * Handle a keydown event
- * Key Resources:
- * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
- * @param ev The keydown event to be handled.
- */
- 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.buffer.ybase !== this.buffer.ydisp) {
- this._bufferService.scrollToBottom();
- }
- return false;
- }
-
- if (event.key === 'Dead' || event.key === 'AltGraph') {
- this._unprocessedDeadKey = true;
- }
-
- const result = evaluateKeyboardEvent(event, this.coreService.decPrivateModes.applicationCursorKeys, this.browser.isMac, this.options.macOptionIsMeta);
-
- this.updateCursorStyle(event);
-
- if (result.type === KeyboardResultType.PAGE_DOWN || result.type === KeyboardResultType.PAGE_UP) {
- const scrollCount = this.rows - 1;
- this.scrollLines(result.type === KeyboardResultType.PAGE_UP ? -scrollCount : scrollCount);
- return this.cancel(event, true);
- }
-
- if (result.type === KeyboardResultType.SELECT_ALL) {
- this.selectAll();
- }
-
- if (this._isThirdLevelShift(this.browser, event)) {
- return true;
- }
-
- if (result.cancel) {
- // The event is canceled at the end already, is this necessary?
- this.cancel(event, true);
- }
-
- if (!result.key) {
- return true;
- }
-
- if (this._unprocessedDeadKey) {
- this._unprocessedDeadKey = false;
- return true;
- }
-
- // If ctrl+c or enter is being sent, clear out the textarea. This is done so that screen readers
- // 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._onKey.fire({ key: result.key, domEvent: event });
- this._showCursor();
- this.coreService.triggerDataEvent(result.key, true);
-
- // Cancel events when not in screen reader mode so events don't get bubbled up and handled by
- // other listeners. When screen reader mode is enabled, this could cause issues if the event
- // is handled at a higher level, this is a compromise in order to echo keys to the screen
- // reader.
- if (!this.optionsService.rawOptions.screenReaderMode) {
- return this.cancel(event, true);
- }
-
- this._keyDownHandled = true;
- }
-
- private _isThirdLevelShift(browser: IBrowser, ev: KeyboardEvent): boolean {
- const thirdLevelKey =
- (browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) ||
- (browser.isWindows && ev.altKey && ev.ctrlKey && !ev.metaKey) ||
- (browser.isWindows && ev.getModifierState('AltGraph'));
-
- if (ev.type === 'keypress') {
- return thirdLevelKey;
- }
-
- // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events)
- return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47);
- }
-
- protected _keyUp(ev: KeyboardEvent): void {
- if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
- return;
- }
-
- if (!wasModifierKeyOnlyEvent(ev)) {
- this.focus();
- }
-
- this.updateCursorStyle(ev);
- this._keyPressHandled = false;
- }
-
- /**
- * Handle a keypress event.
- * Key Resources:
- * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
- * @param ev The keypress event to be handled.
- */
- protected _keyPress(ev: KeyboardEvent): boolean {
- let key;
-
- this._keyPressHandled = false;
-
- if (this._keyDownHandled) {
- return false;
- }
-
- if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
- return false;
- }
-
- this.cancel(ev);
-
- if (ev.charCode) {
- key = ev.charCode;
- } else if (ev.which === null || ev.which === undefined) {
- key = ev.keyCode;
- } else if (ev.which !== 0 && ev.charCode !== 0) {
- key = ev.which;
- } else {
- return false;
- }
-
- if (!key || (
- (ev.altKey || ev.ctrlKey || ev.metaKey) && !this._isThirdLevelShift(this.browser, ev)
- )) {
- return false;
- }
-
- key = String.fromCharCode(key);
-
- this._onKey.fire({ key, domEvent: ev });
- this._showCursor();
- this.coreService.triggerDataEvent(key, true);
-
- this._keyPressHandled = true;
-
- // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
- // keys could be ignored
- this._unprocessedDeadKey = false;
-
- return true;
- }
-
- /**
- * Handle an input event.
- * Key Resources:
- * - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent
- * @param ev The input event to be handled.
- */
- protected _inputEvent(ev: InputEvent): boolean {
- // Only support emoji IMEs when screen reader mode is disabled as the event must bubble up to
- // support reading out character input which can doubling up input characters
- if (ev.data && ev.inputType === 'insertText' && !ev.composed && !this.optionsService.rawOptions.screenReaderMode) {
- if (this._keyPressHandled) {
- return false;
- }
-
- // The key was handled so clear the dead key state, otherwise certain keystrokes like arrow
- // keys could be ignored
- this._unprocessedDeadKey = false;
-
- const text = ev.data;
- this.coreService.triggerDataEvent(text, true);
-
- this.cancel(ev);
- return true;
- }
-
- return false;
- }
-
- /**
- * Ring the bell.
- * Note: We could do sweet things with webaudio here
- */
- public bell(): void {
- if (this._soundBell()) {
- this._soundService?.playBellSound();
- }
-
- this._onBell.fire();
-
- // 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);
- // }
- }
-
- /**
- * Resizes the terminal.
- *
- * @param x The number of columns to resize to.
- * @param y The number of rows to resize to.
- */
- public resize(x: number, y: number): void {
- if (x === this.cols && y === this.rows) {
- // Check if we still need to measure the char size (fixes #785).
- if (this._charSizeService && !this._charSizeService.hasValidSize) {
- this._charSizeService.measure();
- }
- return;
- }
-
- super.resize(x, y);
- }
-
- private _afterResize(x: number, y: number): void {
- this._charSizeService?.measure();
-
- // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
- // invalid location
- this.viewport?.syncScrollArea(true);
- }
-
- /**
- * Clear the entire buffer, making the prompt line the new first line.
- */
- public clear(): void {
- if (this.buffer.ybase === 0 && this.buffer.y === 0) {
- // Don't clear if it's already clear
- return;
- }
- this.buffer.clearMarkers();
- 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;
- this.buffer.y = 0;
- for (let i = 1; i < this.rows; i++) {
- this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA));
- }
- this.refresh(0, this.rows - 1);
- this._onScroll.fire({ position: this.buffer.ydisp, source: ScrollSource.TERMINAL });
- }
-
- /**
- * Reset terminal.
- * Note: Calling this directly from JS is synchronous but does not clear
- * input buffers and does not reset the parser, thus the terminal will
- * continue to apply pending input data.
- * If you need in band reset (synchronous with input data) consider
- * using DECSTR (soft reset, CSI ! p) or RIS instead (hard reset, ESC c).
- */
- public reset(): void {
- /**
- * Since _setup handles a full terminal creation, we have to carry forward
- * a few things that should not reset.
- */
- this.options.rows = this.rows;
- this.options.cols = this.cols;
- const customKeyEventHandler = this._customKeyEventHandler;
-
- this._setup();
- super.reset();
- this._selectionService?.reset();
-
- // reattach
- this._customKeyEventHandler = customKeyEventHandler;
-
- // do a full screen refresh
- this.refresh(0, this.rows - 1);
- this.viewport?.syncScrollArea();
- }
-
- public clearTextureAtlas(): void {
- this._renderService?.clearTextureAtlas();
- }
-
- private _reportFocus(): void {
- if (this.element?.classList.contains('focus')) {
- this.coreService.triggerDataEvent(C0.ESC + '[I');
- } else {
- this.coreService.triggerDataEvent(C0.ESC + '[O');
- }
- }
-
- private _reportWindowsOptions(type: WindowsOptionsReportType): void {
- if (!this._renderService) {
- return;
- }
-
- switch (type) {
- case WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:
- const canvasWidth = this._renderService.dimensions.scaledCanvasWidth.toFixed(0);
- const canvasHeight = this._renderService.dimensions.scaledCanvasHeight.toFixed(0);
- this.coreService.triggerDataEvent(`${C0.ESC}[4;${canvasHeight};${canvasWidth}t`);
- break;
- case WindowsOptionsReportType.GET_CELL_SIZE_PIXELS:
- const cellWidth = this._renderService.dimensions.scaledCellWidth.toFixed(0);
- const cellHeight = this._renderService.dimensions.scaledCellHeight.toFixed(0);
- this.coreService.triggerDataEvent(`${C0.ESC}[6;${cellHeight};${cellWidth}t`);
- break;
- }
- }
-
- // TODO: Remove cancel function and cancelEvents option
- public cancel(ev: Event, force?: boolean): boolean | undefined {
- if (!this.options.cancelEvents && !force) {
- return;
- }
- ev.preventDefault();
- ev.stopPropagation();
- return false;
- }
-
- private _visualBell(): boolean {
- return false;
- // return this.options.bellStyle === 'visual' ||
- // this.options.bellStyle === 'both';
- }
-
- private _soundBell(): boolean {
- return this.options.bellStyle === 'sound';
- // return this.options.bellStyle === 'sound' ||
- // this.options.bellStyle === 'both';
- }
-}
-
-/**
- * Helpers
- */
-
-function wasModifierKeyOnlyEvent(ev: KeyboardEvent): boolean {
- return ev.keyCode === 16 || // Shift
- ev.keyCode === 17 || // Ctrl
- ev.keyCode === 18; // Alt
-}