2Copyright 2018-2022 Intel Corporation
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
17var promise = require('promise');
18var duplex = require('stream').Duplex;
22var STARTF_USESHOWWINDOW = 0x1;
23var STD_INPUT_HANDLE = -10;
24var STD_OUTPUT_HANDLE = -11;
25var EVENT_CONSOLE_CARET = 0x4001;
26var EVENT_CONSOLE_END_APPLICATION = 0x4007;
27var WINEVENT_OUTOFCONTEXT = 0x000;
28var WINEVENT_SKIPOWNPROCESS = 0x0002;
29var CREATE_NEW_PROCESS_GROUP = 0x200;
30var EVENT_CONSOLE_UPDATE_REGION = 0x4002;
31var EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003;
32var EVENT_CONSOLE_UPDATE_SCROLL = 0x4004;
33var EVENT_CONSOLE_LAYOUT = 0x4005;
34var EVENT_CONSOLE_START_APPLICATION = 0x4006;
36var MAPVK_VK_TO_VSC = 0;
39var GM = require('_GenericMarshal');
40var si = GM.CreateVariable(GM.PointerSize == 4 ? 68 : 104);
41var pi = GM.CreateVariable(GM.PointerSize == 4 ? 16 : 24);
43si.Deref(0, 4).toBuffer().writeUInt32LE(GM.PointerSize == 4 ? 68 : 104); // si.cb
44si.Deref(GM.PointerSize == 4 ? 48 : 64, 2).toBuffer().writeUInt16LE(SW_HIDE | SW_MINIMIZE); // si.wShowWindow
45si.Deref(GM.PointerSize == 4 ? 44 : 60, 4).toBuffer().writeUInt32LE(STARTF_USESHOWWINDOW); // si.dwFlags;
47var MSG = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
49function windows_terminal() {
50 this._ObjectID = 'windows_terminal';
51 this._user32 = GM.CreateNativeProxy('User32.dll');
52 this._user32.CreateMethod('DispatchMessageA');
53 this._user32.CreateMethod('GetMessageA');
54 this._user32.CreateMethod('MapVirtualKeyA');
55 this._user32.CreateMethod('PostThreadMessageA');
56 this._user32.CreateMethod('SetWinEventHook');
57 this._user32.CreateMethod('ShowWindow');
58 this._user32.CreateMethod('TranslateMessage');
59 this._user32.CreateMethod('UnhookWinEvent');
60 this._user32.CreateMethod('VkKeyScanA');
61 this._user32.terminal = this;
63 this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
64 this._kernel32.CreateMethod('AllocConsole');
65 this._kernel32.CreateMethod('CreateProcessA');
66 this._kernel32.CreateMethod('CloseHandle');
67 this._kernel32.CreateMethod('FillConsoleOutputAttribute');
68 this._kernel32.CreateMethod('FillConsoleOutputCharacterA');
69 this._kernel32.CreateMethod('GetConsoleScreenBufferInfo');
70 this._kernel32.CreateMethod('GetConsoleWindow');
71 this._kernel32.CreateMethod('GetLastError');
72 this._kernel32.CreateMethod('GetStdHandle');
73 this._kernel32.CreateMethod('GetThreadId');
74 this._kernel32.CreateMethod('ReadConsoleOutputA');
75 this._kernel32.CreateMethod('SetConsoleCursorPosition');
76 this._kernel32.CreateMethod('SetConsoleScreenBufferSize');
77 this._kernel32.CreateMethod('SetConsoleWindowInfo');
78 this._kernel32.CreateMethod('TerminateProcess');
79 this._kernel32.CreateMethod('WaitForSingleObject');
80 this._kernel32.CreateMethod('WriteConsoleInputA');
88 this.SendCursorUpdate = function () {
89 var newCsbi = GM.CreateVariable(22);
91 if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
92 if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY)
95 // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
96 // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
99 this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE();
100 this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE();
104 this.ClearScreen = function ()
107 // Reference for CONSOLE_SCREEN_BUFFER_INFO can be found at:
108 // https://learn.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
112 // Reference for GetConsoleScreenBufferInfo can be found at:
113 // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
117 // Reference for FillConsoleOutputCharacter can be found at:
118 // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter
122 // Reference for FillConsoleOutputAttribute can be found at:
123 // https://learn.microsoft.com/en-us/windows/console/fillconsoleoutputattribute
127 // Reference for SetConsoleCursorPosition can be found at:
128 // https://learn.microsoft.com/en-us/windows/console/setconsolecursorposition
132 // Reference for SetConsoleWindowInfo can be fount at:
133 // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
136 var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
137 if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
139 var coordScreen = GM.CreateVariable(4);
140 var dwConSize = CONSOLE_SCREEN_BUFFER_INFO.Deref(0, 2).toBuffer().readUInt16LE(0) * CONSOLE_SCREEN_BUFFER_INFO.Deref(2, 2).toBuffer().readUInt16LE(0);
141 var cCharsWritten = GM.CreateVariable(4);
143 // Fill the entire screen with blanks.
144 if (this._kernel32.FillConsoleOutputCharacterA(this._stdoutput, 32, dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
146 // Get the current text attribute.
147 if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
149 // Set the buffer's attributes accordingly.
150 if (this._kernel32.FillConsoleOutputAttribute(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO.Deref(8, 2).toBuffer().readUInt16LE(0), dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
152 // Put the cursor at its home coordinates.
153 this._kernel32.SetConsoleCursorPosition(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE());
155 // Put the window to top-left.
156 var rect = GM.CreateVariable(8);
157 var srWindow = CONSOLE_SCREEN_BUFFER_INFO.Deref(10, 8).toBuffer();
158 rect.Deref(4, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(4) - srWindow.readUInt16LE(0));
159 rect.Deref(6, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(6) - srWindow.readUInt16LE(2));
161 this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
164 // This does a rudimentary check if the platform is capable of PowerShell
165 this.PowerShellCapable = function()
167 if (require('os').arch() == 'x64')
169 return (require('fs').existsSync(process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
173 return (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
177 // Starts a Legacy Windows Terminal Session
178 this.StartEx = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, terminalTarget)
180 // The older windows terminal does not support
181 CONSOLE_SCREEN_WIDTH = 80;
182 CONSOLE_SCREEN_HEIGHT = 25;
184 if (this._stream != null)
186 throw ('Concurrent terminal sessions are not supported on Windows.');
188 this.stopping = null;
189 if (this._kernel32.GetConsoleWindow().Val == 0) {
190 if (this._kernel32.AllocConsole().Val == 0) {
191 throw ('AllocConsole failed with: ' + this._kernel32.GetLastError().Val);
195 this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
196 this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
197 this._connected = false;
199 // Coord structure can be found at: https://learn.microsoft.com/en-us/windows/console/coord-str
200 var coordScreen = GM.CreateVariable(4);
201 coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
202 coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
204 var rect = GM.CreateVariable(8);
205 rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
206 rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
209 // Reference for SetConsoleWindowInfo can be found at:
210 // https://learn.microsoft.com/en-us/windows/console/setconsolewindowinfo
212 if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0)
214 throw ('Failed to set Console Screen Size');
218 // Reference for SetConsoleScreenBufferSize can be found at:
219 // https://learn.microsoft.com/en-us/windows/console/setconsolescreenbuffersize
221 if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0)
223 throw ('Failed to set Console Buffer Size');
226 // Hide the console window
227 this._user32.ShowWindow(this._kernel32.GetConsoleWindow().Val, SW_HIDE);
230 this._hookThread(terminalTarget).then(function ()
233 this.terminal.StartCommand(this.userArgs[0]);
235 this._stream = new duplex(
237 'write': function (chunk, flush)
239 if (!this.terminal.connected)
241 //console.log('_write: ' + chunk);
242 if (!this._promise.chunk)
244 this._promise.chunk = [];
246 if (typeof (chunk) == 'string')
248 this._promise.chunk.push(chunk);
251 this._promise.chunk.push(Buffer.alloc(chunk.length));
252 chunk.copy(this._promise.chunk.peek());
254 this._promise.chunk.peek().flush = flush;
255 this._promise.then(function ()
258 while (this.chunk.length > 0)
260 buf = this.chunk.shift();
261 this.terminal._WriteBuffer(buf);
268 //console.log('writeNOW: ' + chunk);
269 this.terminal._WriteBuffer(chunk);
274 'final': function (flush)
276 var p = this.terminal._stop();
278 p.then(function () { this.__flush(); });
281 this._stream.terminal = this;
282 this._stream._promise = new promise(function (res, rej) { this._res = res; this._rej = rej; });
283 this._stream._promise.terminal = this;
284 this._stream.prependOnceListener('end', function ()
286 this.terminal._stream = null;
288 return (this._stream);
290 this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
292 return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\cmd.exe'));
294 this.StartPowerShell = function StartPowerShell(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
296 if (require('os').arch() == 'x64')
298 if (require('fs').existsSync(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'))
300 return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
304 return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\SysWow64\\WindowsPowerShell\\v1.0\\powershell.exe'));
309 return (this.StartEx(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT, process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'));
313 this._stop = function () {
314 if (this.stopping) { return (this.stopping); }
315 //console.log('Stopping Terminal...');
316 this._ConsoleWinEventProc.removeAllListeners('GlobalCallback');
317 this.stopping = new promise(function (res, rej) { this._res = res; this._rej = rej; });
319 var threadID = this._kernel32.GetThreadId(this._user32.SetWinEventHook.async.thread()).Val;
320 this._user32.PostThreadMessageA(threadID, WM_QUIT, 0, 0);
321 this._stream.emit('end');
322 return (this.stopping);
326 // This function uses the SetWinEventHook() method, so we can hook
327 // All events between EVENT_CONSOLE_CARET and EVENT_CONSOLE_END_APPLICATION
329 this._hookThread = function ()
332 // Reference for SetWinEventHook() can be found at:
333 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwineventhook
335 var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
337 for (var a in arguments)
339 ret.userArgs.push(arguments[a]);
342 this._ConsoleWinEventProc = GM.GetGenericGlobalCallback(7);
343 this._ConsoleWinEventProc.terminal = this;
344 var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
347 p.then(function (hwinEventHook)
349 if (hwinEventHook.Val == 0)
351 this.ready._rej('Error calling SetWinEventHook');
354 this.terminal.hwinEventHook = hwinEventHook;
356 this.terminal._GetMessage();
361 // This is the WINEVENTPROC callback for the WinEventHook we set
363 this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime)
366 // Reference for WINEVENTPROC can be found at:
367 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wineventproc
369 if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
373 // Reference for Console WinEvents can be found at:
374 // https://learn.microsoft.com/en-us/windows/console/console-winevents
379 case EVENT_CONSOLE_CARET:
380 // The console caret has moved
382 case EVENT_CONSOLE_UPDATE_REGION:
383 // More than one character has changed
384 if (!this.terminal.connected) {
385 this.terminal.connected = true;
386 this.terminal._stream._promise._res();
388 if (this.terminal._scrollTimer == null) {
389 buffer = this.terminal._GetScreenBuffer(LOWORD(idObject.Val), HIWORD(idObject.Val), LOWORD(idChild.Val), HIWORD(idChild.Val));
390 //console.log('UPDATE REGION: [Left: ' + LOWORD(idObject.Val) + ' Top: ' + HIWORD(idObject.Val) + ' Right: ' + LOWORD(idChild.Val) + ' Bottom: ' + HIWORD(idChild.Val) + ']');
391 this.terminal._SendDataBuffer(buffer);
394 case EVENT_CONSOLE_UPDATE_SIMPLE:
395 // A single character has changed
396 //console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
397 var simplebuffer = { data: [ Buffer.alloc(1, LOWORD(idChild.Val)) ], attributes: [ HIWORD(idChild.Val) ], width: 1, height: 1, x: LOWORD(idObject.Val), y: HIWORD(idObject.Val) };
398 this.terminal._SendDataBuffer(simplebuffer);
400 case EVENT_CONSOLE_UPDATE_SCROLL:
401 // The console has scrolled
402 //console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
403 this.terminal._SendScroll(idObject.Val, idChild.Val);
405 case EVENT_CONSOLE_LAYOUT:
406 // The console layout has changed.
407 //console.log('CONSOLE_LAYOUT');
408 //snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
411 case EVENT_CONSOLE_START_APPLICATION:
412 // A new console process has started
413 //console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
414 //snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
415 //SendConsoleEvent(dwEvent, idObject, idChild);
417 case EVENT_CONSOLE_END_APPLICATION:
418 // A console process has exited
419 if (idObject.Val == this.terminal._hProcessID)
421 //console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
422 this.terminal._hProcess = null;
423 this.terminal._stop().then(function () { console.log('STOPPED'); });
427 //snprintf(Buf, 512, "unknown console event.\r\n");
428 console.log('Unknown event: ' + dwEvent.Val);
432 //mbstowcs_s(&l, wBuf, Buf, 512);
433 //OutputDebugString(wBuf);
439 // Retrieves a message from the calling thread's message queue
440 this._GetMessage = function ()
443 // Reference for GetMessage() can be found at:
444 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage
448 // Reference for TranslateMessage() can be found at:
449 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-translatemessage
453 // Reference for DispatchMessage() can be found at:
454 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dispatchmessage
457 if (this._user32.abort) { console.log('aborting loop'); return; }
458 this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret)
460 //console.log('GetMessage Response');
465 // handle the error and possibly exit
469 // Translates virtual-key messages into character messages
470 //console.log('TranslateMessage');
471 this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
473 // Dispatches a message to a window procedure
474 //console.log('DispatchMessage');
475 this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
477 this.nativeProxy.terminal._GetMessage();
483 this.nativeProxy.UnhookWinEvent.async(this.nativeProxy.terminal._user32.SetWinEventHook.async, this.nativeProxy.terminal.hwinEventHook)
486 if (this.nativeProxy.terminal._hProcess == null) { return; }
488 this.nativeProxy.terminal.stopping._res();
489 if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0)
491 var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
492 console.log('Unable to kill Terminal Process, error: ' + e);
494 this.nativeProxy.terminal.stopping = null;
497 console.log('REJECTED_UnhookWinEvent: ' + err);
502 // Get Message Failed
503 console.log('REJECTED_GETMessage: ' + err);
507 this._WriteBuffer = function (buf)
509 for (var i = 0; i < buf.length; ++i)
511 if (typeof (buf) == 'string')
513 this._WriteCharacter(buf.charCodeAt(i), false);
516 this._WriteCharacter(buf[i], false);
520 this._WriteCharacter = function (key, bControlKey)
523 // Reference for WriteConsoleInput() can be found at:
524 // https://learn.microsoft.com/en-us/windows/console/writeconsoleinput
528 // Reference for INPUT_RECORD can be found at:
529 // https://learn.microsoft.com/en-us/windows/console/input-record-str
532 var rec = GM.CreateVariable(20);
533 rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
534 rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
535 rec.Deref(16, 4).toBuffer().writeUInt32LE(bControlKey); // rec.Event.KeyEvent.dwControlKeyState
536 rec.Deref(14, 1).toBuffer()[0] = key; // rec.Event.KeyEvent.uChar.AsciiChar
537 rec.Deref(8, 2).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.wRepeatCount
538 rec.Deref(10, 2).toBuffer().writeUInt16LE(this._user32.VkKeyScanA(key).Val); // rec.Event.KeyEvent.wVirtualKeyCode
539 rec.Deref(12, 2).toBuffer().writeUInt16LE(this._user32.MapVirtualKeyA(this._user32.VkKeyScanA(key).Val, MAPVK_VK_TO_VSC).Val);
541 var dwWritten = GM.CreateVariable(4);
542 if (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val == 0) { return (false); }
544 rec.Deref(4, 4).toBuffer().writeUInt16LE(0); // rec.Event.KeyEvent.bKeyDown
545 return (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val != 0);
548 // Get the current visible screen buffer
549 this._GetScreenBuffer = function (sx, sy, ex, ey)
552 // Reference for GetConsoleScreenBufferInfo() can be found at:
553 // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
557 // Reference for ReadConsoleOutput() can be found at:
558 // https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
561 var info = GM.CreateVariable(22);
562 if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
564 var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
565 var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
567 if (arguments[3] == null)
569 // Use Default Parameters
576 if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; }
577 if (this._scry != 0) { sy += this._scry; ey += this._scry; }
578 this._scrx = this._scry = 0;
581 var nBuffer = GM.CreateVariable((ex - sx + 1) * (ey - sy + 1) * 4);
582 var size = GM.CreateVariable(4);
583 size.Deref(0, 2).toBuffer().writeUInt16LE(ex - sx + 1, 0);
584 size.Deref(2, 2).toBuffer().writeUInt16LE(ey - sy + 1, 0);
586 var startCoord = GM.CreateVariable(4);
587 startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0);
588 startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0);
590 var region = GM.CreateVariable(8);
591 region.buffer = region.toBuffer();
592 region.buffer.writeUInt16LE(sx, 0);
593 region.buffer.writeUInt16LE(sy, 2);
594 region.buffer.writeUInt16LE(ex, 4);
595 region.buffer.writeUInt16LE(ey, 6);
597 if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0)
599 throw ('Unable to read Console Output');
602 // Lets convert the buffer into something simpler
603 //var retVal = { data: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), attributes: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), width: dw - dx + 1, height: dh - dy + 1, x: dx, y: dy };
605 var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
606 var x, y, line, ifo, tmp, lineWidth = ex - sx + 1;
608 for (y = 0; y <= (ey - sy) ; ++y)
610 retVal.data.push(Buffer.alloc(lineWidth));
611 retVal.attributes.push(Buffer.alloc(lineWidth));
613 line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
614 for (x = 0; x < lineWidth; ++x)
616 retVal.data.peek()[x] = line[x * 4];
617 retVal.attributes.peek()[x] = line[2 + (x * 4)];
624 this._SendDataBuffer = function (data)
626 // { data, attributes, width, height, x, y }
627 if (this._stream != null)
630 for (dy = 0; dy < data.height; ++dy)
632 line = data.data[dy];
633 attr = data.attributes[dy];
634 line.s = line.toString();
636 //line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
637 //attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
638 this._stream.push(TranslateLine(data.x + 1, data.y + dy + 1, line, attr));
643 this._SendScroll = function _SendScroll(dx, dy)
646 // Reference for GetConsoleScreenBufferInfo() can be found at:
647 // https://learn.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
650 if (this._scrollTimer || this._stream == null) { return; }
652 var info = GM.CreateVariable(22);
653 if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
655 var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
656 var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
658 this._stream.push(GetEsc('H', [nHeight - 1, 0]));
659 for (var i = 0; i > nHeight; ++i) { this._stream.push(Buffer.from('\r\n')); }
661 var buffer = this._GetScreenBuffer(0, 0, nWidth - 1, nHeight - 1);
662 this._SendDataBuffer(buffer);
664 this._scrollTimer = setTimeout(function (self, nw, nh) {
665 var buffer = self._GetScreenBuffer(0, 0, nw - 1, nh - 1);
666 self._SendDataBuffer(buffer);
667 self._scrollTimer = null;
668 }, 250, this, nWidth, nHeight);
671 this.StartCommand = function StartCommand(target) {
672 if (this._kernel32.CreateProcessA(GM.CreateVariable(target), 0, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, si, pi).Val == 0)
674 console.log('Error Spawning CMD');
678 this._kernel32.CloseHandle(pi.Deref(GM.PointerSize, GM.PointerSize).Deref()); // pi.hThread
679 this._hProcess = pi.Deref(0, GM.PointerSize).Deref(); // pi.hProcess
680 this._hProcessID = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); // pi.dwProcessId
681 //console.log('Ready => hProcess: ' + this._hProcess._ptr + ' PID: ' + this._hProcessID);
685function LOWORD(val) { return (val & 0xFFFF); }
686function HIWORD(val) { return ((val >> 16) & 0xFFFF); }
687function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); }
688function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); }
689function TranslateLine(x, y, data, attributes)
691 var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])];
692 if (typeof attributes == 'number') { attributes = [attributes]; } // If we get a single attribute, turn it into an array.
694 for (i = 0; i < data.length; i++)
696 if (lastAttr != attributes[i])
697 { // To boost performance, if the attribute is the same as the last one, skip this entire part.
698 fc = (attributes[i] & 0x0007);
699 fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color
700 bc = (attributes[i] & 0x0070) >> 4;
701 bc = ((bc & 0x0001) << 2) + (bc & 0x0002) + ((bc & 0x0004) >> 2); // Background color
702 rc = (attributes[i] & 0x4000); // Reverse color set
703 fb = (attributes[i] & 0x0008) >> 3; // Bright foreground set
704 bb = (attributes[i] & 0x0080); // Bright background set
706 if (rc != rcolor) { if (rc != 0) { esc.push(7); } else { esc.push(0); fcolor = 7; bcolor = 0; fbright = 0; bbright = 0; } rcolor = rc; } // Reverse Color
707 if (fc != fcolor) { esc.push(fc + 30); fcolor = fc; } // Set the foreground color if needed
708 if (bc != bcolor) { esc.push(bc + 40); bcolor = bc; } // Set the background color if needed
709 if (fb != fbright) { esc.push(2 - fb); fbright = fb; } // Set the bright foreground color if needed
710 if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed
712 if (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; }
713 lastAttr = attributes[i];
715 output.push(Buffer.from(String.fromCharCode(data[i])));
718 return Buffer.concat(output);
721module.exports = new windows_terminal();