diff --git a/src/auxiliary/piterminal/main.cpp b/src/auxiliary/piterminal/main.cpp index 3f50c99f..69150b7d 100644 --- a/src/auxiliary/piterminal/main.cpp +++ b/src/auxiliary/piterminal/main.cpp @@ -182,14 +182,12 @@ public: ir[z].Event.KeyEvent.bKeyDown = false; } WriteConsoleInput(cstdin, ir.data(), ir.size_s(), &wrote); - } - break; + } break; case mtResize: { int rw, rh; msg >> rw >> rh; resizeConsole(rw, rh); - } - break; + } break; default: break; } } diff --git a/src/console/piterminal.cpp b/src/console/piterminal.cpp new file mode 100644 index 00000000..995aeddd --- /dev/null +++ b/src/console/piterminal.cpp @@ -0,0 +1,367 @@ +/* + PIP - Platform Independent Primitives + Virtual terminal + Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "piterminal.h" +#ifdef WINDOWS +# include "pisharedmemory.h" +# include +# include +#else +#endif + + +//extern PIMutex __PICout_mutex__; +#define PIPE_BUFFER_SIZE 1024 + +#ifdef WINDOWS +enum PITerminalAuxMessageType { + mtKey = 1, + mtResize, + mtScroll +}; +struct PITerminalAuxData { + int cursor_x; + int cursor_y; + int size_x; + int size_y; + int cells_size; +}; +#endif + + +PRIVATE_DEFINITION_START(PITerminal) +#ifdef WINDOWS + PISharedMemory * shm; + HANDLE hConBuf; + STARTUPINFOA si; + PROCESS_INFORMATION pi; + HANDLE pipe; +#endif +PRIVATE_DEFINITION_END(PITerminal) + + +int writePipe(HANDLE pipe, const PIByteArray & ba) { + DWORD wrote[2]; + int sz = ba.size_s(); + WriteFile(pipe, &sz, 4, &(wrote[0]), 0); + WriteFile(pipe, ba.data(), ba.size_s(), &(wrote[1]), 0); + //piCout << "send" << ba.size_s(); + return int(wrote[0] + wrote[1]); +} + + +PITerminal::PITerminal(): PIThread() { + setName("terminal"); + initPrivate(); + cursor_visible = false; + cursor_x = cursor_y = 0; + dsize_x = 80; + dsize_y = 24; + PRIVATE->shm = 0; +} + + +PITerminal::~PITerminal() { + if (isRunning()) + stop(); + PIThread::waitForFinish(10); + destroy(); + if (PRIVATE->shm) delete PRIVATE->shm; +} + + +void PITerminal::write(const PIByteArray & d) { +#ifdef WINDOWS + PIByteArray msg; + PIVector ke; + for (int i = 0; i < d.size_s(); ++i) + ke << PIKbdListener::KeyEvent(d[i]); + msg << int(mtKey) << ke; + writePipe(PRIVATE->pipe, msg); +#endif + cursor_tm.reset(); + cursor_visible = true; +} + + +void PITerminal::write(PIKbdListener::SpecialKey k, PIKbdListener::KeyModifiers m) { +#ifdef WINDOWS + PIByteArray ba; + switch (k) { + case PIKbdListener::Tab: ba << uchar('\t'); break; + case PIKbdListener::Return: ba << uchar('\r') << uchar('\n'); break; + case PIKbdListener::Space: ba << uchar(' '); break; + default: break; + } + //piCout << "write" << ba.size(); + if (!ba.isEmpty()) write(ba); + else { + PIByteArray msg; + PIVector ke; + ke << PIKbdListener::KeyEvent(k, m); + msg << int(mtKey) << ke; + writePipe(PRIVATE->pipe, msg); + } +#endif + cursor_tm.reset(); + cursor_visible = true; +} + + +PIVector > PITerminal::content() { + readConsole(); + return cells; +} + + +bool PITerminal::isSpecialKey(int k) { + switch (k) { + case PIKbdListener::Tab: + case PIKbdListener::Return: + case PIKbdListener::Esc: + case PIKbdListener::Space: + case PIKbdListener::Backspace: + case PIKbdListener::UpArrow: + case PIKbdListener::DownArrow: + case PIKbdListener::RightArrow: + case PIKbdListener::LeftArrow: + case PIKbdListener::Home: + case PIKbdListener::End: + case PIKbdListener::PageUp: + case PIKbdListener::PageDown: + case PIKbdListener::Insert: + case PIKbdListener::Delete: + case PIKbdListener::F1: + case PIKbdListener::F2: + case PIKbdListener::F3: + case PIKbdListener::F4: + case PIKbdListener::F5: + case PIKbdListener::F6: + case PIKbdListener::F7: + case PIKbdListener::F8: + case PIKbdListener::F9: + case PIKbdListener::F10: + case PIKbdListener::F11: + case PIKbdListener::F12: return true; + default: return false; + } + return false; +} + + +void PITerminal::initPrivate() { +#ifdef WINDOWS + PRIVATE->hConBuf = INVALID_HANDLE_VALUE; + PRIVATE->pipe = INVALID_HANDLE_VALUE; + PRIVATE->pi.hProcess = 0; +#endif + size_x = size_y = 0; +} + + +void PITerminal::readConsole() { +#ifdef WINDOWS + if (!PRIVATE->shm) return; + PITerminalAuxData data; + PRIVATE->shm->read(&data, sizeof(data)); + if (data.cells_size <= 4) return; + cursor_x = data.cursor_x; + cursor_y = data.cursor_y; + size_x = data.size_x; + size_y = data.size_y; + PIByteArray ba; + ba.resize(data.cells_size); + PRIVATE->shm->read(ba.data(), ba.size_s(), sizeof(data)); + ba >> cells; +#endif + //piCout << cursor_x << cursor_y; + if (cursor_visible) + if (cursor_x >= 0 && cursor_x < size_x) + if (cursor_y >= 0 && cursor_y < size_y) + cells[cursor_y][cursor_x].format.color_back = invertColor(cells[cursor_y][cursor_x].format.color_back); +} + + +void PITerminal::getCursor(int & x, int & y) { +#ifdef WINDOWS + if (!PRIVATE->shm) return; + int sz = 0; + PRIVATE->shm->read(&sz, 4); +#endif +} + + +uchar PITerminal::invertColor(uchar c) { + switch ((PIScreenTypes::Color)c) { + case PIScreenTypes::Black: return PIScreenTypes::White; + case PIScreenTypes::Red: return PIScreenTypes::Cyan; + case PIScreenTypes::Green: return PIScreenTypes::Magenta; + case PIScreenTypes::Blue: return PIScreenTypes::Yellow; + case PIScreenTypes::Cyan: return PIScreenTypes::Red; + case PIScreenTypes::Magenta: return PIScreenTypes::Green; + case PIScreenTypes::Yellow: return PIScreenTypes::Blue; + case PIScreenTypes::White: return PIScreenTypes::Black; + default: break; + } + return PIScreenTypes::White; +} + + +void PITerminal::run() { + if (cursor_tm.elapsed_m() >= 500) { + cursor_tm.reset(); + cursor_visible = !cursor_visible; + if (cursor_visible) { + getCursor(cursor_x, cursor_y); + } + } +} + + +bool PITerminal::initialize() { + destroy(); +#ifdef WINDOWS + /*SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + sa.lpSecurityDescriptor = 0; + if (!CreatePipe(&(PRIVATE->pipe_in[0]), &(PRIVATE->pipe_in[1]), &sa, 0)) { + piCoutObj << "CreatePipe error," << errorString(); + initPrivate(); + return false; + } + PRIVATE->hConBuf = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, &sa, CONSOLE_TEXTMODE_BUFFER, 0); + if (PRIVATE->hConBuf == INVALID_HANDLE_VALUE) { + piCoutObj << "CreateConsoleScreenBuffer error," << errorString(); + destroy(); + return false; + }*/ + //CreatePipe(&(PRIVATE->pipe_out[0]), &(PRIVATE->pipe_out[1]), &sa, 0); + //SetHandleInformation(PRIVATE->pipe_in[1], HANDLE_FLAG_INHERIT, 0); + //SetHandleInformation(PRIVATE->hConBuf, HANDLE_FLAG_INHERIT, 0); + //GetStartupInfoA(&PRIVATE->si); + memset(&PRIVATE->si, 0, sizeof(PRIVATE->si)); + PRIVATE->si.cb = sizeof(STARTUPINFO); + //PRIVATE->si.dwFlags |= STARTF_USESTDHANDLES; + PRIVATE->si.dwFlags |= STARTF_USESHOWWINDOW; + PRIVATE->si.dwFlags |= STARTF_USECOUNTCHARS; + //PRIVATE->si.hStdInput = PRIVATE->pipe; + //PRIVATE->si.hStdOutput = PRIVATE->hConBuf; + //PRIVATE->si.hStdError = PRIVATE->hConBuf; + PRIVATE->si.wShowWindow = SW_HIDE; + PRIVATE->si.dwXCountChars = 80; + PRIVATE->si.dwYCountChars = 24; + + memset(&PRIVATE->pi, 0, sizeof(PRIVATE->pi)); + + PIString shmh = PIString::fromNumber(rand() % 10000); + PIString pname = "\\\\.\\pipe\\piterm" + shmh; + PIString cmd = "piterminal \"" + shmh + "\" \"" + pname + "\""; + if(!CreateProcessA(0, (LPSTR)cmd.dataAscii(), 0, 0, false, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, 0, 0, &PRIVATE->si, &PRIVATE->pi)) { + piCoutObj << "CreateProcess error," << errorString(); + destroy(); + return false; + } + PRIVATE->pipe = CreateNamedPipe((LPSTR)pname.dataAscii(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 2, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, 1000, NULL); + if (PRIVATE->pipe == INVALID_HANDLE_VALUE) { + piCoutObj << "CreateNamedPipe error," << errorString(); + destroy(); + return false; + } + PITimeMeasurer tm; + bool ok = false; + while (tm.elapsed_m() < 1000) { + if (ConnectNamedPipe(PRIVATE->pipe, 0) == TRUE) { + ok = true; + break; + } + } + if (!ok) { + piCoutObj << "ConnectNamedPipe error," << errorString(); + destroy(); + return false; + } + if (PRIVATE->shm) delete PRIVATE->shm; + PRIVATE->shm = new PISharedMemory("piterm_aux" + shmh, 1024*1024); + //WaitForSingleObject(PRIVATE->pi.hProcess, INFINITE); + //piCout << PRIVATE->pi.hThread << PRIVATE->pi.hProcess; + CloseHandle(PRIVATE->pi.hThread); + resize(dsize_x, dsize_y); + + /*DWORD written; + WriteFile(PRIVATE->pipe_in[1], "pisd -h\r\n", 9, &written, 0); + piCout << "write" << written; + piMSleep(500);*/ + /*PIByteArray rb(1024); + ReadFile(PRIVATE->pipe_out[0], rb.data(), rb.size_s(), &written, 0); + piCout << "read" << written; + piCout << PIString(rb);*/ + /*bool a = AllocConsole(); + if (a) { + HWND wnd = GetConsoleWindow(); + if (wnd) + ShowWindow(wnd, SW_HIDE); + } + PRIVATE->hConBuf = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, 0, CONSOLE_TEXTMODE_BUFFER, 0); + if (PRIVATE->hConBuf == INVALID_HANDLE_VALUE) { + piCoutObj << "CreateConsoleScreenBuffer error," << errorString(); + return false; + }*/ +#endif + cursor_visible = false; + cursor_tm.reset(); + start(40); + return true; +} + + +void PITerminal::destroy() { + //piCout << "destroy ..."; + stop(); + waitForFinish(1000); +#ifdef WINDOWS + if (PRIVATE->pi.hProcess) { + piCout << "term"; + //TerminateProcess(PRIVATE->pi.hProcess, 0); + GenerateConsoleCtrlEvent(CTRL_C_EVENT, PRIVATE->pi.dwProcessId); + CloseHandle(PRIVATE->pi.hProcess); + } + if (PRIVATE->pipe != INVALID_HANDLE_VALUE) CloseHandle(PRIVATE->pipe); + if (PRIVATE->hConBuf != INVALID_HANDLE_VALUE) CloseHandle(PRIVATE->hConBuf); + //piCout << "destroy" << size_y; +#endif + initPrivate(); +} + + +bool PITerminal::resize(int cols, int rows) { + bool ret = true; + dsize_x = cols; + dsize_y = rows; +#ifdef WINDOWS + if (PRIVATE->pipe == INVALID_HANDLE_VALUE) return false; + PIByteArray msg; + msg << int(mtResize) << dsize_x << dsize_y; + writePipe(PRIVATE->pipe, msg); +#endif + cells.resize(size_y); + for (int i = 0; i < size_y; ++i) + cells[i].resize(size_x); + return ret; +} diff --git a/src/console/piterminal.h b/src/console/piterminal.h new file mode 100644 index 00000000..fdadbed3 --- /dev/null +++ b/src/console/piterminal.h @@ -0,0 +1,69 @@ +/*! \file piterminal.h + * \brief Virtual terminal +*/ +/* + PIP - Platform Independent Primitives + Virtual terminal + Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PITERMINAL_H +#define PITERMINAL_H + +#include "pithread.h" +#include "pikbdlistener.h" +#include "piscreentypes.h" + + +class PIP_EXPORT PITerminal: public PIThread +{ + PIOBJECT_SUBCLASS(PITerminal, PIThread) +public: + + //! Constructs %PITerminal + PITerminal(); + + ~PITerminal(); + + int columns() const {return size_x;} + int rows() const {return size_y;} + bool resize(int cols, int rows); + + void write(const PIByteArray & d); + void write(PIKbdListener::SpecialKey k, PIKbdListener::KeyModifiers m); + PIVector > content(); + static bool isSpecialKey(int k); + + bool initialize(); + void destroy(); +private: + void initPrivate(); + void readConsole(); + void getCursor(int & x, int & y); + uchar invertColor(uchar c); + void run(); + + PRIVATE_DECLARATION + int dsize_x, dsize_y; + int size_x, size_y, cursor_x, cursor_y; + bool cursor_visible; + PITimeMeasurer cursor_tm; + PIVector > cells; + +}; + + +#endif // PITERMINAL_H