/* 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 . */ #include "piincludes_p.h" #ifndef WINDOWS int main (int argc, char * argv[]) { return 0; } #else # define PIP_CONSOLE_STATIC_DEFINE # include "pip_console_export.h" # include "piscreentypes.h" # include "pisharedmemory.h" # include "pifile.h" # include # include # include "../../libs/console/piterminal.cpp" PIVector > 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); } ~PipeReader() { stop(); } 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 ke; msg >> ke; PIVector 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, rh; msg >> rw >> rh; 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"; STARTUPINFO 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 = CreateFile((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