git-svn-id: svn://db.shs.com.ru/pip@688 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
917 lines
28 KiB
C++
917 lines
28 KiB
C++
/*
|
|
PIP - Platform Independent Primitives
|
|
Virtual terminal
|
|
Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "piincludes_p.h"
|
|
#include "piterminal.h"
|
|
#include "pisharedmemory.h"
|
|
#ifndef FREERTOS
|
|
#ifdef WINDOWS
|
|
# include <windows.h>
|
|
# include <wincon.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(10);
|
|
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(ke.key).toConsole1Byte());
|
|
#else
|
|
ba = PIString(PIChar(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(PRIVATE->read_buf));
|
|
PRIVATE->read_buf.clear();
|
|
}
|
|
//printf("%s", PRIVATE->read_buf.data());
|
|
}
|
|
if (!used && !PRIVATE->last_read && !PRIVATE->read_buf.isEmpty()) {
|
|
parseInput(PIString(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(";");
|
|
piForeachC (PIString & 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;
|
|
}
|
|
(target ? PRIVATE->cur_format.color_back : 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 = 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);
|
|
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();
|
|
piForeachC (PIString & e, env)
|
|
if (e.startsWith("TERM=")) {
|
|
PRIVATE->term_type = termType(e.mid(5).trim().toLowerCase());
|
|
//piCout << PRIVATE->term_type;
|
|
piBreak;
|
|
}
|
|
pid_t fr = forkpty(&(PRIVATE->fd), pty, 0, &ws);
|
|
//piCoutObj << fr << PRIVATE->fd << pty;
|
|
if (fr == 0) {
|
|
char ** argv = new char*[2];
|
|
argv[0] = new char[PRIVATE->shell.lengthAscii() + 1];
|
|
memcpy(argv[0], PRIVATE->shell.dataAscii(), PRIVATE->shell.lengthAscii());
|
|
argv[0][PRIVATE->shell.lengthAscii()] = 0;
|
|
argv[1] = 0;
|
|
execvp(PRIVATE->shell.dataAscii(), 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(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;
|
|
#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 // FREERTOS
|