git-svn-id: svn://db.shs.com.ru/pip@400 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
1193
src_main/console/piconsole.cpp
Normal file
1193
src_main/console/piconsole.cpp
Normal file
@@ -0,0 +1,1193 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "piconsole.h"
|
||||
#include "piincludes_p.h"
|
||||
#include "pipeer.h"
|
||||
#include "piprotocol.h"
|
||||
#include "pidiagnostics.h"
|
||||
#include "pisystemmonitor.h"
|
||||
#ifndef WINDOWS
|
||||
# include <sys/ioctl.h>
|
||||
# include <fcntl.h>
|
||||
# include <termios.h>
|
||||
#else
|
||||
# include <wincon.h>
|
||||
# define COMMON_LVB_UNDERSCORE 0x8000
|
||||
#endif
|
||||
|
||||
|
||||
/** \class PIConsole
|
||||
* \brief Console output class
|
||||
* \details
|
||||
* \section PIConsole_sec0 Synopsis
|
||||
* This class provides output to console with automatic alignment and update.
|
||||
* It supports tabs, keyboard listening, formats and colors.
|
||||
*
|
||||
* \section PIConsole_sec1 Layout
|
||||
* %PIConsole works with variable pointers. You should add your variables with
|
||||
* functions \a addVariable() which receives label name, pointer to variable
|
||||
* and optional column and format. Columns count is dynamically increased if
|
||||
* new column used. E.g. if you add variable to empty tab to column 3, columns
|
||||
* count will be increased to 3, but two firsts columns will be empty. Each column
|
||||
* filled from top to bottom, but you can add just string with function
|
||||
* \a addString() or add empty line with function \a addEmptyLine(). Layout scheme:
|
||||
* \image html piconsole_layout.png
|
||||
*
|
||||
* \section PIConsole_sec2 Keyboard usage
|
||||
* %PIConsole should to be single in application. %PIConsole aggregate PIKbdListener
|
||||
* which grab keyboard and automatic switch tabs by theirs bind keys. If there is no
|
||||
* tab binded to pressed key external function "slot" will be called
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
extern PIMutex __PICout_mutex__;
|
||||
|
||||
|
||||
PRIVATE_DEFINITION_START(PIConsole)
|
||||
#ifdef WINDOWS
|
||||
void getWinCurCoord() {GetConsoleScreenBufferInfo(hOut, &csbi); ccoord = csbi.dwCursorPosition;}
|
||||
COORD & getWinCoord(int dx = 0, int dy = 0) {getWinCurCoord(); ccoord.X += dx; ccoord.Y += dy; return ccoord;}
|
||||
void * hOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi, csbi;
|
||||
CONSOLE_CURSOR_INFO curinfo;
|
||||
COORD ccoord, ulcoord;
|
||||
WORD dattr;
|
||||
DWORD smode, written;
|
||||
#endif
|
||||
PRIVATE_DEFINITION_END(PIConsole)
|
||||
|
||||
|
||||
PIConsole::PIConsole(bool startNow, PIKbdListener::KBFunc slot): PIThread() {
|
||||
setName("console");
|
||||
setPriority(piLow);
|
||||
needLockRun(true);
|
||||
ret_func = slot;
|
||||
num_format = systime_format = 0;
|
||||
vid = 0;
|
||||
cur_tab = width = height = pwidth = pheight = max_y = 0;
|
||||
def_align = Nothing;
|
||||
tabs.reserve(16);
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->ulcoord.X = 0;
|
||||
PRIVATE->hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi);
|
||||
PRIVATE->dattr = PRIVATE->sbi.wAttributes;
|
||||
width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left;
|
||||
height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top;
|
||||
PRIVATE->ulcoord.Y = PRIVATE->sbi.srWindow.Top;
|
||||
GetConsoleMode(PRIVATE->hOut, &PRIVATE->smode);
|
||||
GetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
tabs.reserve(16);
|
||||
addTab("main");
|
||||
listener = new PIKbdListener(key_event, this);
|
||||
peer_timer = new PITimer();
|
||||
peer = 0;
|
||||
server_mode = pause_ = false;
|
||||
state = Disconnected;
|
||||
peer_timer->addDelimiter(20);
|
||||
peer_timer->setName("__S__PIConsole::peer_timer");
|
||||
CONNECT2(void, void * , int, peer_timer, tickEvent, this, peerTimer);
|
||||
if (startNow) start();
|
||||
}
|
||||
|
||||
|
||||
PIConsole::~PIConsole() {
|
||||
stopPeer();
|
||||
if (isRunning())
|
||||
stop();
|
||||
clearTabs(false);
|
||||
delete listener;
|
||||
delete peer_timer;
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hOut, PRIVATE->smode);
|
||||
SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->dattr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::addTab(const PIString & name, char bind_key) {
|
||||
if (isRunning()) lock();
|
||||
tabs.push_back(Tab(name, bind_key));
|
||||
cur_tab = tabs.size() - 1;
|
||||
if (isRunning()) unlock();
|
||||
return tabs.size();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::removeTab(uint index) {
|
||||
if (index >= tabs.size()) return;
|
||||
if (isRunning()) lock();
|
||||
tabs.remove(index);
|
||||
if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1;
|
||||
if (isRunning()) unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::removeTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
removeTab(index);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearTab(uint index) {
|
||||
if (index >= tabs.size()) return;
|
||||
lock();
|
||||
tabs[index].columns.clear();
|
||||
if (cur_tab == index) {
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
}
|
||||
if (cur_tab >= tabs.size()) cur_tab = tabs.size() - 1;
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clearTab(index);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::update() {
|
||||
lock();
|
||||
fillLabels();
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTab(uint index) {
|
||||
if (index >= tabs.size())
|
||||
return false;
|
||||
if (!isRunning()) {
|
||||
cur_tab = index;
|
||||
return true;
|
||||
}
|
||||
lock();
|
||||
__PICout_mutex__.lock();
|
||||
cur_tab = index;
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
__PICout_mutex__.unlock();
|
||||
unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTab(const PIString & name) {
|
||||
uint index = tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setTab(index);
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTabBindKey(uint index, char bind_key) {
|
||||
if (index >= tabs.size())
|
||||
return false;
|
||||
tabs[index].key = bind_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PIConsole::setTabBindKey(const PIString & name, char bind_key) {
|
||||
uint index =tabs.size() + 1;
|
||||
for (uint i = 0; i < tabs.size(); ++i) {
|
||||
if (tabs[i].name == name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return setTabBindKey(index, bind_key);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::key_event(PIKbdListener::KeyEvent key, void * t) {
|
||||
PIConsole * p = (PIConsole * )t;
|
||||
int ct = p->cur_tab;
|
||||
if (key.key == PIKbdListener::LeftArrow) {
|
||||
do {
|
||||
ct--;
|
||||
if (ct < 0) return;
|
||||
} while (p->tabs[ct].key == 0);
|
||||
p->setTab(ct);
|
||||
return;
|
||||
}
|
||||
if (key.key == PIKbdListener::RightArrow) {
|
||||
do {
|
||||
ct++;
|
||||
if (ct >= p->tabs.size_s()) return;
|
||||
} while (p->tabs[ct].key == 0);
|
||||
p->setTab(ct);
|
||||
return;
|
||||
}
|
||||
for (uint i = 0; i < p->tabsCount(); ++i) {
|
||||
if (p->tabs[i].key == key.key) {
|
||||
p->setTab(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (p->ret_func != 0) p->ret_func(key, t);
|
||||
p->keyPressed(key, t);
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::couts(const PIString & v) {
|
||||
return printf("%s", v.data());
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::couts(const char * v) {
|
||||
return printf("%s", v);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::clearVariables(bool clearScreen) {
|
||||
if (isRunning()) lock();
|
||||
if (clearScreen && isRunning()) {
|
||||
toUpperLeft();
|
||||
clearScreenLower();
|
||||
}
|
||||
columns().clear();
|
||||
if (isRunning()) unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::stop(bool clear) {
|
||||
PIThread::stop(true);
|
||||
if (clear) clearScreen();
|
||||
moveTo(0, max_y + 4);
|
||||
showCursor();
|
||||
couts(fstr(Normal));
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hOut, PRIVATE->smode);
|
||||
SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->dattr);
|
||||
#endif
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::fstr(FormatFlags f) {
|
||||
num_format = systime_format = 0;
|
||||
if (f[PIConsole::Dec]) num_format = 0;
|
||||
if (f[PIConsole::Hex]) num_format = 1;
|
||||
if (f[PIConsole::Oct]) num_format = 2;
|
||||
if (f[PIConsole::Bin]) num_format = 4;
|
||||
if (f[PIConsole::Scientific]) num_format = 3;
|
||||
if (f[PIConsole::SystemTimeSplit]) systime_format = 0;
|
||||
if (f[PIConsole::SystemTimeSeconds]) systime_format = 1;
|
||||
|
||||
#ifdef WINDOWS
|
||||
WORD attr = 0;
|
||||
|
||||
if (f[PIConsole::Inverse]) {
|
||||
if (f[PIConsole::Red]) attr |= BACKGROUND_RED;
|
||||
if (f[PIConsole::Green]) attr |= BACKGROUND_GREEN;
|
||||
if (f[PIConsole::Blue]) attr |= BACKGROUND_BLUE;
|
||||
if (f[PIConsole::Yellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN);
|
||||
if (f[PIConsole::Magenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::Cyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::White]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackRed]) attr |= FOREGROUND_RED;
|
||||
if (f[PIConsole::BackGreen]) attr |= FOREGROUND_GREEN;
|
||||
if (f[PIConsole::BackBlue]) attr |= FOREGROUND_BLUE;
|
||||
if (f[PIConsole::BackYellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
if (f[PIConsole::BackMagenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackCyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackWhite]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if ((attr & BACKGROUND_RED) + (attr & BACKGROUND_GREEN) + (attr & BACKGROUND_BLUE) == 0)
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
} else {
|
||||
if (f[PIConsole::Red]) attr |= FOREGROUND_RED;
|
||||
if (f[PIConsole::Green]) attr |= FOREGROUND_GREEN;
|
||||
if (f[PIConsole::Blue]) attr |= FOREGROUND_BLUE;
|
||||
if (f[PIConsole::Yellow]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN);
|
||||
if (f[PIConsole::Magenta]) attr |= (FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::Cyan]) attr |= (FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::White]) attr |= (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (f[PIConsole::BackRed]) attr |= BACKGROUND_RED;
|
||||
if (f[PIConsole::BackGreen]) attr |= BACKGROUND_GREEN;
|
||||
if (f[PIConsole::BackBlue]) attr |= BACKGROUND_BLUE;
|
||||
if (f[PIConsole::BackYellow]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN);
|
||||
if (f[PIConsole::BackMagenta]) attr |= (BACKGROUND_RED | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackCyan]) attr |= (BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if (f[PIConsole::BackWhite]) attr |= (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
||||
if ((attr & FOREGROUND_RED) + (attr & FOREGROUND_GREEN) + (attr & FOREGROUND_BLUE) == 0)
|
||||
attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
}
|
||||
if (f[PIConsole::Bold]) attr |= FOREGROUND_INTENSITY;
|
||||
if (f[PIConsole::Underline]) attr |= COMMON_LVB_UNDERSCORE;
|
||||
|
||||
SetConsoleTextAttribute(PRIVATE->hOut, attr);
|
||||
return PIString();
|
||||
#else
|
||||
PIString ts("\e[0");
|
||||
|
||||
if (f[PIConsole::Bold]) ts += ";1";
|
||||
if (f[PIConsole::Faint]) ts += ";2";
|
||||
if (f[PIConsole::Italic]) ts += ";3";
|
||||
if (f[PIConsole::Underline]) ts += ";4";
|
||||
if (f[PIConsole::Blink]) ts += ";5";
|
||||
if (f[PIConsole::Inverse]) ts += ";7";
|
||||
|
||||
if (f[PIConsole::Black]) ts += ";30";
|
||||
if (f[PIConsole::Red]) ts += ";31";
|
||||
if (f[PIConsole::Green]) ts += ";32";
|
||||
if (f[PIConsole::Yellow]) ts += ";33";
|
||||
if (f[PIConsole::Blue]) ts += ";34";
|
||||
if (f[PIConsole::Magenta]) ts += ";35";
|
||||
if (f[PIConsole::Cyan]) ts += ";36";
|
||||
if (f[PIConsole::White]) ts += ";37";
|
||||
|
||||
if (f[PIConsole::BackBlack]) ts += ";40";
|
||||
if (f[PIConsole::BackRed]) ts += ";41";
|
||||
if (f[PIConsole::BackGreen]) ts += ";42";
|
||||
if (f[PIConsole::BackYellow]) ts += ";43";
|
||||
if (f[PIConsole::BackBlue]) ts += ";44";
|
||||
if (f[PIConsole::BackMagenta]) ts += ";45";
|
||||
if (f[PIConsole::BackCyan]) ts += ";46";
|
||||
if (f[PIConsole::BackWhite]) ts += ";47";
|
||||
|
||||
return ts + "m";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline int PIConsole::couts(const bool v) {return (v ? printf("true") : printf("false"));}
|
||||
inline int PIConsole::couts(const char v) {return printf("%c", v);}
|
||||
inline int PIConsole::couts(const short v) {
|
||||
switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hd", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const int v) {
|
||||
switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%d", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const long v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%ld", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const llong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lld", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const uchar v) {
|
||||
switch (num_format) {case (1): return printf("0x%.2X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 1)); break; default: return printf("%u", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ushort v) {
|
||||
switch (num_format) {case (1): return printf("0x%.4hX", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 2)); break; default: return printf("%hu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const uint v) {
|
||||
switch (num_format) {case (1): return printf("0x%.8X", v); break; case (2): return printf("%o", v); break; case (4): return printf("%s", toBin(&v, 4)); break; default: return printf("%u", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ulong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16lX", v); break; case (2): return printf("%lo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%lu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const ullong v) {
|
||||
switch (num_format) {case (1): return printf("0x%.16llX", v); break; case (2): return printf("%llo", v); break; case (4): return printf("%s", toBin(&v, sizeof(v))); break; default: return printf("%llu", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const float v) {
|
||||
switch (num_format) {case (3): return printf("%e", v); break; default: return printf("%.5g", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const double v) {
|
||||
switch (num_format) {case (3): return printf("%le", v); break; default: return printf("%.5lg", v); break;}
|
||||
}
|
||||
inline int PIConsole::couts(const PISystemTime & v) {
|
||||
switch (systime_format) {case (1): return printf("%.6lg", v.toSeconds()); break;
|
||||
default: return couts(v.seconds) + printf(" s, ") + couts(v.nanoseconds) + printf(" ns"); break;}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::toUpperLeft() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ulcoord);
|
||||
#else
|
||||
printf("\e[H");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::moveRight(int n) {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->getWinCoord(n));
|
||||
#else
|
||||
if (n > 0) printf("\e[%dC", n);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::moveLeft(int n) {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->getWinCoord(-n));
|
||||
#else
|
||||
if (n > 0) printf("\e[%dD", n);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::moveTo(int x, int y) {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->ccoord.X = x;
|
||||
PRIVATE->ccoord.Y = PRIVATE->ulcoord.Y + y;
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord);
|
||||
#else
|
||||
printf("\e[%d;%dH", y, x);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::clearScreen() {
|
||||
#ifdef WINDOWS
|
||||
couts(fstr(Normal));
|
||||
toUpperLeft();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written);
|
||||
#else
|
||||
couts(fstr(Normal)); printf("\e[H\e[J");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::clearScreenLower() {
|
||||
#ifdef WINDOWS
|
||||
couts(fstr(Normal));
|
||||
PRIVATE->getWinCurCoord();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
#else
|
||||
couts(fstr(Normal)); printf("\e[J");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::clearLine() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->getWinCurCoord();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
#else
|
||||
printf("\e[K");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::newLine() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->getWinCurCoord();
|
||||
PRIVATE->ccoord.X = 0; PRIVATE->ccoord.Y++;
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord);
|
||||
#else
|
||||
printf("\eE");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::hideCursor() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->curinfo.bVisible = false;
|
||||
SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
printf("\e[?25l");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIConsole::showCursor() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->curinfo.bVisible = true; SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
printf("\e[?25h");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::begin() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hOut, ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
#endif
|
||||
max_y = 0;
|
||||
__PICout_mutex__.lock();
|
||||
clearScreen();
|
||||
hideCursor();
|
||||
fillLabels();
|
||||
__PICout_mutex__.unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::run() {
|
||||
if (pause_) return;
|
||||
uint cx, clen = 0;
|
||||
int j;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi);
|
||||
width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left;
|
||||
height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
//fflush(0); return;
|
||||
__PICout_mutex__.lock();
|
||||
if (pwidth != width || pheight != height) {
|
||||
clearScreen();
|
||||
fillLabels();
|
||||
}
|
||||
pwidth = width;
|
||||
pheight = height;
|
||||
col_cnt = columns().size();
|
||||
col_wid = (col_cnt > 0) ? width / col_cnt : width;
|
||||
for (uint i = 0; i < col_cnt; ++i) {
|
||||
PIVector<Variable> & cvars(tabs[cur_tab].columns[i].variables);
|
||||
cx = col_wid * i;
|
||||
toUpperLeft();
|
||||
if (max_y < cvars.size()) max_y = cvars.size();
|
||||
j = 0;
|
||||
piForeachC (Variable & tv_, cvars) {
|
||||
if (j > height - 3) continue;
|
||||
j++;
|
||||
moveRight(cx);
|
||||
if (tv_.type == 15) {
|
||||
newLine();
|
||||
continue;
|
||||
}
|
||||
moveRight(tv_.offset);
|
||||
const void * ptr = 0;
|
||||
if (tv_.remote) {
|
||||
if (tv_.type == 0) {
|
||||
rstr.clear();
|
||||
rba = tv_.rdata;
|
||||
rba >> rstr;
|
||||
rstr.trim();
|
||||
ptr = &rstr;
|
||||
} else
|
||||
ptr = tv_.rdata.data();
|
||||
} else
|
||||
ptr = tv_.ptr;
|
||||
switch (tv_.type) {
|
||||
case 0: clen = printValue(ptr != 0 ? *(const PIString*)ptr : PIString(), tv_.format); break;
|
||||
case 1: clen = printValue(ptr != 0 ? *(const bool*)ptr : false, tv_.format); break;
|
||||
case 2: clen = printValue(ptr != 0 ? *(const int*)ptr : 0, tv_.format); break;
|
||||
case 3: clen = printValue(ptr != 0 ? *(const long*)ptr : 0l, tv_.format); break;
|
||||
case 4: clen = printValue(ptr != 0 ? *(const char*)ptr : char(0), tv_.format); break;
|
||||
case 5: clen = printValue(ptr != 0 ? *(const float*)ptr : 0.f, tv_.format); break;
|
||||
case 6: clen = printValue(ptr != 0 ? *(const double*)ptr : 0., tv_.format); break;
|
||||
case 7: clen = printValue(ptr != 0 ? *(const short*)ptr : short(0), tv_.format); break;
|
||||
case 8: clen = printValue(ptr != 0 ? *(const uint*)ptr : 0u, tv_.format); break;
|
||||
case 9: clen = printValue(ptr != 0 ? *(const ulong*)ptr : 0ul, tv_.format); break;
|
||||
case 10: clen = printValue(ptr != 0 ? *(const ushort*)ptr : ushort(0), tv_.format); break;
|
||||
case 11: clen = printValue(ptr != 0 ? *(const uchar*)ptr : uchar(0), tv_.format); break;
|
||||
case 12: clen = printValue(ptr != 0 ? *(const llong*)ptr : 0l, tv_.format); break;
|
||||
case 13: clen = printValue(ptr != 0 ? *(const ullong*)ptr: 0ull, tv_.format); break;
|
||||
case 20: clen = printValue(ptr != 0 ? *(const PISystemTime*)ptr: PISystemTime(), tv_.format); break;
|
||||
case 14: clen = printValue(bitsValue(ptr, tv_.bitFrom, tv_.bitCount), tv_.format); break;
|
||||
}
|
||||
if (clen + tv_.offset < (uint)col_wid) {
|
||||
PIString ts = PIString(
|
||||
#if defined(QNX) || defined(FREE_BSD)
|
||||
col_wid - clen - tv_.offset - 1, ' ');
|
||||
#else
|
||||
col_wid - clen - tv_.offset, ' ');
|
||||
#endif
|
||||
printf("%s", ts.data());
|
||||
}
|
||||
newLine();
|
||||
}
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
moveTo(0, max_y + 1);
|
||||
#else
|
||||
moveTo(0, max_y + 2);
|
||||
#endif
|
||||
fflush(0);
|
||||
__PICout_mutex__.unlock();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::fillLabels() {
|
||||
if (!isRunning()) return;
|
||||
uint cx, cy, mx = 0, dx;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi);
|
||||
width = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left;
|
||||
height = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
width = ws.ws_col;
|
||||
height = ws.ws_row;
|
||||
#endif
|
||||
max_y = 0;
|
||||
col_cnt = columns().size();
|
||||
col_wid = (col_cnt > 0) ? width / col_cnt : width;
|
||||
for (uint i = 0; i < col_cnt; ++i) {
|
||||
Column & ccol(tabs[cur_tab].columns[i]);
|
||||
PIVector<Variable> & cvars(ccol.variables);
|
||||
if (ccol.alignment != Nothing) {
|
||||
mx = 0;
|
||||
piForeachC (Variable & j, cvars)
|
||||
if (!j.isEmpty())
|
||||
if (mx < j.name.size())
|
||||
mx = j.name.size();
|
||||
mx += 2;
|
||||
}
|
||||
cx = col_wid * i;
|
||||
cy = 1;
|
||||
toUpperLeft();
|
||||
for (uint j = 0; j < cvars.size(); ++j) {
|
||||
if (int(j) > height - 3) continue;
|
||||
if (max_y < j) max_y = j;
|
||||
moveRight(cx);
|
||||
Variable & tv_(cvars[j]);
|
||||
cvars[j].nx = cx;
|
||||
cvars[j].ny = cy;
|
||||
if (tv_.name.isEmpty()) {
|
||||
cvars[j].offset = 0;
|
||||
clearLine();
|
||||
newLine();
|
||||
cy++;
|
||||
continue;
|
||||
}
|
||||
clearLine();
|
||||
//piCout << tv_.name << tv_.type << tv_.ptr;
|
||||
if (tv_.type == 15) {
|
||||
cvars[j].offset = cvars[j].name.length();
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printLine(tv_.name, cx, tv_.format);
|
||||
newLine();
|
||||
cy++;
|
||||
continue;
|
||||
}
|
||||
if (!tv_.isEmpty()) {
|
||||
switch (ccol.alignment) {
|
||||
case Nothing:
|
||||
cvars[j].offset = (tv_.name + ": ").length();
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printValue(tv_.name + ": ", tv_.format);
|
||||
break;
|
||||
case Left:
|
||||
cvars[j].offset = mx;
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
printValue(tv_.name + ": ", tv_.format);
|
||||
break;
|
||||
case Right:
|
||||
cvars[j].offset = mx;
|
||||
cvars[j].nx += cvars[j].offset;
|
||||
dx = mx - (tv_.name + ": ").length();
|
||||
moveRight(dx);
|
||||
printValue(tv_.name + ": ", tv_.format);
|
||||
moveLeft(dx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
newLine();
|
||||
cy++;
|
||||
}
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
moveTo(0, max_y + 1);
|
||||
#else
|
||||
moveTo(0, max_y + 2);
|
||||
#endif
|
||||
if (!tabs[cur_tab].status.isEmpty()) {
|
||||
printValue(tabs[cur_tab].status);
|
||||
newLine();
|
||||
}
|
||||
status();
|
||||
//couts(fstr(Normal));
|
||||
//fflush(0);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::status() {
|
||||
Tab * ctab;
|
||||
//clearLine();
|
||||
for (uint i = 0; i < tabsCount(); ++i) {
|
||||
ctab = &tabs[i];
|
||||
if (ctab->key == 0) continue;
|
||||
printValue(ctab->key, PIConsole::White | PIConsole::Bold);
|
||||
if (i == cur_tab)
|
||||
printValue(ctab->name + " ", PIConsole::BackYellow | PIConsole::Black);
|
||||
else
|
||||
printValue(ctab->name + " ", PIConsole::Cyan | PIConsole::Inverse);
|
||||
printValue(" ");
|
||||
}
|
||||
newLine();
|
||||
}
|
||||
|
||||
|
||||
int PIConsole::bitsValue(const void * src, int offset, int count) const {
|
||||
int ret = 0, stbyte = offset / 8, cbit = offset - stbyte * 8;
|
||||
char cbyte = reinterpret_cast<const char * >(src)[stbyte];
|
||||
for (int i = 0; i < count; i++) {
|
||||
ret |= ((cbyte >> cbit & 1) << i);
|
||||
cbit++;
|
||||
if (cbit == 8) {
|
||||
cbit = 0;
|
||||
stbyte++;
|
||||
cbyte = reinterpret_cast<const char * >(src)[stbyte];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const char * PIConsole::toBin(const void * d, int s) {
|
||||
binstr.clear();
|
||||
uchar cc, b;
|
||||
for (int i = 0; i < s; ++i) {
|
||||
cc = ((const uchar *)d)[i];
|
||||
b = 1;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
binstr << (cc & b ? "1" : "0");
|
||||
b <<= 1;
|
||||
}
|
||||
if (i < s - 1) binstr << " ";
|
||||
}
|
||||
binstr.reverse();
|
||||
return binstr.data();
|
||||
}
|
||||
|
||||
|
||||
#define ADD_VAR_BODY vid++; tv.id = vid; tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; tv.remote = false; checkColumn(col);
|
||||
|
||||
void PIConsole::addString(const PIString & name, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 15; tv.size = 0; tv.ptr = 0; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const PIString * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 0; tv.size = 0; tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const bool * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 1; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const int * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 2; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const long * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 3; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const char * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 4; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const float * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 5; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const double * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 6; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const short * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 7; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const uint * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 8; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ulong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 9; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ushort * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 10; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const uchar * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 11; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const llong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 12; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const ullong * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 13; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
void PIConsole::addVariable(const PIString & name, const PISystemTime * ptr, int col, FormatFlags format) {
|
||||
ADD_VAR_BODY tv.type = 20; tv.size = sizeof(*ptr); tv.ptr = ptr; column(col).push_back(tv);}
|
||||
/** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
* \details This function add to column "column" next lines:
|
||||
* * "protocol <name>"
|
||||
* * "Rec - receiverDeviceName": \a PIProtocol::receiverDeviceState
|
||||
* * "Send - senderDeviceName": \a PIProtocol::senderDeviceState
|
||||
* * "Received count": \a PIProtocol::receiveCount
|
||||
* * "Invalid count": \a PIProtocol::wrongCount
|
||||
* * "Missed count": \a PIProtocol::missedCount
|
||||
* * "Sended count": \a PIProtocol::sendCount
|
||||
* * "Immediate Frequency, Hz": \a PIProtocol::immediateFrequency
|
||||
* * "Integral Frequency, Hz": \a PIProtocol::integralFrequency
|
||||
* * "Receive speed": \a PIProtocol::receiveSpeed
|
||||
* * "Send speed": \a PIProtocol::sendSpeed
|
||||
* * "Receiver history size": \a PIProtocol::receiverHistorySize
|
||||
* * "Sender history size": \a PIProtocol::senderHistorySize
|
||||
* * "Disconnect Timeout, s": \a PIProtocol::disconnectTimeout
|
||||
* * "Quality": \a PIProtocol::quality
|
||||
* */
|
||||
void PIConsole::addVariable(const PIString & name, const PIProtocol * ptr, int col, FormatFlags format) {
|
||||
addString("protocol " + name, col, format | PIConsole::Bold);
|
||||
addVariable("Rec - " + ptr->receiverDeviceName(), ptr->receiverDeviceState_ptr(), col, format);
|
||||
addVariable("Send - " + ptr->senderDeviceName(), ptr->senderDeviceState_ptr(), col, format);
|
||||
addVariable("Received count", ptr->receiveCount_ptr(), col, format);
|
||||
addVariable("Invalid count", ptr->wrongCount_ptr(), col, format);
|
||||
addVariable("Missed count", ptr->missedCount_ptr(), col, format);
|
||||
addVariable("Sended count", ptr->sendCount_ptr(), col, format);
|
||||
addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format);
|
||||
addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format);
|
||||
addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format);
|
||||
addVariable("Send speed", ptr->sendSpeed_ptr(), col, format);
|
||||
addVariable("Disconnect Timeout, s", ptr->disconnectTimeout_ptr(), col, format);
|
||||
addVariable("Quality", ptr->quality_ptr(), col, format);
|
||||
}
|
||||
/** \brief Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
* \details This function add to column "column" next lines:
|
||||
* * "<name> diagnostics"
|
||||
* * "Received count": \a PIDiagnostics::receiveCount
|
||||
* * "Invalid count": \a PIDiagnostics::wrongCount
|
||||
* * "Sended count": \a PIDiagnostics::sendCount
|
||||
* * "Immediate Frequency, Hz": \a PIDiagnostics::immediateFrequency
|
||||
* * "Integral Frequency, Hz": \a PIDiagnostics::integralFrequency
|
||||
* * "Receive speed": \a PIDiagnostics::receiveSpeed
|
||||
* * "Send speed": \a PIDiagnostics::sendSpeed
|
||||
* * "Quality": \a PIDiagnostics::quality
|
||||
* */
|
||||
void PIConsole::addVariable(const PIString & name, const PIDiagnostics * ptr, int col, FormatFlags format) {
|
||||
addString(name + " diagnostics", col, format | PIConsole::Bold);
|
||||
addVariable("Received count", ptr->receiveCount_ptr(), col, format);
|
||||
addVariable("Invalid count", ptr->wrongCount_ptr(), col, format);
|
||||
addVariable("Sended count", ptr->sendCount_ptr(), col, format);
|
||||
addVariable("Immediate Frequency, Hz", ptr->immediateFrequency_ptr(), col, format);
|
||||
addVariable("Integral Frequency, Hz", ptr->integralFrequency_ptr(), col, format);
|
||||
addVariable("Receive speed", ptr->receiveSpeed_ptr(), col, format);
|
||||
addVariable("Send speed", ptr->sendSpeed_ptr(), col, format);
|
||||
addVariable("Quality", ptr->quality_ptr(), col, format);
|
||||
}
|
||||
void PIConsole::addVariable(const PIString & name, const PISystemMonitor * ptr, int col, FormatFlags format) {
|
||||
addString("monitor " + name, col, format | PIConsole::Bold);
|
||||
addVariable("PID", &(ptr->statistic().ID), col, format);
|
||||
//addVariable("state", &(ptr->statistic().state), col, format);
|
||||
addVariable("threads", &(ptr->statistic().threads), col, format);
|
||||
addVariable("priority", &(ptr->statistic().priority), col, format);
|
||||
addVariable("memory physical", &(ptr->statistic().physical_memsize_readable), col, format);
|
||||
addVariable("memory shared", &(ptr->statistic().share_memsize_readable), col, format);
|
||||
addVariable("cpu load kernel", &(ptr->statistic().cpu_load_system), col, format);
|
||||
addVariable("cpu load user", &(ptr->statistic().cpu_load_user), col, format);
|
||||
}
|
||||
void PIConsole::addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitCount, int col, FormatFlags format) {
|
||||
vid++; tv.id = vid; tv.size = sizeof(ullong); tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format;
|
||||
checkColumn(col); column(col).push_back(tv);}
|
||||
void PIConsole::addEmptyLine(int col, uint count) {
|
||||
tv.id = 0; tv.size = 0; tv.name = ""; tv.type = 0; tv.ptr = 0; tv.format = Normal;
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
checkColumn(col);
|
||||
column(col).push_back(tv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::getString(int x, int y) {
|
||||
bool run = isRunning();
|
||||
if (run) PIThread::stop(true);
|
||||
listener->setActive(false);
|
||||
msleep(50);
|
||||
#ifdef WINDOWS
|
||||
moveTo(x - 1, y - 1);
|
||||
#else
|
||||
moveTo(x, y);
|
||||
#endif
|
||||
showCursor();
|
||||
PIByteArray ba(4096);
|
||||
#ifdef CC_VC
|
||||
int ret = scanf_s(" %s", ba.data());
|
||||
#else
|
||||
int ret = scanf(" %s", ba.data());
|
||||
#endif
|
||||
listener->setActive(true);
|
||||
if (run) start();
|
||||
if (ret >= 1) return PIString(ba);
|
||||
else return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PIConsole::getString(const PIString & name) {
|
||||
piForeachC (Column & i, tabs[cur_tab].columns)
|
||||
piForeachC (Variable & j, i.variables)
|
||||
if (j.name == name)
|
||||
return getString(j.nx + 1, j.ny);
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
#define PRINT_VAR_BODY couts(fstr(format)); int ret = couts(value); couts(fstr(PIConsole::Dec)); return ret;
|
||||
|
||||
inline void PIConsole::printLine(const PIString & value, int dx, FormatFlags format) {
|
||||
int i = width - value.length() - dx;
|
||||
#if defined(QNX) || defined(FREE_BSD)
|
||||
--i;
|
||||
#endif
|
||||
PIString ts = fstr(format);
|
||||
couts(ts);
|
||||
if (i >= 0) ts = value + PIString(i, ' ');
|
||||
else ts = value.left(value.size() + i);
|
||||
couts(ts);
|
||||
couts(fstr(Dec));
|
||||
}
|
||||
inline int PIConsole::printValue(const PIString & value, FormatFlags format) {
|
||||
couts(fstr(format));
|
||||
int ret = couts(value);
|
||||
fstr(PIConsole::Dec);
|
||||
return ret;
|
||||
}
|
||||
inline int PIConsole::printValue(const char * value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const bool value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const int value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const long value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const llong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const float value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const double value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const char value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const short value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const uchar value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ushort value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const uint value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ulong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const ullong value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
inline int PIConsole::printValue(const PISystemTime & value, FormatFlags format) {PRINT_VAR_BODY}
|
||||
|
||||
|
||||
|
||||
void PIConsole::startServer(const PIString & name) {
|
||||
stopPeer();
|
||||
server_mode = true;
|
||||
peer = new PIPeer("_rcs_:" + name);
|
||||
CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived);
|
||||
CONNECT1(void, const PIString & , peer, peerDisconnectedEvent, this, peerDisconnectedEvent);
|
||||
peer_timer->start(50.);
|
||||
serverSendInfo();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::stopPeer() {
|
||||
remote_clients.clear();
|
||||
peer_timer->stop();
|
||||
if (peer != 0) delete peer;
|
||||
peer = 0;
|
||||
state = Disconnected;
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConsole::clients() const {
|
||||
PIStringList sl;
|
||||
if (peer == 0) return sl;
|
||||
piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) {
|
||||
if (i.name.left(6) != "_rcc_:") continue;
|
||||
sl << i.name.right(i.name.length() - 6);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::listenServers() {
|
||||
stopPeer();
|
||||
server_mode = false;
|
||||
server_name.clear();
|
||||
randomize();
|
||||
peer = new PIPeer("_rcc_:" + PIDateTime::current().toString("hhmmssddMMyy_") + PIString::fromNumber(randomi()));
|
||||
CONNECT2(void, const PIString & , const PIByteArray &, peer, dataReceivedEvent, this, peerReceived);
|
||||
peer_timer->start(100.);
|
||||
}
|
||||
|
||||
|
||||
PIStringList PIConsole::availableServers() const {
|
||||
PIStringList sl;
|
||||
if (peer == 0) return sl;
|
||||
piForeachC (PIPeer::PeerInfo & i, peer->allPeers()) {
|
||||
if (i.name.left(6) != "_rcs_:") continue;
|
||||
sl << i.name.right(i.name.length() - 6);
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::connectToServer(const PIString & name) {
|
||||
if (peer == 0) listenServers();
|
||||
server_name = name;
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::disconnect() {
|
||||
stopPeer();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::serverSendInfo() {
|
||||
if (peer == 0) return;
|
||||
PIByteArray ba;
|
||||
ba << int(0xAA);
|
||||
peer->sendToAll(ba);
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::serverSendData() {
|
||||
if (peer == 0) return;
|
||||
PIByteArray ba;
|
||||
PIVector<VariableContent> content;
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
if (!v.isEmpty() && v.id > 0) {
|
||||
VariableContent vc;
|
||||
vc.id = v.id;
|
||||
v.writeData(vc.rdata);
|
||||
content << vc;
|
||||
}
|
||||
piForeach (RemoteClient & rc, remote_clients) {
|
||||
ba.clear();
|
||||
switch (rc.state) {
|
||||
case FetchingData:
|
||||
ba << int(0xCC) << tabs;
|
||||
//piCout << "server send const data" << rc.name << ba.size_s();
|
||||
break;
|
||||
case Committing:
|
||||
ba << int(0xDD);
|
||||
break;
|
||||
case Connected:
|
||||
ba << int(0xEE) << content;
|
||||
//piCout << "send data" << ba.size();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (!ba.isEmpty())
|
||||
peer->send(rc.name, ba);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PIConsole::RemoteClient & PIConsole::remoteClient(const PIString & fname) {
|
||||
piForeach (RemoteClient & i, remote_clients)
|
||||
if (i.name == fname)
|
||||
return i;
|
||||
remote_clients << RemoteClient(fname);
|
||||
return remote_clients.back();
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerReceived(const PIString & from, const PIByteArray & data) {
|
||||
int type;
|
||||
PIByteArray ba(data);
|
||||
ba >> type;
|
||||
//piCout << "rec packet from" << from << "type" << PICoutManipulators::Hex << type;
|
||||
if (server_mode) {
|
||||
if (from.left(5) != "_rcc_") return;
|
||||
//PIString rcn = from.right(from.length() - 6);
|
||||
RemoteClient & rc(remoteClient(from));
|
||||
switch (type) {
|
||||
case 0xBB: // fetch const data request
|
||||
//piCout << "fetch data request from" << from << rc.state;
|
||||
if (rc.state != Connected)
|
||||
rc.state = FetchingData;
|
||||
break;
|
||||
case 0xCC: // const data commit
|
||||
//piCout << "commit from" << from;
|
||||
if (rc.state != Connected)
|
||||
rc.state = Connected;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
PIVector<VariableContent> content;
|
||||
PIMap<int, Variable * > vids;
|
||||
if (from.left(5) != "_rcs_") return;
|
||||
//PIString rcn = from.right(from.length() - 6);
|
||||
switch (type) {
|
||||
case 0xAA: // new server
|
||||
//piCout << "new server" << rcn;
|
||||
break;
|
||||
case 0xCC: // const data
|
||||
//piCout << "received const data";
|
||||
state = Committing;
|
||||
ba >> tabs;
|
||||
cur_tab = tabs.isEmpty() ? -1 : 0;
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
v.remote = true;
|
||||
break;
|
||||
case 0xDD: // const data commit
|
||||
//piCout << "received commit";
|
||||
state = Connected;
|
||||
break;
|
||||
case 0xEE: // dynamic data
|
||||
//piCout << "received data" << ba.size_s();
|
||||
piForeach (Tab & t, tabs)
|
||||
piForeach (Column & c, t.columns)
|
||||
piForeach (Variable & v, c.variables)
|
||||
if (!v.isEmpty() && v.id > 0)
|
||||
vids[v.id] = &v;
|
||||
ba >> content;
|
||||
piForeach (VariableContent & vc, content) {
|
||||
if (vc.id <= 0) continue;
|
||||
Variable * v = vids.at(vc.id);
|
||||
if (v == 0) continue;
|
||||
//piCout << "read" << v->name << vc.rdata.size_s();
|
||||
v->rdata = vc.rdata;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerTimer(void * data, int delim) {
|
||||
if (peer == 0) return;
|
||||
//piCout << "timer" << delim;
|
||||
if (server_mode) {
|
||||
if (delim == 20)
|
||||
serverSendInfo();
|
||||
else
|
||||
serverSendData();
|
||||
} else {
|
||||
if (delim != 1 || server_name.isEmpty()) return;
|
||||
const PIPeer::PeerInfo * p = peer->getPeerByName("_rcs_:" + server_name);
|
||||
if (p == 0) return;
|
||||
PIByteArray ba;
|
||||
switch (state) {
|
||||
case Disconnected:
|
||||
peer_tm.reset();
|
||||
ba << int(0xBB);
|
||||
//piCout << "send to" << server_name << "fetch request disc";
|
||||
peer->send(p, ba);
|
||||
state = FetchingData;
|
||||
break;
|
||||
case FetchingData:
|
||||
if (peer_tm.elapsed_s() < 3.)
|
||||
return;
|
||||
peer_tm.reset();
|
||||
ba << int(0xBB);
|
||||
//piCout << "send to" << server_name << "fetch request fd";
|
||||
peer->send(p, ba);
|
||||
break;
|
||||
case Committing:
|
||||
peer_tm.reset();
|
||||
ba << int(0xCC);
|
||||
//piCout << "send to" << server_name << "committing";
|
||||
state = Connected;
|
||||
peer->send(p, ba);
|
||||
break;
|
||||
default: break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIConsole::peerDisconnectedEvent(const PIString & name) {
|
||||
for (int i = 0; i < remote_clients.size_s(); ++i)
|
||||
if (remote_clients[i].name == name) {
|
||||
remote_clients.remove(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
475
src_main/console/piconsole.h
Normal file
475
src_main/console/piconsole.h
Normal file
@@ -0,0 +1,475 @@
|
||||
/*! \file piconsole.h
|
||||
* \brief Console output class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONSOLE_H
|
||||
#define PICONSOLE_H
|
||||
|
||||
#include "pikbdlistener.h"
|
||||
|
||||
class PIProtocol;
|
||||
class PIDiagnostics;
|
||||
class PISystemMonitor;
|
||||
class PIPeer;
|
||||
class PITimer;
|
||||
|
||||
class PIP_EXPORT PIConsole: public PIThread
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIConsole, PIThread)
|
||||
public:
|
||||
|
||||
//! Constructs %PIConsole with key handler "slot" and if "startNow" start it
|
||||
explicit PIConsole(bool startNow = true, PIKbdListener::KBFunc slot = 0);
|
||||
|
||||
~PIConsole();
|
||||
|
||||
|
||||
//! Variables output format
|
||||
enum Format {
|
||||
Normal /** Default console format */ = 0x01,
|
||||
Bold /** Bold text */ = 0x02,
|
||||
Faint = 0x04,
|
||||
Italic = 0x08,
|
||||
Underline /** Underlined text */ = 0x10,
|
||||
Blink /** Blinked text */ = 0x20,
|
||||
Inverse /** Swap text and background colors */ = 0x40,
|
||||
Black /** Black text */ = 0x100,
|
||||
Red /** Red text */ = 0x200,
|
||||
Green /** Green text */ = 0x400,
|
||||
Yellow /** Yellow text */ = 0x800,
|
||||
Blue /** Blue text */ = 0x1000,
|
||||
Magenta /** Magenta text */ = 0x2000,
|
||||
Cyan /** Cyan text */ = 0x4000,
|
||||
White /** White text */ = 0x8000,
|
||||
BackBlack /** Black background */ = 0x10000,
|
||||
BackRed /** Red background */ = 0x20000,
|
||||
BackGreen /** Green background */ = 0x40000,
|
||||
BackYellow /** Yellow background */ = 0x80000,
|
||||
BackBlue /** Blue background */ = 0x100000,
|
||||
BackMagenta /** Magenta background */ = 0x200000,
|
||||
BackCyan /** Cyan background */ = 0x400000,
|
||||
BackWhite /** White background */ = 0x800000,
|
||||
Dec /** Decimal base for integers */ = 0x1000000,
|
||||
Hex /** Hexadecimal base for integers */ = 0x2000000,
|
||||
Oct /** Octal base for integers */ = 0x4000000,
|
||||
Bin /** Binary base for integers */ = 0x8000000,
|
||||
Scientific /** Scientific representation of floats */ = 0x10000000,
|
||||
SystemTimeSplit /** PISystemTime split representation (* s, * ns) */ = 0x20000000,
|
||||
SystemTimeSeconds /** PISystemTime seconds representation (*.* s) */ = 0x40000000
|
||||
};
|
||||
|
||||
//! Column labels alignment
|
||||
enum Alignment {
|
||||
Nothing /** No alignment */ ,
|
||||
Left /** Labels align left and variables align left */ ,
|
||||
Right /** Labels align right and variables align left */
|
||||
};
|
||||
|
||||
typedef PIFlags<PIConsole::Format> FormatFlags;
|
||||
|
||||
//! Add to current tab to column "column" string "name" with format "format"
|
||||
void addString(const PIString & name, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const PIString * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const char * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const bool * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const short * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const int * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const long * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const llong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const uchar * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ushort * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const uint * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ulong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const ullong * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const float * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const double * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" variable with label "name", pointer "ptr" and format "format"
|
||||
void addVariable(const PIString & name, const PISystemTime * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
void addVariable(const PIString & name, const PIProtocol * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
void addVariable(const PIString & name, const PIDiagnostics * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
void addVariable(const PIString & name, const PISystemMonitor * ptr, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" bits field with label "name", pointer "ptr" and format "format"
|
||||
void addBitVariable(const PIString & name, const void * ptr, int fromBit, int bitsCount, int column = 1, FormatFlags format = PIConsole::Normal);
|
||||
|
||||
//! Add to current tab to column "column" "count" empty lines
|
||||
void addEmptyLine(int column = 1, uint count = 1);
|
||||
|
||||
PIString getString(int x, int y);
|
||||
short getShort(int x, int y) {return getString(x, y).toShort();}
|
||||
int getInt(int x, int y) {return getString(x, y).toInt();}
|
||||
float getFloat(int x, int y) {return getString(x, y).toFloat();}
|
||||
double getDouble(int x, int y) {return getString(x, y).toDouble();}
|
||||
PIString getString(const PIString & name);
|
||||
short getShort(const PIString & name) {return getString(name).toShort();}
|
||||
int getInt(const PIString & name) {return getString(name).toInt();}
|
||||
float getFloat(const PIString & name) {return getString(name).toFloat();}
|
||||
double getDouble(const PIString & name) {return getString(name).toDouble();}
|
||||
|
||||
|
||||
//! Returns tabs count
|
||||
uint tabsCount() const {return tabs.size();}
|
||||
|
||||
//! Returns current tab name
|
||||
PIString currentTab() const {return tabs[cur_tab].name;}
|
||||
|
||||
//! Returns current tab index
|
||||
int currentTabIndex() const {return cur_tab;}
|
||||
|
||||
//! Add new tab with name "name", bind key "bind_key" and returns this tab index
|
||||
int addTab(const PIString & name, char bind_key = 0);
|
||||
|
||||
//! Remove tab with index "index"
|
||||
void removeTab(uint index);
|
||||
|
||||
//! Remove tab with name "name"
|
||||
void removeTab(const PIString & name);
|
||||
|
||||
//! Clear content of tab with index "index"
|
||||
void clearTab(uint index);
|
||||
|
||||
//! Clear content of tab with name "name"
|
||||
void clearTab(const PIString & name);
|
||||
|
||||
//! Set current tab to tab with index "index", returns if tab exists
|
||||
bool setTab(uint index);
|
||||
|
||||
//! Set current tab to tab with name "name", returns if tab exists
|
||||
bool setTab(const PIString & name);
|
||||
|
||||
//! Set tab with index "index" bind key to "bind_key", returns if tab exists
|
||||
bool setTabBindKey(uint index, char bind_key);
|
||||
|
||||
//! Set tab with name "name" bind key to "bind_key", returns if tab exists
|
||||
bool setTabBindKey(const PIString & name, char bind_key);
|
||||
|
||||
//! Remove all tabs and if "clearScreen" clear the screen
|
||||
void clearTabs(bool clearScreen = true) {if (clearScreen && isRunning()) {toUpperLeft(); clearScreenLower();} tabs.clear();}
|
||||
|
||||
|
||||
//! Set custom status text of current tab to "str"
|
||||
void addCustomStatus(const PIString & str) {tabs[cur_tab].status = str;}
|
||||
|
||||
//! Clear custom status text of current tab
|
||||
void clearCustomStatus() {tabs[cur_tab].status.clear();}
|
||||
|
||||
//! Returns default alignment
|
||||
Alignment defaultAlignment() const {return def_align;}
|
||||
|
||||
//! Set default alignment to "align"
|
||||
void setDefaultAlignment(Alignment align) {def_align = align;}
|
||||
|
||||
//! Set column "col" alignment to "align"
|
||||
void setColumnAlignment(int col, Alignment align) {if (col < 0 || col >= columns().size_s()) return; column(col).alignment = align;}
|
||||
|
||||
//! Set all columns of all tabs alignment to "align"
|
||||
void setColumnAlignmentToAll(Alignment align) {piForeach (Tab & i, tabs) piForeach (Column & j, i.columns) j.alignment = align; fillLabels();}
|
||||
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void enableExitCapture(char key = 'Q') {listener->enableExitCapture(key);}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void disableExitCapture() {listener->disableExitCapture();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
bool exitCaptured() const {return listener->exitCaptured();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
char exitKey() const {return listener->exitKey();}
|
||||
|
||||
|
||||
int windowWidth() const {return width;}
|
||||
int windowHeight() const {return height;}
|
||||
|
||||
PIString fstr(FormatFlags f);
|
||||
void update();
|
||||
void pause(bool yes) {pause_ = yes;}
|
||||
|
||||
// Server functions
|
||||
void startServer(const PIString & name);
|
||||
void stopPeer();
|
||||
bool isServerStarted() const {return peer != 0;}
|
||||
PIStringList clients() const;
|
||||
|
||||
// Client functions
|
||||
void listenServers();
|
||||
PIStringList availableServers() const;
|
||||
PIString selectedServer() const {return server_name;}
|
||||
void connectToServer(const PIString & name);
|
||||
void disconnect();
|
||||
bool isConnected() const {return state == Connected;}
|
||||
|
||||
void toUpperLeft();
|
||||
void moveRight(int n = 1);
|
||||
void moveLeft(int n = 1);
|
||||
void moveTo(int x = 0, int y = 0);
|
||||
void clearScreen();
|
||||
void clearScreenLower();
|
||||
void clearLine();
|
||||
void newLine();
|
||||
void hideCursor();
|
||||
void showCursor();
|
||||
|
||||
EVENT_HANDLER0(void, clearVariables) {clearVariables(true);}
|
||||
EVENT_HANDLER1(void, clearVariables, bool, clearScreen);
|
||||
|
||||
EVENT_HANDLER0(void, waitForFinish) {WAIT_FOR_EXIT}
|
||||
EVENT_HANDLER0(void, start) {start(false);}
|
||||
EVENT_HANDLER1(void, start, bool, wait) {PIThread::start(40); if (wait) waitForFinish();}
|
||||
EVENT_HANDLER0(void, stop) {stop(false);}
|
||||
EVENT_HANDLER1(void, stop, bool, clear);
|
||||
|
||||
EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void * , data)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void waitForFinish()
|
||||
//! \brief block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void clearVariables(bool clearScreen = true)
|
||||
//! \brief Remove all columns at current tab and if "clearScreen" clear the screen
|
||||
|
||||
//! \fn void start(bool wait = false)
|
||||
//! \brief Start console output and if "wait" block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void stop(bool clear = false)
|
||||
//! \brief Stop console output and if "clear" clear the screen
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(PIKbdListener::KeyEvent key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is pointer to %PIConsole object
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
void begin();
|
||||
void run();
|
||||
void fillLabels();
|
||||
void status();
|
||||
void checkColumn(uint col) {while (columns().size() < col) columns().push_back(Column(def_align));}
|
||||
int bitsValue(const void * src, int offset, int count) const;
|
||||
const char * toBin(const void * d, int s);
|
||||
inline void printLine(const PIString & str, int dx = 0, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const PIString & str, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const char * str, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const bool value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const int value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const long value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const llong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const float value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const double value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const char value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const short value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const uchar value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ushort value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const uint value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ulong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const ullong value, FormatFlags format = PIConsole::Normal);
|
||||
inline int printValue(const PISystemTime & value, FormatFlags format = PIConsole::Normal);
|
||||
static void key_event(PIKbdListener::KeyEvent key, void * t);
|
||||
|
||||
struct Variable {
|
||||
Variable() {nx = ny = type = offset = bitFrom = bitCount = size = 0; format = Normal; remote = false; ptr = 0; id = 1;}
|
||||
Variable(const Variable & src) {remote = src.remote; name = src.name; format = src.format; type = src.type; offset = src.offset; size = src.size;
|
||||
bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr; nx = src.nx; ny = src.ny; rdata = src.rdata; id = src.id;}
|
||||
bool isEmpty() const {return (remote ? false : ptr == 0);}
|
||||
const void * data() {return (remote ? rdata.data() : ptr);}
|
||||
void writeData(PIByteArray & ba) {
|
||||
if (remote) ba << rdata;
|
||||
else {
|
||||
if (type == 0) ba << (*(PIString * )ptr);
|
||||
else ba << PIByteArray::RawData(ptr, size);
|
||||
}
|
||||
}
|
||||
PIString name;
|
||||
FormatFlags format;
|
||||
int nx;
|
||||
int ny;
|
||||
int type;
|
||||
int offset;
|
||||
int bitFrom;
|
||||
int bitCount;
|
||||
int size;
|
||||
int id;
|
||||
bool remote;
|
||||
const void * ptr;
|
||||
PIByteArray rdata;
|
||||
void operator =(const Variable & src) {remote = src.remote; name = src.name; format = src.format; type = src.type; offset = src.offset; size = src.size;
|
||||
bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr; nx = src.nx; ny = src.ny; rdata = src.rdata; id = src.id;}
|
||||
};
|
||||
|
||||
struct VariableContent {
|
||||
int id;
|
||||
PIByteArray rdata;
|
||||
};
|
||||
|
||||
struct Column {
|
||||
Column(Alignment align = PIConsole::Right) {variables.reserve(32); alignment = align;}
|
||||
PIVector<Variable> variables;
|
||||
Alignment alignment;
|
||||
uint size() const {return variables.size();}
|
||||
Variable & operator [](int index) {return variables[index];}
|
||||
const Variable & operator [](int index) const {return variables[index];}
|
||||
void push_back(const Variable & v) {variables.push_back(v);}
|
||||
void operator =(const Column & src) {variables = src.variables; alignment = src.alignment;}
|
||||
};
|
||||
|
||||
struct Tab {
|
||||
Tab(PIString n = "", char k = 0) {columns.reserve(8); name = n; key = k;}
|
||||
PIVector<Column> columns;
|
||||
PIString name;
|
||||
PIString status;
|
||||
char key;
|
||||
};
|
||||
|
||||
enum ConnectedState {Disconnected, FetchingData, Committing, Connected};
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v);
|
||||
|
||||
friend PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v);
|
||||
friend PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v);
|
||||
|
||||
PIVector<Column> & columns() {return tabs[cur_tab].columns;}
|
||||
Column & column(int index) {return tabs[cur_tab].columns[index - 1];}
|
||||
int couts(const PIString & v);
|
||||
int couts(const char * v);
|
||||
int couts(const bool v);
|
||||
int couts(const char v);
|
||||
int couts(const short v);
|
||||
int couts(const int v);
|
||||
int couts(const long v);
|
||||
int couts(const llong v);
|
||||
int couts(const uchar v);
|
||||
int couts(const ushort v);
|
||||
int couts(const uint v);
|
||||
int couts(const ulong v);
|
||||
int couts(const ullong v);
|
||||
int couts(const float v);
|
||||
int couts(const double v);
|
||||
int couts(const PISystemTime & v);
|
||||
|
||||
struct RemoteClient;
|
||||
|
||||
void serverSendInfo();
|
||||
void serverSendData();
|
||||
RemoteClient & remoteClient(const PIString & fname);
|
||||
EVENT_HANDLER2(void, peerReceived, const PIString &, from, const PIByteArray &, data);
|
||||
EVENT_HANDLER2(void, peerTimer, void * , data, int, delim);
|
||||
EVENT_HANDLER1(void, peerDisconnectedEvent, const PIString &, name);
|
||||
|
||||
PRIVATE_DECLARATION
|
||||
PIVector<Tab> tabs;
|
||||
PIString binstr, rstr;
|
||||
PIByteArray rba;
|
||||
Variable tv;
|
||||
PIKbdListener * listener;
|
||||
Alignment def_align;
|
||||
PIKbdListener::KBFunc ret_func;
|
||||
int width, height, pwidth, pheight, ret, col_wid, num_format, systime_format;
|
||||
uint max_y;
|
||||
int vid;
|
||||
uint cur_tab, col_cnt;
|
||||
|
||||
PIPeer * peer;
|
||||
PITimer * peer_timer;
|
||||
PITimeMeasurer peer_tm;
|
||||
PIString server_name;
|
||||
bool server_mode, pause_;
|
||||
ConnectedState state;
|
||||
|
||||
/*struct RemoteData {
|
||||
RemoteData() {msg_count = msg_rec = msg_send = 0;}
|
||||
void clear() {msg_count = msg_rec = msg_send = 0; data.clear();}
|
||||
bool isEmpty() const {return msg_count == 0;}
|
||||
bool isReadyRec() const {return msg_count == msg_rec;}
|
||||
bool isReadySend() const {return msg_count == msg_send;}
|
||||
void setData(const PIByteArray & ba) {data = ba; msg_rec = msg_send = 0; msg_count = (data.size_s() - 1) / 4096 + 1;}
|
||||
PIByteArray data;
|
||||
int msg_count;
|
||||
int msg_rec;
|
||||
int msg_send;
|
||||
};*/
|
||||
|
||||
struct RemoteClient {
|
||||
RemoteClient(const PIString & n = "") {name = n; state = Disconnected;}
|
||||
PIString name;
|
||||
ConnectedState state;
|
||||
};
|
||||
|
||||
PIVector<RemoteClient> remote_clients;
|
||||
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::VariableContent & v) {ba << v.id << v.rdata; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::VariableContent & v) {ba >> v.id; ba >> v.rdata; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Variable & v) {ba << v.name << v.id << (int)v.format << v.type << v.size << v.bitFrom << v.bitCount; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Variable & v) {ba >> v.name >> v.id >> (int & )v.format >> v.type >> v.size >> v.bitFrom >> v.bitCount; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Column & v) {ba << (int)v.alignment << v.variables; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Column & v) {int a; ba >> a >> v.variables; v.alignment = (PIConsole::Alignment)a; return ba;}
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const PIConsole::Tab & v) {ba << v.name << v.status << (uchar)v.key << v.columns; return ba;}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, PIConsole::Tab & v) {ba >> v.name >> v.status >> (uchar&)v.key >> v.columns; return ba;}
|
||||
|
||||
#endif // PICONSOLE_H
|
||||
28
src_main/console/piconsolemodule.h
Normal file
28
src_main/console/piconsolemodule.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Module includes
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICONSOLEMODULE_H
|
||||
#define PICONSOLEMODULE_H
|
||||
|
||||
#include "pikbdlistener.h"
|
||||
#include "piconsole.h"
|
||||
#include "piscreen.h"
|
||||
#include "piscreentiles.h"
|
||||
|
||||
#endif // PICONSOLEMODULE_H
|
||||
356
src_main/console/pikbdlistener.cpp
Normal file
356
src_main/console/pikbdlistener.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Keyboard grabber for console
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "piincludes_p.h"
|
||||
#include "pikbdlistener.h"
|
||||
#ifndef WINDOWS
|
||||
# include <termios.h>
|
||||
#else
|
||||
# include <wincon.h>
|
||||
#endif
|
||||
|
||||
/** \class PIKbdListener
|
||||
* \brief Keyboard console input listener
|
||||
* \details This class provide listening of console keyboard input.
|
||||
* There is two ways to receive pressed key:
|
||||
* * external static function with format "void func(char key, void * data_)"
|
||||
* * event \a keyPressed()
|
||||
*
|
||||
* Also there is static variable \a exiting which by default is set to
|
||||
* \b false. If \a enableExitCapture() was called and listener was started
|
||||
* with function \a start(), this variable will be set to \b true if exit
|
||||
* key will be pressed. By default exit key is 'Q' = shift + 'q'.
|
||||
* To wait for this variable changes to \b true there is WAIT_FOR_EXIT macro
|
||||
* \snippet pikbdlistener.cpp main
|
||||
* */
|
||||
|
||||
bool PIKbdListener::exiting;
|
||||
PIKbdListener * PIKbdListener::_object = 0;
|
||||
|
||||
|
||||
#ifndef WINDOWS
|
||||
// unix
|
||||
const PIKbdListener::EscSeq PIKbdListener::esc_seq[] = {
|
||||
{"OA", PIKbdListener::UpArrow, 0, 0 , 1 },
|
||||
{"OA", PIKbdListener::UpArrow, 4, 0 , 0 },
|
||||
{"[1A", PIKbdListener::UpArrow, 0, 0 , 0 },
|
||||
{"[A", PIKbdListener::UpArrow, 0, vt_all , 0 },
|
||||
{"OB", PIKbdListener::DownArrow, 0, 0 , 1 },
|
||||
{"OB", PIKbdListener::DownArrow, 4, 0 , 0 },
|
||||
{"[1B", PIKbdListener::DownArrow, 0, 0 , 0 },
|
||||
{"[B", PIKbdListener::DownArrow, 0, vt_all , 0 },
|
||||
{"OC", PIKbdListener::RightArrow, 0, 0 , 1 },
|
||||
{"OC", PIKbdListener::RightArrow, 4, 0 , 0 },
|
||||
{"[1C", PIKbdListener::RightArrow, 0, 0 , 0 },
|
||||
{"[C", PIKbdListener::RightArrow, 0, vt_all , 0 },
|
||||
{"OD", PIKbdListener::LeftArrow, 0, 0 , 1 },
|
||||
{"OD", PIKbdListener::LeftArrow, 4, 0 , 0 },
|
||||
{"[1D", PIKbdListener::LeftArrow, 0, 0 , 0 },
|
||||
{"[D", PIKbdListener::LeftArrow, 0, vt_all , 0 },
|
||||
{"[H", PIKbdListener::Home, 0, vt_xterm , 0 },
|
||||
{"[1H", PIKbdListener::Home, 0, 0 , 0 },
|
||||
{"OH", PIKbdListener::Home, 0, 0 , 1 },
|
||||
{"[1~", PIKbdListener::Home, 0, vt_linux , 0 },
|
||||
{"[F", PIKbdListener::End, 0, vt_xterm , 0 },
|
||||
{"[1F", PIKbdListener::End, 0, 0 , 0 },
|
||||
{"OF", PIKbdListener::End, 0, 0 , 1 },
|
||||
{"[4~", PIKbdListener::End, 0, vt_linux , 0 },
|
||||
{"[2~", PIKbdListener::Insert, 0, vt_all , 0 },
|
||||
{"[3~", PIKbdListener::Delete, 0, vt_all , 0 },
|
||||
{"[5~", PIKbdListener::PageUp, 0, vt_all , 0 },
|
||||
{"[6~", PIKbdListener::PageDown, 0, vt_all , 0 },
|
||||
{"[Z", PIKbdListener::Tab, 1, vt_xterm , 0 },
|
||||
{"OP", PIKbdListener::F1, 0, vt_xterm , 0 },
|
||||
{"[[A", PIKbdListener::F1, 0, vt_linux , 0 },
|
||||
{"[11~", PIKbdListener::F1, 0, 0 , 0 },
|
||||
{"[25~", PIKbdListener::F1, 1, vt_linux , 0 },
|
||||
{"OQ", PIKbdListener::F2, 0, vt_xterm , 0 },
|
||||
{"[[B", PIKbdListener::F2, 0, vt_linux , 0 },
|
||||
{"[12~", PIKbdListener::F2, 0, 0 , 0 },
|
||||
{"[26~", PIKbdListener::F2, 1, vt_linux , 0 },
|
||||
{"OR", PIKbdListener::F3, 0, vt_xterm , 0 },
|
||||
{"[[C", PIKbdListener::F3, 0, vt_linux , 0 },
|
||||
{"[13~", PIKbdListener::F3, 0, 0 , 0 },
|
||||
{"[28~", PIKbdListener::F3, 1, vt_linux , 0 },
|
||||
{"OS", PIKbdListener::F4, 0, vt_xterm , 0 },
|
||||
{"[[D", PIKbdListener::F4, 0, vt_linux , 0 },
|
||||
{"[14~", PIKbdListener::F4, 0, 0 , 0 },
|
||||
{"[29~", PIKbdListener::F4, 1, vt_linux , 0 },
|
||||
{"[[E", PIKbdListener::F5, 0, vt_linux , 0 },
|
||||
{"OT", PIKbdListener::F5, 0, 0 , 0 },
|
||||
{"[15~", PIKbdListener::F5, 0, vt_xterm , 0 },
|
||||
{"[31~", PIKbdListener::F5, 1, vt_linux , 0 },
|
||||
{"OU", PIKbdListener::F6, 0, 0 , 0 },
|
||||
{"[17~", PIKbdListener::F6, 0, vt_all , 0 },
|
||||
{"[32~", PIKbdListener::F6, 1, vt_linux , 0 },
|
||||
{"OV", PIKbdListener::F7, 0, 0 , 0 },
|
||||
{"[18~", PIKbdListener::F7, 0, vt_all , 0 },
|
||||
{"[33~", PIKbdListener::F7, 1, vt_linux , 0 },
|
||||
{"OW", PIKbdListener::F8, 0, 0 , 0 },
|
||||
{"[19~", PIKbdListener::F8, 0, vt_all , 0 },
|
||||
{"[34~", PIKbdListener::F8, 1, vt_linux , 0 },
|
||||
{"OX", PIKbdListener::F9, 0, 0 , 0 },
|
||||
{"[20~", PIKbdListener::F9, 0, vt_all , 0 },
|
||||
{"[35~", PIKbdListener::F9, 1, vt_linux , 0 },
|
||||
{"OY", PIKbdListener::F10, 0, 0 , 0 },
|
||||
{"[21~", PIKbdListener::F10, 0, vt_all , 0 },
|
||||
{"[36~", PIKbdListener::F10, 1, vt_linux , 0 },
|
||||
{"OZ", PIKbdListener::F11, 0, 0 , 0 },
|
||||
{"[23~", PIKbdListener::F11, 0, vt_all , 0 },
|
||||
{"O[", PIKbdListener::F12, 0, 0 , 0 },
|
||||
{"[24~", PIKbdListener::F12, 0, vt_all , 0 },
|
||||
// End
|
||||
{0, 0, 0, 0, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
PRIVATE_DEFINITION_START(PIKbdListener)
|
||||
#ifdef WINDOWS
|
||||
void * hIn;
|
||||
DWORD smode, tmode;
|
||||
#else
|
||||
struct termios sterm, tterm;
|
||||
#endif
|
||||
#ifdef WINDOWS
|
||||
DWORD
|
||||
#else
|
||||
int
|
||||
#endif
|
||||
ret;
|
||||
PRIVATE_DEFINITION_END(PIKbdListener)
|
||||
|
||||
|
||||
PIKbdListener::PIKbdListener(KBFunc slot, void * _d, bool startNow): PIThread() {
|
||||
setName("keyboard_listener");
|
||||
_object = this;
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(PRIVATE->hIn, &PRIVATE->smode);
|
||||
#else
|
||||
tcgetattr(0, &PRIVATE->sterm);
|
||||
#endif
|
||||
is_active = true;
|
||||
ret_func = slot;
|
||||
kbddata_ = _d;
|
||||
PIKbdListener::exiting = exit_enabled = false;
|
||||
if (startNow) start();
|
||||
}
|
||||
|
||||
|
||||
PIKbdListener::~PIKbdListener() {
|
||||
terminate();
|
||||
end();
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::begin() {
|
||||
#ifdef WINDOWS
|
||||
GetConsoleMode(PRIVATE->hIn, &PRIVATE->tmode);
|
||||
SetConsoleMode(PRIVATE->hIn, ENABLE_PROCESSED_INPUT);
|
||||
#else
|
||||
struct termios term;
|
||||
tcgetattr(0, &term);
|
||||
term.c_lflag &= ~(ECHO | ICANON);
|
||||
term.c_lflag |= NOFLSH | IEXTEN;
|
||||
PRIVATE->tterm = term;
|
||||
tcsetattr(0, TCSANOW, &term);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::readKeyboard() {
|
||||
ke.key = 0;
|
||||
ke.modifiers = 0;
|
||||
memset(rc, 0, 8);
|
||||
#ifdef WINDOWS
|
||||
INPUT_RECORD ir;
|
||||
ReadConsoleInput(PRIVATE->hIn, &ir, 1, &(PRIVATE->ret));
|
||||
if (ir.EventType == KEY_EVENT) {
|
||||
KEY_EVENT_RECORD ker = ir.Event.KeyEvent;
|
||||
if (ker.bKeyDown) {
|
||||
bool ctrl = ker.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
|
||||
bool shift = ker.dwControlKeyState & SHIFT_PRESSED;
|
||||
bool alt = ker.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED);
|
||||
if (ctrl) ke.modifiers |= Ctrl;
|
||||
if (shift) ke.modifiers |= Shift;
|
||||
if (alt) ke.modifiers |= Alt;
|
||||
//if (meta) ke.modifiers |= Meta;
|
||||
if (ker.dwControlKeyState & CAPSLOCK_ON) shift = !shift;
|
||||
//piCout << "key" << int(ker.wVirtualKeyCode) << int(ker.uChar.AsciiChar);
|
||||
switch (ker.wVirtualKeyCode) {
|
||||
case 8: PRIVATE->ret = 1; ke.key = Backspace; break;
|
||||
case 33: PRIVATE->ret = 1; ke.key = PageUp; break;
|
||||
case 34: PRIVATE->ret = 1; ke.key = PageDown; break;
|
||||
case 35: PRIVATE->ret = 1; ke.key = End; break;
|
||||
case 36: PRIVATE->ret = 1; ke.key = Home; break;
|
||||
case 37: PRIVATE->ret = 1; ke.key = LeftArrow; break;
|
||||
case 38: PRIVATE->ret = 1; ke.key = UpArrow; break;
|
||||
case 39: PRIVATE->ret = 1; ke.key = RightArrow; break;
|
||||
case 40: PRIVATE->ret = 1; ke.key = DownArrow; break;
|
||||
case 45: PRIVATE->ret = 1; ke.key = Insert; break;
|
||||
case 46: PRIVATE->ret = 1; ke.key = Delete; break;
|
||||
case '\r':
|
||||
case '\n': PRIVATE->ret = 1; ke.key = Return; break;
|
||||
case ' ': PRIVATE->ret = 1; ke.key = Space; break;
|
||||
case '\t': PRIVATE->ret = 1; ke.key = Tab; break;
|
||||
case 112: PRIVATE->ret = 1; ke.key = F1; break;
|
||||
case 113: PRIVATE->ret = 1; ke.key = F2; break;
|
||||
case 114: PRIVATE->ret = 1; ke.key = F3; break;
|
||||
case 115: PRIVATE->ret = 1; ke.key = F4; break;
|
||||
case 116: PRIVATE->ret = 1; ke.key = F5; break;
|
||||
case 117: PRIVATE->ret = 1; ke.key = F6; break;
|
||||
case 118: PRIVATE->ret = 1; ke.key = F7; break;
|
||||
case 119: PRIVATE->ret = 1; ke.key = F8; break;
|
||||
case 120: PRIVATE->ret = 1; ke.key = F9; break;
|
||||
case 121: PRIVATE->ret = 1; ke.key = F10; break;
|
||||
case 122: PRIVATE->ret = 1; ke.key = F11; break;
|
||||
case 123: PRIVATE->ret = 1; ke.key = F12; break;
|
||||
default:
|
||||
PRIVATE->ret = 1;
|
||||
rc[0] = 1;
|
||||
{
|
||||
PIChar ch = PIChar::fromConsole(ker.uChar.AsciiChar);
|
||||
if (shift) ch = ch.toUpper();
|
||||
ke.key = ch.unicode16Code();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ke.key == 0) {piMSleep(10); return;}
|
||||
} else {piMSleep(10); return;}
|
||||
} else {piMSleep(10); return;}
|
||||
/*if (lc == 0) {
|
||||
ReadConsole(hIn, &rc, 1, &(PRIVATE->ret), 0);
|
||||
//cout << "read console" << endl;
|
||||
lc = char(rc);
|
||||
}*/
|
||||
/*if (PRIVATE->ret < 0 || PRIVATE->ret > 3) return;
|
||||
lc = char(((uchar * )&rc)[PRIVATE->ret - 1]);
|
||||
for (int i = 0; i < PRIVATE->ret; ++i)
|
||||
cout << std::hex << int(((uchar * )&rc)[i]) << ' ';
|
||||
cout << endl << std::hex << rc << endl;*/
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
||||
PRIVATE->ret = read(0, rc, 8);
|
||||
//piCout << "key" << PIString(rc).replaceAll("\e", "\\e");
|
||||
/*for (int i = 0; i < PRIVATE->ret; ++i)
|
||||
cout << std::hex << int(((uchar * )&rc)[i]) << ' ';
|
||||
cout << endl;
|
||||
for (int i = 0; i < PRIVATE->ret; ++i)
|
||||
cout << "'" << (char)(rc[i]) << "' ";
|
||||
cout << endl;*/
|
||||
if (rc[0] == 0) {piMSleep(10); return;}
|
||||
if (PRIVATE->ret < 0 || PRIVATE->ret > 7) {piMSleep(10); return;}
|
||||
if (PRIVATE->ret == 1) ke.key = PIChar::fromConsole(rc[0]).unicode16Code();
|
||||
int mod(0);
|
||||
// 2 - shift 1
|
||||
// 3 - alt 2
|
||||
// 4 - alt+shift 3
|
||||
// 5 - ctrl 4
|
||||
// 8 - ctrl+alt+shift 7
|
||||
if (rc[0] == '\e') { // search for Alt
|
||||
if (PRIVATE->ret == 2) {
|
||||
mod = 2;
|
||||
ke.key = PIChar::fromConsole(rc[1]).unicode16Code();
|
||||
} else {// escape-seq
|
||||
if (rc[1] == '\e') { // search for Alt
|
||||
for (int i = 1; i < 7; ++i) rc[i] = rc[i + 1];
|
||||
mod = 2;
|
||||
PRIVATE->ret--;
|
||||
}
|
||||
if (rc[1] == '[') {
|
||||
for (int i = 2; i < 7; ++i) // search for modifier
|
||||
if (rc[i] == ';') {
|
||||
mod |= rc[i + 1] - '0' - 1;
|
||||
for (int j = i; j < 6; ++j) rc[j] = rc[j + 2];
|
||||
rc[6] = rc[7] = 0;
|
||||
PRIVATE->ret -= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (PRIVATE->ret >= 3 && rc[1] == 'O') { // search for modifier (F1-F4)
|
||||
if (rc[2] >= '1' && rc[2] <= '8') {
|
||||
mod |= rc[2] - '0' - 1;
|
||||
for (int j = 2; j < 6; ++j) rc[j] = rc[j + 1];
|
||||
rc[7] = 0;
|
||||
PRIVATE->ret--;
|
||||
}
|
||||
}
|
||||
for (int i = 0; ; ++i) {
|
||||
if (!esc_seq[i].seq) break;
|
||||
//piCout << "search" << rc[1] << esc_seq[i].seq;
|
||||
if (strcmp(esc_seq[i].seq, &(rc[1])) == 0) {
|
||||
ke.key = esc_seq[i].key;
|
||||
mod |= esc_seq[i].mod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mod >= 0 && mod <= 15) {
|
||||
if (mod & 0x1) ke.modifiers |= Shift;
|
||||
if (mod & 0x2) ke.modifiers |= Alt;
|
||||
if (mod & 0x4) ke.modifiers |= Ctrl;
|
||||
//if (mod & 0x8) ke.modifiers |= Meta;
|
||||
}
|
||||
/*cout << "wo mods (" << mod << ")\n";
|
||||
for (int i = 0; i < PRIVATE->ret; ++i)
|
||||
cout << "'" << (char)(rc[i]) << "' ";
|
||||
cout << endl;*/
|
||||
}
|
||||
if (ke.key == 0 && PRIVATE->ret > 1)
|
||||
ke.key = PIChar(rc).unicode16Code();
|
||||
#endif
|
||||
if ((rc[0] == '\n' || rc[0] == '\r') && PRIVATE->ret == 1)
|
||||
ke.key = Return;
|
||||
if (exit_enabled && ke.key == exit_key) {
|
||||
PIKbdListener::exiting = true;
|
||||
return;
|
||||
}
|
||||
if (PRIVATE->ret > 0) {
|
||||
keyPressed(ke, kbddata_);
|
||||
if (ret_func != 0) ret_func(ke, kbddata_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::end() {
|
||||
//cout << "list end" << endl;
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIKbdListener::setActive(bool yes) {
|
||||
is_active = yes;
|
||||
if (is_active) {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->tmode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &PRIVATE->tterm);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hIn, PRIVATE->smode);
|
||||
#else
|
||||
tcsetattr(0, TCSANOW, &PRIVATE->sterm);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
195
src_main/console/pikbdlistener.h
Normal file
195
src_main/console/pikbdlistener.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*! \file pikbdlistener.h
|
||||
* \brief Keyboard console input listener
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Keyboard grabber for console
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIKBDLISTENER_H
|
||||
#define PIKBDLISTENER_H
|
||||
|
||||
#include "pithread.h"
|
||||
|
||||
#define WAIT_FOR_EXIT while (!PIKbdListener::exiting) piMSleep(5);
|
||||
|
||||
|
||||
class PIP_EXPORT PIKbdListener: public PIThread
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIKbdListener, PIThread)
|
||||
friend class PIConsole;
|
||||
friend class PITerminal;
|
||||
public:
|
||||
|
||||
//! Special keyboard keys
|
||||
enum SpecialKey {
|
||||
Tab /** Tab key */ = 0x09,
|
||||
Return /** Enter key */ = 0x0a,
|
||||
Esc /** Escape key */ = 0x1b,
|
||||
Space /** Space key */ = 0x20,
|
||||
Backspace /** Backspace key */ = 0x7f,
|
||||
UpArrow /** Up arrow key */ = -1,
|
||||
DownArrow /** Down arrow key */ = -2,
|
||||
RightArrow /** Right arrow key */ = -3,
|
||||
LeftArrow /** Left arrow key */ = -4,
|
||||
Home /** Home key */ = -5,
|
||||
End /** End key */ = -6,
|
||||
PageUp /** Page up key */ = -7,
|
||||
PageDown /** Page down key */ = -8,
|
||||
Insert /** Delete key */ = -9,
|
||||
Delete /** Delete key */ = -10,
|
||||
F1 /** F1 key */ = -11,
|
||||
F2 /** F2 key */ = -12,
|
||||
F3 /** F3 key */ = -13,
|
||||
F4 /** F4 key */ = -14,
|
||||
F5 /** F5 key */ = -15,
|
||||
F6 /** F6 key */ = -16,
|
||||
F7 /** F7 key */ = -17,
|
||||
F8 /** F8 key */ = -18,
|
||||
F9 /** F9 key */ = -19,
|
||||
F10 /** F10 key */ = -20,
|
||||
F11 /** F11 key */ = -21,
|
||||
F12 /** F12 key */ = -22
|
||||
};
|
||||
|
||||
//! Keyboard modifiers
|
||||
enum KeyModifier {
|
||||
Ctrl /** Control key */ = 0x1,
|
||||
Shift /** Shift key */ = 0x2,
|
||||
Alt /** Alt key */ = 0x4
|
||||
//Meta /** Meta (windows) key */ = 0x8
|
||||
};
|
||||
|
||||
typedef PIFlags<KeyModifier> KeyModifiers;
|
||||
|
||||
//! This struct contains information about pressed keyboard key
|
||||
struct KeyEvent {
|
||||
KeyEvent(int k = 0, KeyModifiers m = 0) {key = k; modifiers = m;}
|
||||
|
||||
//! Pressed key. It can be simple \b char or special key (see PIKbdListener::SpecialKey)
|
||||
int key;
|
||||
|
||||
//! Active keyboard modifiers. It contains PIKbdListener::KeyModifier bitfields
|
||||
KeyModifiers modifiers;
|
||||
};
|
||||
|
||||
typedef void (*KBFunc)(KeyEvent, void * );
|
||||
|
||||
|
||||
//! Constructs keyboard listener with external function "slot" and custom data "data"
|
||||
explicit PIKbdListener(KBFunc slot = 0, void * data = 0, bool startNow = true);
|
||||
|
||||
~PIKbdListener();
|
||||
|
||||
|
||||
//! Returns custom data
|
||||
void * data() {return kbddata_;}
|
||||
|
||||
//! Set custom data to "_data"
|
||||
void setData(void * _data) {kbddata_ = _data;}
|
||||
|
||||
//! Set external function to "slot"
|
||||
void setSlot(KBFunc slot) {ret_func = slot;}
|
||||
|
||||
//! Returns if exit key if awaiting
|
||||
bool exitCaptured() const {return exit_enabled;}
|
||||
|
||||
//! Returns exit key, default 'Q'
|
||||
int exitKey() const {return exit_key;}
|
||||
|
||||
void readKeyboard();
|
||||
|
||||
//! Returns if keyboard listening is active (not running!)
|
||||
bool isActive() {return is_active;}
|
||||
|
||||
EVENT_HANDLER( void, enableExitCapture) {enableExitCapture('Q');}
|
||||
EVENT_HANDLER1(void, enableExitCapture, int, key) {exit_enabled = true; exit_key = key;}
|
||||
EVENT_HANDLER(void, disableExitCapture) {exit_enabled = false;}
|
||||
EVENT_HANDLER(void, setActive) {setActive(true);}
|
||||
EVENT_HANDLER1(void, setActive, bool, yes);
|
||||
|
||||
EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void * , data)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void enableExitCapture(int key = 'Q')
|
||||
//! \brief Enable exit key "key" awaiting
|
||||
|
||||
//! \fn void disableExitCapture()
|
||||
//! \brief Disable exit key awaiting
|
||||
|
||||
//! \fn void setActive(bool yes = true)
|
||||
//! \brief Set keyboard listening is active or not
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(PIKbdListener::KeyEvent key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is custom data
|
||||
|
||||
//! \}
|
||||
|
||||
static bool exiting;
|
||||
static PIKbdListener * instance() {return _object;}
|
||||
|
||||
private:
|
||||
void begin();
|
||||
void run() {readKeyboard();}
|
||||
void end();
|
||||
|
||||
#ifndef WINDOWS
|
||||
struct EscSeq {
|
||||
const char * seq;
|
||||
int key;
|
||||
int mod;
|
||||
int vt;
|
||||
int flags;
|
||||
// 1 - shift
|
||||
// 2 - alt
|
||||
// 4 - ctrl
|
||||
};
|
||||
|
||||
enum VTType {
|
||||
vt_none,
|
||||
vt_xterm = 0x1,
|
||||
vt_linux = 0x2,
|
||||
vt_all = 0xFF
|
||||
};
|
||||
|
||||
static const EscSeq esc_seq[];
|
||||
#endif
|
||||
|
||||
PRIVATE_DECLARATION
|
||||
KBFunc ret_func;
|
||||
int exit_key;
|
||||
bool exit_enabled, is_active;
|
||||
void * kbddata_;
|
||||
char rc[8];
|
||||
KeyEvent ke;
|
||||
static PIKbdListener * _object;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIKbdListener::KeyEvent & v) {s << v.key << int(v.modifiers); return s;}
|
||||
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIKbdListener::KeyEvent & v) {int m(0); s >> v.key >> m; v.modifiers = m; return s;}
|
||||
|
||||
|
||||
#endif // PIKBDLISTENER_H
|
||||
577
src_main/console/piscreen.cpp
Normal file
577
src_main/console/piscreen.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "piscreen.h"
|
||||
#include "piincludes_p.h"
|
||||
#ifndef WINDOWS
|
||||
# include <sys/ioctl.h>
|
||||
# include <fcntl.h>
|
||||
# include <termios.h>
|
||||
#else
|
||||
# include <wincon.h>
|
||||
# ifndef COMMON_LVB_UNDERSCORE
|
||||
# define COMMON_LVB_UNDERSCORE 0x8000
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
using namespace PIScreenTypes;
|
||||
|
||||
extern PIMutex __PICout_mutex__;
|
||||
|
||||
|
||||
PRIVATE_DEFINITION_START(PIScreen::SystemConsole)
|
||||
#ifdef WINDOWS
|
||||
void * hOut;
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi, csbi;
|
||||
CONSOLE_CURSOR_INFO curinfo;
|
||||
COORD ccoord, ulcoord, bs, bc;
|
||||
SMALL_RECT srect;
|
||||
WORD dattr;
|
||||
DWORD smode, written;
|
||||
PIVector<CHAR_INFO> chars;
|
||||
#endif
|
||||
PRIVATE_DEFINITION_END(PIScreen::SystemConsole)
|
||||
|
||||
|
||||
PIScreen::SystemConsole::SystemConsole() {
|
||||
width = height = pwidth = pheight = 0;
|
||||
int w, h;
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->ulcoord.X = 0;
|
||||
PRIVATE->hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi);
|
||||
PRIVATE->dattr = PRIVATE->sbi.wAttributes;
|
||||
w = PRIVATE->sbi.srWindow.Right - PRIVATE->sbi.srWindow.Left;
|
||||
h = PRIVATE->sbi.srWindow.Bottom - PRIVATE->sbi.srWindow.Top;
|
||||
PRIVATE->ulcoord.Y = PRIVATE->sbi.srWindow.Top;
|
||||
GetConsoleMode(PRIVATE->hOut, &PRIVATE->smode);
|
||||
GetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
w = ws.ws_col;
|
||||
h = ws.ws_row;
|
||||
#endif
|
||||
resize(w, h);
|
||||
}
|
||||
|
||||
|
||||
PIScreen::SystemConsole::~SystemConsole() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hOut, PRIVATE->smode);
|
||||
SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->dattr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::begin() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleMode(PRIVATE->hOut, ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->sbi);
|
||||
PRIVATE->bc.X = 0;
|
||||
PRIVATE->bc.Y = 0;
|
||||
#endif
|
||||
clear();
|
||||
hideCursor();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::end() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleTextAttribute(PRIVATE->hOut, PRIVATE->dattr);
|
||||
#else
|
||||
printf("\e[0m");
|
||||
#endif
|
||||
moveTo(0, height);
|
||||
showCursor();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::prepare() {
|
||||
int w, h;
|
||||
#ifdef WINDOWS
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->csbi);
|
||||
w = PRIVATE->csbi.srWindow.Right - PRIVATE->csbi.srWindow.Left + 1;
|
||||
h = PRIVATE->csbi.srWindow.Bottom - PRIVATE->csbi.srWindow.Top + 1;
|
||||
#else
|
||||
winsize ws;
|
||||
ioctl(0, TIOCGWINSZ, &ws);
|
||||
w = ws.ws_col;
|
||||
h = ws.ws_row;
|
||||
#endif
|
||||
resize(w, h);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::clear() {
|
||||
for (int i = 0; i < cells.size_s(); ++i)
|
||||
cells[i].fill(Cell());
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::resize(int w, int h) {
|
||||
if (w == pwidth && h == pheight) return;
|
||||
width = piMaxi(w, 0);
|
||||
height = piMaxi(h, 0);
|
||||
pwidth = width;
|
||||
pheight = height;
|
||||
cells.resize(height);
|
||||
pcells.resize(height);
|
||||
for (int i = 0; i < height; ++i) {
|
||||
cells[i].resize(width);
|
||||
pcells[i].resize(width, Cell(0));
|
||||
}
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->sbi.srWindow = PRIVATE->csbi.srWindow;
|
||||
PRIVATE->chars.resize(width * height);
|
||||
#endif
|
||||
for (int i = 0; i < pcells.size_s(); ++i)
|
||||
pcells[i].fill(Cell());
|
||||
clear();
|
||||
clearScreen();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::print() {
|
||||
#ifdef WINDOWS
|
||||
//static int cnt = 0;
|
||||
PRIVATE->srect = PRIVATE->sbi.srWindow;
|
||||
int dx0 = -1, dx1 = -1, dy0 = -1, dy1 = -1;
|
||||
for (int j = 0; j < height; ++j) {
|
||||
PIVector<Cell> & ccv(cells[j]);
|
||||
PIVector<Cell> & pcv(pcells[j]);
|
||||
for (int i = 0; i < width; ++i)
|
||||
if (ccv[i] != pcv[i]) {
|
||||
if (dx0 < 0) {
|
||||
dx0 = dx1 = i;
|
||||
dy0 = dy1 = j;
|
||||
} else {
|
||||
dx0 = piMini(dx0, i);
|
||||
dx1 = piMaxi(dx1, i);
|
||||
dy0 = piMini(dy0, j);
|
||||
dy1 = piMaxi(dy1, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dx0 < 0) return;
|
||||
int dw = dx1 - dx0 + 1, dh = dy1 - dy0 + 1;
|
||||
for (int i = 0; i < dw; ++i)
|
||||
for (int j = 0; j < dh; ++j) {
|
||||
int k = j * dw + i;
|
||||
Cell & c(cells[j + dy0][i + dx0]);
|
||||
PRIVATE->chars[k].Char.UnicodeChar = 0;
|
||||
PRIVATE->chars[k].Char.AsciiChar = c.symbol.toConcole1Byte();
|
||||
//PRIVATE->chars[k].Char.UnicodeChar = c.symbol.toInt();
|
||||
PRIVATE->chars[k].Attributes = attributes(c);
|
||||
}
|
||||
//PRIVATE->bc.X = dx0;
|
||||
//PRIVATE->bc.Y = dy0;
|
||||
//piCout << "draw" << dw << dh;
|
||||
PRIVATE->bs.X = dw;
|
||||
PRIVATE->bs.Y = dh;
|
||||
PRIVATE->srect.Left += dx0;
|
||||
PRIVATE->srect.Top += dy0;
|
||||
PRIVATE->srect.Right -= width - dx1 - 1;
|
||||
PRIVATE->srect.Bottom -= height - dy1 - 1;
|
||||
WriteConsoleOutput(PRIVATE->hOut, PRIVATE->chars.data(), PRIVATE->bs, PRIVATE->bc, &PRIVATE->srect);
|
||||
#else
|
||||
PIString s;
|
||||
int si = 0, sj = 0;
|
||||
CellFormat prf(0xFFFFFFFF);
|
||||
for (int j = 0; j < height; ++j) {
|
||||
PIVector<Cell> & ccv(cells[j]);
|
||||
PIVector<Cell> & pcv(pcells[j]);
|
||||
for (int i = 0; i < width; ++i) {
|
||||
Cell & cc(ccv[i]);
|
||||
Cell & pc(pcv[i]);
|
||||
if (cc != pc) {
|
||||
if (s.isEmpty()) {
|
||||
si = i;
|
||||
sj = j;
|
||||
}
|
||||
if (prf != cc.format) {
|
||||
prf = cc.format;
|
||||
s += formatString(cc);
|
||||
}
|
||||
s += cc.symbol;
|
||||
} else {
|
||||
if (!s.isEmpty()) {
|
||||
moveTo(si, sj);
|
||||
printf("%s", s.data());
|
||||
s.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.isEmpty()) {
|
||||
moveTo(si, sj);
|
||||
printf("%s", s.data());
|
||||
s.clear();
|
||||
}
|
||||
/*for (int i = 0; i < width; ++i) {
|
||||
moveTo(i, j);
|
||||
printf("%s", (formatString(cells[j][i]) + cells[j][i].symbol).data());
|
||||
}*/
|
||||
}
|
||||
printf("\e[0m");
|
||||
fflush(0);
|
||||
#endif
|
||||
pcells = cells;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WINDOWS
|
||||
#define FOREGROUND_MASK (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||
#define BACKGROUND_MASK (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
|
||||
ushort PIScreen::SystemConsole::attributes(const PIScreenTypes::Cell & c) {
|
||||
WORD attr = PRIVATE->dattr;
|
||||
if (c.format.flags & Bold) attr |= FOREGROUND_INTENSITY;
|
||||
else attr &= ~FOREGROUND_INTENSITY;
|
||||
if (c.format.flags & Underline) attr |= COMMON_LVB_UNDERSCORE;
|
||||
else attr &= ~COMMON_LVB_UNDERSCORE;
|
||||
switch (c.format.color_char) {
|
||||
case Black: attr = (attr & ~FOREGROUND_MASK); break;
|
||||
case Red: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_RED; break;
|
||||
case Green: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_GREEN; break;
|
||||
case Blue: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_BLUE; break;
|
||||
case Cyan: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
|
||||
case Magenta: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_RED | FOREGROUND_BLUE; break;
|
||||
case Yellow: attr = (attr & ~FOREGROUND_MASK) | FOREGROUND_RED | FOREGROUND_GREEN; break;
|
||||
case White: attr = attr | FOREGROUND_MASK; break;
|
||||
}
|
||||
switch (c.format.color_back) {
|
||||
case Black: attr = (attr & ~BACKGROUND_MASK); break;
|
||||
case Red: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_RED; break;
|
||||
case Green: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_GREEN; break;
|
||||
case Blue: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_BLUE; break;
|
||||
case Cyan: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE; break;
|
||||
case Magenta: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_RED | BACKGROUND_BLUE; break;
|
||||
case Yellow: attr = (attr & ~BACKGROUND_MASK) | BACKGROUND_RED | BACKGROUND_GREEN; break;
|
||||
case White: attr = attr | BACKGROUND_MASK; break;
|
||||
}
|
||||
if ((c.format.flags & Inverse) == Inverse) {
|
||||
uchar f = attr & 0xFF;
|
||||
attr &= 0xFFFFFF00;
|
||||
f = (f << 4) | (f >> 4);
|
||||
attr |= f;
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
#undef FOREGROUND_MASK
|
||||
#undef BACKGROUND_MASK
|
||||
|
||||
void PIScreen::SystemConsole::getWinCurCoord() {
|
||||
GetConsoleScreenBufferInfo(PRIVATE->hOut, &PRIVATE->csbi);
|
||||
PRIVATE->ccoord = PRIVATE->csbi.dwCursorPosition;
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::clearLine() {
|
||||
getWinCurCoord();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width - PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::newLine() {
|
||||
getWinCurCoord();
|
||||
PRIVATE->ccoord.X = 0; PRIVATE->ccoord.Y++;
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord);
|
||||
}
|
||||
#else // WINDOWS
|
||||
PIString PIScreen::SystemConsole::formatString(const PIScreenTypes::Cell & c) {
|
||||
PIString ts("\e[0");
|
||||
switch (c.format.color_char) {
|
||||
case Black: ts += ";30"; break;
|
||||
case Red: ts += ";31"; break;
|
||||
case Green: ts += ";32"; break;
|
||||
case Blue: ts += ";34"; break;
|
||||
case Cyan: ts += ";36"; break;
|
||||
case Magenta: ts += ";35"; break;
|
||||
case Yellow: ts += ";33"; break;
|
||||
case White: ts += ";37"; break;
|
||||
}
|
||||
switch (c.format.color_back) {
|
||||
case Black: ts += ";40"; break;
|
||||
case Red: ts += ";41"; break;
|
||||
case Green: ts += ";42"; break;
|
||||
case Blue: ts += ";44"; break;
|
||||
case Cyan: ts += ";46"; break;
|
||||
case Magenta: ts += ";45"; break;
|
||||
case Yellow: ts += ";43"; break;
|
||||
case White: ts += ";47"; break;
|
||||
}
|
||||
if ((c.format.flags & Bold) == Bold) ts += ";1";
|
||||
if ((c.format.flags & Underline) == Underline) ts += ";4";
|
||||
if ((c.format.flags & Blink) == Blink) ts += ";5";
|
||||
if ((c.format.flags & Inverse) == Inverse) ts += ";7";
|
||||
return ts + "m";
|
||||
}
|
||||
#endif // WINDOWS
|
||||
|
||||
|
||||
void PIScreen::SystemConsole::toUpperLeft() {
|
||||
#ifdef WINDOWS
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ulcoord);
|
||||
#else
|
||||
printf("\e[H");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::moveTo(int x, int y) {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->ccoord.X = x;
|
||||
PRIVATE->ccoord.Y = PRIVATE->ulcoord.Y + y;
|
||||
SetConsoleCursorPosition(PRIVATE->hOut, PRIVATE->ccoord);
|
||||
#else
|
||||
printf("\e[%d;%dH", y + 1, x + 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::clearScreen() {
|
||||
#ifdef WINDOWS
|
||||
toUpperLeft();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * (height + 1), PRIVATE->ulcoord, &PRIVATE->written);
|
||||
#else
|
||||
printf("\e[0m\e[H\e[J");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::clearScreenLower() {
|
||||
#ifdef WINDOWS
|
||||
getWinCurCoord();
|
||||
FillConsoleOutputAttribute(PRIVATE->hOut, PRIVATE->dattr, width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
FillConsoleOutputCharacter(PRIVATE->hOut, ' ', width * height - width * PRIVATE->ccoord.Y + PRIVATE->ccoord.X, PRIVATE->ccoord, &PRIVATE->written);
|
||||
#else
|
||||
printf("\e[0m\e[J");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::hideCursor() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->curinfo.bVisible = false;
|
||||
SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
printf("\e[?25l");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PIScreen::SystemConsole::showCursor() {
|
||||
#ifdef WINDOWS
|
||||
PRIVATE->curinfo.bVisible = true;
|
||||
SetConsoleCursorInfo(PRIVATE->hOut, &PRIVATE->curinfo);
|
||||
#else
|
||||
printf("\e[?25h");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PIScreen::PIScreen(bool startNow, PIKbdListener::KBFunc slot): PIThread(), drawer_(console.cells), root("rootTile") {
|
||||
setName("screen");
|
||||
setPriority(piLow);
|
||||
needLockRun(true);
|
||||
ret_func = slot;
|
||||
tile_focus = tile_dialog = 0;
|
||||
root.screen = this;
|
||||
listener = new PIKbdListener(key_eventS, this, startNow);
|
||||
if (startNow) start();
|
||||
}
|
||||
|
||||
|
||||
PIScreen::~PIScreen() {
|
||||
if (isRunning())
|
||||
stop();
|
||||
PIThread::waitForFinish(10);
|
||||
listener->waitForFinish(10);
|
||||
delete listener;
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::key_event(PIKbdListener::KeyEvent key) {
|
||||
/** DEBUG
|
||||
if (ret_func != 0) ret_func(key, t);
|
||||
keyPressed(key, t);
|
||||
return;
|
||||
*/
|
||||
PIScreenTile * rtile = rootTile();
|
||||
if (tile_dialog)
|
||||
rtile = tile_dialog;
|
||||
bool used = nextFocus(rtile, key);
|
||||
if (used) return;
|
||||
if (!used && tile_focus) {
|
||||
if (tile_focus->visible) {
|
||||
if (tile_focus->keyEvent(key))
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ret_func != 0) ret_func(key, data_);
|
||||
keyPressed(key, data_);
|
||||
}
|
||||
|
||||
|
||||
bool PIScreen::nextFocus(PIScreenTile * rt, PIKbdListener::KeyEvent key) {
|
||||
PIVector<PIScreenTile*> vtl = rt->children(true), ftl;
|
||||
piForeach (PIScreenTile * t, vtl) {
|
||||
if (t->focus_flags[CanHasFocus])
|
||||
ftl << t;
|
||||
}
|
||||
int ind = -1;
|
||||
for (int i = 0; i < ftl.size_s(); ++i)
|
||||
if (ftl[i] == tile_focus) {
|
||||
ind = i;
|
||||
break;
|
||||
}
|
||||
if (ind < 0)
|
||||
tile_focus = 0;
|
||||
if (ftl.isEmpty())
|
||||
tile_focus = 0;
|
||||
else {
|
||||
if (tile_focus)
|
||||
if (!tile_focus->visible)
|
||||
tile_focus = 0;
|
||||
int next = tile_focus ? 0 : 1;
|
||||
if (tile_focus) {
|
||||
if (tile_focus->focus_flags[NextByTab] && key.key == PIKbdListener::Tab)
|
||||
next = 1;
|
||||
if (tile_focus->focus_flags[NextByArrowsHorizontal]) {
|
||||
if (key.key == PIKbdListener::LeftArrow) next = -1;
|
||||
if (key.key == PIKbdListener::RightArrow) next = 1;
|
||||
}
|
||||
if (tile_focus->focus_flags[NextByArrowsVertical]) {
|
||||
if (key.key == PIKbdListener::UpArrow) next = -1;
|
||||
if (key.key == PIKbdListener::DownArrow) next = 1;
|
||||
}
|
||||
}
|
||||
//piCout << ftl.size() << ind << next;
|
||||
if (next != 0) {
|
||||
PIVector<PIScreenTile*> tl = rt->children();
|
||||
piForeach (PIScreenTile * t, tl)
|
||||
t->has_focus = false;
|
||||
if (!ftl.isEmpty()) {
|
||||
ind += next;
|
||||
if (ind >= ftl.size_s()) ind = 0;
|
||||
if (ind < 0) ind = ftl.size_s() - 1;
|
||||
tile_focus = ftl[ind];
|
||||
tile_focus->has_focus = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::tileEventInternal(PIScreenTile * t, TileEvent e) {
|
||||
tileEvent(t, e);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::tileRemovedInternal(PIScreenTile * t) {
|
||||
if (tile_dialog == t)
|
||||
tile_dialog = 0;
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::tileSetFocusInternal(PIScreenTile * t) {
|
||||
PIScreenTile * rt = rootTile();
|
||||
if (tile_dialog)
|
||||
rt = tile_dialog;
|
||||
PIVector<PIScreenTile*> tl = rt->children(), ftl;
|
||||
piForeach (PIScreenTile * i, tl)
|
||||
i->has_focus = false;
|
||||
tile_focus = t;
|
||||
if (!tile_focus) return;
|
||||
if (tile_focus->focus_flags[CanHasFocus])
|
||||
tile_focus->has_focus = true;
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::setDialogTile(PIScreenTile * t) {
|
||||
tile_dialog = t;
|
||||
if (!tile_dialog) {
|
||||
nextFocus(&root);
|
||||
return;
|
||||
}
|
||||
tile_dialog->setScreen(this);
|
||||
tile_dialog->parent = 0;
|
||||
nextFocus(tile_dialog);
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::waitForFinish() {
|
||||
WAIT_FOR_EXIT
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::stop(bool clear) {
|
||||
PIThread::stop(true);
|
||||
if (clear) console.clearScreen();
|
||||
#ifndef WINDOWS
|
||||
fflush(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::begin() {
|
||||
listener->start();
|
||||
nextFocus(&root);
|
||||
console.begin();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::run() {
|
||||
console.prepare();
|
||||
root.width_ = drawer_.width = console.width;
|
||||
root.height_ = drawer_.height = console.height;
|
||||
root.layout();
|
||||
root.drawEventInternal(&drawer_);
|
||||
if (tile_dialog) {
|
||||
int sw(0), sh(0);
|
||||
tile_dialog->sizeHint(sw, sh);
|
||||
sw = piClampi(sw, tile_dialog->minimumWidth, tile_dialog->maximumWidth);
|
||||
sh = piClampi(sh, tile_dialog->minimumHeight, tile_dialog->maximumHeight);
|
||||
tile_dialog->x_ = (console.width - sw) / 2;
|
||||
tile_dialog->y_ = (console.height - sh) / 2;
|
||||
tile_dialog->width_ = sw;
|
||||
tile_dialog->height_ = sh;
|
||||
tile_dialog->layout();
|
||||
int dx = tile_dialog->x_ - 1, dy = tile_dialog->y_ - 1, dw = tile_dialog->width_, dh = tile_dialog->height_;
|
||||
drawer_.drawFrame(dx, dy, dx + dw + 1, dy + dh + 1, (Color)tile_dialog->back_format.color_char,
|
||||
(Color)tile_dialog->back_format.color_back, (CharFlags)tile_dialog->back_format.flags);
|
||||
tile_dialog->drawEventInternal(&drawer_);
|
||||
}
|
||||
console.print();
|
||||
}
|
||||
|
||||
|
||||
void PIScreen::end() {
|
||||
listener->stop();
|
||||
console.end();
|
||||
}
|
||||
|
||||
|
||||
PIScreenTile * PIScreen::tileByName(const PIString & name) {
|
||||
PIVector<PIScreenTile*> tl(tiles());
|
||||
piForeach (PIScreenTile * t, tl)
|
||||
if (t->name() == name)
|
||||
return t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
149
src_main/console/piscreen.h
Normal file
149
src_main/console/piscreen.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*! \file piscreen.h
|
||||
* \brief Console output class
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISCREEN_H
|
||||
#define PISCREEN_H
|
||||
|
||||
#include "piscreentile.h"
|
||||
#include "piscreendrawer.h"
|
||||
|
||||
|
||||
class PIP_EXPORT PIScreen: public PIThread, public PIScreenTypes::PIScreenBase
|
||||
{
|
||||
PIOBJECT_SUBCLASS(PIScreen, PIThread)
|
||||
class SystemConsole;
|
||||
public:
|
||||
|
||||
//! Constructs %PIScreen with key handler "slot" and if "startNow" start it
|
||||
PIScreen(bool startNow = true, PIKbdListener::KBFunc slot = 0);
|
||||
|
||||
~PIScreen();
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void enableExitCapture(int key = 'Q') {listener->enableExitCapture(key);}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
void disableExitCapture() {listener->disableExitCapture();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
bool exitCaptured() const {return listener->exitCaptured();}
|
||||
|
||||
//! Directly call function from \a PIKbdListener
|
||||
int exitKey() const {return listener->exitKey();}
|
||||
|
||||
int windowWidth() const {return console.width;}
|
||||
int windowHeight() const {return console.height;}
|
||||
|
||||
PIScreenTile * rootTile() {return &root;}
|
||||
PIScreenTile * tileByName(const PIString & name);
|
||||
|
||||
void setDialogTile(PIScreenTile * t);
|
||||
PIScreenTile * dialogTile() const {return tile_dialog;}
|
||||
|
||||
PIScreenDrawer * drawer() {return &drawer_;}
|
||||
void clear() {drawer_.clear();}
|
||||
|
||||
EVENT_HANDLER0(void, waitForFinish);
|
||||
EVENT_HANDLER0(void, start) {start(false);}
|
||||
EVENT_HANDLER1(void, start, bool, wait) {PIThread::start(40); if (wait) waitForFinish();}
|
||||
EVENT_HANDLER0(void, stop) {stop(false);}
|
||||
EVENT_HANDLER1(void, stop, bool, clear);
|
||||
|
||||
EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void * , data)
|
||||
EVENT2(tileEvent, PIScreenTile * , tile, PIScreenTypes::TileEvent, e)
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void waitForFinish()
|
||||
//! \brief block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void start(bool wait = false)
|
||||
//! \brief Start console output and if "wait" block until finished (exit key will be pressed)
|
||||
|
||||
//! \fn void stop(bool clear = false)
|
||||
//! \brief Stop console output and if "clear" clear the screen
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void keyPressed(PIKbdListener::KeyEvent key, void * data)
|
||||
//! \brief Raise on key "key" pressed, "data" is pointer to %PIConsole object
|
||||
|
||||
//! \fn void tileEvent(PIScreenTile * tile, PIScreenTypes::TileEvent e)
|
||||
//! \brief Raise on some event "e" from tile "tile"
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
class SystemConsole {
|
||||
public:
|
||||
SystemConsole();
|
||||
~SystemConsole();
|
||||
void begin();
|
||||
void end();
|
||||
void prepare();
|
||||
void clear();
|
||||
void print();
|
||||
void resize(int w, int h);
|
||||
void toUpperLeft();
|
||||
void moveTo(int x = 0, int y = 0);
|
||||
void hideCursor();
|
||||
void showCursor();
|
||||
void clearScreen();
|
||||
void clearScreenLower();
|
||||
#ifdef WINDOWS
|
||||
void getWinCurCoord();
|
||||
void clearLine();
|
||||
void newLine();
|
||||
ushort attributes(const PIScreenTypes::Cell & c);
|
||||
#else
|
||||
PIString formatString(const PIScreenTypes::Cell & c);
|
||||
#endif
|
||||
PRIVATE_DECLARATION
|
||||
int width, height, pwidth, pheight;
|
||||
PIVector<PIVector<PIScreenTypes::Cell> > cells, pcells, dcells;
|
||||
};
|
||||
|
||||
void begin();
|
||||
void run();
|
||||
void end();
|
||||
void key_event(PIKbdListener::KeyEvent key);
|
||||
static void key_eventS(PIKbdListener::KeyEvent key, void * t) {((PIScreen*)t)->key_event(key);}
|
||||
PIVector<PIScreenTile*> tiles() {return root.children();}
|
||||
bool nextFocus(PIScreenTile * rt, PIKbdListener::KeyEvent key = PIKbdListener::KeyEvent());
|
||||
void tileEventInternal(PIScreenTile * t, PIScreenTypes::TileEvent e);
|
||||
void tileRemovedInternal(PIScreenTile * t);
|
||||
void tileSetFocusInternal(PIScreenTile * t);
|
||||
|
||||
SystemConsole console;
|
||||
PIScreenDrawer drawer_;
|
||||
PIKbdListener * listener;
|
||||
PIKbdListener::KBFunc ret_func;
|
||||
PIScreenTile root;
|
||||
PIScreenTile * tile_focus, * tile_dialog;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PISCREEN_H
|
||||
23
src_main/console/piscreenconsole.cpp
Normal file
23
src_main/console/piscreenconsole.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "piscreenconsole.h"
|
||||
|
||||
using namespace PIScreenTypes;
|
||||
|
||||
|
||||
TileVars::TileVars(const PIString &n) : PIScreenTile(n) {
|
||||
alignment = Left;
|
||||
}
|
||||
|
||||
|
||||
void TileVars::sizeHint(int &w, int &h) const {
|
||||
|
||||
}
|
||||
|
||||
void TileVars::drawEvent(PIScreenDrawer *d) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
PIScreenConsole::PIScreenConsole() {
|
||||
|
||||
}
|
||||
|
||||
50
src_main/console/piscreenconsole.h
Normal file
50
src_main/console/piscreenconsole.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef PISCREENCONSOLE_H
|
||||
#define PISCREENCONSOLE_H
|
||||
|
||||
#include "piscreentiles.h"
|
||||
|
||||
class PIP_EXPORT TileVars: public PIScreenTile {
|
||||
public:
|
||||
TileVars(const PIString & n = PIString());
|
||||
protected:
|
||||
struct Variable {
|
||||
Variable() {nx = ny = type = offset = bitFrom = bitCount = size = 0; format = PIScreenTypes::CellFormat(); ptr = 0;}
|
||||
bool isEmpty() const {return (ptr == 0);}
|
||||
PIString name;
|
||||
PIScreenTypes::CellFormat format;
|
||||
int nx;
|
||||
int ny;
|
||||
int type;
|
||||
int offset;
|
||||
int bitFrom;
|
||||
int bitCount;
|
||||
int size;
|
||||
const void * ptr;
|
||||
/*void operator =(const Variable & src) {
|
||||
name = src.name;
|
||||
format = src.format;
|
||||
nx = src.nx;
|
||||
ny = src.ny;
|
||||
type = src.type;
|
||||
offset = src.offset;
|
||||
bitFrom = src.bitFrom;
|
||||
bitCount = src.bitCount;
|
||||
size = src.size;
|
||||
ptr = src.ptr;
|
||||
}*/
|
||||
};
|
||||
PIVector<Variable> variables;
|
||||
PIScreenTypes::Alignment alignment;
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PIScreenConsole : public PIScreenTile
|
||||
{
|
||||
public:
|
||||
PIScreenConsole();
|
||||
};
|
||||
|
||||
#endif // PISCREENCONSOLE_H
|
||||
255
src_main/console/piscreendrawer.cpp
Normal file
255
src_main/console/piscreendrawer.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Console output/input
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piscreendrawer.h"
|
||||
|
||||
|
||||
using namespace PIScreenTypes;
|
||||
|
||||
|
||||
PIScreenDrawer::PIScreenDrawer(PIVector<PIVector<Cell> > & c): cells(c) {
|
||||
arts_[LineVertical] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("│");
|
||||
#else
|
||||
PIChar('|');
|
||||
#endif
|
||||
|
||||
arts_[LineHorizontal] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("─");
|
||||
#else
|
||||
PIChar('-');
|
||||
#endif
|
||||
|
||||
arts_[Cross] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("┼");
|
||||
#else
|
||||
PIChar('+');
|
||||
#endif
|
||||
|
||||
arts_[CornerTopLeft] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("┌");
|
||||
#else
|
||||
PIChar('+');
|
||||
#endif
|
||||
|
||||
arts_[CornerTopRight] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("┐");
|
||||
#else
|
||||
PIChar('+');
|
||||
#endif
|
||||
|
||||
arts_[CornerBottomLeft] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("└");
|
||||
#else
|
||||
PIChar('+');
|
||||
#endif
|
||||
|
||||
arts_[CornerBottomRight] =
|
||||
#ifdef PIP_ICU
|
||||
PIChar::fromUTF8("┘");
|
||||
#else
|
||||
PIChar('+');
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::clear() {
|
||||
clear(cells);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::drawPixel(int x, int y, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return;
|
||||
cells[y][x].symbol = c;
|
||||
cells[y][x].format.color_char = col_char;
|
||||
cells[y][x].format.color_back = col_back;
|
||||
cells[y][x].format.flags = flags_char;
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::drawLine(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
|
||||
Cell cc;
|
||||
cc.symbol = c;
|
||||
cc.format.color_char = col_char;
|
||||
cc.format.color_back = col_back;
|
||||
cc.format.flags = flags_char;
|
||||
int x = 0, y = 0;
|
||||
if (piAbsi(x1 - x0) >= piAbsi(y1 - y0)) {
|
||||
float dy = (y1 - y0) / float(piAbsi(x1 - x0)), cy = y0;
|
||||
int dx = x0 < x1 ? 1 : -1;
|
||||
for (int i = x0; i != x1; i += dx) {
|
||||
x = i; y = piRound(cy);
|
||||
if (x >= 0 && x < width && y >= 0 && y < height)
|
||||
cells[y][x] = cc;
|
||||
cy += dy;
|
||||
}
|
||||
y = piRound(cy);
|
||||
if (x1 >= 0 && x1 < width && y >= 0 && y < height)
|
||||
cells[y][x1] = cc;
|
||||
} else {
|
||||
float dx = (x1 - x0) / float(piAbsi(y1 - y0)), cx = x0;
|
||||
int dy = y0 < y1 ? 1 : -1;
|
||||
for (int i = y0; i != y1; i += dy) {
|
||||
x = piRound(cx); y = i;
|
||||
if (x >= 0 && x < width && y >= 0 && y < height)
|
||||
cells[y][x] = cc;
|
||||
cx += dx;
|
||||
}
|
||||
x = piRound(cx);
|
||||
if (x >= 0 && x < width && y1 >= 0 && y1 < height)
|
||||
cells[y1][x] = cc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::drawRect(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
|
||||
Cell cc;
|
||||
cc.symbol = c;
|
||||
cc.format.color_char = col_char;
|
||||
cc.format.color_back = col_back;
|
||||
cc.format.flags = flags_char;
|
||||
int dx = x0 < x1 ? 1 : -1;
|
||||
int dy = y0 < y1 ? 1 : -1;
|
||||
int xs[2] = {x0, x1};
|
||||
int ys[2] = {y0, y1};
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
int j = ys[k];
|
||||
if (j >= 0 && j < height) {
|
||||
PIVector<Cell> & cv(cells[j]);
|
||||
for (int i = x0; i != x1; i += dx)
|
||||
if (i >= 0 && i < width)
|
||||
cv[i] = cc;
|
||||
}
|
||||
j = xs[k];
|
||||
if (j >= 0 && j < width) {
|
||||
for (int i = y0; i != y1; i += dy)
|
||||
if (i >= 0 && i < height)
|
||||
cells[i][j] = cc;
|
||||
}
|
||||
}
|
||||
int i = x1, j = y1;
|
||||
if (i >= 0 && i < width && j >= 0 && j < height)
|
||||
cells[j][i] = cc;
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::drawFrame(int x0, int y0, int x1, int y1, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x0 == x1 && y0 == y1) return;
|
||||
Cell cc;
|
||||
cc.format.color_char = col_char;
|
||||
cc.format.color_back = col_back;
|
||||
cc.format.flags = flags_char;
|
||||
int dx = x0 < x1 ? 1 : -1;
|
||||
int dy = y0 < y1 ? 1 : -1;
|
||||
int xs[2] = {x0, x1};
|
||||
int ys[2] = {y0, y1};
|
||||
for (int k = 0; k < 2; ++k) {
|
||||
int j = ys[k];
|
||||
if (j >= 0 && j < height) {
|
||||
PIVector<Cell> & cv(cells[j]);
|
||||
cc.symbol = artChar(LineHorizontal);
|
||||
for (int i = x0 + 1; i != x1; i += dx)
|
||||
if (i >= 0 && i < width)
|
||||
cv[i] = cc;
|
||||
}
|
||||
j = xs[k];
|
||||
if (j >= 0 && j < width) {
|
||||
cc.symbol = artChar(LineVertical);
|
||||
for (int i = y0 + 1; i != y1; i += dy)
|
||||
if (i >= 0 && i < height)
|
||||
cells[i][j] = cc;
|
||||
}
|
||||
}
|
||||
int i = x0, j = y0; cc.symbol = artChar(CornerTopLeft);
|
||||
if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
|
||||
i = x1, j = y0; cc.symbol = artChar(CornerTopRight);
|
||||
if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
|
||||
i = x0, j = y1; cc.symbol = artChar(CornerBottomLeft);
|
||||
if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
|
||||
i = x1, j = y1; cc.symbol = artChar(CornerBottomRight);
|
||||
if (i >= 0 && i < width && j >= 0 && j < height) cells[j][i] = cc;
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::fillRect(int x0, int y0, int x1, int y1, const PIChar & c, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x0 == x1 && y0 == y1) drawPixel(x0, y0, c, col_char, col_back, flags_char);
|
||||
Cell cc;
|
||||
cc.symbol = c;
|
||||
cc.format.color_char = col_char;
|
||||
cc.format.color_back = col_back;
|
||||
cc.format.flags = flags_char;
|
||||
int dx = x0 < x1 ? 1 : -1;
|
||||
int dy = y0 < y1 ? 1 : -1;
|
||||
for (int j = y0; j != y1; j += dy)
|
||||
if (j >= 0 && j < height) {
|
||||
PIVector<Cell> & cv(cells[j]);
|
||||
for (int i = x0; i != x1; i += dx)
|
||||
if (i >= 0 && i < width)
|
||||
cv[i] = cc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::fillRect(int x0, int y0, int x1, int y1, PIVector<PIVector<Cell> > & content) {
|
||||
if (x0 > x1) piSwap(x0, x1);
|
||||
if (y0 > y1) piSwap(y0, y1);
|
||||
int w = x1 - x0;
|
||||
int h = y1 - y0;
|
||||
for (int j = 0; j < h; ++j)
|
||||
if (j < piMini(height, content.size_s())) {
|
||||
if ((j + y0) >= 0 && (j + y0) < height) {
|
||||
PIVector<Cell> & cv(cells[y0 + j]);
|
||||
PIVector<Cell> & contv(content[j]);
|
||||
for (int i = 0; i < piMini(w, contv.size_s()); ++i)
|
||||
if ((i + x0) >= 0 && (i + x0) < width)
|
||||
cv[x0 + i] = contv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::clear(PIVector<PIVector<Cell> > & cells) {
|
||||
for (int i = 0; i < cells.size_s(); ++i)
|
||||
cells[i].fill(Cell());
|
||||
}
|
||||
|
||||
|
||||
void PIScreenDrawer::drawText(int x, int y, const PIString & s, Color col_char, Color col_back, CharFlags flags_char) {
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return;
|
||||
PIVector<Cell> & cv(cells[y]);
|
||||
Cell cc;
|
||||
cc.format.color_char = col_char;
|
||||
cc.format.color_back = col_back;
|
||||
cc.format.flags = flags_char;
|
||||
for (int i = 0; i < s.size_s(); ++i) {
|
||||
int j = i + x;
|
||||
if (j >= 0 && j < width) {
|
||||
cc.symbol = s[i];
|
||||
cv[j] = cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src_main/console/piscreendrawer.h
Normal file
58
src_main/console/piscreendrawer.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*! \file piscreendrawer.h
|
||||
* \brief Drawer for PIScreen
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Drawer for PIScreen
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISCREENDRAWER_H
|
||||
#define PISCREENDRAWER_H
|
||||
|
||||
#include "piscreentypes.h"
|
||||
#include "pistring.h"
|
||||
|
||||
class PIP_EXPORT PIScreenDrawer
|
||||
{
|
||||
friend class PIScreen;
|
||||
PIScreenDrawer(PIVector<PIVector<PIScreenTypes::Cell> > & c);
|
||||
public:
|
||||
enum ArtChar {LineVertical = 1, LineHorizontal, Cross, CornerTopLeft, CornerTopRight, CornerBottomLeft, CornerBottomRight};
|
||||
|
||||
void clear();
|
||||
void clearRect(int x0, int y0, int x1, int y1) {fillRect(x0, y0, x1, y1, ' ');}
|
||||
void drawPixel(int x, int y, const PIChar & c, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void drawLine(int x0, int y0, int x1, int y1, const PIChar & c, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void drawRect(int x0, int y0, int x1, int y1, const PIChar & c, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void drawFrame(int x0, int y0, int x1, int y1, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void drawText(int x, int y, const PIString & s, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Transparent, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void fillRect(int x0, int y0, int x1, int y1, const PIChar & c, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0);
|
||||
void fillRect(int x0, int y0, int x1, int y1, PIVector<PIVector<PIScreenTypes::Cell> > & content);
|
||||
|
||||
PIChar artChar(const ArtChar type) const {return arts_.value(type, PIChar(' '));}
|
||||
|
||||
static void clear(PIVector<PIVector<PIScreenTypes::Cell> > & cells);
|
||||
|
||||
private:
|
||||
PIVector<PIVector<PIScreenTypes::Cell> > & cells;
|
||||
int width, height;
|
||||
PIMap<ArtChar, PIChar> arts_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PISCREENDRAWER_H
|
||||
242
src_main/console/piscreentile.cpp
Normal file
242
src_main/console/piscreentile.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Basic PIScreen tile
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piscreentile.h"
|
||||
#include "piscreendrawer.h"
|
||||
|
||||
|
||||
using namespace PIScreenTypes;
|
||||
|
||||
|
||||
PIScreenTile::PIScreenTile(const PIString & n, Direction d, SizePolicy p): PIObject(n) {
|
||||
direction = d;
|
||||
size_policy = p;
|
||||
focus_flags = 0;
|
||||
screen = 0;
|
||||
minimumWidth = minimumHeight = x_ = y_ = width_ = height_ = pw = ph = 0;
|
||||
maximumWidth = maximumHeight = 65535;
|
||||
marginLeft = marginRight = marginTop = marginBottom = spacing = 0;
|
||||
parent = 0;
|
||||
back_symbol = ' ';
|
||||
visible = true;
|
||||
has_focus = false;
|
||||
}
|
||||
|
||||
|
||||
PIScreenTile::~PIScreenTile() {
|
||||
//piCout << this << "~";
|
||||
if (screen)
|
||||
screen->tileRemovedInternal(this);
|
||||
setScreen(0);
|
||||
deleteChildren();
|
||||
if (!parent) return;
|
||||
parent->tiles.removeOne(this);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::addTile(PIScreenTile * t) {
|
||||
if (t == 0) return;
|
||||
if (tiles.contains(t)) return;
|
||||
tiles << t;
|
||||
t->parent = this;
|
||||
t->setScreen(screen);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::takeTile(PIScreenTile * t) {
|
||||
if (!tiles.contains(t)) return;
|
||||
tiles.removeOne(t);
|
||||
t->parent = 0;
|
||||
t->setScreen(0);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::removeTile(PIScreenTile * t) {
|
||||
if (!tiles.contains(t)) return;
|
||||
tiles.removeOne(t);
|
||||
t->parent = 0;
|
||||
t->setScreen(0);
|
||||
delete t;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PIScreenTile * > PIScreenTile::children(bool only_visible) {
|
||||
PIVector<PIScreenTile * > ret;
|
||||
piForeach (PIScreenTile * t, tiles)
|
||||
if (t->visible || !only_visible)
|
||||
ret << t << t->children(only_visible);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::raiseEvent(TileEvent e) {
|
||||
if (!screen) return;
|
||||
screen->tileEventInternal(this, e);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::setScreen(PIScreenBase * s) {
|
||||
screen = s;
|
||||
piForeach (PIScreenTile * t, tiles)
|
||||
t->setScreen(s);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::deleteChildren() {
|
||||
//piCout << this << "deleteChildren";
|
||||
piForeach (PIScreenTile * t, tiles) {
|
||||
//piCout << this << " child" << t;
|
||||
//t->deleteChildren();
|
||||
t->parent = 0;
|
||||
delete t;
|
||||
}
|
||||
tiles.clear();
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::setFocus() {
|
||||
if (!screen || !focus_flags[CanHasFocus]) return;
|
||||
screen->tileSetFocusInternal(this);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::drawEventInternal(PIScreenDrawer * d) {
|
||||
if (!visible) {
|
||||
//d->clearRect(x, y, x + width, y + height);
|
||||
return;
|
||||
}
|
||||
d->fillRect(x_, y_, x_ + width_, y_ + height_, back_symbol, (Color)back_format.color_char, (Color)back_format.color_back, back_format.flags);
|
||||
drawEvent(d);
|
||||
piForeach (PIScreenTile * t, tiles)
|
||||
t->drawEventInternal(d);
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::sizeHint(int & w, int & h) const {
|
||||
w = 0;
|
||||
h = 0;
|
||||
if (tiles.isEmpty()) return;
|
||||
int sl = spacing * (tiles.size_s() - 1);
|
||||
if (direction == Horizontal) w += sl;
|
||||
else h += sl;
|
||||
piForeachC (PIScreenTile * t, tiles) {
|
||||
if (!t->visible) continue;
|
||||
int cw(0), ch(0);
|
||||
t->sizeHint(cw, ch);
|
||||
cw = piClampi(cw, t->minimumWidth, t->maximumWidth);
|
||||
ch = piClampi(ch, t->minimumHeight, t->maximumHeight);
|
||||
if (direction == Horizontal) {
|
||||
w += cw; h = piMaxi(h, ch);
|
||||
} else {
|
||||
h += ch; w = piMaxi(w, cw);
|
||||
}
|
||||
}
|
||||
w += marginLeft + marginRight;
|
||||
h += marginTop + marginBottom;
|
||||
}
|
||||
|
||||
|
||||
void PIScreenTile::layout() {
|
||||
if (tiles.isEmpty() || !visible) return;
|
||||
int as(0), ts(0), ts2(0), ecnt(0), pcnt(0);
|
||||
ts = (direction == Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom);
|
||||
ts2 = (direction != Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom);
|
||||
ts -= spacing * (tiles.size_s() - 1);
|
||||
PIVector<int> hints(tiles.size_s());
|
||||
PIVector<float> asizes(tiles.size_s());
|
||||
for (int i = 0; i < tiles.size_s(); ++i) {
|
||||
PIScreenTile * t(tiles[i]);
|
||||
int cw(0), ch(0), cs(0);
|
||||
if (t->visible && t->needLayout()) {
|
||||
t->sizeHint(cw, ch);
|
||||
cw = piClampi(cw, t->minimumWidth, t->maximumWidth);
|
||||
ch = piClampi(ch, t->minimumHeight, t->maximumHeight);
|
||||
if (t->size_policy == Expanding) ++ecnt;
|
||||
if (t->size_policy == Preferred) ++pcnt;
|
||||
cs = (direction == Horizontal) ? cw : ch;
|
||||
as += cs;
|
||||
}
|
||||
hints[i] = cs;
|
||||
asizes[i] = 0.f;
|
||||
}
|
||||
if (as <= ts) {
|
||||
int acnt(0);
|
||||
SizePolicy pol;
|
||||
if (ecnt > 0) {
|
||||
acnt = ecnt;
|
||||
pol = Expanding;
|
||||
} else if (pcnt > 0) {
|
||||
acnt = pcnt;
|
||||
pol = Preferred;
|
||||
}
|
||||
if (acnt > 0) {
|
||||
float add_a = float(ts - as), add_s = add_a / acnt, add_da(0.);
|
||||
asizes.fill(add_s);
|
||||
PISet<int> max_tl;
|
||||
for (int i = 0; i < tiles.size_s(); ++i) {
|
||||
if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) {
|
||||
float maxs = (direction == Horizontal) ? tiles[i]->maximumWidth : tiles[i]->maximumHeight;
|
||||
if (hints[i] + asizes[i] > maxs) {
|
||||
max_tl << i;
|
||||
float pas = asizes[i];
|
||||
asizes[i] = maxs - hints[i];
|
||||
acnt--;
|
||||
if (acnt > 0) {
|
||||
pas = (pas - asizes[i]) / acnt;
|
||||
for (int j = 0; j < tiles.size_s(); ++j) {
|
||||
if (i == j) continue;
|
||||
if (max_tl[j]) continue;
|
||||
if (tiles[j]->size_policy == pol && tiles[j]->visible && tiles[j]->needLayout())
|
||||
asizes[j] += pas;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < tiles.size_s(); ++i) {
|
||||
if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) {
|
||||
int a = piRound(asizes[i] + add_da);
|
||||
add_da += asizes[i] - a;
|
||||
hints[i] += a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int cx = x_ + marginLeft, cy = y_ + marginTop;
|
||||
for (int i = 0; i < tiles.size_s(); ++i) {
|
||||
PIScreenTile * t(tiles[i]);
|
||||
if (!t->visible || !t->needLayout()) continue;
|
||||
t->x_ = cx;
|
||||
t->y_ = cy;
|
||||
if (direction == Horizontal) {
|
||||
t->width_ = hints[i];
|
||||
t->height_ = ts2;
|
||||
cx += hints[i] + spacing;
|
||||
} else {
|
||||
t->width_ = ts2;
|
||||
t->height_ = hints[i];
|
||||
cy += hints[i] + spacing;
|
||||
}
|
||||
if (t->pw != t->width_ || t->ph != t->height_)
|
||||
t->resizeEvent(t->width_, t->height_);
|
||||
t->pw = t->width_;
|
||||
t->ph = t->height_;
|
||||
t->layout();
|
||||
}
|
||||
}
|
||||
99
src_main/console/piscreentile.h
Normal file
99
src_main/console/piscreentile.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*! \file piscreentile.h
|
||||
* \brief Basic PIScreen tile
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Basic PIScreen tile
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISCREENTILE_H
|
||||
#define PISCREENTILE_H
|
||||
|
||||
#include "piscreentypes.h"
|
||||
#include "pikbdlistener.h"
|
||||
|
||||
class PIScreenDrawer;
|
||||
|
||||
class PIP_EXPORT PIScreenTile: public PIObject {
|
||||
friend class PIScreen;
|
||||
PIOBJECT_SUBCLASS(PIScreenTile, PIObject)
|
||||
public:
|
||||
PIScreenTile(const PIString & n = PIString(), PIScreenTypes::Direction d = PIScreenTypes::Vertical, PIScreenTypes::SizePolicy p = PIScreenTypes::Preferred);
|
||||
virtual ~PIScreenTile();
|
||||
|
||||
void addTile(PIScreenTile * t);
|
||||
void takeTile(PIScreenTile * t);
|
||||
void removeTile(PIScreenTile * t);
|
||||
PIScreenTile * parentTile() const {return parent;}
|
||||
PIVector<PIScreenTile * > children(bool only_visible = false);
|
||||
void show() {visible = true;}
|
||||
void hide() {visible = false;}
|
||||
void setFocus();
|
||||
bool hasFocus() const {return has_focus;}
|
||||
void setMargins(int m) {marginLeft = marginRight = marginTop = marginBottom = m;}
|
||||
void setMargins(int l, int r, int t, int b) {marginLeft = l; marginRight = r; marginTop = t; marginBottom = b;}
|
||||
|
||||
int x() const {return x_;}
|
||||
int y() const {return y_;}
|
||||
int width() const {return width_;}
|
||||
int height() const {return height_;}
|
||||
|
||||
PIScreenTypes::Direction direction;
|
||||
PIScreenTypes::SizePolicy size_policy;
|
||||
PIScreenTypes::FocusFlags focus_flags;
|
||||
PIScreenTypes::CellFormat back_format;
|
||||
PIChar back_symbol;
|
||||
int minimumWidth, minimumHeight;
|
||||
int maximumWidth, maximumHeight;
|
||||
int marginLeft, marginRight, marginTop, marginBottom;
|
||||
int spacing;
|
||||
bool visible;
|
||||
|
||||
protected:
|
||||
|
||||
//! Returns desired tile size in "w" and "h"
|
||||
virtual void sizeHint(int & w, int & h) const;
|
||||
|
||||
//! Tile has been resized to "w"x"h"
|
||||
virtual void resizeEvent(int w, int h) {}
|
||||
|
||||
//! Draw tile with drawer "d" in world-space coordinates
|
||||
virtual void drawEvent(PIScreenDrawer * d) {}
|
||||
|
||||
//! Return "true" if you process key
|
||||
virtual bool keyEvent(PIKbdListener::KeyEvent key) {return false;}
|
||||
|
||||
void raiseEvent(PIScreenTypes::TileEvent e);
|
||||
void setScreen(PIScreenTypes::PIScreenBase * s);
|
||||
void deleteChildren();
|
||||
void drawEventInternal(PIScreenDrawer * d);
|
||||
void layout();
|
||||
bool needLayout() {return size_policy != PIScreenTypes::Ignore;}
|
||||
|
||||
PIVector<PIScreenTile * > tiles;
|
||||
PIScreenTile * parent;
|
||||
PIScreenTypes::PIScreenBase * screen;
|
||||
int x_, y_, width_, height_;
|
||||
bool has_focus;
|
||||
|
||||
private:
|
||||
int pw, ph;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PISCREENTILE_H
|
||||
639
src_main/console/piscreentiles.cpp
Normal file
639
src_main/console/piscreentiles.cpp
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Various tiles for PIScreen
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "piscreentiles.h"
|
||||
#include "piscreendrawer.h"
|
||||
|
||||
|
||||
using namespace PIScreenTypes;
|
||||
|
||||
|
||||
TileSimple::TileSimple(const PIString & n): PIScreenTile(n) {
|
||||
alignment = Left;
|
||||
}
|
||||
|
||||
|
||||
TileSimple::TileSimple(const TileSimple::Row & r): PIScreenTile() {
|
||||
alignment = Left;
|
||||
content << r;
|
||||
}
|
||||
|
||||
|
||||
void TileSimple::sizeHint(int & w, int & h) const {
|
||||
w = h = 0;
|
||||
piForeachC (Row & r, content)
|
||||
w = piMaxi(w, r.first.size_s());
|
||||
h = content.size_s();
|
||||
}
|
||||
|
||||
|
||||
void TileSimple::drawEvent(PIScreenDrawer * d) {
|
||||
for (int i = 0; i < content.size_s(); ++i) {
|
||||
Row & r(content[i]);
|
||||
int rx = 0;
|
||||
switch (alignment) {
|
||||
case Left: rx = x_; break;
|
||||
case Center: rx = x_ + (width_ - r.first.size_s()) / 2; break;
|
||||
case Right: rx = x_ + width_ - r.first.size_s(); break;
|
||||
};
|
||||
d->drawText(rx, y_ + i, r.first, (Color)r.second.color_char, (Color)r.second.color_back, r.second.flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileScrollBar::TileScrollBar(const PIString & n) {
|
||||
direction = Vertical;
|
||||
thickness = 1;
|
||||
minimum_ = value_ = 0;
|
||||
maximum_ = 100;
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::setMinimum(int v) {
|
||||
minimum_ = v;
|
||||
_check();
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::setMaximum(int v) {
|
||||
maximum_ = v;
|
||||
_check();
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::setValue(int v) {
|
||||
value_ = v;
|
||||
_check();
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::_check() {
|
||||
value_ = piClampi(value_, minimum_, maximum_);
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::sizeHint(int & w, int & h) const {
|
||||
w = h = 0;
|
||||
if (direction == Vertical) {
|
||||
w = thickness;
|
||||
h = 255;
|
||||
} else {
|
||||
h = thickness;
|
||||
w = 255;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TileScrollBar::drawEvent(PIScreenDrawer * d) {
|
||||
line_char = d->artChar(direction == Vertical ? PIScreenDrawer::LineVertical : PIScreenDrawer::LineHorizontal);
|
||||
d->fillRect(x_, y_, x_ + width_, y_ + height_, line_char, Green);
|
||||
if (value_ >= minimum_ && value_ <= maximum_) {
|
||||
if (direction == Vertical) {
|
||||
int c = piRoundf((float(value_ - minimum_) / (maximum_ - minimum_)) * (height_ - 1));
|
||||
d->drawLine(x_, y_ + c, x_ + width_ - 1, y_ + c, ' ', Green, Green);
|
||||
} else {
|
||||
int c = piRoundf((float(value_ - minimum_) / (maximum_ - minimum_)) * (width_ - 1));
|
||||
d->drawLine(x_ + c, y_, x_ + c, y_ + height_ - 1, ' ', Green, Green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileList::TileList(const PIString & n): PIScreenTile(n) {
|
||||
alignment = Left;
|
||||
focus_flags = CanHasFocus | NextByArrowsHorizontal | NextByTab;
|
||||
lhei = offset = cur = 0;
|
||||
selection_mode = NoSelection;
|
||||
scroll = new TileScrollBar();
|
||||
scroll->size_policy = Ignore;
|
||||
addTile(scroll);
|
||||
}
|
||||
|
||||
|
||||
TileList::~TileList() {
|
||||
delete scroll;
|
||||
}
|
||||
|
||||
|
||||
void TileList::sizeHint(int & w, int & h) const {
|
||||
w = h = 0;
|
||||
piForeachC (Row & r, content)
|
||||
w = piMaxi(w, r.first.size_s());
|
||||
h = 3;
|
||||
}
|
||||
|
||||
|
||||
void TileList::resizeEvent(int w, int h) {
|
||||
scroll->x_ = x_ + width_ - 1;
|
||||
scroll->y_ = y_;
|
||||
scroll->width_ = 1;
|
||||
scroll->height_ = height_;
|
||||
}
|
||||
|
||||
|
||||
void TileList::drawEvent(PIScreenDrawer * d) {
|
||||
lhei = height_ - 2;
|
||||
//int osp = piMini(3, lhei / 4);
|
||||
int is = piClampi(offset, 0, piMaxi(0, content.size_s() - 1)), ie = piClampi(offset + lhei, 0, content.size_s());
|
||||
if (is > 0) d->drawText(x_, y_, PIString(" /\\ ").repeat(width_ / 4), Green, Default, Bold);
|
||||
if (ie < content.size_s()) d->drawText(x_, y_ + height_ - 1, PIString(" \\/ ").repeat(width_ / 4), Green, Default, Bold);
|
||||
//piCout << is << ie << offset << lhei << content.size_s();
|
||||
for (int i = is; i < ie; ++i) {
|
||||
Row & r(content[i]);
|
||||
bool sel = i == cur && has_focus;
|
||||
if (sel) {
|
||||
int cy = y_ + i - is + 1;
|
||||
d->drawLine(x_, cy, x_ + width_ - 2, cy, ' ', Default, Blue);
|
||||
}
|
||||
int rx(0);
|
||||
switch (alignment) {
|
||||
case Left: rx = x_; break;
|
||||
case Center: rx = x_ + (width_ - 1 - r.first.size_s()) / 2; break;
|
||||
case Right: rx = x_ + width_ - 1 - r.first.size_s(); break;
|
||||
};
|
||||
CharFlags cf = r.second.flags;
|
||||
Color cc = (Color)r.second.color_char;
|
||||
if (selected[i]) {
|
||||
cf |= Bold;
|
||||
cc = Yellow;
|
||||
}
|
||||
d->drawText(rx, y_ + i - is + 1, r.first, cc, sel ? Blue : Default, cf);
|
||||
}
|
||||
scroll->setMaximum(piMaxi(0, content.size_s() - 1));
|
||||
scroll->setValue(cur);
|
||||
/*int cx = x_ + width_ - 1;
|
||||
d->drawLine(cx, y_ + 1, cx, y_ + height_ - 2, vert_line, Green);
|
||||
if (content.size_s() > 1)
|
||||
d->drawPixel(cx, y_ + piRound(float(cur) / (content.size_s() - 1) * (lhei - 1)) + 1, ' ', Green, Green);
|
||||
if (ie < content.size_s()) d->drawText(x_, y_ + height_ - 1, PIString(" \\/ ").repeat(width_ / 4), Green, Default, Bold);*/
|
||||
}
|
||||
|
||||
|
||||
bool TileList::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
lhei = height_ - 2;
|
||||
int oo(0), osp = piMini(3, lhei / 4);
|
||||
switch (key.key) {
|
||||
case PIKbdListener::PageUp:
|
||||
cur -= lhei / 2;
|
||||
oo -= lhei / 2;
|
||||
case PIKbdListener::UpArrow:
|
||||
cur--;
|
||||
oo--;
|
||||
if (key.modifiers[PIKbdListener::Ctrl]) {
|
||||
cur -= 4;
|
||||
oo -= 4;
|
||||
}
|
||||
if (cur < 0) cur = 0;
|
||||
if (cur - offset < osp) offset += oo;
|
||||
if (offset < 0) offset = 0;
|
||||
return true;
|
||||
case PIKbdListener::Space:
|
||||
if (cur < 0 || cur >= content.size_s()) return true;
|
||||
switch (selection_mode) {
|
||||
case NoSelection: return false;
|
||||
case SingleSelection:
|
||||
if (selected.isEmpty()) selected << cur;
|
||||
else {
|
||||
bool add = !selected[cur];
|
||||
selected.clear();
|
||||
if (add) selected << cur;
|
||||
}
|
||||
raiseEvent(TileEvent(SelectionChanged));
|
||||
return true;
|
||||
case MultiSelection:
|
||||
if (selected[cur]) selected.remove(cur);
|
||||
else selected << cur;
|
||||
raiseEvent(TileEvent(SelectionChanged));
|
||||
break;
|
||||
}
|
||||
case PIKbdListener::PageDown:
|
||||
if (key.key == PIKbdListener::PageDown) {
|
||||
cur += lhei / 2;
|
||||
oo += lhei / 2;
|
||||
}
|
||||
case PIKbdListener::DownArrow:
|
||||
cur++;
|
||||
oo++;
|
||||
if (key.modifiers[PIKbdListener::Ctrl]) {
|
||||
cur += 4;
|
||||
oo += 4;
|
||||
}
|
||||
if (cur >= content.size_s()) cur = content.size_s() - 1;
|
||||
if (cur - offset >= lhei - osp) offset += oo;
|
||||
if (offset >= content.size_s() - lhei) offset = content.size_s() - lhei;
|
||||
if (offset < 0) offset = 0;
|
||||
return true;
|
||||
case PIKbdListener::Home:
|
||||
cur = offset = 0;
|
||||
return true;
|
||||
case PIKbdListener::End:
|
||||
cur = content.size_s() - 1;
|
||||
offset = content.size_s() - lhei;
|
||||
if (offset < 0) offset = 0;
|
||||
return true;
|
||||
case PIKbdListener::Return:
|
||||
if (cur >= 0 && cur < content.size_s())
|
||||
raiseEvent(TileEvent(RowPressed, cur));
|
||||
return true;
|
||||
case '*':
|
||||
if (selection_mode == TileList::MultiSelection) {
|
||||
PISet<int> nsel;
|
||||
for (int i = 0; i < content.size_s(); ++i)
|
||||
if (!selected[i]) nsel << i;
|
||||
selected = nsel;
|
||||
}
|
||||
raiseEvent(TileEvent(SelectionChanged));
|
||||
return true;
|
||||
case 'A':
|
||||
if (selection_mode == TileList::MultiSelection) {
|
||||
selected.clear();
|
||||
for (int i = 0; i < content.size_s(); ++i)
|
||||
selected << i;
|
||||
}
|
||||
raiseEvent(TileEvent(SelectionChanged));
|
||||
return true;
|
||||
}
|
||||
return PIScreenTile::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileButton::TileButton(const PIString & n): PIScreenTile(n) {
|
||||
focus_flags = CanHasFocus | NextByTab | NextByArrowsAll;
|
||||
}
|
||||
|
||||
|
||||
void TileButton::sizeHint(int & w, int & h) const {
|
||||
w = text.size_s() + 2;
|
||||
h = 1;
|
||||
}
|
||||
|
||||
|
||||
void TileButton::drawEvent(PIScreenDrawer * d) {
|
||||
Color cb = has_focus ? Blue : Cyan;
|
||||
Color ct = has_focus ? White : Black;
|
||||
int ff = has_focus ? Bold : 0;
|
||||
d->fillRect(x_, y_, x_ + width_, y_ + 1, ' ', Default, cb);
|
||||
d->drawText(x_, y_, "[", ct, Transparent, ff);
|
||||
d->drawText(x_ + (width_ - text.size_s()) / 2, y_, text, ct, Transparent, ff);
|
||||
d->drawText(x_ + width_ - 1, y_, "]", ct, Transparent, ff);
|
||||
}
|
||||
|
||||
|
||||
bool TileButton::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
if (key.key == PIKbdListener::Space || key.key == PIKbdListener::Return) {
|
||||
raiseEvent(TileEvent(ButtonClicked));
|
||||
return true;
|
||||
}
|
||||
return PIScreenTile::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileButtons::TileButtons(const PIString & n): PIScreenTile(n) {
|
||||
focus_flags = CanHasFocus | NextByTab;
|
||||
direction = Horizontal;
|
||||
alignment = PIScreenTypes::Center;
|
||||
cur = 0;
|
||||
}
|
||||
|
||||
|
||||
void TileButtons::sizeHint(int & w, int & h) const {
|
||||
w = h = 0;
|
||||
if (direction == Horizontal) {
|
||||
piForeachC (Button & b, content)
|
||||
w += b.first.size_s() + 4;
|
||||
w += piMaxi(0, content.size_s() - 1) * 2;
|
||||
h += 1;
|
||||
} else {
|
||||
piForeachC (Button & b, content)
|
||||
w = piMaxi(w, b.first.size_s() + 4);
|
||||
h += content.size_s();
|
||||
h += piMaxi(0, content.size_s() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TileButtons::drawEvent(PIScreenDrawer * d) {
|
||||
int cx = x_, cy = y_, shw, shh;
|
||||
sizeHint(shw, shh);
|
||||
int dx = 0;
|
||||
switch (alignment) {
|
||||
case PIScreenTypes::Center: dx = (width_ - shw) / 2; break;
|
||||
case PIScreenTypes::Right: dx = width_ - shw; break;
|
||||
default: break;
|
||||
}
|
||||
if (direction == PIScreenTypes::Horizontal)
|
||||
cx += dx;
|
||||
for (int i = 0; i < content.size_s(); ++i) {
|
||||
Color cb = Cyan;
|
||||
Color ct = Black;
|
||||
int ff = 0;
|
||||
if (i == cur && has_focus) {
|
||||
cb = Blue;
|
||||
ct = White;
|
||||
ff = Bold;
|
||||
}
|
||||
Button & b(content[i]);
|
||||
int cw = b.first.size_s() + 2, xo(0);
|
||||
if (direction == Vertical) {
|
||||
cw = width_ - 2;
|
||||
xo = (cw - b.first.size_s()) / 2 - 1;
|
||||
}
|
||||
d->fillRect(cx, cy, cx + cw + 2, cy + 1, ' ', Default, cb);
|
||||
d->drawText(cx, cy, "[", ct, Transparent, ff);
|
||||
d->drawText(cx + xo + 2, cy, b.first, ct, Transparent, ff);
|
||||
d->drawText(cx + cw + 1, cy, "]", ct, Transparent, ff);
|
||||
if (direction == Horizontal)
|
||||
cx += b.first.size_s() + 6;
|
||||
else
|
||||
cy += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TileButtons::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
switch (key.key) {
|
||||
case PIKbdListener::LeftArrow:
|
||||
case PIKbdListener::UpArrow:
|
||||
cur--;
|
||||
if (cur < 0) cur = 0;
|
||||
return true;
|
||||
case PIKbdListener::RightArrow:
|
||||
case PIKbdListener::DownArrow:
|
||||
cur++;
|
||||
if (cur >= content.size_s()) cur = content.size_s() - 1;
|
||||
return true;
|
||||
case PIKbdListener::Space:
|
||||
case PIKbdListener::Return:
|
||||
raiseEvent(TileEvent(ButtonSelected, cur));
|
||||
return true;
|
||||
};
|
||||
return PIScreenTile::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileCheck::TileCheck(const PIString & n): PIScreenTile(n) {
|
||||
focus_flags = CanHasFocus | NextByTab | NextByArrowsAll;
|
||||
toggled = false;
|
||||
}
|
||||
|
||||
|
||||
void TileCheck::sizeHint(int & w, int & h) const {
|
||||
w = text.size_s() + 4;
|
||||
h = 1;
|
||||
}
|
||||
|
||||
|
||||
void TileCheck::drawEvent(PIScreenDrawer * d) {
|
||||
Color cb = has_focus ? Blue : Cyan;
|
||||
Color ct = has_focus ? White : Black;
|
||||
int ff = has_focus ? Bold : 0;
|
||||
PIString cs("[ ]");
|
||||
if (toggled) cs[1] = '*';
|
||||
d->fillRect(x_, y_, x_ + width_, y_ + 1, ' ', Default, cb);
|
||||
d->drawText(x_, y_, cs, ct, Transparent, ff);
|
||||
d->drawText(x_ + 4, y_, text, ct, Transparent, ff);
|
||||
}
|
||||
|
||||
|
||||
bool TileCheck::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
if (key.key == PIKbdListener::Space || key.key == PIKbdListener::Return) {
|
||||
toggled = !toggled;
|
||||
raiseEvent(TileEvent(Toggled, toggled));
|
||||
return true;
|
||||
}
|
||||
return PIScreenTile::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileProgress::TileProgress(const PIString & n): PIScreenTile(n) {
|
||||
maximum = 100.;
|
||||
value = 0.;
|
||||
suffix = " %";
|
||||
}
|
||||
|
||||
|
||||
void TileProgress::sizeHint(int & w, int & h) const {
|
||||
w = 20;
|
||||
h = 1;
|
||||
}
|
||||
|
||||
|
||||
void TileProgress::drawEvent(PIScreenDrawer * d) {
|
||||
int v = maximum == 0. ? 0 : piClampd(piRoundd(value / maximum * 100.), 0, 100);
|
||||
PIString s = prefix + PIString::fromNumber(piRoundd(value)) + suffix;
|
||||
int w = piRoundd(v / 100. * width_), sx = (width_ - s.size_s()) / 2;
|
||||
d->fillRect(x_, y_, x_ + width_, y_ + 1, ' ', Default, Cyan);
|
||||
d->fillRect(x_, y_, x_ + w, y_ + 1, ' ', Default, Blue);
|
||||
if (w < sx)
|
||||
d->drawText(x_ + sx, y_, s, Black, Transparent);
|
||||
else if (w >= sx + s.size_s())
|
||||
d->drawText(x_ + sx, y_, s, White, Transparent);
|
||||
else {
|
||||
int fw = w - sx;
|
||||
d->drawText(x_ + sx, y_, s.left(fw), White, Transparent);
|
||||
d->drawText(x_ + sx + fw, y_, s.cutLeft(fw), Black, Transparent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TilePICout::TilePICout(const PIString & n): TileList(n) {
|
||||
max_lines = 1024;
|
||||
selection_mode = TileList::SingleSelection;
|
||||
PICout::setBufferActive(true);
|
||||
}
|
||||
|
||||
|
||||
void TilePICout::drawEvent(PIScreenDrawer * d) {
|
||||
PIString out = PICout::buffer(true);
|
||||
if (!out.isEmpty()) {
|
||||
PIStringList l = out.split("\n");
|
||||
bool scroll = (cur == content.size_s() - 1) || !has_focus;
|
||||
piForeachC (PIString & s, l)
|
||||
content << TileList::Row(s, format);
|
||||
if (content.size_s() > max_lines)
|
||||
content.remove(0, content.size_s() - max_lines);
|
||||
if (scroll) {
|
||||
offset = piMaxi(0, content.size_s() - lhei);
|
||||
cur = content.size_s() - 1;
|
||||
}
|
||||
}
|
||||
TileList::drawEvent(d);
|
||||
}
|
||||
|
||||
|
||||
bool TilePICout::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
if (key.key == 'C') {
|
||||
content.clear();
|
||||
cur = offset = 0;
|
||||
return true;
|
||||
}
|
||||
return TileList::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TileInput::TileInput(const PIString & n): PIScreenTile(n) {
|
||||
focus_flags = CanHasFocus | NextByTab;
|
||||
back_format.color_back = White;
|
||||
format.color_char = Black;
|
||||
format.color_back = White;
|
||||
max_length = 1024;
|
||||
cur = offset = 0;
|
||||
inv = false;
|
||||
}
|
||||
|
||||
|
||||
void TileInput::sizeHint(int & w, int & h) const {
|
||||
w = 32;
|
||||
h = 1;
|
||||
}
|
||||
|
||||
|
||||
void TileInput::drawEvent(PIScreenDrawer * d) {
|
||||
PIString ps = text.mid(offset, width_ - 2);
|
||||
d->drawText(x_ + 1, y_, ps, (Color)format.color_char, Transparent, (CharFlags)format.flags);
|
||||
if (offset > 0)
|
||||
d->drawText(x_, y_, "<", Green, Black, Bold);
|
||||
if (text.size_s() - offset >= width_ - 2)
|
||||
d->drawText(x_ + width_ - 1, y_, ">", Green, Black, Bold);
|
||||
if (!has_focus) return;
|
||||
Color cb = (Color)format.color_char, cc = (Color)format.color_back;
|
||||
if (tm_blink.elapsed_m() >= 650) {
|
||||
tm_blink.reset();
|
||||
inv = !inv;
|
||||
}
|
||||
if (inv) piSwap(cb, cc);
|
||||
d->drawText(x_ + 1 + cur - offset, y_, text.mid(cur, 1).expandLeftTo(1, ' '), cc, cb, (CharFlags)format.flags);
|
||||
}
|
||||
|
||||
|
||||
bool TileInput::keyEvent(PIKbdListener::KeyEvent key) {
|
||||
int lwid = piMaxi(0, width_ - 2);
|
||||
int oo(0), osp = piMini(3, lwid / 4);
|
||||
lwid--;
|
||||
switch (key.key) {
|
||||
case PIKbdListener::LeftArrow:
|
||||
cur--;
|
||||
oo--;
|
||||
if (key.modifiers[PIKbdListener::Ctrl]) {
|
||||
cur -= 4;
|
||||
oo -= 4;
|
||||
}
|
||||
if (cur < 0) cur = 0;
|
||||
if (cur - offset < osp) offset += oo;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::RightArrow:
|
||||
cur++;
|
||||
oo++;
|
||||
if (key.modifiers[PIKbdListener::Ctrl]) {
|
||||
cur += 4;
|
||||
oo += 4;
|
||||
}
|
||||
if (cur > text.size_s()) cur = text.size_s();
|
||||
if (cur - offset >= lwid - osp) offset += oo;
|
||||
if (offset >= text.size_s() - lwid) offset = text.size_s() - lwid;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::Home:
|
||||
cur = offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::End:
|
||||
cur = text.size_s();
|
||||
offset = text.size_s() - lwid;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::Backspace:
|
||||
if (cur > text.size_s() || text.isEmpty())
|
||||
return true;
|
||||
text.remove(cur - 1, 1);
|
||||
cur--;
|
||||
if (cur > text.size_s()) cur = text.size_s();
|
||||
if (cur - offset >= lwid - osp) offset += oo;
|
||||
if (offset >= text.size_s() - lwid) offset = text.size_s() - lwid;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::Delete:
|
||||
if (cur >= text.size_s() || text.isEmpty())
|
||||
return true;
|
||||
text.remove(cur, 1);
|
||||
if (cur < 0) cur = 0;
|
||||
if (cur > text.size_s()) cur = text.size_s();
|
||||
if (cur - offset < osp) offset += oo;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
case PIKbdListener::UpArrow:
|
||||
case PIKbdListener::DownArrow:
|
||||
case PIKbdListener::PageUp:
|
||||
case PIKbdListener::PageDown:
|
||||
case PIKbdListener::Insert:
|
||||
case PIKbdListener::Return:
|
||||
case PIKbdListener::Esc:
|
||||
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:
|
||||
break;
|
||||
default:
|
||||
text.insert(cur, PIChar(key.key));
|
||||
cur++;
|
||||
oo++;
|
||||
if (cur - offset >= lwid - osp) offset += oo;
|
||||
if (offset >= text.size_s() - lwid) offset = text.size_s() - lwid;
|
||||
if (offset < 0) offset = 0;
|
||||
reserCursor();
|
||||
return true;
|
||||
}
|
||||
return PIScreenTile::keyEvent(key);
|
||||
}
|
||||
|
||||
|
||||
void TileInput::reserCursor() {
|
||||
tm_blink.reset();
|
||||
inv = false;
|
||||
}
|
||||
215
src_main/console/piscreentiles.h
Normal file
215
src_main/console/piscreentiles.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*! \file piscreentiles.h
|
||||
* \brief Various tiles for PIScreen
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Various tiles for PIScreen
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISCREENTILES_H
|
||||
#define PISCREENTILES_H
|
||||
|
||||
#include "piscreentile.h"
|
||||
|
||||
|
||||
class PIP_EXPORT TileSimple: public PIScreenTile {
|
||||
public:
|
||||
typedef PIPair<PIString, PIScreenTypes::CellFormat> Row;
|
||||
TileSimple(const PIString & n = PIString());
|
||||
TileSimple(const Row & r);
|
||||
PIVector<Row> content;
|
||||
PIScreenTypes::Alignment alignment;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
};
|
||||
|
||||
|
||||
class TileList;
|
||||
|
||||
class PIP_EXPORT TileScrollBar: public PIScreenTile {
|
||||
friend class TileList;
|
||||
public:
|
||||
TileScrollBar(const PIString & n = PIString());
|
||||
void setMinimum(int v);
|
||||
void setMaximum(int v);
|
||||
void setValue(int v);
|
||||
int minimum() const {return minimum_;}
|
||||
int maximum() const {return maximum_;}
|
||||
int value() const {return value_;}
|
||||
int thickness;
|
||||
protected:
|
||||
void _check();
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
int minimum_, maximum_, value_;
|
||||
PIChar line_char;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileList: public PIScreenTile {
|
||||
public:
|
||||
TileList(const PIString & n = PIString());
|
||||
~TileList();
|
||||
enum SelectionMode {
|
||||
NoSelection,
|
||||
SingleSelection,
|
||||
MultiSelection
|
||||
};
|
||||
enum EventType {
|
||||
SelectionChanged,
|
||||
RowPressed
|
||||
};
|
||||
typedef PIPair<PIString, PIScreenTypes::CellFormat> Row;
|
||||
PIDeque<Row> content;
|
||||
PIScreenTypes::Alignment alignment;
|
||||
SelectionMode selection_mode;
|
||||
PISet<int> selected;
|
||||
int lhei, cur, offset;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void resizeEvent(int w, int h);
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
TileScrollBar * scroll;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileButton: public PIScreenTile {
|
||||
public:
|
||||
TileButton(const PIString & n = PIString());
|
||||
enum EventType {
|
||||
ButtonClicked
|
||||
};
|
||||
PIScreenTypes::CellFormat format;
|
||||
PIString text;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class PIP_EXPORT TileButtons: public PIScreenTile {
|
||||
public:
|
||||
TileButtons(const PIString & n = PIString());
|
||||
enum EventType {
|
||||
ButtonSelected
|
||||
};
|
||||
typedef PIPair<PIString, PIScreenTypes::CellFormat> Button;
|
||||
PIScreenTypes::Alignment alignment;
|
||||
PIVector<Button> content;
|
||||
int cur;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileCheck: public PIScreenTile {
|
||||
public:
|
||||
TileCheck(const PIString & n = PIString());
|
||||
enum EventType {
|
||||
Toggled
|
||||
};
|
||||
PIScreenTypes::CellFormat format;
|
||||
PIString text;
|
||||
bool toggled;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileProgress: public PIScreenTile {
|
||||
public:
|
||||
TileProgress(const PIString & n = PIString());
|
||||
PIScreenTypes::CellFormat format;
|
||||
PIString prefix;
|
||||
PIString suffix;
|
||||
double maximum;
|
||||
double value;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TilePICout: public TileList {
|
||||
public:
|
||||
TilePICout(const PIString & n = PIString());
|
||||
PIScreenTypes::CellFormat format;
|
||||
int max_lines;
|
||||
protected:
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileInput: public PIScreenTile {
|
||||
public:
|
||||
TileInput(const PIString & n = PIString());
|
||||
/*enum EventType {
|
||||
EditFinished
|
||||
};*/
|
||||
PIScreenTypes::CellFormat format;
|
||||
PIString text;
|
||||
int max_length;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
void reserCursor();
|
||||
int cur, offset;
|
||||
bool inv;
|
||||
PITimeMeasurer tm_blink;
|
||||
};
|
||||
|
||||
|
||||
class PIP_EXPORT TileTabs: public PIScreenTile {
|
||||
public:
|
||||
TileTabs(const PIString & n = PIString());
|
||||
enum EventType {
|
||||
TabChanged
|
||||
};
|
||||
struct Tab {
|
||||
Tab(const PIString & l = PIString(), int k = 0, PIScreenTile * t = 0) {
|
||||
label = l;
|
||||
key = k;
|
||||
tile = t;
|
||||
}
|
||||
PIString label;
|
||||
int key;
|
||||
PIScreenTile * tile;
|
||||
};
|
||||
PIScreenTypes::CellFormat format_tab;
|
||||
PIScreenTypes::CellFormat format_tab_selected;
|
||||
PIDeque<Tab> tabs;
|
||||
protected:
|
||||
void sizeHint(int & w, int & h) const;
|
||||
void drawEvent(PIScreenDrawer * d);
|
||||
bool keyEvent(PIKbdListener::KeyEvent key);
|
||||
void selectTab(int index);
|
||||
int cur;
|
||||
};
|
||||
|
||||
|
||||
#endif // PISCREENTILES_H
|
||||
151
src_main/console/piscreentypes.h
Normal file
151
src_main/console/piscreentypes.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*! \file piscreentypes.h
|
||||
* \brief Types for PIScreen
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Types for PIScreen
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISCREENTYPES_H
|
||||
#define PISCREENTYPES_H
|
||||
|
||||
#include "pivariant.h"
|
||||
|
||||
class PIScreenTile;
|
||||
|
||||
namespace PIScreenTypes {
|
||||
|
||||
//! Color for chars or background
|
||||
enum Color {
|
||||
Default /** Default */,
|
||||
Black /** Black */,
|
||||
Red /** Red */,
|
||||
Green /** Green */,
|
||||
Blue /** Blue */,
|
||||
Cyan /** Cyan */,
|
||||
Magenta /** Magenta */,
|
||||
Yellow /** Yellow */,
|
||||
White /** White */,
|
||||
Transparent /** Save previous color */
|
||||
};
|
||||
|
||||
//! Flags for chars
|
||||
enum CharFlag {
|
||||
Bold /** Bold or bright */ = 0x1,
|
||||
Blink /** Blink text */ = 0x2,
|
||||
Underline /** Underline text */ = 0x4,
|
||||
Inverse = 0x08
|
||||
};
|
||||
|
||||
//! Alignment
|
||||
enum Alignment {
|
||||
Left /** Left */ ,
|
||||
Center /** Center */ ,
|
||||
Right /** Right */
|
||||
};
|
||||
|
||||
//! Size policy
|
||||
enum SizePolicy {
|
||||
Fixed /** Fixed size */ ,
|
||||
Preferred /** Preferred size */ ,
|
||||
Expanding /** Maximum available size */ ,
|
||||
Ignore /** Ignore layout logic */
|
||||
};
|
||||
|
||||
//! Direction
|
||||
enum Direction {
|
||||
Horizontal /** Horizontal */ ,
|
||||
Vertical /** Vertical */
|
||||
};
|
||||
|
||||
//! Position
|
||||
enum Position {
|
||||
//Left /** Left */ ,
|
||||
//Right /** Right */ ,
|
||||
//Top /** Top */ ,
|
||||
//Bottom /** Bottom */
|
||||
};
|
||||
|
||||
//! Focus flags
|
||||
enum FocusFlag {
|
||||
CanHasFocus /** Tile can has focus */ = 0x1,
|
||||
NextByTab /** Focus passed to next tile by tab key */ = 0x2,
|
||||
NextByArrowsHorizontal /** Focus passed to next tile by arrow keys left or right */ = 0x4,
|
||||
NextByArrowsVertical /** Focus passed to next tile by arrow keys up or down */ = 0x8,
|
||||
NextByArrowsAll /** Focus passed to next tile by any arrow key */ = NextByArrowsHorizontal | NextByArrowsVertical
|
||||
};
|
||||
|
||||
typedef PIFlags<CharFlag> CharFlags;
|
||||
typedef PIFlags<FocusFlag> FocusFlags;
|
||||
|
||||
union CellFormat {
|
||||
CellFormat(uint f = 0) {raw_format = f;}
|
||||
CellFormat(Color col_char, Color col_back = Default, CharFlags flags_ = 0) {
|
||||
color_char = col_char;
|
||||
color_back = col_back;
|
||||
flags = flags_;
|
||||
}
|
||||
uint raw_format;
|
||||
struct {
|
||||
uchar color_char;
|
||||
uchar color_back;
|
||||
ushort flags;
|
||||
};
|
||||
bool operator ==(const CellFormat & c) const {return raw_format == c.raw_format;}
|
||||
bool operator !=(const CellFormat & c) const {return raw_format != c.raw_format;}
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
Cell(PIChar c = PIChar(' '), CellFormat f = CellFormat()) {symbol = c; format = f;}
|
||||
CellFormat format;
|
||||
PIChar symbol;
|
||||
bool operator ==(const Cell & c) const {return format == c.format && symbol == c.symbol;}
|
||||
bool operator !=(const Cell & c) const {return format != c.format || symbol != c.symbol;}
|
||||
Cell & operator =(const Cell & c) {
|
||||
symbol = c.symbol;
|
||||
if (c.format.color_back == Transparent) {
|
||||
format.color_char = c.format.color_char;
|
||||
format.flags = c.format.flags;
|
||||
} else format = c.format;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct TileEvent {
|
||||
TileEvent(int t = -1, const PIVariant & d = PIVariant()): type(t), data(d) {}
|
||||
int type;
|
||||
PIVariant data;
|
||||
};
|
||||
|
||||
class PIScreenBase {
|
||||
public:
|
||||
PIScreenBase() {}
|
||||
virtual ~PIScreenBase() {}
|
||||
virtual void tileEventInternal(PIScreenTile * , TileEvent) {}
|
||||
virtual void tileRemovedInternal(PIScreenTile * ) {}
|
||||
virtual void tileSetFocusInternal(PIScreenTile * ) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & s, const PIScreenTypes::Cell & v) {s << v.symbol << v.format.raw_format; return s;}
|
||||
|
||||
inline PIByteArray & operator >>(PIByteArray & s, PIScreenTypes::Cell & v) {s >> v.symbol >> v.format.raw_format; return s;}
|
||||
|
||||
|
||||
#endif // PISCREENTYPES_H
|
||||
896
src_main/console/piterminal.cpp
Normal file
896
src_main/console/piterminal.cpp
Normal file
@@ -0,0 +1,896 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "piincludes_p.h"
|
||||
#include "piterminal.h"
|
||||
# include "pisharedmemory.h"
|
||||
#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
|
||||
#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
|
||||
if (PRIVATE->fd == 0) return;
|
||||
//ssize_t wrote = 0;
|
||||
//wrote =
|
||||
::write(PRIVATE->fd, d.data(), d.size_s());
|
||||
//piCout << "wrote" << wrote << d;
|
||||
#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).toConcole1Byte());
|
||||
#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
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
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
|
||||
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
|
||||
if (PRIVATE->pid != 0)
|
||||
kill(PRIVATE->pid, SIGKILL);
|
||||
if (PRIVATE->fd != 0)
|
||||
::close(PRIVATE->fd);
|
||||
#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
|
||||
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
|
||||
cells.resize(size_y);
|
||||
for (int i = 0; i < size_y; ++i)
|
||||
cells[i].resize(size_x);
|
||||
return ret;
|
||||
}
|
||||
76
src_main/console/piterminal.h
Normal file
76
src_main/console/piterminal.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*! \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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PITERMINAL_H
|
||||
#define PITERMINAL_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);
|
||||
void write(PIKbdListener::KeyEvent ke);
|
||||
PIVector<PIVector<PIScreenTypes::Cell> > 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();
|
||||
#ifndef WINDOWS
|
||||
void parseInput(const PIString & s);
|
||||
bool isCompleteEscSeq(const PIString & es);
|
||||
void applyEscSeq(PIString es);
|
||||
void moveCursor(int dx, int dy);
|
||||
int termType(const PIString & t);
|
||||
#endif
|
||||
|
||||
PRIVATE_DECLARATION
|
||||
int dsize_x, dsize_y;
|
||||
int size_x, size_y, cursor_x, cursor_y;
|
||||
bool cursor_blink, cursor_visible;
|
||||
PITimeMeasurer cursor_tm;
|
||||
PIVector<PIVector<PIScreenTypes::Cell> > cells;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // PITERMINAL_H
|
||||
Reference in New Issue
Block a user