|
|
|
|
|
|
|
|
|
|
|
|
|
import { Socket } from 'net'; |
|
import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal'; |
|
import { WindowsPtyAgent } from './windowsPtyAgent'; |
|
import { IPtyOpenOptions, IWindowsPtyForkOptions } from './interfaces'; |
|
import { ArgvOrCommandLine } from './types'; |
|
import { assign } from './utils'; |
|
|
|
const DEFAULT_FILE = 'cmd.exe'; |
|
const DEFAULT_NAME = 'Windows Shell'; |
|
|
|
export class WindowsTerminal extends Terminal { |
|
private _isReady: boolean; |
|
private _deferreds: any[]; |
|
private _agent: WindowsPtyAgent; |
|
|
|
constructor(file?: string, args?: ArgvOrCommandLine, opt?: IWindowsPtyForkOptions) { |
|
super(opt); |
|
|
|
|
|
args = args || []; |
|
file = file || DEFAULT_FILE; |
|
opt = opt || {}; |
|
opt.env = opt.env || process.env; |
|
|
|
if (opt.encoding) { |
|
console.warn('Setting encoding on Windows is not supported'); |
|
} |
|
|
|
const env = assign({}, opt.env); |
|
this._cols = opt.cols || DEFAULT_COLS; |
|
this._rows = opt.rows || DEFAULT_ROWS; |
|
const cwd = opt.cwd || process.cwd(); |
|
const name = opt.name || env.TERM || DEFAULT_NAME; |
|
const parsedEnv = this._parseEnv(env); |
|
|
|
|
|
this._isReady = false; |
|
|
|
|
|
this._deferreds = []; |
|
|
|
|
|
this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.conptyInheritCursor); |
|
this._socket = this._agent.outSocket; |
|
|
|
|
|
this._pid = this._agent.innerPid; |
|
this._fd = this._agent.fd; |
|
this._pty = this._agent.pty; |
|
|
|
|
|
|
|
this._socket.on('ready_datapipe', () => { |
|
|
|
|
|
['connect', 'data', 'end', 'timeout', 'drain'].forEach(event => { |
|
this._socket.on(event, () => { |
|
|
|
|
|
if (!this._isReady && event === 'data') { |
|
|
|
|
|
|
|
this._isReady = true; |
|
|
|
|
|
this._deferreds.forEach(fn => { |
|
|
|
|
|
|
|
|
|
fn.run(); |
|
}); |
|
|
|
|
|
this._deferreds = []; |
|
|
|
} |
|
}); |
|
}); |
|
|
|
|
|
this._socket.on('error', err => { |
|
|
|
this._close(); |
|
|
|
|
|
|
|
|
|
|
|
if ((<any>err).code) { |
|
if (~(<any>err).code.indexOf('errno 5') || ~(<any>err).code.indexOf('EIO')) return; |
|
} |
|
|
|
|
|
if (this.listeners('error').length < 2) { |
|
throw err; |
|
} |
|
}); |
|
|
|
|
|
this._socket.on('close', () => { |
|
this.emit('exit', this._agent.exitCode); |
|
this._close(); |
|
}); |
|
|
|
}); |
|
|
|
this._file = file; |
|
this._name = name; |
|
|
|
this._readable = true; |
|
this._writable = true; |
|
|
|
this._forwardEvents(); |
|
} |
|
|
|
protected _write(data: string): void { |
|
this._defer(this._doWrite, data); |
|
} |
|
|
|
private _doWrite(data: string): void { |
|
this._agent.inSocket.write(data); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static open(options?: IPtyOpenOptions): void { |
|
throw new Error('open() not supported on windows, use Fork() instead.'); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
public resize(cols: number, rows: number): void { |
|
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) { |
|
throw new Error('resizing must be done using positive cols and rows'); |
|
} |
|
this._defer(() => { |
|
this._agent.resize(cols, rows); |
|
this._cols = cols; |
|
this._rows = rows; |
|
}); |
|
} |
|
|
|
public destroy(): void { |
|
this._defer(() => { |
|
this.kill(); |
|
}); |
|
} |
|
|
|
public kill(signal?: string): void { |
|
this._defer(() => { |
|
if (signal) { |
|
throw new Error('Signals not supported on windows.'); |
|
} |
|
this._close(); |
|
this._agent.kill(); |
|
}); |
|
} |
|
|
|
private _defer<A extends any>(deferredFn: (arg?: A) => void, arg?: A): void { |
|
|
|
if (this._isReady) { |
|
deferredFn.call(this, arg); |
|
return; |
|
} |
|
|
|
|
|
this._deferreds.push({ |
|
run: () => deferredFn.call(this, arg) |
|
}); |
|
} |
|
|
|
public get process(): string { return this._name; } |
|
public get master(): Socket { throw new Error('master is not supported on Windows'); } |
|
public get slave(): Socket { throw new Error('slave is not supported on Windows'); } |
|
} |
|
|