* 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",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "(gdb) cursesPty starten",
|
"name": "(gdb) start NCursesPtyApp",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/build/NCursesPtyApp",
|
"program": "${workspaceFolder}/build/NCursesPtyApp",
|
||||||
|
@ -27,7 +27,26 @@
|
||||||
"ignoreFailures": true
|
"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(LIBVTERM_LIBRARY vterm)
|
||||||
find_library(UTIL_LIBRARY util)
|
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})
|
target_link_libraries(NCursesPtyWindow ${CURSES_LIBRARIES} ${LIBVTERM_LIBRARY} ${UTIL_LIBRARY})
|
||||||
|
|
||||||
### threads
|
### threads
|
||||||
|
|
|
@ -42,17 +42,18 @@ namespace krikkel::NCursesPtyWindow
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int input;
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
input = ptyWindow->getch();
|
SingleUserInput input = ptyWindow->readSingleUserInput();
|
||||||
if(input == KEY_RESIZE)
|
if(input.isNormalKey())
|
||||||
|
ptyWindow->writeUnicodeCharToClient(input.getRawInput());
|
||||||
|
if(input.isFunctionKey())
|
||||||
{
|
{
|
||||||
|
if(input.getRawInput() == KEY_RESIZE)
|
||||||
ptyWindow->wresize(Root_Window->lines(), Root_Window->cols());
|
ptyWindow->wresize(Root_Window->lines(), Root_Window->cols());
|
||||||
continue;
|
else
|
||||||
|
ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm());
|
||||||
}
|
}
|
||||||
|
|
||||||
ptyWindow->writeToClient((char *) &input, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
#include <gsl/gsl>
|
#include <gsl/gsl>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
namespace krikkel::NCursesPtyWindow
|
namespace krikkel::NCursesPtyWindow
|
||||||
{
|
{
|
||||||
using gsl::narrow;
|
using gsl::narrow;
|
||||||
|
using std::lock_guard;
|
||||||
|
|
||||||
NCursesPtyWindow::NCursesPtyWindow(int lines, int columns, int y, int x)
|
NCursesPtyWindow::NCursesPtyWindow(int lines, int columns, int y, int x)
|
||||||
: NCursesWindow(lines, columns, y, x)
|
: NCursesWindow(lines, columns, y, x)
|
||||||
|
@ -38,13 +40,12 @@ namespace krikkel::NCursesPtyWindow
|
||||||
vterm_screen_reset(pseudoTerminalScreen, true);
|
vterm_screen_reset(pseudoTerminalScreen, true);
|
||||||
vterm_screen_set_callbacks(pseudoTerminalScreen, &screenCallbacks, this);
|
vterm_screen_set_callbacks(pseudoTerminalScreen, &screenCallbacks, this);
|
||||||
vterm_output_set_callback(pseudoTerminal, &staticHandlerOutput, 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
|
//raw(); //— cbreak might suffice
|
||||||
//noecho(); — already set
|
//noecho(); — already set
|
||||||
//nodelay(true); — @todo needs some reprogramming
|
//nodelay(true); — @todo needs some reprogramming
|
||||||
keypad(false);
|
keypad(true);
|
||||||
nonl();
|
nonl();
|
||||||
|
|
||||||
/// @todo block all signals, this thread does not handle any
|
/// @todo block all signals, this thread does not handle any
|
||||||
|
@ -64,12 +65,31 @@ namespace krikkel::NCursesPtyWindow
|
||||||
return fdPtyClient;
|
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);
|
write(fdPtyHost, output, length);
|
||||||
__debug_log("written to PTY client: '" + __debug_make_bytes_printable(std::string(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()
|
void NCursesPtyWindow::readFromPtyClientThreadMethod()
|
||||||
{
|
{
|
||||||
/// @todo in theory, there is no need for a timeout or select …
|
/// @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);
|
size_t bytesRead = read(fdPtyHost, ptyClientOutputBuffer, PTY_CLIENT_OUTPUT_BUFFER_SIZE);
|
||||||
if(bytesRead != -1 && bytesRead != 0)
|
if(bytesRead != -1 && bytesRead != 0)
|
||||||
|
{
|
||||||
|
lock_guard writeLock(writeToPseudoTerminalMutex);
|
||||||
vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead);
|
vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead);
|
||||||
|
}
|
||||||
__debug_log("read from PTY client: '" + __debug_make_bytes_printable(std::string(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);
|
return ::wadd_wch(w, character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int NCursesPtyWindow::get_wch(wint_t *character)
|
||||||
|
{
|
||||||
|
return ::wget_wch(w, character);
|
||||||
|
}
|
||||||
|
|
||||||
int NCursesPtyWindow::refresh()
|
int NCursesPtyWindow::refresh()
|
||||||
{
|
{
|
||||||
move(cursorY, cursorX);
|
move(cursorY, cursorX);
|
||||||
|
@ -243,6 +271,7 @@ namespace krikkel::NCursesPtyWindow
|
||||||
/// resizing?
|
/// resizing?
|
||||||
int NCursesPtyWindow::wresize(int rows, int cols)
|
int NCursesPtyWindow::wresize(int rows, int cols)
|
||||||
{
|
{
|
||||||
|
std::lock_guard writeLock(writeToPseudoTerminalMutex);
|
||||||
winsize windowSize =
|
winsize windowSize =
|
||||||
{
|
{
|
||||||
.ws_row = narrow<unsigned short>(rows)
|
.ws_row = narrow<unsigned short>(rows)
|
||||||
|
@ -305,7 +334,7 @@ namespace krikkel::NCursesPtyWindow
|
||||||
void NCursesPtyWindow::staticHandlerOutput(const char *s, size_t len, void *user)
|
void NCursesPtyWindow::staticHandlerOutput(const char *s, size_t len, void *user)
|
||||||
{
|
{
|
||||||
NCursesPtyWindow *instance = static_cast<NCursesPtyWindow *>(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);
|
instance->writeToClient(s, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,11 +3,14 @@
|
||||||
* @author Christian Burger (christian@krikkel.de)
|
* @author Christian Burger (christian@krikkel.de)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "SingleUserInput.hpp"
|
||||||
|
|
||||||
#include <cursesw.h>
|
#include <cursesw.h>
|
||||||
#include <pty.h>
|
#include <pty.h>
|
||||||
#include <vterm.h>
|
#include <vterm.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#ifdef add_wch
|
#ifdef add_wch
|
||||||
inline void UNDEF(add_wch)(const cchar_t *character) { add_wch(character); }
|
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)
|
#define add_wch UNDEF(add_wch)
|
||||||
#endif
|
#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
|
namespace krikkel::NCursesPtyWindow
|
||||||
{
|
{
|
||||||
class NCursesPtyWindow : public NCursesWindow
|
class NCursesPtyWindow : public NCursesWindow
|
||||||
|
@ -24,9 +33,14 @@ namespace krikkel::NCursesPtyWindow
|
||||||
~NCursesPtyWindow();
|
~NCursesPtyWindow();
|
||||||
|
|
||||||
int getFdPtyClient() const;
|
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 add_wch(const cchar_t *character);
|
||||||
|
int get_wch(wint_t *character);
|
||||||
int refresh() override;
|
int refresh() override;
|
||||||
int wresize(int rows, int cols);
|
int wresize(int rows, int cols);
|
||||||
|
|
||||||
|
@ -34,10 +48,11 @@ namespace krikkel::NCursesPtyWindow
|
||||||
int fdPtyHost, fdPtyClient;
|
int fdPtyHost, fdPtyClient;
|
||||||
struct termios terminalParameters;
|
struct termios terminalParameters;
|
||||||
VTerm *pseudoTerminal;
|
VTerm *pseudoTerminal;
|
||||||
|
std::mutex writeToPseudoTerminalMutex;
|
||||||
VTermScreen *pseudoTerminalScreen;
|
VTermScreen *pseudoTerminalScreen;
|
||||||
static VTermScreenCallbacks screenCallbacks;
|
static VTermScreenCallbacks screenCallbacks;
|
||||||
/// @todo one line is at most 4096 chars long
|
/// @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];
|
char ptyClientOutputBuffer[PTY_CLIENT_OUTPUT_BUFFER_SIZE];
|
||||||
uint16_t cursorX, cursorY;
|
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