MeshCentral/agents/modules_meshcore/win-terminal.js
2018-12-20 15:44:12 -08:00

495 lines
25 KiB
JavaScript

/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var promise = require('promise');
var duplex = require('stream').Duplex;
var SW_HIDE = 0;
var SW_MINIMIZE = 6;
var STARTF_USESHOWWINDOW = 0x1;
var STD_INPUT_HANDLE = -10;
var STD_OUTPUT_HANDLE = -11;
var EVENT_CONSOLE_CARET = 0x4001;
var EVENT_CONSOLE_END_APPLICATION = 0x4007;
var WINEVENT_OUTOFCONTEXT = 0x000;
var WINEVENT_SKIPOWNPROCESS = 0x0002;
var CREATE_NEW_PROCESS_GROUP = 0x200;
var EVENT_CONSOLE_UPDATE_REGION = 0x4002;
var EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003;
var EVENT_CONSOLE_UPDATE_SCROLL = 0x4004;
var EVENT_CONSOLE_LAYOUT = 0x4005;
var EVENT_CONSOLE_START_APPLICATION = 0x4006;
var KEY_EVENT = 0x1;
var MAPVK_VK_TO_VSC = 0;
var WM_QUIT = 0x12;
var GM = require('_GenericMarshal');
var si = GM.CreateVariable(GM.PointerSize == 4 ? 68 : 104);
var pi = GM.CreateVariable(GM.PointerSize == 4 ? 16 : 24);
si.Deref(0, 4).toBuffer().writeUInt32LE(GM.PointerSize == 4 ? 68 : 104); // si.cb
si.Deref(GM.PointerSize == 4 ? 48 : 64, 2).toBuffer().writeUInt16LE(SW_HIDE | SW_MINIMIZE); // si.wShowWindow
si.Deref(GM.PointerSize == 4 ? 44 : 60, 4).toBuffer().writeUInt32LE(STARTF_USESHOWWINDOW); // si.dwFlags;
var MSG = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
function windows_terminal() {
this._ObjectID = 'windows_terminal';
this._user32 = GM.CreateNativeProxy('User32.dll');
this._user32.CreateMethod('DispatchMessageA');
this._user32.CreateMethod('GetMessageA');
this._user32.CreateMethod('MapVirtualKeyA');
this._user32.CreateMethod('PostThreadMessageA');
this._user32.CreateMethod('SetWinEventHook');
this._user32.CreateMethod('ShowWindow');
this._user32.CreateMethod('TranslateMessage');
this._user32.CreateMethod('UnhookWinEvent');
this._user32.CreateMethod('VkKeyScanA');
this._user32.terminal = this;
this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
this._kernel32.CreateMethod('AllocConsole');
this._kernel32.CreateMethod('CreateProcessA');
this._kernel32.CreateMethod('CloseHandle');
this._kernel32.CreateMethod('FillConsoleOutputAttribute');
this._kernel32.CreateMethod('FillConsoleOutputCharacterA');
this._kernel32.CreateMethod('GetConsoleScreenBufferInfo');
this._kernel32.CreateMethod('GetConsoleWindow');
this._kernel32.CreateMethod('GetLastError');
this._kernel32.CreateMethod('GetStdHandle');
this._kernel32.CreateMethod('GetThreadId');
this._kernel32.CreateMethod('ReadConsoleOutputA');
this._kernel32.CreateMethod('SetConsoleCursorPosition');
this._kernel32.CreateMethod('SetConsoleScreenBufferSize');
this._kernel32.CreateMethod('SetConsoleWindowInfo');
this._kernel32.CreateMethod('TerminateProcess');
this._kernel32.CreateMethod('WaitForSingleObject');
this._kernel32.CreateMethod('WriteConsoleInputA');
var currentX = 0;
var currentY = 0;
this._scrx = 0;
this._scry = 0;
this.SendCursorUpdate = function () {
var newCsbi = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
if (newCsbi.Deref(4, 2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6, 2).toBuffer().readUInt16LE() != this.currentY) {
//wchar_t mywbuf[512];
//swprintf(mywbuf, 512, TEXT("csbi.dwCursorPosition.X = %d, csbi.dwCursorPosition.Y = %d, newCsbi.dwCursorPosition.X = %d, newCsbi.dwCursorPosition.Y = %d\r\n"), csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, newCsbi.dwCursorPosition.X, newCsbi.dwCursorPosition.Y);
//OutputDebugString(mywbuf);
//m_viewOffset = newCsbi.srWindow.Top;
//WriteMoveCursor((SerialAgent *)this->sa, (char)(newCsbi.dwCursorPosition.Y - m_viewOffset), (char)(newCsbi.dwCursorPosition.X - m_viewOffset));
//LowStackSendData((SerialAgent *)(this->sa), "", 0);
this.currentX = newCsbi.Deref(4, 2).toBuffer().readUInt16LE();
this.currentY = newCsbi.Deref(6, 2).toBuffer().readUInt16LE();
}
}
this.ClearScreen = function () {
var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
var coordScreen = GM.CreateVariable(4);
var dwConSize = CONSOLE_SCREEN_BUFFER_INFO.Deref(0, 2).toBuffer().readUInt16LE(0) * CONSOLE_SCREEN_BUFFER_INFO.Deref(2, 2).toBuffer().readUInt16LE(0);
var cCharsWritten = GM.CreateVariable(4);
// Fill the entire screen with blanks.
if (this._kernel32.FillConsoleOutputCharacterA(this._stdoutput, 32, dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
// Get the current text attribute.
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
// Set the buffer's attributes accordingly.
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; }
// Put the cursor at its home coordinates.
this._kernel32.SetConsoleCursorPosition(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE());
// Put the window to top-left.
var rect = GM.CreateVariable(8);
var srWindow = CONSOLE_SCREEN_BUFFER_INFO.Deref(10, 8).toBuffer();
rect.Deref(4, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(4) - srWindow.readUInt16LE(0));
rect.Deref(6, 2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(6) - srWindow.readUInt16LE(2));
this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
}
this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT) {
if (this._kernel32.GetConsoleWindow().Val == 0) {
if (this._kernel32.AllocConsole().Val == 0) {
throw ('AllocConsole failed with: ' + this._kernel32.GetLastError().Val);
}
}
this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
this._connected = false;
var coordScreen = GM.CreateVariable(4);
coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
var rect = GM.CreateVariable(8);
rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
if (this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0) {
throw ('Failed to set Console Screen Size');
}
if (this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE()).Val == 0) {
throw ('Failed to set Console Buffer Size');
}
// Hide the console window
this._user32.ShowWindow(this._kernel32.GetConsoleWindow().Val, SW_HIDE);
this.ClearScreen();
this._hookThread().then(function () {
// Hook Ready
this.terminal.StartCommand();
}, console.log);
this._stream = new duplex({
'write': function (chunk, flush) {
if (!this.terminal.connected) {
//console.log('_write: ' + chunk);
if (!this._promise.chunk) {
this._promise.chunk = [];
}
if (typeof (chunk) == 'string') {
this._promise.chunk.push(chunk);
} else {
this._promise.chunk.push(Buffer.alloc(chunk.length));
chunk.copy(this._promise.chunk.peek());
}
this._promise.chunk.peek().flush = flush;
this._promise.then(function () {
var buf;
while (this.chunk.length > 0) {
buf = this.chunk.shift();
this.terminal._WriteBuffer(buf);
buf.flush();
}
});
}
else {
//console.log('writeNOW: ' + chunk);
this.terminal._WriteBuffer(chunk);
flush();
}
},
'final': function (flush) {
var p = this.terminal._stop();
p.__flush = flush;
p.then(function () { this.__flush(); });
}
});
this._stream.terminal = this;
this._stream._promise = new promise(function (res, rej) { this._res = res; this._rej = rej; });
this._stream._promise.terminal = this;
return (this._stream);
};
this._stop = function () {
if (this.stopping) { return (this.stopping); }
//console.log('Stopping Terminal...');
this.stopping = new promise(function (res, rej) { this._res = res; this._rej = rej; });
var threadID = this._kernel32.GetThreadId(this._user32.SetWinEventHook.async.thread()).Val;
this._user32.PostThreadMessageA(threadID, WM_QUIT, 0, 0);
return (this.stopping);
}
this._hookThread = function () {
var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
ret.terminal = this;
this._ConsoleWinEventProc = GM.GetGenericGlobalCallback(7);
this._ConsoleWinEventProc.terminal = this;
var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
p.ready = ret;
p.terminal = this;
p.then(function (hwinEventHook) {
if (hwinEventHook.Val == 0) {
this.ready._rej('Error calling SetWinEventHook');
} else {
this.terminal.hwinEventHook = hwinEventHook;
this.ready._res();
this.terminal._GetMessage();
}
});
this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime) {
if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
var buffer = null;
switch (dwEvent.Val) {
case EVENT_CONSOLE_CARET:
break;
case EVENT_CONSOLE_UPDATE_REGION:
if (!this.terminal.connected) {
this.terminal.connected = true;
this.terminal._stream._promise._res();
}
if (this.terminal._scrollTimer == null) {
buffer = this.terminal._GetScreenBuffer(LOWORD(idObject.Val), HIWORD(idObject.Val), LOWORD(idChild.Val), HIWORD(idChild.Val));
//console.log('UPDATE REGION: [Left: ' + LOWORD(idObject.Val) + ' Top: ' + HIWORD(idObject.Val) + ' Right: ' + LOWORD(idChild.Val) + ' Bottom: ' + HIWORD(idChild.Val) + ']');
this.terminal._SendDataBuffer(buffer);
}
break;
case EVENT_CONSOLE_UPDATE_SIMPLE:
//console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
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) };
this.terminal._SendDataBuffer(simplebuffer);
break;
case EVENT_CONSOLE_UPDATE_SCROLL:
//console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
this.terminal._SendScroll(idObject.Val, idChild.Val);
break;
case EVENT_CONSOLE_LAYOUT:
//console.log('CONSOLE_LAYOUT');
//snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
//SendLayout();
break;
case EVENT_CONSOLE_START_APPLICATION:
//console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
//snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
//SendConsoleEvent(dwEvent, idObject, idChild);
break;
case EVENT_CONSOLE_END_APPLICATION:
if (idObject.Val == this.terminal._hProcessID) {
//console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
this.terminal._stop().then(function () { console.log('STOPPED'); });
}
break;
default:
//snprintf(Buf, 512, "unknown console event.\r\n");
console.log('Unknown event: ' + dwEvent.Val);
break;
}
//mbstowcs_s(&l, wBuf, Buf, 512);
//OutputDebugString(wBuf);
});
return (ret);
}
this._GetMessage = function () {
if (this._user32.abort) { console.log('aborting loop'); return; }
this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret) {
//console.log('GetMessage Response');
if (ret.Val != 0) {
if (ret.Val == -1) {
// handle the error and possibly exit
} else {
//console.log('TranslateMessage');
this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () {
//console.log('DispatchMessage');
this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function () {
this.nativeProxy.terminal._GetMessage();
}, console.log);
}, console.log);
}
} else {
this.nativeProxy.UnhookWinEvent.async(this.nativeProxy.terminal._user32.SetWinEventHook.async, this.nativeProxy.terminal.hwinEventHook)
.then(function () {
this.nativeProxy.terminal.stopping._res();
if (this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0) {
var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
console.log('Unable to kill Terminal Process, error: ' + e);
}
this.nativeProxy.terminal.stopping = null;
}, function (err) {
console.log('REJECTED_UnhookWinEvent: ' + err);
});
}
}, function (err) {
// Get Message Failed
console.log('REJECTED_GETMessage: ' + err);
});
}
this._WriteBuffer = function (buf) {
for (var i = 0; i < buf.length; ++i) {
if (typeof (buf) == 'string') {
this._WriteCharacter(buf.charCodeAt(i), false);
} else {
this._WriteCharacter(buf[i], false);
}
}
}
this._WriteCharacter = function (key, bControlKey) {
var rec = GM.CreateVariable(20);
rec.Deref(0, 2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
rec.Deref(4, 4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
rec.Deref(16, 4).toBuffer().writeUInt32LE(bControlKey); // rec.Event.KeyEvent.dwControlKeyState
rec.Deref(14, 1).toBuffer()[0] = key; // rec.Event.KeyEvent.uChar.AsciiChar
rec.Deref(8, 2).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.wRepeatCount
rec.Deref(10, 2).toBuffer().writeUInt16LE(this._user32.VkKeyScanA(key).Val); // rec.Event.KeyEvent.wVirtualKeyCode
rec.Deref(12, 2).toBuffer().writeUInt16LE(this._user32.MapVirtualKeyA(this._user32.VkKeyScanA(key).Val, MAPVK_VK_TO_VSC).Val);
var dwWritten = GM.CreateVariable(4);
if (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val == 0) { return (false); }
rec.Deref(4, 4).toBuffer().writeUInt16LE(0); // rec.Event.KeyEvent.bKeyDown
return (this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val != 0);
}
// Get the current visible screen buffer
this._GetScreenBuffer = function (sx, sy, ex, ey) {
var info = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
if (arguments[3] == null) {
// Use Default Parameters
sx = 0;
sy = 0;
ex = nWidth - 1;
ey = nHeight - 1;
} else {
if (this._scrx != 0) { sx += this._scrx; ex += this._scrx; }
if (this._scry != 0) { sy += this._scry; ey += this._scry; }
this._scrx = this._scry = 0;
}
var nBuffer = GM.CreateVariable((ex - sx + 1) * (ey - sy + 1) * 4);
var size = GM.CreateVariable(4);
size.Deref(0, 2).toBuffer().writeUInt16LE(ex - sx + 1, 0);
size.Deref(2, 2).toBuffer().writeUInt16LE(ey - sy + 1, 0);
var startCoord = GM.CreateVariable(4);
startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0);
startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0);
var region = GM.CreateVariable(8);
region.buffer = region.toBuffer();
region.buffer.writeUInt16LE(sx, 0);
region.buffer.writeUInt16LE(sy, 2);
region.buffer.writeUInt16LE(ex, 4);
region.buffer.writeUInt16LE(ey, 6);
if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0) {
throw ('Unable to read Console Output');
}
// Lets convert the buffer into something simpler
//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 };
var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
var x, y, line, ifo, tmp, lineWidth = ex - sx + 1;
for (y = 0; y <= (ey - sy) ; ++y) {
retVal.data.push(Buffer.alloc(lineWidth));
retVal.attributes.push(Buffer.alloc(lineWidth));
line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
for (x = 0; x < lineWidth; ++x) {
retVal.data.peek()[x] = line[x * 4];
retVal.attributes.peek()[x] = line[2 + (x * 4)];
}
}
return (retVal);
}
this._SendDataBuffer = function (data) {
// { data, attributes, width, height, x, y }
var dy, line, attr;
for (dy = 0; dy < data.height; ++dy) {
line = data.data[dy];
attr = data.attributes[dy];
line.s = line.toString();
//line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
//attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
this._stream.push(TranslateLine(data.x + 1, data.y + dy + 1, line, attr));
}
}
this._SendScroll = function _SendScroll(dx, dy) {
if (this._scrollTimer) { return; }
var info = GM.CreateVariable(22);
if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
this._stream.push(GetEsc('H', [nHeight - 1, 0]));
for (var i = 0; i > nHeight; ++i) { this._stream.push(Buffer.from('\r\n')); }
var buffer = this._GetScreenBuffer(0, 0, nWidth - 1, nHeight - 1);
this._SendDataBuffer(buffer);
this._scrollTimer = setTimeout(function (self, nw, nh) {
var buffer = self._GetScreenBuffer(0, 0, nw - 1, nh - 1);
self._SendDataBuffer(buffer);
self._scrollTimer = null;
}, 250, this, nWidth, nHeight);
}
this.StartCommand = function StartCommand() {
if (this._kernel32.CreateProcessA(GM.CreateVariable(process.env['windir'] + '\\system32\\cmd.exe'), 0, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, si, pi).Val == 0) {
console.log('Error Spawning CMD');
return;
}
this._kernel32.CloseHandle(pi.Deref(GM.PointerSize, GM.PointerSize).Deref()); // pi.hThread
this._hProcess = pi.Deref(0, GM.PointerSize).Deref(); // pi.hProcess
this._hProcessID = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); // pi.dwProcessId
//console.log('Ready => hProcess: ' + this._hProcess._ptr + ' PID: ' + this._hProcessID);
}
}
function LOWORD(val) { return (val & 0xFFFF); }
function HIWORD(val) { return ((val >> 16) & 0xFFFF); }
function GetEsc(op, args) { return (Buffer.from('\x1B[' + args.join(';') + op)); }
function MeshConsole(msg) { require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": JSON.stringify(msg) }); }
function TranslateLine(x, y, data, attributes) {
var i, fcolor, bcolor, rcolor, fbright, bbright, lastAttr, fc, bc, rc, fb, bb, esc = [], output = [GetEsc('H', [y, x])];
if (typeof attributes == 'number') { attributes = [ attributes ]; } // If we get a single attribute, turn it into an array.
for (i = 0; i < data.length; i++) {
if (lastAttr != attributes[i]) { // To boost performance, if the attribute is the same as the last one, skip this entire part.
fc = (attributes[i] & 0x0007);
fc = ((fc & 0x0001) << 2) + (fc & 0x0002) + ((fc & 0x0004) >> 2); // Foreground color
bc = (attributes[i] & 0x0070) >> 4;
bc = ((bc & 0x0001) << 2) + (bc & 0x0002) + ((bc & 0x0004) >> 2); // Background color
rc = (attributes[i] & 0x4000); // Reverse color set
fb = (attributes[i] & 0x0008) >> 3; // Bright foreground set
bb = (attributes[i] & 0x0080); // Bright background set
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
if (fc != fcolor) { esc.push(fc + 30); fcolor = fc; } // Set the foreground color if needed
if (bc != bcolor) { esc.push(bc + 40); bcolor = bc; } // Set the background color if needed
if (fb != fbright) { esc.push(2 - fb); fbright = fb; } // Set the bright foreground color if needed
if (bb != bbright) { if (bb == 0) { esc.push(bcolor + 40); } else { esc.push(bcolor + 100); bbright = bb; } } // Set bright Background color if needed
if (esc.length > 0) { output.push(GetEsc('m', esc)); esc = []; }
lastAttr = attributes[i];
}
output.push(Buffer.from(String.fromCharCode(data[i])));
}
return Buffer.concat(output);
}
module.exports = new windows_terminal();