Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix terminal mouse reporting via binary events #120145

Merged
merged 17 commits into from Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Expand Up @@ -93,6 +93,7 @@ export interface IOffProcessTerminalService {
onPtyHostRestart: Event<void>;

attachToProcess(id: number): Promise<ITerminalChildProcess | undefined>;
processBinary(id: number, data: string): void;
listProcesses(reduceGraceTime?: boolean): Promise<IProcessDetails[]>;
setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise<void>;
getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined>;
Expand Down Expand Up @@ -155,6 +156,7 @@ export interface IPtyService {
getCwd(id: number): Promise<string>;
getLatency(id: number): Promise<number>;
acknowledgeDataEvent(id: number, charCount: number): Promise<void>;
processBinary(id: number, data: string): void;
/** Confirm the process is _not_ an orphan. */
orphanQuestionReply(id: number): Promise<void>;

Expand Down Expand Up @@ -346,6 +348,7 @@ export interface ITerminalChildProcess {
*/
shutdown(immediate: boolean): void;
input(data: string): void;
processBinary(data: string): void;
resize(cols: number, rows: number): void;

/**
Expand Down
3 changes: 3 additions & 0 deletions src/vs/platform/terminal/node/ptyHostService.ts
Expand Up @@ -188,6 +188,9 @@ export class PtyHostService extends Disposable implements IPtyService {
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
return this._proxy.setTerminalLayoutInfo(args);
}
processBinary(id: number, data: string): void {
this._proxy.processBinary(id, data);
}
async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
return await this._proxy.getTerminalLayoutInfo(args);
}
Expand Down
7 changes: 7 additions & 0 deletions src/vs/platform/terminal/node/ptyService.ts
Expand Up @@ -157,6 +157,10 @@ export class PtyService extends Disposable implements IPtyService {
return this._throwIfNoPty(id).orphanQuestionReply();
}

processBinary(id: number, data: string): void {
return this._throwIfNoPty(id).writeBinary(data);
}

async setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
this._workspaceLayoutInfos.set(args.workspaceId, args);
}
Expand Down Expand Up @@ -337,6 +341,9 @@ export class PersistentTerminalProcess extends Disposable {
}
return this._terminalProcess.input(data);
}
writeBinary(data: string): void {
return this._terminalProcess.processBinary(data);
}
resize(cols: number, rows: number): void {
if (this._inReplay) {
return;
Expand Down
24 changes: 17 additions & 7 deletions src/vs/platform/terminal/node/terminalProcess.ts
Expand Up @@ -310,23 +310,27 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
}
}

public input(data: string): void {
public input(data: string, isBinary?: boolean): void {
if (this._isDisposed || !this._ptyProcess) {
return;
}
for (let i = 0; i <= Math.floor(data.length / WRITE_MAX_CHUNK_SIZE); i++) {
this._writeQueue.push(data.substr(i * WRITE_MAX_CHUNK_SIZE, WRITE_MAX_CHUNK_SIZE));
}
this._startWrite();
this._startWrite(isBinary);
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}

private _startWrite(): void {
public processBinary(data: string): void {
this.input(data, true);
}

private _startWrite(isBinary?: boolean): void {
// Don't write if it's already queued of is there is nothing to write
if (this._writeTimeout !== undefined || this._writeQueue.length === 0) {
return;
}

this._doWrite();
this._doWrite(isBinary);

// Don't queue more writes if the queue is empty
if (this._writeQueue.length === 0) {
Expand All @@ -341,10 +345,16 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
}, WRITE_INTERVAL_MS);
}

private _doWrite(): void {
private _doWrite(isBinary?: boolean): void {
console.info('writing binary', isBinary);
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
const data = this._writeQueue.shift()!;
this._logService.trace('IPty#write', `${data.length} characters`);
this._ptyProcess!.write(data);
if (isBinary) {
this._logService.info('IPty#writeBinary', `${data.length} characters`);
this._ptyProcess!.write(Buffer.from(data, 'binary').toString('binary'));
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
} else {
this._logService.info('IPty#write', `${data.length} characters`);
this._ptyProcess!.write(data);
}
}

public resize(cols: number, rows: number): void {
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/api/common/extHostTerminalService.ts
Expand Up @@ -225,6 +225,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
}
}

processBinary(data: string) {
throw new Error('not implemented');
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

acknowledgeDataEvent(charCount: number): void {
// No-op, flow control is not supported in extension owned terminals. If this is ever
// implemented it will need new pause and resume VS Code APIs.
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/remotePty.ts
Expand Up @@ -46,6 +46,9 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
super();
this._startBarrier = new Barrier();
}
processBinary(data: string): void {
throw new Error('Method not implemented.');
}

public async start(): Promise<ITerminalLaunchError | undefined> {
// Fetch the environment to check shell permissions
Expand Down
Expand Up @@ -118,6 +118,9 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
this._remoteTerminalChannel = null;
}
}
processBinary(id: number, data: string): void {
throw new Error('Method not implemented.');
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

public async createProcess(shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined, cols: number, rows: number, shouldPersist: boolean, configHelper: ITerminalConfigHelper): Promise<ITerminalChildProcess> {
if (!this._remoteTerminalChannel) {
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Expand Up @@ -292,6 +292,11 @@ export interface ITerminalInstance {
*/
onData: Event<string>;

/**
* Attach a listener to the binary data stream coming from xterm and going to pty
*/
onBinary: Event<string>;

/**
* Attach a listener to listen for new lines added to this terminal instance.
*
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Expand Up @@ -184,6 +184,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
public get onTitleChanged(): Event<ITerminalInstance> { return this._onTitleChanged.event; }
private readonly _onData = new Emitter<string>();
public get onData(): Event<string> { return this._onData.event; }
private readonly _onBinary = new Emitter<string>();
public get onBinary(): Event<string> { return this._onBinary.event; }
private readonly _onLineData = new Emitter<string>();
public get onLineData(): Event<string> { return this._onLineData.event; }
private readonly _onRequestExtHostProcess = new Emitter<ITerminalInstance>();
Expand Down Expand Up @@ -468,6 +470,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {

this._processManager.onProcessData(e => this._onProcessData(e));
this._xterm.onData(data => this._processManager.write(data));
this._xterm.onBinary(data => this._processManager.processBinary(data));
this.processReady.then(async () => {
if (this._linkManager) {
this._linkManager.processCwd = await this._processManager.getInitialCwd();
Expand Down
Expand Up @@ -102,6 +102,10 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal
}
}

processBinary(data: string): void {
throw new Error('not implemented');
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

public async start(): Promise<ITerminalLaunchError | undefined> {
if (!this._shellLaunchConfig.isExtensionCustomPtyTerminal) {
throw new Error('Attempt to start an ext host process that is not an extension terminal');
Expand Down
Expand Up @@ -499,6 +499,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
}

public processBinary(data: string): void {
if (this._process) {
this._process?.processBinary(data);
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}

public getInitialCwd(): Promise<string> {
return Promise.resolve(this._initialCwd ? this._initialCwd : '');
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Expand Up @@ -306,6 +306,7 @@ export interface ITerminalProcessManager extends IDisposable {
setDimensions(cols: number, rows: number, sync: false): Promise<void>;
setDimensions(cols: number, rows: number, sync: true): void;
acknowledgeDataEvent(charCount: number): void;
processBinary(data: string): void;

getInitialCwd(): Promise<string>;
getCwd(): Promise<string>;
Expand Down
Expand Up @@ -50,6 +50,12 @@ export class LocalPty extends Disposable implements ITerminalChildProcess {
shutdown(immediate: boolean): void {
this._localPtyService.shutdown(this.id, immediate);
}
processBinary(data: string): void {
if (this._inReplay) {
return;
}
this._localPtyService.processBinary(this.id, data);
}
input(data: string): void {
if (this._inReplay) {
return;
Expand Down
Expand Up @@ -136,6 +136,10 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe
return result;
}

public processBinary(id: number, data: string): void {
this._localPtyService.processBinary(id, data);
}

private _getWorkspaceId(): string {
return this._workspaceContextService.getWorkspace().id;
}
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/test/browser/workbenchTestServices.ts
Expand Up @@ -1521,6 +1521,9 @@ export class TestLocalTerminalService implements ILocalTerminalService {
async listProcesses(reduceGraceTime: boolean): Promise<IProcessDetails[]> { throw new Error('Method not implemented.'); }
async setTerminalLayoutInfo(argsOrLayout?: ISetTerminalLayoutInfoArgs | ITerminalsLayoutInfoById) { throw new Error('Method not implemented.'); }
async getTerminalLayoutInfo(): Promise<ITerminalsLayoutInfo | undefined> { throw new Error('Method not implemented.'); }
processBinary(id: number, data: string): void {
throw new Error('Method not implemented.');
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}

class TestTerminalChildProcess implements ITerminalChildProcess {
Expand All @@ -1544,6 +1547,9 @@ class TestTerminalChildProcess implements ITerminalChildProcess {
async getInitialCwd(): Promise<string> { return ''; }
async getCwd(): Promise<string> { return ''; }
async getLatency(): Promise<number> { return 0; }
processBinary(data: string): void {
throw new Error('not implemented');
}
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
}

export class TestQuickInputService implements IQuickInputService {
Expand Down