* using `get_wch()` now and mapping most of the function keys documented in `vterm_keycodes.h` (not num keypad) * there are still a lot of mappings from ncurses missing (in conjunction with modifier keys, related issue #11) * using mutex to make sure writing to terminal client is serialized
This commit is contained in:
parent
34cebb1019
commit
ef07c3ac06
23
.vscode/launch.json
vendored
23
.vscode/launch.json
vendored
|
@ -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
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
if(input.getRawInput() == KEY_RESIZE)
|
||||
ptyWindow->wresize(Root_Window->lines(), Root_Window->cols());
|
||||
continue;
|
||||
else
|
||||
ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm());
|
||||
}
|
||||
|
||||
ptyWindow->writeToClient((char *) &input, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
#include <gsl/gsl>
|
||||
#include <cctype>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
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)
|
||||
{
|
||||
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<unsigned short>(rows)
|
||||
|
@ -305,7 +334,7 @@ namespace krikkel::NCursesPtyWindow
|
|||
void NCursesPtyWindow::staticHandlerOutput(const char *s, size_t len, void *user)
|
||||
{
|
||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(user);
|
||||
for(size_t index = 0; index < len; ++index)
|
||||
__debug_log("output handler writing to client: '" + __debug_make_bytes_printable(std::string(s, len)) + "'");
|
||||
instance->writeToClient(s, len);
|
||||
}
|
||||
}
|
|
@ -3,11 +3,14 @@
|
|||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "SingleUserInput.hpp"
|
||||
|
||||
#include <cursesw.h>
|
||||
#include <pty.h>
|
||||
#include <vterm.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#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;
|
||||
|
||||
|
|
106
SingleUserInput.cpp
Normal file
106
SingleUserInput.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "SingleUserInput.hpp"
|
||||
#include "Debug.cpp"
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <vterm.h>
|
||||
|
||||
#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<VTermKey>(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
|
||||
}
|
||||
}
|
26
SingleUserInput.hpp
Normal file
26
SingleUserInput.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E
|
||||
#define F0E30ED4_3883_40D6_A6EE_08BA4DF9E92E
|
||||
|
||||
#include <cursesw.h>
|
||||
#include <vterm_keycodes.h>
|
||||
|
||||
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 */
|
Loading…
Reference in a new issue