Skip to content

Commit

Permalink
Add more granular windowsPty option
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyriar committed May 29, 2023
1 parent 22a4dc4 commit 22140b5
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 19 deletions.
6 changes: 5 additions & 1 deletion demo/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,11 @@ function createTerminal(): void {
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
term = new Terminal({
allowProposedApi: true,
windowsMode: isWindows,
windowsPty: isWindows ? {
// In a real scenario, these values should be verified on the backend
backend: 'conpty',
buildNumber: 22621
} : undefined,
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
theme: xtermjsTheme
} as ITerminalOptions);
Expand Down
35 changes: 19 additions & 16 deletions src/common/CoreTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {

protected _inputHandler: InputHandler;
private _writeBuffer: WriteBuffer;
private _windowsMode: IDisposable | undefined;
private _windowsWrappingHeuristics: IDisposable | undefined;

private readonly _onBinary = this.register(new EventEmitter<string>());
public readonly onBinary = this._onBinary.event;
Expand Down Expand Up @@ -146,8 +146,8 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed));

this.register(toDisposable(() => {
this._windowsMode?.dispose();
this._windowsMode = undefined;
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
}));
}

Expand Down Expand Up @@ -250,8 +250,13 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
}

protected _setup(): void {
if (this.optionsService.rawOptions.windowsMode) {
this._enableWindowsMode();
const windowsPty = this.optionsService.rawOptions.windowsPty;
if (windowsPty) {
if (windowsPty.buildNumber && windowsPty.backend === 'conpty' && windowsPty.buildNumber < 21376) {
this._enableWindowsWrappingHeuristics();
}
} else if (this.optionsService.rawOptions.windowsMode) {
this._enableWindowsWrappingHeuristics();
}
}

Expand All @@ -265,28 +270,26 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {

private _handleWindowsModeOptionChange(value: boolean): void {
if (value) {
this._enableWindowsMode();
this._enableWindowsWrappingHeuristics();
} else {
this._windowsMode?.dispose();
this._windowsMode = undefined;
this._windowsWrappingHeuristics?.dispose();
this._windowsWrappingHeuristics = undefined;
}
}

protected _enableWindowsMode(): void {
if (!this._windowsMode) {
protected _enableWindowsWrappingHeuristics(): void {
if (!this._windowsWrappingHeuristics) {
const disposables: IDisposable[] = [];
disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
disposables.push(this.registerCsiHandler({ final: 'H' }, () => {
updateWindowsModeWrappedState(this._bufferService);
return false;
}));
this._windowsMode = {
dispose: () => {
for (const d of disposables) {
d.dispose();
}
this._windowsWrappingHeuristics = toDisposable(() => {
for (const d of disposables) {
d.dispose();
}
};
});
}
}
}
6 changes: 5 additions & 1 deletion src/common/buffer/Buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class Buffer implements IBuffer {
if (this._rows < newRows) {
for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) {
if (this._optionsService.rawOptions.windowsMode) {
if (this._optionsService.rawOptions.windowsMode || this._optionsService.rawOptions.windowsPty.backend !== undefined || this._optionsService.rawOptions.windowsPty.buildNumber !== undefined) {
// Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, nullCell));
Expand Down Expand Up @@ -290,6 +290,10 @@ export class Buffer implements IBuffer {
}

private get _isReflowEnabled(): boolean {
const windowsPty = this._optionsService.rawOptions.windowsPty;
if (windowsPty && windowsPty.buildNumber) {
return this._hasScrollback && windowsPty.backend === 'conpty' && windowsPty.buildNumber >= 21376;
}
return this._hasScrollback && !this._optionsService.rawOptions.windowsMode;
}

Expand Down
1 change: 1 addition & 0 deletions src/common/services/OptionsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
rightClickSelectsWord: isMac,
windowOptions: {},
windowsMode: false,
windowsPty: {},
wordSeparator: ' ()[]{}\',"`',
altClickMovesCursor: true,
convertEol: false,
Expand Down
3 changes: 2 additions & 1 deletion src/common/services/Services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, IOscLinkData } from 'common/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
import { IDecorationOptions, IDecoration, ILinkHandler } from 'xterm';
import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty } from 'xterm';

export const IBufferService = createDecorator<IBufferService>('BufferService');
export interface IBufferService {
Expand Down Expand Up @@ -242,6 +242,7 @@ export interface ITerminalOptions {
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;
windowsPty?: IWindowsPty;
windowOptions?: IWindowOptions;
wordSeparator?: string;
overviewRulerWidth?: number;
Expand Down
39 changes: 39 additions & 0 deletions typings/xterm-headless.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,34 @@ declare module 'xterm-headless' {
* - Reflow is disabled.
* - Lines are assumed to be wrapped if the last character of the line is
* not whitespace.
*
* When using conpty on Windows 11 version >= 21376, it is recommended to
* disable this because native text wrapping sequences are output correctly
* thanks to https://github.com/microsoft/terminal/issues/405
*
* @deprecated Use {@link windowsPty}. This value will be ignored if
* windowsPty is set.
*/
windowsMode?: boolean;

/**
* Compatibility information when the pty is known to be hosted on Windows.
* Setting this will turn on certain heuristics/workarounds depending on the
* values:
*
* - `if (!!windowsCompat)`
* - When increasing the rows in the terminal, the amount increased into
* the scrollback. This is done because ConPTY does not behave like
* expect scrollback to come back into the viewport, instead it makes
* empty rows at of the viewport. Not having this behavior can result in
* missing data as the rows get replaced.
* - `if !(backend === 'conpty' && buildNumber >= 21376)`
* - Reflow is disabled
* - Lines are assumed to be wrapped if the last character of the line is
* not whitespace.
*/
windowsPty?: IWindowsPty;

/**
* A string containing all characters that are considered word separated by the
* double click to select work logic.
Expand Down Expand Up @@ -265,6 +290,20 @@ declare module 'xterm-headless' {
extendedAnsi?: string[];
}

/**
* Pty information for Windows.
*/
export interface IWindowsPty {
/**
* What pty emulation backend is being used.
*/
backend?: 'conpty' | 'winpty';
/**
* The Windows build version (eg. 19045)
*/
buildNumber?: number;
}

/**
* An object that can be disposed via a dispose function.
*/
Expand Down
35 changes: 35 additions & 0 deletions typings/xterm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,30 @@ declare module 'xterm' {
* When using conpty on Windows 11 version >= 21376, it is recommended to
* disable this because native text wrapping sequences are output correctly
* thanks to https://github.com/microsoft/terminal/issues/405
*
* @deprecated Use {@link windowsPty}. This value will be ignored if
* windowsPty is set.
*/
windowsMode?: boolean;

/**
* Compatibility information when the pty is known to be hosted on Windows.
* Setting this will turn on certain heuristics/workarounds depending on the
* values:
*
* - `if (backend !== undefined || buildNumber !== undefined)`
* - When increasing the rows in the terminal, the amount increased into
* the scrollback. This is done because ConPTY does not behave like
* expect scrollback to come back into the viewport, instead it makes
* empty rows at of the viewport. Not having this behavior can result in
* missing data as the rows get replaced.
* - `if !(backend === 'conpty' && buildNumber >= 21376)`
* - Reflow is disabled
* - Lines are assumed to be wrapped if the last character of the line is
* not whitespace.
*/
windowsPty?: IWindowsPty;

/**
* A string containing all characters that are considered word separated by the
* double click to select work logic.
Expand Down Expand Up @@ -330,6 +351,20 @@ declare module 'xterm' {
extendedAnsi?: string[];
}

/**
* Pty information for Windows.
*/
export interface IWindowsPty {
/**
* What pty emulation backend is being used.
*/
backend?: 'conpty' | 'winpty';
/**
* The Windows build version (eg. 19045)
*/
buildNumber?: number;
}

/**
* An object that can be disposed via a dispose function.
*/
Expand Down

0 comments on commit 22140b5

Please sign in to comment.