Files
pip/utils/piterminal/main.cpp
peri4 1c7fc39b6c version 4.0.0_alpha
in almost all methods removed timeouts in milliseconds, replaced to PISystemTime
PITimer rewrite, remove internal impl, now only thread implementation, API similar to PIThread
PITimer API no longer pass void*
PIPeer, PIConnection improved stability on reinit and exit
PISystemTime new methods
pisd now exit without hanging
2024-07-30 14:18:02 +03:00

298 lines
9.7 KiB
C++

/*
PIP - Platform Independent Primitives
Terminal client for windows, used by PITerminal and pisd
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piincludes_p.h"
#ifndef WINDOWS
int main(int argc, char * argv[]) {
return 0;
}
#else
# define PIP_CONSOLE_STATIC_DEFINE
# include "../../libs/console/piterminal.cpp"
# include "pifile.h"
# include "piliterals_time.h"
# include "pip_console_export.h"
# include "piscreentypes.h"
# include "pisharedmemory.h"
# include <wincon.h>
# include <wingdi.h>
PIVector<PIVector<PIScreenTypes::Cell>> cells;
CONSOLE_SCREEN_BUFFER_INFO sbi;
CHAR_INFO * chars = 0;
HANDLE console = 0, cstdin = 0, pipe = 0, cmd_proc = 0;
PITerminalAuxData data_out;
PIMutex con_mutex;
int con_w = -1, con_h = -1;
PIScreenTypes::Cell CharInfo2Cell(const CHAR_INFO & c) {
PIScreenTypes::Cell ret;
ret.symbol = PIChar::fromConsole(c.Char.AsciiChar);
ret.format.color_char = PIScreenTypes::Black;
if ((c.Attributes & (FOREGROUND_RED)) == FOREGROUND_RED) ret.format.color_char = PIScreenTypes::Red;
if ((c.Attributes & (FOREGROUND_GREEN)) == FOREGROUND_GREEN) ret.format.color_char = PIScreenTypes::Green;
if ((c.Attributes & (FOREGROUND_BLUE)) == FOREGROUND_BLUE) ret.format.color_char = PIScreenTypes::Blue;
if ((c.Attributes & (FOREGROUND_GREEN | FOREGROUND_BLUE)) == (FOREGROUND_GREEN | FOREGROUND_BLUE))
ret.format.color_char = PIScreenTypes::Cyan;
if ((c.Attributes & (FOREGROUND_RED | FOREGROUND_BLUE)) == (FOREGROUND_RED | FOREGROUND_BLUE))
ret.format.color_char = PIScreenTypes::Magenta;
if ((c.Attributes & (FOREGROUND_RED | FOREGROUND_GREEN)) == (FOREGROUND_RED | FOREGROUND_GREEN))
ret.format.color_char = PIScreenTypes::Yellow;
if ((c.Attributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)) == (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE))
ret.format.color_char = PIScreenTypes::White;
ret.format.color_back = PIScreenTypes::Black;
if ((c.Attributes & (BACKGROUND_RED)) == (BACKGROUND_RED)) ret.format.color_back = PIScreenTypes::Red;
if ((c.Attributes & (BACKGROUND_GREEN)) == (BACKGROUND_GREEN)) ret.format.color_back = PIScreenTypes::Green;
if ((c.Attributes & (BACKGROUND_BLUE)) == (BACKGROUND_BLUE)) ret.format.color_back = PIScreenTypes::Blue;
if ((c.Attributes & (BACKGROUND_GREEN | BACKGROUND_BLUE)) == (BACKGROUND_GREEN | BACKGROUND_BLUE))
ret.format.color_back = PIScreenTypes::Cyan;
if ((c.Attributes & (BACKGROUND_RED | BACKGROUND_BLUE)) == (BACKGROUND_RED | BACKGROUND_BLUE))
ret.format.color_back = PIScreenTypes::Magenta;
if ((c.Attributes & (BACKGROUND_RED | BACKGROUND_GREEN)) == (BACKGROUND_RED | BACKGROUND_GREEN))
ret.format.color_back = PIScreenTypes::Yellow;
if ((c.Attributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)) == (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE))
ret.format.color_back = PIScreenTypes::White;
if ((c.Attributes & (FOREGROUND_INTENSITY)) > 0) ret.format.flags |= PIScreenTypes::Bold;
return ret;
}
int SpecialKey2VirtualKeyCode(PIKbdListener::SpecialKey k) {
switch (k) {
case PIKbdListener::Tab: return '\t'; break;
case PIKbdListener::Return: return '\n'; break;
case PIKbdListener::Space: return ' '; break;
case PIKbdListener::Backspace: return 8;
case PIKbdListener::PageUp: return 33;
case PIKbdListener::PageDown: return 34;
case PIKbdListener::End: return 35;
case PIKbdListener::Home: return 36;
case PIKbdListener::LeftArrow: return 37;
case PIKbdListener::UpArrow: return 38;
case PIKbdListener::RightArrow: return 39;
case PIKbdListener::DownArrow: return 40;
case PIKbdListener::Insert: return 45;
case PIKbdListener::Delete: return 46;
case PIKbdListener::F1: return 112;
case PIKbdListener::F2: return 113;
case PIKbdListener::F3: return 114;
case PIKbdListener::F4: return 115;
case PIKbdListener::F5: return 116;
case PIKbdListener::F6: return 117;
case PIKbdListener::F7: return 118;
case PIKbdListener::F8: return 119;
case PIKbdListener::F9: return 120;
case PIKbdListener::F10: return 121;
case PIKbdListener::F11: return 122;
case PIKbdListener::F12: return 123;
default: break;
}
return 0;
}
int KeyModifiers2ControlKeyState(PIKbdListener::KeyModifiers m) {
int ret(0);
if (m[PIKbdListener::Ctrl]) ret |= LEFT_CTRL_PRESSED;
if (m[PIKbdListener::Shift]) ret |= SHIFT_PRESSED;
if (m[PIKbdListener::Alt]) ret |= LEFT_ALT_PRESSED;
return ret;
}
void readConsole(int x, int y, int w, int h) {
GetConsoleScreenBufferInfo(console, &sbi);
COORD bs, bc;
bs.X = w;
bs.Y = h;
bc.X = bc.Y = 0;
memset(chars, 0, w * h * sizeof(CHAR_INFO));
ReadConsoleOutput(console, chars, bs, bc, &(sbi.srWindow));
for (int i = 0; i < h; ++i)
for (int j = 0; j < w; ++j)
cells[i][j] = CharInfo2Cell(chars[i * w + j]);
}
void resizeCells(int w, int h) {
if (chars) delete[] chars;
chars = new CHAR_INFO[w * h];
cells.resize(h);
for (int i = 0; i < h; ++i)
cells[i].resize(w);
}
void resizeConsole(int w, int h) {
if (con_w == w && con_h == h) return;
con_w = w;
con_h = h;
GetConsoleScreenBufferInfo(console, &sbi);
sbi.srWindow.Left = 0;
sbi.srWindow.Right = sbi.srWindow.Left + w - 1;
sbi.srWindow.Bottom = sbi.srWindow.Top + h - 1;
COORD sz;
sz.X = w;
sz.Y = h;
SetConsoleScreenBufferSize(console, sz);
SetConsoleWindowInfo(console, TRUE, &(sbi.srWindow));
// system(("mode CON: COLS=" + PIString::fromNumber(w) + " LINES=" + PIString::fromNumber(h)).dataAscii());
resizeCells(w, h);
}
class PipeReader: public PIThread {
public:
PipeReader(): PIThread() {
wrote = readed = 0;
msg_size = 0;
start(1_ms);
}
~PipeReader() {
stop();
if (!waitForFinish(100_ms)) terminate();
}
void run() {
in.resize(PIPE_BUFFER_SIZE);
ReadFile(pipe, in.data(), in.size_s(), &readed, 0);
if (GetLastError() == ERROR_BROKEN_PIPE) {
stop();
return;
}
// piCout << errorString();
if (readed > 0) {
in.resize(readed);
stream.append(in);
if (msg_size == 0) {
if (stream.size_s() < 4) return;
stream >> msg_size;
}
if (stream.size_s() >= msg_size) {
msg = PIByteArray(stream.data(), msg_size);
stream.remove(0, msg_size);
msg_size = 0;
parseMessage();
}
if (msg_size == 0 && stream.size_s() < 4) return;
}
}
void parseMessage() {
if (msg.size_s() < 4) return;
PIMutexLocker _ml(con_mutex);
int type = 0;
msg >> type;
// piCout << "msg" << type;
switch ((PITerminalAuxMessageType)type) {
case mtKey: {
PIVector<PIKbdListener::KeyEvent> ke;
msg >> ke;
PIVector<INPUT_RECORD> ir(ke.size() * 2);
for (int i = 0; i < ke.size_s(); ++i) {
PIKbdListener::KeyEvent k(ke[i]);
int j = i + i, z = j + 1;
ir[j].EventType = KEY_EVENT;
ir[j].Event.KeyEvent.wRepeatCount = 1;
ir[j].Event.KeyEvent.dwControlKeyState = KeyModifiers2ControlKeyState(k.modifiers);
if (PITerminal::isSpecialKey(k.key)) {
ir[j].Event.KeyEvent.wVirtualKeyCode = SpecialKey2VirtualKeyCode((PIKbdListener::SpecialKey)k.key);
ir[j].Event.KeyEvent.uChar.AsciiChar = PIChar((ushort)piMaxi(k.key, 0)).toAscii();
} else
ir[j].Event.KeyEvent.uChar.UnicodeChar = PIChar((ushort)piMaxi(k.key, 0)).toWChar();
// piCout << ir[j].Event.KeyEvent.wVirtualKeyCode << int(ir[j].Event.KeyEvent.uChar.AsciiChar);
ir[j].Event.KeyEvent.bKeyDown = true;
ir[z] = ir[j];
ir[z].Event.KeyEvent.bKeyDown = false;
}
WriteConsoleInput(cstdin, ir.data(), ir.size_s(), &wrote);
} break;
case mtResize: {
int rw = -1, rh = -1;
msg >> rw >> rh;
if (rw > 0 && rh > 0) resizeConsole(rw, rh);
} break;
default: break;
}
}
DWORD wrote, readed;
int msg_size;
PIByteArray in, stream, msg;
};
void getCursor(int & x, int & y) {
GetConsoleScreenBufferInfo(console, &sbi);
x = sbi.dwCursorPosition.X - sbi.srWindow.Left;
y = sbi.dwCursorPosition.Y - sbi.srWindow.Top;
}
int main(int argc, char * argv[]) {
// piCout << "start";
STARTUPINFOA si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
memset(&sbi, 0, sizeof(sbi));
PIString shmh, pname;
if (argc > 1) shmh = argv[1];
if (argc > 2) pname = argv[2];
if (!CreateProcessA(0, (LPSTR) "cmd", 0, 0, true, 0, 0, 0, &si, &pi)) {
return 1;
}
PISharedMemory shm("piterm_aux" + shmh, 1024 * 1024);
pipe = CreateFileA((LPSTR)pname.dataAscii(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if (pipe == INVALID_HANDLE_VALUE) {
return 1;
}
CloseHandle(pi.hThread);
cmd_proc = pi.hProcess;
console = GetStdHandle(STD_OUTPUT_HANDLE);
cstdin = GetStdHandle(STD_INPUT_HANDLE);
resizeConsole(80, 24);
PipeReader pipe_reader;
pipe_reader.waitForStart();
PIByteArray scr;
while (true) {
// piCout << "loop";
if (!pipe_reader.isRunning()) break;
con_mutex.lock();
getCursor(data_out.cursor_x, data_out.cursor_y);
readConsole(0, 0, con_w, con_h);
scr.clear();
scr << cells;
data_out.size_x = con_w;
data_out.size_y = con_h;
data_out.cells_size = scr.size_s();
con_mutex.unlock();
shm.write(&data_out, sizeof(data_out));
shm.write(scr, sizeof(data_out));
piMSleep(25);
}
// piCout << "exit";
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hProcess);
return 0;
}
#endif