parent
7b11e6519e
commit
d5cf4ac0eb
16 changed files with 75 additions and 7 deletions
|
@ -155,6 +155,7 @@ export interface IPtyService {
|
||||||
getCwd(id: number): Promise<string>;
|
getCwd(id: number): Promise<string>;
|
||||||
getLatency(id: number): Promise<number>;
|
getLatency(id: number): Promise<number>;
|
||||||
acknowledgeDataEvent(id: number, charCount: number): Promise<void>;
|
acknowledgeDataEvent(id: number, charCount: number): Promise<void>;
|
||||||
|
processBinary(id: number, data: string): void;
|
||||||
/** Confirm the process is _not_ an orphan. */
|
/** Confirm the process is _not_ an orphan. */
|
||||||
orphanQuestionReply(id: number): Promise<void>;
|
orphanQuestionReply(id: number): Promise<void>;
|
||||||
|
|
||||||
|
@ -346,6 +347,7 @@ export interface ITerminalChildProcess {
|
||||||
*/
|
*/
|
||||||
shutdown(immediate: boolean): void;
|
shutdown(immediate: boolean): void;
|
||||||
input(data: string): void;
|
input(data: string): void;
|
||||||
|
processBinary(data: string): void;
|
||||||
resize(cols: number, rows: number): void;
|
resize(cols: number, rows: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -188,6 +188,9 @@ export class PtyHostService extends Disposable implements IPtyService {
|
||||||
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
|
setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
|
||||||
return this._proxy.setTerminalLayoutInfo(args);
|
return this._proxy.setTerminalLayoutInfo(args);
|
||||||
}
|
}
|
||||||
|
processBinary(id: number, data: string): void {
|
||||||
|
this._proxy.processBinary(id, data);
|
||||||
|
}
|
||||||
async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
|
async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
|
||||||
return await this._proxy.getTerminalLayoutInfo(args);
|
return await this._proxy.getTerminalLayoutInfo(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,10 @@ export class PtyService extends Disposable implements IPtyService {
|
||||||
return this._throwIfNoPty(id).orphanQuestionReply();
|
return this._throwIfNoPty(id).orphanQuestionReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processBinary(id: number, data: string): void {
|
||||||
|
return this._throwIfNoPty(id).writeBinary(data);
|
||||||
|
}
|
||||||
|
|
||||||
async setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
|
async setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise<void> {
|
||||||
this._workspaceLayoutInfos.set(args.workspaceId, args);
|
this._workspaceLayoutInfos.set(args.workspaceId, args);
|
||||||
}
|
}
|
||||||
|
@ -337,6 +341,9 @@ export class PersistentTerminalProcess extends Disposable {
|
||||||
}
|
}
|
||||||
return this._terminalProcess.input(data);
|
return this._terminalProcess.input(data);
|
||||||
}
|
}
|
||||||
|
writeBinary(data: string): void {
|
||||||
|
return this._terminalProcess.processBinary(data);
|
||||||
|
}
|
||||||
resize(cols: number, rows: number): void {
|
resize(cols: number, rows: number): void {
|
||||||
if (this._inReplay) {
|
if (this._inReplay) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -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) {
|
if (this._isDisposed || !this._ptyProcess) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i <= Math.floor(data.length / WRITE_MAX_CHUNK_SIZE); i++) {
|
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._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
|
// Don't write if it's already queued of is there is nothing to write
|
||||||
if (this._writeTimeout !== undefined || this._writeQueue.length === 0) {
|
if (this._writeTimeout !== undefined || this._writeQueue.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._doWrite();
|
this._doWrite(isBinary);
|
||||||
|
|
||||||
// Don't queue more writes if the queue is empty
|
// Don't queue more writes if the queue is empty
|
||||||
if (this._writeQueue.length === 0) {
|
if (this._writeQueue.length === 0) {
|
||||||
|
@ -342,11 +346,17 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
||||||
}, WRITE_INTERVAL_MS);
|
}, WRITE_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _doWrite(): void {
|
private _doWrite(isBinary?: boolean): void {
|
||||||
|
console.info('writing binary', isBinary);
|
||||||
const data = this._writeQueue.shift()!;
|
const data = this._writeQueue.shift()!;
|
||||||
this._logService.trace('IPty#write', `${data.length} characters`);
|
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);
|
this._ptyProcess!.write(data);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public resize(cols: number, rows: number): void {
|
public resize(cols: number, rows: number): void {
|
||||||
if (this._isDisposed) {
|
if (this._isDisposed) {
|
||||||
|
|
|
@ -225,6 +225,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processBinary(data: string) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
acknowledgeDataEvent(charCount: number): void {
|
acknowledgeDataEvent(charCount: number): void {
|
||||||
// No-op, flow control is not supported in extension owned terminals. If this is ever
|
// 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.
|
// implemented it will need new pause and resume VS Code APIs.
|
||||||
|
|
|
@ -17,6 +17,8 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
|
||||||
|
|
||||||
public readonly _onProcessData = this._register(new Emitter<string | IProcessDataEvent>());
|
public readonly _onProcessData = this._register(new Emitter<string | IProcessDataEvent>());
|
||||||
public readonly onProcessData: Event<string | IProcessDataEvent> = this._onProcessData.event;
|
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>());
|
private readonly _onProcessExit = this._register(new Emitter<number | undefined>());
|
||||||
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
|
public readonly onProcessExit: Event<number | undefined> = this._onProcessExit.event;
|
||||||
public readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
|
public readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>());
|
||||||
|
@ -118,6 +120,9 @@ export class RemotePty extends Disposable implements ITerminalChildProcess {
|
||||||
handleData(e: string | IProcessDataEvent) {
|
handleData(e: string | IProcessDataEvent) {
|
||||||
this._onProcessData.fire(e);
|
this._onProcessData.fire(e);
|
||||||
}
|
}
|
||||||
|
processBinary(e: string) {
|
||||||
|
this._onProcessBinary.fire(e);
|
||||||
|
}
|
||||||
handleExit(e: number | undefined) {
|
handleExit(e: number | undefined) {
|
||||||
this._onProcessExit.fire(e);
|
this._onProcessExit.fire(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ export class RemoteTerminalService extends Disposable implements IRemoteTerminal
|
||||||
this._remoteTerminalChannel = channel;
|
this._remoteTerminalChannel = channel;
|
||||||
|
|
||||||
channel.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event));
|
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 => {
|
channel.onProcessExit(e => {
|
||||||
const pty = this._ptys.get(e.id);
|
const pty = this._ptys.get(e.id);
|
||||||
if (pty) {
|
if (pty) {
|
||||||
|
|
|
@ -292,6 +292,11 @@ export interface ITerminalInstance {
|
||||||
*/
|
*/
|
||||||
onData: Event<string>;
|
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.
|
* Attach a listener to listen for new lines added to this terminal instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -184,6 +184,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||||
public get onTitleChanged(): Event<ITerminalInstance> { return this._onTitleChanged.event; }
|
public get onTitleChanged(): Event<ITerminalInstance> { return this._onTitleChanged.event; }
|
||||||
private readonly _onData = new Emitter<string>();
|
private readonly _onData = new Emitter<string>();
|
||||||
public get onData(): Event<string> { return this._onData.event; }
|
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>();
|
private readonly _onLineData = new Emitter<string>();
|
||||||
public get onLineData(): Event<string> { return this._onLineData.event; }
|
public get onLineData(): Event<string> { return this._onLineData.event; }
|
||||||
private readonly _onRequestExtHostProcess = new Emitter<ITerminalInstance>();
|
private readonly _onRequestExtHostProcess = new Emitter<ITerminalInstance>();
|
||||||
|
@ -468,6 +470,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||||
|
|
||||||
this._processManager.onProcessData(e => this._onProcessData(e));
|
this._processManager.onProcessData(e => this._onProcessData(e));
|
||||||
this._xterm.onData(data => this._processManager.write(data));
|
this._xterm.onData(data => this._processManager.write(data));
|
||||||
|
this._xterm.onBinary(data => this._processManager.processBinary(data));
|
||||||
this.processReady.then(async () => {
|
this.processReady.then(async () => {
|
||||||
if (this._linkManager) {
|
if (this._linkManager) {
|
||||||
this._linkManager.processCwd = await this._processManager.getInitialCwd();
|
this._linkManager.processCwd = await this._processManager.getInitialCwd();
|
||||||
|
|
|
@ -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> {
|
public async start(): Promise<ITerminalLaunchError | undefined> {
|
||||||
if (!this._shellLaunchConfig.isExtensionCustomPtyTerminal) {
|
if (!this._shellLaunchConfig.isExtensionCustomPtyTerminal) {
|
||||||
throw new Error('Attempt to start an ext host process that is not an extension terminal');
|
throw new Error('Attempt to start an ext host process that is not an extension terminal');
|
||||||
|
|
|
@ -499,6 +499,10 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public processBinary(data: string): void {
|
||||||
|
this._process?.processBinary(data);
|
||||||
|
}
|
||||||
|
|
||||||
public getInitialCwd(): Promise<string> {
|
public getInitialCwd(): Promise<string> {
|
||||||
return Promise.resolve(this._initialCwd ? this._initialCwd : '');
|
return Promise.resolve(this._initialCwd ? this._initialCwd : '');
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,9 @@ export class RemoteTerminalChannelClient {
|
||||||
public get onProcessData(): Event<{ id: number, event: IProcessDataEvent | string }> {
|
public get onProcessData(): Event<{ id: number, event: IProcessDataEvent | string }> {
|
||||||
return this._channel.listen<{ id: number, event: IProcessDataEvent | string }>('$onProcessDataEvent');
|
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 }> {
|
public get onProcessExit(): Event<{ id: number, event: number | undefined }> {
|
||||||
return this._channel.listen<{ id: number, event: number | undefined }>('$onProcessExitEvent');
|
return this._channel.listen<{ id: number, event: number | undefined }>('$onProcessExitEvent');
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,7 @@ export interface ITerminalProcessManager extends IDisposable {
|
||||||
setDimensions(cols: number, rows: number, sync: false): Promise<void>;
|
setDimensions(cols: number, rows: number, sync: false): Promise<void>;
|
||||||
setDimensions(cols: number, rows: number, sync: true): void;
|
setDimensions(cols: number, rows: number, sync: true): void;
|
||||||
acknowledgeDataEvent(charCount: number): void;
|
acknowledgeDataEvent(charCount: number): void;
|
||||||
|
processBinary(data: string): void;
|
||||||
|
|
||||||
getInitialCwd(): Promise<string>;
|
getInitialCwd(): Promise<string>;
|
||||||
getCwd(): Promise<string>;
|
getCwd(): Promise<string>;
|
||||||
|
|
|
@ -50,6 +50,12 @@ export class LocalPty extends Disposable implements ITerminalChildProcess {
|
||||||
shutdown(immediate: boolean): void {
|
shutdown(immediate: boolean): void {
|
||||||
this._localPtyService.shutdown(this.id, immediate);
|
this._localPtyService.shutdown(this.id, immediate);
|
||||||
}
|
}
|
||||||
|
processBinary(data: string): void {
|
||||||
|
if (this._inReplay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._localPtyService.processBinary(this.id, data);
|
||||||
|
}
|
||||||
input(data: string): void {
|
input(data: string): void {
|
||||||
if (this._inReplay) {
|
if (this._inReplay) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -136,6 +136,10 @@ export class LocalTerminalService extends Disposable implements ILocalTerminalSe
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public processBinary(id: number, data: string): void {
|
||||||
|
this._localPtyService.processBinary(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
private _getWorkspaceId(): string {
|
private _getWorkspaceId(): string {
|
||||||
return this._workspaceContextService.getWorkspace().id;
|
return this._workspaceContextService.getWorkspace().id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1521,6 +1521,9 @@ export class TestLocalTerminalService implements ILocalTerminalService {
|
||||||
async listProcesses(reduceGraceTime: boolean): Promise<IProcessDetails[]> { throw new Error('Method not implemented.'); }
|
async listProcesses(reduceGraceTime: boolean): Promise<IProcessDetails[]> { throw new Error('Method not implemented.'); }
|
||||||
async setTerminalLayoutInfo(argsOrLayout?: ISetTerminalLayoutInfoArgs | ITerminalsLayoutInfoById) { 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.'); }
|
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 {
|
class TestTerminalChildProcess implements ITerminalChildProcess {
|
||||||
|
@ -1544,6 +1547,9 @@ class TestTerminalChildProcess implements ITerminalChildProcess {
|
||||||
async getInitialCwd(): Promise<string> { return ''; }
|
async getInitialCwd(): Promise<string> { return ''; }
|
||||||
async getCwd(): Promise<string> { return ''; }
|
async getCwd(): Promise<string> { return ''; }
|
||||||
async getLatency(): Promise<number> { return 0; }
|
async getLatency(): Promise<number> { return 0; }
|
||||||
|
processBinary(data: string): void {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TestQuickInputService implements IQuickInputService {
|
export class TestQuickInputService implements IQuickInputService {
|
||||||
|
|
Loading…
Reference in a new issue