2 * Copyright (c) 2012-2015, Christopher Jeffrey, Peter Sunde (MIT License)
3 * Copyright (c) 2016, Daniel Imms (MIT License).
4 * Copyright (c) 2018, Microsoft Corporation (MIT License).
7 import { Socket } from 'net';
8 import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal';
9 import { WindowsPtyAgent } from './windowsPtyAgent';
10 import { IPtyOpenOptions, IWindowsPtyForkOptions } from './interfaces';
11 import { ArgvOrCommandLine } from './types';
12 import { assign } from './utils';
14 const DEFAULT_FILE = 'cmd.exe';
15 const DEFAULT_NAME = 'Windows Shell';
17 export class WindowsTerminal extends Terminal {
18 private _isReady: boolean;
19 private _deferreds: any[];
20 private _agent: WindowsPtyAgent;
22 constructor(file?: string, args?: ArgvOrCommandLine, opt?: IWindowsPtyForkOptions) {
25 // Initialize arguments
27 file = file || DEFAULT_FILE;
29 opt.env = opt.env || process.env;
32 console.warn('Setting encoding on Windows is not supported');
35 const env = assign({}, opt.env);
36 this._cols = opt.cols || DEFAULT_COLS;
37 this._rows = opt.rows || DEFAULT_ROWS;
38 const cwd = opt.cwd || process.cwd();
39 const name = opt.name || env.TERM || DEFAULT_NAME;
40 const parsedEnv = this._parseEnv(env);
42 // If the terminal is ready
43 this._isReady = false;
45 // Functions that need to run after `ready` event is emitted.
49 this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.conptyInheritCursor);
50 this._socket = this._agent.outSocket;
52 // Not available until `ready` event emitted.
53 this._pid = this._agent.innerPid;
54 this._fd = this._agent.fd;
55 this._pty = this._agent.pty;
57 // The forked windows terminal is not available until `ready` event is
59 this._socket.on('ready_datapipe', () => {
61 // These events needs to be forwarded.
62 ['connect', 'data', 'end', 'timeout', 'drain'].forEach(event => {
63 this._socket.on(event, () => {
65 // Wait until the first data event is fired then we can run deferreds.
66 if (!this._isReady && event === 'data') {
68 // Terminal is now ready and we can avoid having to defer method
72 // Execute all deferred methods
73 this._deferreds.forEach(fn => {
74 // NB! In order to ensure that `this` has all its references
75 // updated any variable that need to be available in `this` before
76 // the deferred is run has to be declared above this forEach
88 // Shutdown if `error` event is emitted.
89 this._socket.on('error', err => {
90 // Close terminal session.
93 // EIO, happens when someone closes our child process: the only process
95 // node < 0.6.14: errno 5
96 // node >= 0.6.14: read EIO
97 if ((<any>err).code) {
98 if (~(<any>err).code.indexOf('errno 5') || ~(<any>err).code.indexOf('EIO')) return;
101 // Throw anything else.
102 if (this.listeners('error').length < 2) {
107 // Cleanup after the socket is closed.
108 this._socket.on('close', () => {
109 this.emit('exit', this._agent.exitCode);
118 this._readable = true;
119 this._writable = true;
121 this._forwardEvents();
124 protected _write(data: string): void {
125 this._defer(this._doWrite, data);
128 private _doWrite(data: string): void {
129 this._agent.inSocket.write(data);
136 public static open(options?: IPtyOpenOptions): void {
137 throw new Error('open() not supported on windows, use Fork() instead.');
144 public resize(cols: number, rows: number): void {
145 if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
146 throw new Error('resizing must be done using positive cols and rows');
149 this._agent.resize(cols, rows);
155 public destroy(): void {
161 public kill(signal?: string): void {
164 throw new Error('Signals not supported on windows.');
171 private _defer<A extends any>(deferredFn: (arg?: A) => void, arg?: A): void {
172 // If the terminal is ready, execute.
174 deferredFn.call(this, arg);
178 // Queue until terminal is ready.
179 this._deferreds.push({
180 run: () => deferredFn.call(this, arg)
184 public get process(): string { return this._name; }
185 public get master(): Socket { throw new Error('master is not supported on Windows'); }
186 public get slave(): Socket { throw new Error('slave is not supported on Windows'); }