Files
pip/libs/console/piterminal.cpp
peri4 caa7880cc4 get rid of piForeach
apply some code analyzer recommendations
ICU flag now check if libicu exists
prepare for more accurate growth of containers (limited PoT, then constantly increase size)
2024-11-20 20:01:47 +03:00

981 lines
28 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#include "piterminal.h"
#include "piincludes_p.h"
#include "piliterals_time.h"
#include "pisharedmemory.h"
#ifndef MICRO_PIP
# ifdef WINDOWS
# include <wincon.h>
# include <windows.h>
# include <wingdi.h>
# include <winuser.h>
# else
# include "piprocess.h"
# include <csignal>
# include <fcntl.h>
# include <sys/ioctl.h>
# if defined(QNX) || defined(BLACKBERRY)
# include <unix.h>
# else
# ifdef MAC_OS
# include <util.h>
# else
# include <pty.h>
# 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<int, bool> DEC;
PIVector<PIVector<PIScreenTypes::Cell>> 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<PIKbdListener::KeyEvent> 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 =
::write(PRIVATE->fd, d.data(), d.size_s());
// 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<PIKbdListener::KeyEvent> 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<PIVector<PIScreenTypes::Cell>> PITerminal::content() {
readConsole();
PIVector<PIVector<PIScreenTypes::Cell>> 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);
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(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];
memset(pty, 0, 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