diff --git a/.vscode/launch.json b/.vscode/launch.json index 83e446a..0553753 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) cursesPty starten", + "name": "(gdb) start NCursesPtyApp", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/NCursesPtyApp", @@ -27,7 +27,26 @@ "ignoreFailures": true } ] - } - + }, + { + "name": "(gdb) attach to running NCursesPtyApp", + "type": "cppdbg", + "request": "attach", + "program": "${workspaceFolder}/build/NCursesPtyApp", + "processId": "${command:pickProcess}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Disassemblierungsvariante auf Intel festlegen", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2472c31..e8be85d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ include("cmake/gsl.cmake") find_library(LIBVTERM_LIBRARY vterm) find_library(UTIL_LIBRARY util) -add_library(NCursesPtyWindow NCursesPtyWindow.cpp Debug.cpp) +add_library(NCursesPtyWindow NCursesPtyWindow.cpp SingleUserInput.cpp Debug.cpp) target_link_libraries(NCursesPtyWindow ${CURSES_LIBRARIES} ${LIBVTERM_LIBRARY} ${UTIL_LIBRARY}) ### threads diff --git a/NCursesPtyApp.cpp b/NCursesPtyApp.cpp index 84d445c..ac38fd9 100644 --- a/NCursesPtyApp.cpp +++ b/NCursesPtyApp.cpp @@ -42,17 +42,18 @@ namespace krikkel::NCursesPtyWindow exit(1); } - int input; while(true) { - input = ptyWindow->getch(); - if(input == KEY_RESIZE) + SingleUserInput input = ptyWindow->readSingleUserInput(); + if(input.isNormalKey()) + ptyWindow->writeUnicodeCharToClient(input.getRawInput()); + if(input.isFunctionKey()) { - ptyWindow->wresize(Root_Window->lines(), Root_Window->cols()); - continue; + if(input.getRawInput() == KEY_RESIZE) + ptyWindow->wresize(Root_Window->lines(), Root_Window->cols()); + else + ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm()); } - - ptyWindow->writeToClient((char *) &input, 1); } return 0; diff --git a/NCursesPtyWindow.cpp b/NCursesPtyWindow.cpp index bed2651..05b62a3 100644 --- a/NCursesPtyWindow.cpp +++ b/NCursesPtyWindow.cpp @@ -11,10 +11,12 @@ #include #include #include +#include namespace krikkel::NCursesPtyWindow { using gsl::narrow; + using std::lock_guard; NCursesPtyWindow::NCursesPtyWindow(int lines, int columns, int y, int x) : NCursesWindow(lines, columns, y, x) @@ -38,13 +40,12 @@ namespace krikkel::NCursesPtyWindow vterm_screen_reset(pseudoTerminalScreen, true); vterm_screen_set_callbacks(pseudoTerminalScreen, &screenCallbacks, this); vterm_output_set_callback(pseudoTerminal, &staticHandlerOutput, this); - //vterm_screen_enable_altscreen(pseudoTerminalScreen, true); + vterm_screen_enable_altscreen(pseudoTerminalScreen, true); - // the terminal is doing most of the work //raw(); //— cbreak might suffice //noecho(); — already set //nodelay(true); — @todo needs some reprogramming - keypad(false); + keypad(true); nonl(); /// @todo block all signals, this thread does not handle any @@ -64,12 +65,31 @@ namespace krikkel::NCursesPtyWindow return fdPtyClient; } - void NCursesPtyWindow::writeToClient(const char *output, size_t length) const + void NCursesPtyWindow::writeToClient(const char *output, size_t length) { - write(fdPtyHost, output, length); + lock_guard writeLock(writeToPseudoTerminalMutex); + write(fdPtyHost, output, length); __debug_log("written to PTY client: '" + __debug_make_bytes_printable(std::string(output, length)) + '\''); } + void NCursesPtyWindow::writeUnicodeCharToClient(wint_t character) + { + vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE); + } + + void NCursesPtyWindow::writeKeyToClient(VTermKey key) + { + __debug_log("writing key: " + std::to_string(key)); + vterm_keyboard_key(pseudoTerminal, key, VTERM_MOD_NONE); + } + + SingleUserInput NCursesPtyWindow::readSingleUserInput() + { + wint_t input; + int result = get_wch(&input); + return SingleUserInput(result, input); + } + void NCursesPtyWindow::readFromPtyClientThreadMethod() { /// @todo in theory, there is no need for a timeout or select … @@ -91,7 +111,10 @@ namespace krikkel::NCursesPtyWindow { size_t bytesRead = read(fdPtyHost, ptyClientOutputBuffer, PTY_CLIENT_OUTPUT_BUFFER_SIZE); if(bytesRead != -1 && bytesRead != 0) + { + lock_guard writeLock(writeToPseudoTerminalMutex); vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead); + } __debug_log("read from PTY client: '" + __debug_make_bytes_printable(std::string(ptyClientOutputBuffer, bytesRead)) + '\''); } @@ -233,6 +256,11 @@ namespace krikkel::NCursesPtyWindow return ::wadd_wch(w, character); } + int NCursesPtyWindow::get_wch(wint_t *character) + { + return ::wget_wch(w, character); + } + int NCursesPtyWindow::refresh() { move(cursorY, cursorX); @@ -243,6 +271,7 @@ namespace krikkel::NCursesPtyWindow /// resizing? int NCursesPtyWindow::wresize(int rows, int cols) { + std::lock_guard writeLock(writeToPseudoTerminalMutex); winsize windowSize = { .ws_row = narrow(rows) @@ -305,7 +334,7 @@ namespace krikkel::NCursesPtyWindow void NCursesPtyWindow::staticHandlerOutput(const char *s, size_t len, void *user) { NCursesPtyWindow *instance = static_cast(user); - for(size_t index = 0; index < len; ++index) - instance->writeToClient(s, len); + __debug_log("output handler writing to client: '" + __debug_make_bytes_printable(std::string(s, len)) + "'"); + instance->writeToClient(s, len); } } \ No newline at end of file diff --git a/NCursesPtyWindow.hpp b/NCursesPtyWindow.hpp index 404e956..cf19a1f 100644 --- a/NCursesPtyWindow.hpp +++ b/NCursesPtyWindow.hpp @@ -3,11 +3,14 @@ * @author Christian Burger (christian@krikkel.de) */ +#include "SingleUserInput.hpp" + #include #include #include #include #include +#include #ifdef add_wch inline void UNDEF(add_wch)(const cchar_t *character) { add_wch(character); } @@ -15,6 +18,12 @@ inline void UNDEF(add_wch)(const cchar_t *character) { add_wch(character); } #define add_wch UNDEF(add_wch) #endif +#ifdef get_wch +inline void UNDEF(get_wch)(wint_t *character) { get_wch(character); } +#undef get_wch +#define get_wch UNDEF(get_wch) +#endif + namespace krikkel::NCursesPtyWindow { class NCursesPtyWindow : public NCursesWindow @@ -24,9 +33,14 @@ namespace krikkel::NCursesPtyWindow ~NCursesPtyWindow(); int getFdPtyClient() const; - void writeToClient(const char * string, size_t length) const; + void writeToClient(const char * string, size_t length); + void writeUnicodeCharToClient(wint_t character); + void writeKeyToClient(VTermKey key); + + SingleUserInput readSingleUserInput(); int add_wch(const cchar_t *character); + int get_wch(wint_t *character); int refresh() override; int wresize(int rows, int cols); @@ -34,10 +48,11 @@ namespace krikkel::NCursesPtyWindow int fdPtyHost, fdPtyClient; struct termios terminalParameters; VTerm *pseudoTerminal; + std::mutex writeToPseudoTerminalMutex; VTermScreen *pseudoTerminalScreen; static VTermScreenCallbacks screenCallbacks; /// @todo one line is at most 4096 chars long - static const size_t PTY_CLIENT_OUTPUT_BUFFER_SIZE = 8192; + static const uint16_t PTY_CLIENT_OUTPUT_BUFFER_SIZE = 2 * 4096; char ptyClientOutputBuffer[PTY_CLIENT_OUTPUT_BUFFER_SIZE]; uint16_t cursorX, cursorY; @@ -67,5 +82,5 @@ namespace krikkel::NCursesPtyWindow static int staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user); static int staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user); static void staticHandlerOutput(const char *s, size_t len, void *user); - }; + }; } \ No newline at end of file diff --git a/SingleUserInput.cpp b/SingleUserInput.cpp new file mode 100644 index 0000000..1663a0f --- /dev/null +++ b/SingleUserInput.cpp @@ -0,0 +1,106 @@ +/** + * @author Christian Burger (christian@krikkel.de) + */ + +#include "SingleUserInput.hpp" +#include "Debug.cpp" + +#include +#include + +#define KEY_TAB '\t' +#define KEY_ESCAPE 27 +#define KEY_RETURN 10 +#define KEY_ALT_BACKSPACE 127 +#define KEY_ALT_ALT_BACKSPACE '\b' + +namespace krikkel::NCursesPtyWindow +{ + SingleUserInput::SingleUserInput(int _resultGetWchCall, wint_t _input) + : input(_input), resultGetWchCall(_resultGetWchCall) + { + + } + + bool SingleUserInput::isNormalKey() + { + return resultGetWchCall == OK; + } + + bool SingleUserInput::isFunctionKey() + { + return resultGetWchCall == KEY_CODE_YES; + } + + VTermKey SingleUserInput::mapKeyNcursesToVTerm() + { + if(input >= KEY_MAX) + return VTERM_KEY_NONE; + + debug(); + + // thought about array mapping instead of `switch()` statements, but as + // it is only processing user input, it is not time critical enough for + // the wasted space + + // @tode unmapped keys: keys erase + if(resultGetWchCall == OK) + switch(input) + { + case KEY_TAB: + return VTERM_KEY_TAB; + case KEY_ESCAPE: + return VTERM_KEY_ESCAPE; + default: + ; // we cannot translate + } + + if(resultGetWchCall == KEY_CODE_YES) + switch(input) + { + case KEY_ENTER: + return VTERM_KEY_ENTER; + case KEY_BACKSPACE: + return VTERM_KEY_BACKSPACE; + case KEY_UP: + return VTERM_KEY_UP; + case KEY_DOWN: + return VTERM_KEY_DOWN; + case KEY_LEFT: + return VTERM_KEY_LEFT; + case KEY_RIGHT: + return VTERM_KEY_RIGHT; + case KEY_IC: + return VTERM_KEY_INS; + case KEY_DC: + return VTERM_KEY_DEL; + case KEY_HOME: + return VTERM_KEY_HOME; + case KEY_END: + return VTERM_KEY_END; + case KEY_PPAGE: + return VTERM_KEY_PAGEUP; + case KEY_NPAGE: + return VTERM_KEY_PAGEDOWN; + default: + if(input >= KEY_F0 && input < KEY_F0 + 12) + return static_cast(VTERM_KEY_FUNCTION(input - KEY_F0)); + } + __debug_log("previous ncurses input could not be decoded to vterm input"); + return VTERM_KEY_NONE; + } + + wint_t SingleUserInput::getRawInput() + { + return input; + } + + void SingleUserInput::debug() + { +#ifndef NDEGUG + char octalRepresentation[16]; + snprintf(octalRepresentation, 16, "%o", input); + __debug_log("mapping ncurses key: " + std::to_string(input) + " octal: " + octalRepresentation); +#endif // NDEBUG + } +} \ No newline at end of file diff --git a/SingleUserInput.hpp b/SingleUserInput.hpp new file mode 100644 index 0000000..b0ec8e6 --- /dev/null +++ b/SingleUserInput.hpp @@ -0,0 +1,26 @@ +#ifndef F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E +#define F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E + +#include +#include + +namespace krikkel::NCursesPtyWindow +{ + class SingleUserInput + { + public: + SingleUserInput(int resultGetWchCall, wint_t input); + + bool isNormalKey(); + bool isFunctionKey(); + VTermKey mapKeyNcursesToVTerm(); + wint_t getRawInput(); + + private: + wint_t input; + int resultGetWchCall; + void debug(); + }; +} + +#endif /* F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E */