283 lines
9.8 KiB
C++
283 lines
9.8 KiB
C++
/**
|
|
* @author Christian Burger (christian@krikkel.de)
|
|
*/
|
|
|
|
#include "NCursesPtyWindow.hpp"
|
|
#include "Debug.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <unistd.h>
|
|
#include <sys/select.h>
|
|
#include <gsl/gsl>
|
|
#include <cctype>
|
|
#include <termios.h>
|
|
|
|
namespace krikkel::NCursesPty
|
|
{
|
|
using gsl::narrow;
|
|
|
|
NCursesPtyWindow::NCursesPtyWindow(int lines, int columns, int y, int x)
|
|
: NCursesWindow(lines, columns, y, x)
|
|
{
|
|
// to get the original terminal we need to shutdown ncurses for a moment
|
|
::endwin();
|
|
tcgetattr(STDIN_FILENO, &terminalParameters);
|
|
::refresh();
|
|
|
|
winsize windowSize =
|
|
{
|
|
.ws_row = narrow<short unsigned int>(this->height())
|
|
, .ws_col = narrow<short unsigned int>(this->width())
|
|
};
|
|
openpty(&fdPtyHost, &fdPtyClient, NULL, &terminalParameters, &windowSize);
|
|
|
|
pseudoTerminal = vterm_new(windowSize.ws_row, windowSize.ws_col);
|
|
__debug_log("window size (x: " + std::to_string(windowSize.ws_col) + ", y: " + std::to_string(windowSize.ws_row) + ")");
|
|
vterm_set_utf8(pseudoTerminal, true);
|
|
pseudoTerminalScreen = vterm_obtain_screen(pseudoTerminal);
|
|
vterm_screen_reset(pseudoTerminalScreen, true);
|
|
vterm_screen_set_callbacks(pseudoTerminalScreen, &screenCallbacks, this);
|
|
|
|
// the terminal is doing most of the work
|
|
keypad(false);
|
|
|
|
/// @todo block all signals, this thread does not handle any
|
|
readPtyClientThread = std::thread(&NCursesPtyWindow::readFromPtyClientThreadMethod, this);
|
|
}
|
|
|
|
NCursesPtyWindow::~NCursesPtyWindow()
|
|
{
|
|
close(fdPtyHost);
|
|
close(fdPtyClient);
|
|
vterm_free(pseudoTerminal);
|
|
}
|
|
|
|
int NCursesPtyWindow::getFdPtyClient() const
|
|
{
|
|
return fdPtyClient;
|
|
}
|
|
|
|
/// @todo maybe implement a function with a string buffer
|
|
void NCursesPtyWindow::writeToClient(char character)
|
|
{
|
|
//__debug_log(std::string("written '") + character + "' to client");
|
|
write(fdPtyHost, &character, sizeof(character));
|
|
}
|
|
|
|
void NCursesPtyWindow::readFromPtyClientThreadMethod()
|
|
{
|
|
/// @todo in theory, there is no need for a timeout or select …
|
|
/// file descriptor is blocking
|
|
while(1)
|
|
{
|
|
readFromPtyClient();
|
|
struct timeval timeout = { .tv_sec = 0, .tv_usec = 200000 };
|
|
fd_set readFds;
|
|
|
|
FD_ZERO(&readFds);
|
|
FD_SET(fdPtyHost, &readFds);
|
|
if(select(fdPtyHost + 1, &readFds, NULL, NULL, &timeout) < 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NCursesPtyWindow::readFromPtyClient()
|
|
{
|
|
size_t bytesRead = read(fdPtyHost, clientOutputBuffer, CLIENT_OUTPUT_BUFFER_SIZE);
|
|
if(bytesRead)
|
|
vterm_input_write(pseudoTerminal, clientOutputBuffer, bytesRead);
|
|
__debug_log("read " + std::to_string(bytesRead) + " bytes from PTY client");
|
|
/*string readable;
|
|
for(char character : clientOutputBuffer)
|
|
if(std::isprint((unsigned char) character))
|
|
readable += character;
|
|
else
|
|
readable += "\\" + std::to_string((int) character) + "/";
|
|
__debug_log("read: '" + readable + "'");*/
|
|
}
|
|
|
|
VTermScreenCallbacks NCursesPtyWindow::screenCallbacks =
|
|
{
|
|
.damage = staticHandlerDamage,
|
|
/* .moverect = staticHandlerMoveRect,
|
|
.movecursor = staticHandlerMoveCursor,
|
|
.settermprop = staticHandlerSetTermProp,
|
|
.bell = staticHandlerBell,
|
|
.resize = staticHandlerResize,
|
|
.sb_pushline = staticHandlerPushLine,
|
|
.sb_popline = staticHandlerPopLine,*/
|
|
};
|
|
|
|
int NCursesPtyWindow::handlerDamage(VTermRect rect)
|
|
{
|
|
__debug_log("damage to rectangle ("
|
|
+ std::to_string(rect.start_col) + ", "
|
|
+ std::to_string(rect.start_row) + ", "
|
|
+ std::to_string(rect.end_col) + ", "
|
|
+ std::to_string(rect.end_row) + ") "
|
|
+ "size: " + std::to_string((rect.start_col - rect.end_col) * (rect.start_row - rect.end_row)) );
|
|
|
|
int cursorX, cursorY;
|
|
getyx(cursorY, cursorX);
|
|
|
|
for(int x = rect.start_col; x < rect.end_col; ++x)
|
|
for(int y = rect.start_row; y < rect.end_row; ++y)
|
|
copyPtyCellToNCursesWindow(x, y);
|
|
|
|
move(cursorY, cursorX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerMoveRect(VTermRect dest, VTermRect src)
|
|
{
|
|
__debug_log("(unimplemented) move content in rectangle from ("
|
|
+ std::to_string(src.start_col) + ", "
|
|
+ std::to_string(src.start_row) + ", "
|
|
+ std::to_string(src.end_col) + ", "
|
|
+ std::to_string(src.end_row) + ") "
|
|
+ "size: " + std::to_string((src.start_col - src.end_col) * (src.start_row - src.end_row))
|
|
+ " to ("
|
|
+ std::to_string(dest.start_col) + ", "
|
|
+ std::to_string(dest.start_row) + ", "
|
|
+ std::to_string(dest.end_col) + ", "
|
|
+ std::to_string(dest.end_row) + ") "
|
|
+ "size: " + std::to_string((dest.start_col - dest.end_col) * (dest.start_row - dest.end_row)));
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible)
|
|
{
|
|
__debug_log("cursor moved to ("
|
|
+ std::to_string(pos.col) + ", "
|
|
+ std::to_string(pos.row) + ") "
|
|
+ "visible: " + (visible ? "true" : "false"));
|
|
move(pos.row, pos.col);
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerSetTermProp(VTermProp prop, VTermValue *val)
|
|
{
|
|
|
|
__debug_log(std::string("(unimplemented) set terminal property: ")
|
|
+ (prop == VTERM_PROP_CURSORVISIBLE
|
|
? std::string("cursor visible = ")
|
|
+ (val->boolean ? "true" : "false")
|
|
: "")
|
|
+ (prop == VTERM_PROP_CURSORBLINK
|
|
? std::string("cursor blink = ")
|
|
+ (val->boolean ? "true" : "false")
|
|
: "")
|
|
+ (prop == VTERM_PROP_ALTSCREEN
|
|
? std::string("alt screen = ")
|
|
+ (val->boolean ? "true" : "false")
|
|
: "")
|
|
+ (prop == VTERM_PROP_TITLE
|
|
? std::string("title = ")
|
|
+ val->string
|
|
: "")
|
|
+ (prop == VTERM_PROP_ICONNAME
|
|
? std::string("icon name = ")
|
|
+ val->string
|
|
: "")
|
|
+ (prop == VTERM_PROP_REVERSE
|
|
? std::string("alt screen = ")
|
|
+ (val->boolean ? "true" : "false")
|
|
: "")
|
|
+ (prop == VTERM_PROP_CURSORSHAPE
|
|
? std::string("icon name = ")
|
|
+ std::to_string(val->number)
|
|
: "")
|
|
+ (prop == VTERM_PROP_MOUSE
|
|
? std::string("mouse = ")
|
|
+ std::to_string(val->number)
|
|
: "")
|
|
+ (prop == VTERM_N_PROPS
|
|
? "n props" : "")
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerBell()
|
|
{
|
|
beep();
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerResize(int rows, int cols)
|
|
{
|
|
__debug_log("unimplemented handler called");
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerPushLine(int cols, const VTermScreenCell *cells)
|
|
{
|
|
__debug_log("(unimplemented) push line with " + std::to_string(cols) + " columns");
|
|
return 0;
|
|
}
|
|
|
|
int NCursesPtyWindow::handlerPopLine(int cols, VTermScreenCell *cells)
|
|
{
|
|
__debug_log("unimplemented handler called");
|
|
return 0;
|
|
}
|
|
|
|
void NCursesPtyWindow::copyPtyCellToNCursesWindow(int x, int y)
|
|
{
|
|
VTermPos cellPosition = {y, x};
|
|
VTermScreenCell cell;
|
|
vterm_screen_get_cell(pseudoTerminalScreen, cellPosition, &cell);
|
|
move(y, x);
|
|
addch(cell.chars[0] ? cell.chars[0] : ' ' );
|
|
/// @todo boy, is this expensive …
|
|
refresh();
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerDamage(VTermRect rect, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerDamage(rect);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerMoveRect(VTermRect dest, VTermRect src, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerMoveRect(dest, src);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerMoveCursor(pos, oldpos, visible);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerSetTermProp(VTermProp prop, VTermValue *val, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerSetTermProp(prop, val);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerBell(void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerBell();
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerResize(int rows, int cols, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerResize(rows, cols);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerPushLine(cols, cells);
|
|
}
|
|
|
|
int NCursesPtyWindow::staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user)
|
|
{
|
|
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
|
return instance->handlerPopLine(cols, cells);
|
|
}
|
|
} |