Skip to content

Commit

Permalink
Fix terminal mouse reporting via binary events (#120145)
Browse files Browse the repository at this point in the history
* fix #96058
  • Loading branch information
meganrogge committed Mar 31, 2021
1 parent 7b11e65 commit d5cf4ac
Show file tree
Hide file tree
Showing 16 changed files with 75 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Expand Up @@ -155,6 +155,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 +347,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 @@ -311,23 +311,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);
}

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 @@ -342,10 +346,16 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
}, WRITE_INTERVAL_MS);
}

private _doWrite(): void {
private _doWrite(isBinary?: boolean): void {
console.info('writing binary', isBinary);
const data = this._writeQueue.shift()!;
this._logService.trace('IPty#write', `${data.length} characters`);
this._ptyProcess!.write(data);
if (isBinary) {
this._logService.info('IPty#write (binary)', `${data.length} characters`);
this._ptyProcess!.write(Buffer.from(data, 'binary') as any);
} 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');
}

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
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/remotePty.ts
Expand Up @@ -17,6 +17,8 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {

public readonly _onProcessData = this._register(new Emitter<string | IProcessDataEvent>());
public readonly onProcessData: Event<string | IProcessDataEvent> = this._onProcessData.event;
public readonly _onProcessBinary = this._register(new Emitter<string>());
public readonly onProcessBinary: Event<string> = this._onProcessBinary.event;
private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
public readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
Expand Down Expand Up @@ -118,6 +120,9 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
handleData(e: string | IProcessDataEvent) {
this._onProcessData.fire(e);
}
processBinary(e: string) {
this._onProcessBinary.fire(e);
}
handleExit(e: number | undefined) {
this._onProcessExit.fire(e);
}
Expand Down
Expand Up @@ -49,6 +49,7 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
this._remoteTerminalChannel = channel;

channel.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event));
channel.onProcessBinary(e => this._ptys.get(e.id)?.processBinary(e.event));
channel.onProcessExit(e => {
const pty = this._ptys.get(e.id);
if (pty) {
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');
}

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,10 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
}
}

public processBinary(data: string): void {
this._process?.processBinary(data);
}

public getInitialCwd(): Promise<string> {
return Promise.resolve(this._initialCwd ? this._initialCwd : '');
}
Expand Down
Expand Up @@ -95,6 +95,9 @@ export class RemoteTerminalChannelClient {
public get onProcessData(): Event<{ id: number, event: IProcessDataEvent | string }> {
return this._channel.listen<{ id: number, event: IProcessDataEvent | string }>('$onProcessDataEvent');
}
public get onProcessBinary(): Event<{ id: number, event: string }> {
return this._channel.listen<{ id: number, event: string }>('$onProcessBinaryEvent');
}
public get onProcessExit(): Event<{ id: number, event: number | undefined }> {
return this._channel.listen<{ id: number, event: number | undefined }>('$onProcessExitEvent');
}
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.');
}
}

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');
}
}

export class TestQuickInputService implements IQuickInputService {
Expand Down

0 comments on commit d5cf4ac

Please sign in to comment.