/* PIP - Platform Independent Primitives Virtual terminal 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 "piterminal.h" #include "piincludes_p.h" #include "piliterals_time.h" #include "pisharedmemory.h" #ifndef MICRO_PIP # ifdef WINDOWS # include # include # include # else # include "piprocess.h" # include # include # include # if defined(QNX) || defined(BLACKBERRY) # include # else # ifdef MAC_OS # include # else # include # endif # endif # ifdef ANDROID # if __ANDROID_API__ >= 23 # define HAS_FORKPTY # endif # else # define HAS_FORKPTY # endif # endif // extern PIMutex PICout::__mutex__; # ifdef WINDOWS # define PIPE_BUFFER_SIZE 1024 enum PITerminalAuxMessageType { mtKey = 1, mtResize, mtScroll }; struct PITerminalAuxData { int cursor_x; int cursor_y; int size_x; int size_y; int cells_size; }; # else # define BUFFER_SIZE 4096 enum DECType { CKM = 1 }; # endif PRIVATE_DEFINITION_START(PITerminal) # ifdef WINDOWS PISharedMemory * shm; HANDLE hConBuf; STARTUPINFOA si; PROCESS_INFORMATION pi; HANDLE pipe; # else PIString shell; PIByteArray read_buf, tmp_buf; PIScreenTypes::CellFormat cur_format, line_format; int term_type; int fd, cur_x, cur_y; int save_cur_x, save_cur_y; int win_y0, win_y1; pid_t pid; PIString esc_seq; bool is_esc_seq, last_read; PIMap DEC; PIVector> cells_save; # endif PRIVATE_DEFINITION_END(PITerminal) # ifdef WINDOWS 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]); } # endif PITerminal::PITerminal(): PIThread() { setName("terminal"); initPrivate(); cursor_blink = false; cursor_x = cursor_y = 0; dsize_x = 80; dsize_y = 24; # ifdef WINDOWS PRIVATE->shm = 0; # endif } PITerminal::~PITerminal() { if (isRunning()) stop(); PIThread::waitForFinish(1_s); destroy(); # ifdef WINDOWS if (PRIVATE->shm) delete PRIVATE->shm; # endif } 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); # else # ifdef HAS_FORKPTY if (PRIVATE->fd == 0) return; // ssize_t wrote = 0; // wrote = auto _r = ::write(PRIVATE->fd, d.data(), d.size_s()); NO_UNUSED(_r); // piCout << "wrote" << wrote << d; # endif # endif cursor_tm.reset(); cursor_blink = true; } void PITerminal::write(PIKbdListener::SpecialKey k, PIKbdListener::KeyModifiers m) { PIByteArray ba; # ifdef WINDOWS 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); } # else int term = PRIVATE->term_type; int flags = 0; switch (k) { case PIKbdListener::Tab: ba << uchar('\t'); break; case PIKbdListener::Return: ba << uchar('\n'); break; case PIKbdListener::Esc: ba << uchar('\e'); break; case PIKbdListener::Space: ba << uchar(' '); break; case PIKbdListener::Backspace: ba << uchar(0x7f); break; case PIKbdListener::UpArrow: case PIKbdListener::DownArrow: case PIKbdListener::RightArrow: case PIKbdListener::LeftArrow: if (PRIVATE->DEC.value(CKM, false)) flags = 1; /*case PIKbdListener::Home: //break; case PIKbdListener::End: //break; case PIKbdListener::PageUp: //ba << uchar('\e') << uchar('[') << uchar('5') << uchar('~'); break; case PIKbdListener::PageDown: //ba << uchar('\e') << uchar('[') << uchar('6') << uchar('~'); break; case PIKbdListener::Insert: //ba << uchar('\e') << uchar('[') << uchar('2') << uchar('~'); break; case PIKbdListener::Delete: //ba << uchar('\e') << uchar('[') << uchar('3') << uchar('~'); break; case PIKbdListener::F1: //break; case PIKbdListener::F2: //break; case PIKbdListener::F3: //break; case PIKbdListener::F4: //break; case PIKbdListener::F5: //break; case PIKbdListener::F6: //break; case PIKbdListener::F7: //break; case PIKbdListener::F8: //break; case PIKbdListener::F9: //break; case PIKbdListener::F10: //break; case PIKbdListener::F11: //break; case PIKbdListener::F12: //break; */ default: { // piCout << flags; // int mod = 0; if (m[PIKbdListener::Shift]) m |= 1; if (m[PIKbdListener::Alt]) m |= 2; if (m[PIKbdListener::Ctrl]) m |= 4; for (int i = 0;; ++i) { const PIKbdListener::EscSeq & e(PIKbdListener::esc_seq[i]); if (!e.seq) break; // piCout << "search" << rc[1] << esc_seq[i].seq; if (e.key == k && e.mod == m) { if (((e.vt & term) == term) || (((e.flags & flags) == flags) && (flags != 0))) { // piCout << "found key" << PIString(e.seq).replaceAll("\e", "\\e"); PIByteArray d = ("\e" + PIString(e.seq)).toByteArray(); write(d); break; } } } } break; } // piCout << "write" << ba.size(); if (!ba.isEmpty()) write(ba); # endif cursor_tm.reset(); cursor_blink = true; } void PITerminal::write(PIKbdListener::KeyEvent ke) { if (isSpecialKey(ke.key)) write((PIKbdListener::SpecialKey)ke.key, ke.modifiers); else { PIByteArray ba; # ifdef WINDOWS ba << uchar(PIChar((ushort)ke.key).toConsole1Byte()); # else ba = PIString(PIChar((ushort)ke.key)).toUTF8(); # endif write(ba); } } PIVector> PITerminal::content() { readConsole(); PIVector> ret = cells; if (cursor_blink && cursor_visible) if (cursor_x >= 0 && cursor_x < size_x) if (cursor_y >= 0 && cursor_y < size_y) ret[cursor_y][cursor_x].format.flags ^= PIScreenTypes::Inverse; return ret; } 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; # else PRIVATE->shell = "/bin/bash"; PRIVATE->read_buf.reserve(BUFFER_SIZE); PRIVATE->read_buf.fill(0); PRIVATE->fd = PRIVATE->cur_x = PRIVATE->cur_y = 0; PRIVATE->save_cur_x = PRIVATE->save_cur_y = 0; PRIVATE->pid = 0; PRIVATE->term_type = 0; PRIVATE->is_esc_seq = false; PRIVATE->last_read = true; PRIVATE->esc_seq.clear(); PRIVATE->cur_format = PIScreenTypes::CellFormat(); # endif cursor_blink = cursor_visible = true; 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; } void PITerminal::getCursor(int & x, int & y) { # ifdef WINDOWS if (!PRIVATE->shm) return; int sz = 0; PRIVATE->shm->read(&sz, 4); # else x = PRIVATE->cur_x; y = PRIVATE->cur_y; # 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() { getCursor(cursor_x, cursor_y); if (cursor_tm.elapsed_m() >= 500) { cursor_tm.reset(); cursor_blink = !cursor_blink; } # ifndef WINDOWS # ifdef HAS_FORKPTY if (PRIVATE->fd == 0) return; PRIVATE->tmp_buf.resize(BUFFER_SIZE); int readed = ::read(PRIVATE->fd, PRIVATE->tmp_buf.data(), BUFFER_SIZE - PRIVATE->read_buf.size_s()); bool used = false; if (readed > 0) { PRIVATE->last_read = true; // piCoutObj << "readed" << readed << PIString(PRIVATE->tmp_buf.resized(readed)).replaceAll("\e", "\\e"); // piCoutObj << "readed" << readed << (PRIVATE->tmp_buf.resized(readed)); PRIVATE->read_buf.append(PRIVATE->tmp_buf.resized(readed)); for (;;) { int ind = -1; for (int i = PRIVATE->read_buf.size_s() - 1; i >= 0; --i) if (PRIVATE->read_buf[i] == uchar('\n') || PRIVATE->read_buf[i] == uchar('\e')) { ind = i; break; } if (ind <= 0) break; used = true; parseInput(PIString((const char *)PRIVATE->read_buf.data(), ind)); PRIVATE->read_buf.remove(0, ind); } bool parse = PRIVATE->read_buf.size_s() >= BUFFER_SIZE; if (PRIVATE->read_buf.size_s() == 1) if (PRIVATE->read_buf[0] < 0x80) parse = true; if (parse) { parseInput(PIString::fromUTF8(PRIVATE->read_buf)); PRIVATE->read_buf.clear(); } // printf("%s", PRIVATE->read_buf.data()); } if (!used && !PRIVATE->last_read && !PRIVATE->read_buf.isEmpty()) { parseInput(PIString::fromUTF8(PRIVATE->read_buf)); PRIVATE->read_buf.clear(); } PRIVATE->last_read = false; # endif # endif } # ifndef WINDOWS void PITerminal::parseInput(const PIString & s) { // piCoutObj << s.replaceAll("\e", "\\e"); // printf("%s", s.data()); for (int i = 0; i < s.size_s(); ++i) { if (s[i].unicode16Code() == 0) break; if (PRIVATE->is_esc_seq) { if (s[i] == '\e') { applyEscSeq(PRIVATE->esc_seq); PRIVATE->esc_seq.clear(); PRIVATE->is_esc_seq = true; } else { PRIVATE->esc_seq += s[i]; if (isCompleteEscSeq(PRIVATE->esc_seq)) { PRIVATE->is_esc_seq = false; applyEscSeq(PRIVATE->esc_seq); // piCoutObj << PRIVATE->esc_seq; } } } else { if (s[i] == '\e') { PRIVATE->esc_seq.clear(); PRIVATE->is_esc_seq = true; } else { if (s[i] == '\a') continue; if (s[i] == '\b') { moveCursor(-1, 0); continue; } if (s[i] == '\r') continue; if (s[i] == '\n') { // piCoutObj << "new line"; for (int i = PRIVATE->cur_x; i < size_x; ++i) cells[PRIVATE->cur_y][i].format = PRIVATE->cur_format; PRIVATE->line_format = PRIVATE->cur_format; PRIVATE->cur_x = 0; moveCursor(0, 1); continue; } // piCoutObj << "char" << s[i] << s[i].unicode16Code() << "at" << PRIVATE->cur_x << PRIVATE->cur_y; cells[PRIVATE->cur_y][PRIVATE->cur_x].symbol = s[i]; cells[PRIVATE->cur_y][PRIVATE->cur_x].format = PRIVATE->cur_format; moveCursor(1, 0); } } } } bool PITerminal::isCompleteEscSeq(const PIString & es) { if (es.size_s() < 2) return false; if (es.front() == ']') { if (es.back().toAscii() == '\\' || es.back().toAscii() == '\a') return true; } else { if (es.back().toAscii() >= 64 && es.back().toAscii() <= 126) return true; } return false; } void PITerminal::applyEscSeq(PIString es) { piCoutObj << es; if (es.size_s() < 2) return; // PIScreenTypes::Cell line_cell = PIScreenTypes::Cell(' ', PRIVATE->line_format); PIScreenTypes::Cell def_cell = PIScreenTypes::Cell(' ', PRIVATE->cur_format); if (es[1] == '?' && es.size_s() >= 2) { char a = es.takeRight(1)[0].toAscii(); bool val = false; if (a == 'l') val = false; if (a == 'h') val = true; int dec = es.mid(2).toInt(); piCoutObj << "DEC" << dec << val; PRIVATE->DEC[dec] = val; switch (dec) { case 25: cursor_visible = val; break; case 1049: if (val) { PRIVATE->save_cur_x = PRIVATE->cur_x; PRIVATE->save_cur_y = PRIVATE->cur_y; } else { PRIVATE->cur_x = PRIVATE->save_cur_x; PRIVATE->cur_y = PRIVATE->save_cur_y; } case 1047: if (val) { PRIVATE->cells_save = cells; for (int i = 0; i < size_y; ++i) cells[i].fill(def_cell); } else { cells = PRIVATE->cells_save; } break; } } if (es[0] == '[') { // CSI if (es.back() == 'm') { es.cutLeft(1).cutRight(1); if (es.isEmpty()) { PRIVATE->cur_format = PIScreenTypes::CellFormat(); return; } PIStringList args = es.split(";"); for (const auto & a: args) { int av = a.toInt(); switch (av) { case 0: PRIVATE->cur_format = PIScreenTypes::CellFormat(); break; case 1: PRIVATE->cur_format.flags |= PIScreenTypes::Bold; break; case 4: PRIVATE->cur_format.flags |= PIScreenTypes::Underline; break; case 5: PRIVATE->cur_format.flags |= PIScreenTypes::Blink; break; case 7: PRIVATE->cur_format.flags |= PIScreenTypes::Inverse; break; default: { bool col = false, target = false; int cid = av % 10; if (av >= 30 && av <= 37) { col = true; target = false; } if (av >= 40 && av <= 47) { col = true; target = true; } if (col) { int cfl = 0; switch (cid) { case 0: cfl = PIScreenTypes::Black; break; case 1: cfl = PIScreenTypes::Red; break; case 2: cfl = PIScreenTypes::Green; break; case 3: cfl = PIScreenTypes::Yellow; break; case 4: cfl = PIScreenTypes::Blue; break; case 5: cfl = PIScreenTypes::Magenta; break; case 6: cfl = PIScreenTypes::Cyan; break; case 7: cfl = PIScreenTypes::White; break; } if (target) PRIVATE->cur_format.color_back = cfl; else PRIVATE->cur_format.color_char = cfl; break; } } break; } } /*if ((PRIVATE->cur_format.flags & PIScreenTypes::Inverse) == PIScreenTypes::Inverse) { uchar t = PRIVATE->cur_format.color_char; PRIVATE->cur_format.color_char = PRIVATE->cur_format.color_back; PRIVATE->cur_format.color_back = t; }*/ } if (es.back() == 'r') { piCoutObj << es; es.cutLeft(1).cutRight(1); PIStringList args = es.split(";"); args.resize(2); int y0(0), y1(0); if (!args[0].isEmpty()) y0 = args[0].toInt() - 1; if (!args[1].isEmpty()) y1 = args[1].toInt() - 1; PRIVATE->win_y0 = piClamp(y0, 0, size_y - 1); PRIVATE->win_y1 = piClamp(y1, 0, size_y - 1); } if (es.back() == 's') { PRIVATE->save_cur_x = PRIVATE->cur_x; PRIVATE->save_cur_y = PRIVATE->cur_y; } if (es.back() == 'u') { PRIVATE->cur_x = PRIVATE->save_cur_x; PRIVATE->cur_y = PRIVATE->save_cur_y; } if (es.back() == 'H' || es.back() == 'f' || es.back() == 'r') { es.cutLeft(1).cutRight(1); PIStringList args = es.split(";"); args.resize(2); int x(0), y(0); if (!args[0].isEmpty()) y = args[0].toInt() - 1; if (!args[1].isEmpty()) x = args[1].toInt() - 1; // piCoutObj << x << y; PRIVATE->cur_x = piClamp(x, 0, size_x - 1); PRIVATE->cur_y = piClamp(y, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'A') { // cursor up es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_y = piClamp(PRIVATE->cur_y - v, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'B') { // cursor down es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_y = piClamp(PRIVATE->cur_y + v, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'C' || es.back() == 'a') { // cursor forward, next column es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_x = piClamp(PRIVATE->cur_x + v, 0, size_x - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'D') { // cursor back es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_x = piClamp(PRIVATE->cur_x - v, 0, size_x - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'G' || es.back() == '`') { // goto column es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v) PRIVATE->cur_x = piClamp(v - 1, 0, size_x - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'd') { // goto line es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_x = 0; PRIVATE->cur_y = piClamp(v - 1, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'E' || es.back() == 'e') { // next line es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_x = 0; PRIVATE->cur_y = piClamp(PRIVATE->cur_y + v, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'F') { // previous line es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; PRIVATE->cur_x = 0; PRIVATE->cur_y = piClamp(PRIVATE->cur_y - v, 0, size_y - 1); PRIVATE->line_format = PRIVATE->cur_format; } if (es.back() == 'L') { // insert lines es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; for (int i = piClamp(size_y - 1, PRIVATE->win_y0, PRIVATE->win_y1); i >= piClamp(PRIVATE->cur_y + v, PRIVATE->win_y0, PRIVATE->win_y1); --i) cells[i] = cells[i - v]; for (int j = piClamp(PRIVATE->cur_y, PRIVATE->win_y0, PRIVATE->win_y1); j < piClamp(PRIVATE->cur_y + v, PRIVATE->win_y0, PRIVATE->win_y1); ++j) for (int i = 0; i < PRIVATE->cur_x; ++i) cells[j][i] = def_cell; } if (es.back() == 'M') { // delete lines es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; for (int i = piClamp(PRIVATE->cur_y, PRIVATE->win_y0, PRIVATE->win_y1); i < piClamp(size_y - v, PRIVATE->win_y0, PRIVATE->win_y1); ++i) cells[i] = cells[i + v]; for (int j = piClamp(size_y - v, PRIVATE->win_y0, PRIVATE->win_y1); j < piClamp(size_y, PRIVATE->win_y0, PRIVATE->win_y1); ++j) for (int i = 0; i < PRIVATE->cur_x; ++i) cells[j][i] = def_cell; } if (es.back() == 'P') { // delete characters es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; for (int i = PRIVATE->cur_x; i < size_x - v; ++i) cells[PRIVATE->cur_y][i] = cells[PRIVATE->cur_y][i + v]; for (int i = size_x - v; i < size_x; ++i) cells[PRIVATE->cur_y][i] = def_cell; } if (es.back() == '@') { // delete characters es.cutLeft(1).cutRight(1); int v = es.toInt(); if (v == 0) v = 1; for (int i = size_x - 1; i >= PRIVATE->cur_x + v; --i) cells[PRIVATE->cur_y][i] = cells[PRIVATE->cur_y][i - v]; for (int i = PRIVATE->cur_x; i < PRIVATE->cur_x + v; ++i) cells[PRIVATE->cur_y][i] = def_cell; } if (es.back() == 'J') { // erase data es.cutLeft(1).cutRight(1); int v = es.toInt(); switch (v) { case 0: for (int i = PRIVATE->cur_x; i < size_x; ++i) cells[PRIVATE->cur_y][i] = def_cell; for (int i = PRIVATE->cur_y + 1; i < size_y; ++i) cells[i].fill(def_cell); break; case 1: for (int i = 0; i <= PRIVATE->cur_x; ++i) cells[PRIVATE->cur_y][i] = def_cell; for (int i = 0; i < PRIVATE->cur_y; ++i) cells[i].fill(def_cell); break; case 2: for (int i = 0; i < size_y; ++i) cells[i].fill(def_cell); // PRIVATE->cur_x = PRIVATE->cur_y = 0; break; } } if (es.back() == 'K') { // erase in line es.cutLeft(1).cutRight(1); int v = es.toInt(); switch (v) { case 0: for (int i = PRIVATE->cur_x; i < size_x; ++i) cells[PRIVATE->cur_y][i] = def_cell; break; case 1: for (int i = 0; i <= PRIVATE->cur_x; ++i) cells[PRIVATE->cur_y][i] = def_cell; break; case 2: cells[PRIVATE->cur_y].fill(def_cell); break; } } } } void PITerminal::moveCursor(int dx, int dy) { PRIVATE->cur_x += dx; PRIVATE->cur_y += dy; if (PRIVATE->cur_x < 0) PRIVATE->cur_x = 0; if (PRIVATE->cur_y < 0) PRIVATE->cur_y = 0; if (PRIVATE->cur_x >= size_x) { PRIVATE->line_format = PRIVATE->cur_format; PRIVATE->cur_x = 0; PRIVATE->cur_y++; } if (PRIVATE->cur_y >= size_y) { int scroll = piMini(PRIVATE->cur_y - size_y + 1, size_y - 1); // piCout << "scroll" << size_x << size_y << size_y - scroll - 1; PRIVATE->cur_y = size_y - 1; for (int y = 0; y < size_y - scroll; ++y) cells[y] = cells[y + scroll]; for (int y = size_y - scroll; y < size_y; ++y) cells[y].fill(PIScreenTypes::Cell()); } } int PITerminal::termType(const PIString & t) { if (t == "xterm") return PIKbdListener::vt_xterm; else if (t == "linux") return PIKbdListener::vt_linux; return PIKbdListener::vt_none; } # endif 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); piZeroMemory(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; piZeroMemory(PRIVATE->pi); PIString shmh = PIString::fromNumber(randomi() % 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 = CreateNamedPipeA((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); CloseHandle(PRIVATE->pi.hThread); resize(dsize_x, dsize_y); # else # ifdef HAS_FORKPTY char pty[256]; piZeroMemory(pty, 256); winsize ws; ws.ws_col = dsize_x; ws.ws_row = dsize_y; PIStringList env = PIProcess::currentEnvironment(); for (const auto & e: env) if (e.startsWith("TERM=")) { PRIVATE->term_type = termType(e.mid(5).trim().toLowerCase()); // piCout << PRIVATE->term_type; break; } pid_t fr = forkpty(&(PRIVATE->fd), pty, 0, &ws); // piCoutObj << fr << PRIVATE->fd << pty; if (fr == 0) { char ** argv = new char *[2]; PIByteArray shell = PRIVATE->shell.toByteArray(); argv[0] = new char[shell.size() + 1]; memcpy(argv[0], shell.data(), shell.size()); argv[0][shell.size()] = 0; argv[1] = 0; execvp(argv[0], argv); delete[] argv[0]; delete[] argv; exit(0); } else { if (fr < 0 || PRIVATE->fd < 0) { piCoutObj << "forkpty error," << errorString(); initPrivate(); return false; } PRIVATE->pid = fr; fcntl(PRIVATE->fd, F_SETFL, O_NONBLOCK); /* tcgetattr(PRIVATE->fd, &PRIVATE->desc); PRIVATE->desc.c_oflag = PRIVATE->desc.c_lflag = PRIVATE->desc.c_cflag = 0; PRIVATE->desc.c_iflag = IGNBRK; PRIVATE->desc.c_cflag = CLOCAL | HUPCL; PRIVATE->desc.c_cflag |= (CSIZE & CS8); PRIVATE->desc.c_cflag |= CREAD; PRIVATE->desc.c_cc[VMIN] = 1; PRIVATE->desc.c_cc[VTIME] = 1; cfsetispeed(&PRIVATE->desc, B38400); cfsetospeed(&PRIVATE->desc, B38400); if (tcsetattr(PRIVATE->fd, TCSANOW, &PRIVATE->desc) < 0) { piCoutObj << "Can`t set attributes for \"" << pty << "\""; destroy(); return false; } */ size_x = dsize_x; size_y = dsize_y; resize(size_x, size_y); } # endif # endif cursor_blink = false; cursor_tm.reset(); start(25_Hz); return true; } void PITerminal::destroy() { // piCout << "destroy ..."; stop(); waitForFinish(1_s); # 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; # else # ifdef HAS_FORKPTY if (PRIVATE->pid != 0) kill(PRIVATE->pid, SIGKILL); if (PRIVATE->fd != 0) ::close(PRIVATE->fd); # endif # 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); # else # ifdef HAS_FORKPTY if (PRIVATE->fd == 0) return false; size_x = dsize_x; size_y = dsize_y; // piCout << "resize" << PRIVATE->fd << size_x << size_y; winsize ws; ws.ws_col = cols; ws.ws_row = rows; ioctl(PRIVATE->fd, TIOCSWINSZ, &ws); PRIVATE->win_y0 = 0; PRIVATE->win_y1 = size_y - 1; PRIVATE->cells_save.resize(size_y); for (int i = 0; i < size_y; ++i) PRIVATE->cells_save[i].resize(size_x); # endif # endif cells.resize(size_y); for (int i = 0; i < size_y; ++i) cells[i].resize(size_x); return ret; } #endif // MICRO_PIP