Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
Christian Burger | 8784bb2ce3 | ||
Christian Burger | 01b90fe0ef | ||
Christian Burger | 6fe1a7c5fb | ||
Christian Burger | c6218e9acc | ||
Christian Burger | 4b709c92fb | ||
Christian Burger | ba378b2a43 | ||
Christian Burger | 870fa80fc7 | ||
Christian Burger | f48641bb58 | ||
Christian Burger | 3be5336ca0 | ||
Christian Burger | 0a086ff604 | ||
Christian Burger | 9ddb769cb4 | ||
Christian Burger | 4fb2e6a976 | ||
Christian Burger | 7e7372ee52 | ||
Christian Burger | a801752620 | ||
Christian Burger | 792f12c96c | ||
Christian Burger | 1357a7f6bf | ||
Christian Burger | b723aa5f33 | ||
Christian Burger | 320f5ba63a | ||
Christian Burger | e42711f123 | ||
Christian Burger | b9e32941fb | ||
Christian Burger | 0462a68c54 | ||
Christian Burger | f8db9dc660 | ||
Christian Burger | 2601d78453 | ||
Christian Burger | 02266dfb0c | ||
Christian Burger | 78672521c4 | ||
Christian Burger | a300dd492b | ||
Christian Burger | ed534363a2 | ||
Christian Burger | 3af1284cf8 | ||
Christian Burger | 746e458cd2 | ||
Christian Burger | ae6130f095 | ||
Christian Burger | f994cb2261 | ||
Christian Burger | 2d7dda88c5 | ||
Christian Burger | bc76ae227c | ||
Christian Burger | d3bdb305d0 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
build/
|
||||
sandbox/
|
||||
VERSION
|
17
.vscode/c_cpp_properties.json
vendored
Normal file
17
.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${default}"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "gnu++14",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.cmake-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
|
@ -5,10 +5,10 @@
|
|||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) start NCursesPtyApp",
|
||||
"name": "(gdb) start kNCursesDemoApp",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/NCursesPtyApp",
|
||||
"program": "${workspaceFolder}/build/kNCursesDemoApp",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
@ -29,10 +29,10 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "(gdb) attach to running NCursesPtyApp",
|
||||
"name": "(gdb) attach to running kNCursesDemoApp",
|
||||
"type": "cppdbg",
|
||||
"request": "attach",
|
||||
"program": "${workspaceFolder}/build/NCursesPtyApp",
|
||||
"program": "${workspaceFolder}/build/kNCursesDemoApp",
|
||||
"processId": "${command:pickProcess}",
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
|
|
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
|
@ -53,6 +53,14 @@
|
|||
"iostream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"pointers": "cpp"
|
||||
}
|
||||
"pointers": "cpp",
|
||||
"list": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"mutex": "cpp",
|
||||
"gsl_util": "cpp"
|
||||
},
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
"C_Cpp.default.includePath": [
|
||||
"${workspaceFolder}/include"
|
||||
]
|
||||
}
|
63
App.cpp
63
App.cpp
|
@ -1,63 +0,0 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "App.hpp"
|
||||
#include "Window.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace krikkel::NCursesPtyWindow
|
||||
{
|
||||
App::App() : NCursesApplication(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int App::run()
|
||||
{
|
||||
std::mutex writeMutex;
|
||||
|
||||
Window *ptyWindow = new Window(&writeMutex
|
||||
, Root_Window->lines()
|
||||
, Root_Window->cols()
|
||||
, 0
|
||||
, 0);
|
||||
|
||||
if(fork() == 0)
|
||||
{
|
||||
const char *shellPath = getenv("SHELL");
|
||||
if(!shellPath)
|
||||
shellPath = "/bin/bash";
|
||||
|
||||
login_tty(ptyWindow->getFdPtyClient());
|
||||
const int sizeArg = 2;
|
||||
const char *arg[sizeArg] = {shellPath, NULL};
|
||||
fprintf(stderr, "<CTRL>+C exits shell.\n\n");
|
||||
execv(shellPath, const_cast<char * const *>(arg));
|
||||
fprintf(stderr, "Well, well, well … could not start a shell. We "
|
||||
"tried `%s`. Maybe set `SHELL` environment variable"
|
||||
" to a working value?", shellPath);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while(true)
|
||||
{
|
||||
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());
|
||||
else
|
||||
ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
23
App.hpp
23
App.hpp
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* @brief demo application for the library
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
#define A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
|
||||
#include <cursesapp.h>
|
||||
|
||||
namespace krikkel::NCursesPtyWindow
|
||||
{
|
||||
class App : public NCursesApplication
|
||||
{
|
||||
public:
|
||||
App();
|
||||
|
||||
private:
|
||||
int run() override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* A3B2AE4E_0A39_468C_8CCA_E6508166702A */
|
|
@ -1,48 +1,82 @@
|
|||
##
|
||||
# @author Christian Burger <christian@krikkel.de>
|
||||
|
||||
cmake_minimum_required(VERSION 3.16.3)
|
||||
project(NCursesPtyWindow VERSION 0.1.0)
|
||||
|
||||
include("cmake/version.cmake")
|
||||
project(kNCurses
|
||||
HOMEPAGE_URL "https://gitea.xndr.de/christian/kNCurses"
|
||||
VERSION ${SEMANTIC_VERSION})
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
|
||||
include(ExternalProject)
|
||||
include("cmake/ncurses.cmake")
|
||||
include("cmake/gsl.cmake")
|
||||
add_library(kNCurses src/krikkel/NCurses/Window.cpp
|
||||
src/krikkel/NCurses/PtyWindow.cpp
|
||||
src/krikkel/NCurses/SingleUserInput.cpp
|
||||
src/krikkel/NCurses/TilingWindowManager.cpp
|
||||
src/krikkel/NCurses/VerticalTilingWindowManager.cpp
|
||||
src/krikkel/NCurses/HorizontalTilingWindowManager.cpp
|
||||
src/krikkel/Debug.cpp
|
||||
)
|
||||
|
||||
add_library(NCursesPtyWindow Window.cpp SingleUserInput.cpp Debug.cpp)
|
||||
### let's be annoyingly pedantic
|
||||
target_compile_options(kNCurses PRIVATE "-Wall" "-Wextra" "-Werror" "-pedantic")
|
||||
|
||||
### path to own includes
|
||||
include_directories("${CMAKE_SOURCE_DIR}/include")
|
||||
|
||||
### libraries
|
||||
|
||||
include("cmake/ncurses.cmake")
|
||||
target_link_libraries(kNCurses ${CURSES_LIBRARIES})
|
||||
|
||||
include("cmake/gsl.cmake")
|
||||
|
||||
include("cmake/libvterm.cmake")
|
||||
if(EXISTS libvtermProject)
|
||||
add_dependencies(NCursesPtyWindow libvtermProject)
|
||||
add_dependencies(kNCurses libvtermProject)
|
||||
endif()
|
||||
target_link_libraries(kNCurses ${LIBVTERM_LIBRARY})
|
||||
|
||||
find_library(UTIL_LIBRARY util)
|
||||
target_link_libraries(kNCurses ${UTIL_LIBRARY})
|
||||
|
||||
target_link_libraries(NCursesPtyWindow ${CURSES_LIBRARIES} ${LIBVTERM_LIBRARY} ${UTIL_LIBRARY})
|
||||
|
||||
### threads
|
||||
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(NCursesPtyWindow Threads::Threads)
|
||||
target_link_libraries(kNCurses Threads::Threads)
|
||||
|
||||
### demo application
|
||||
add_executable(NCursesPtyApp main.cpp App.cpp)
|
||||
target_link_libraries(NCursesPtyApp ${CURSES_LIBRARIES} NCursesPtyWindow)
|
||||
add_executable(kNCursesDemoApp src/krikkel/NCurses/main.cpp
|
||||
src/krikkel/NCurses/DemoApp.cpp)
|
||||
target_compile_options(kNCursesDemoApp PRIVATE "-Wall" "-Wextra" "-Werror" "-pedantic")
|
||||
target_link_libraries(kNCursesDemoApp kNCurses)
|
||||
|
||||
### installation and packaging
|
||||
set_target_properties(NCursesPtyWindow PROPERTIES PUBLIC_HEADER "Window.hpp;SingleUserInput.hpp")
|
||||
install(TARGETS NCursesPtyWindow ARCHIVE PUBLIC_HEADER)
|
||||
set(KNCURSES_SYSTEM_INCLUDE "include/kNCurses")
|
||||
string(CONCAT KNCURSES_PUBLIC_HEADERS "${KNCURSES_SYSTEM_INCLUDE}/Window.hpp;"
|
||||
"${KNCURSES_SYSTEM_INCLUDE}/SingleUserInput.hpp;"
|
||||
"${KNCURSES_SYSTEM_INCLUDE}/PtyWindow.hpp;"
|
||||
"${KNCURSES_SYSTEM_INCLUDE}/TilingWindowManager.hpp;"
|
||||
"${KNCURSES_SYSTEM_INCLUDE}/VerticalTilingWindowManager.hpp;"
|
||||
"${KNCURSES_SYSTEM_INCLUDE}/HorizontalTilingWindowManager.hpp;"
|
||||
)
|
||||
set_target_properties(kNCurses PROPERTIES
|
||||
PUBLIC_HEADER "${KNCURSES_PUBLIC_HEADERS}"
|
||||
VERSION "${CMAKE_PROJECT_VERSION}")
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS kNCurses kNCursesDemoApp ARCHIVE
|
||||
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/kNCurses/")
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
set(CPACK_PACKAGE_CONTACT "Christian Burger <christian@krikkel.de>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "Library for a pseudo terminal where the host end is a ncurses window")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Library for a pseudo terminal where the host end is a ncurses window")
|
||||
# 3.16.3 does not pick up the project's homepage URL by itself
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CMAKE_PROJECT_HOMEPAGE_URL})
|
||||
|
||||
set(CPACK_GENERATOR "DEB" "TGZ")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libncursesw6 (>=6.2), libvterm-dev (>= 0.1.2)")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libncursesw6 (>=6.2), libvterm0 (>= 0.1.2)")
|
||||
include(CPack)
|
||||
|
|
503
Debug.cpp
503
Debug.cpp
|
@ -1,503 +0,0 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
* @todo Switch over to <seccomp.h>? For resolving system call numbers?
|
||||
* Maybe keep the current solution as a fallback?
|
||||
* @todo catch `out of range` exception from stoi()
|
||||
*
|
||||
* Contains mapping of system calls numbers to names from original dev system:
|
||||
* "Linux 5.13.0-28-generic #31~20.04.1-Ubuntu SMP \
|
||||
* Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux"
|
||||
* See comment at the end for the actual command to gather mapping information.
|
||||
*/
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
#include <syscall.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define BUFFER_SIZE 128
|
||||
|
||||
namespace krikkel
|
||||
{
|
||||
using std::string;
|
||||
using std::fstream;
|
||||
using std::filesystem::is_directory;
|
||||
using std::endl;
|
||||
using std::time;
|
||||
using std::localtime;
|
||||
using std::strftime;
|
||||
using std::min;
|
||||
|
||||
Debug::Debug()
|
||||
{
|
||||
string directory = ".";
|
||||
if(is_directory("sandbox"))
|
||||
directory = "./sandbox";
|
||||
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
|
||||
logFile << endl << endl << endl << endl << "New instance of class Debug." << endl;
|
||||
logFile << getUname();
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
string Debug::getUname()
|
||||
{
|
||||
string result = "";
|
||||
char buffer[BUFFER_SIZE];
|
||||
FILE *unameProcess = popen("uname -srvmpio", "r");
|
||||
|
||||
if(unameProcess && fgets(buffer, BUFFER_SIZE, unameProcess))
|
||||
result = buffer;
|
||||
pclose(unameProcess);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string Debug::getTimestamp()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
tm *localNow = localtime(&now);
|
||||
static char formattedLocalNow[32];
|
||||
strftime(formattedLocalNow, 32, "%c", localNow);
|
||||
|
||||
return formattedLocalNow;
|
||||
}
|
||||
|
||||
void Debug::log(string message, string fileName, int lineNo, string functionName)
|
||||
{
|
||||
string output = "";
|
||||
size_t position = message.find("sysCall(");
|
||||
|
||||
if(position != string::npos)
|
||||
{
|
||||
size_t end = message.find_first_not_of("0123456789", position + 8);
|
||||
unsigned long long systemCallNumber = std::stoi(message.substr(position + 8, end - position - 8));
|
||||
string systemCallName = strlen(syscalls[systemCallNumber]) != 0 ? syscalls[systemCallNumber]
|
||||
: "unknown_system_call";
|
||||
if(systemCallNumber < MAX_NUMBER_OF_SYSCALLS)
|
||||
output = message.replace(position, end - position + 1, systemCallName + "(");
|
||||
}
|
||||
if(output == "")
|
||||
output = message;
|
||||
|
||||
logFile << getTimestamp() << ": " << output << " (in "
|
||||
<< fileName.substr(fileName.rfind('/') + 1) << ":"
|
||||
<< lineNo << " in " << functionName << "())" << endl;
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
std::string Debug::peekData(pid_t shellPid, void *data, size_t length)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for(int index = 0; index < length / sizeof(long); ++index)
|
||||
{
|
||||
long datum = ptrace(PTRACE_PEEKDATA, shellPid, ((char *) data) + index * sizeof(long));
|
||||
if(length == -1)
|
||||
{
|
||||
size_t datumLength = strnlen((char *) &datum, sizeof(long));
|
||||
result += string((char *) &datum, datumLength);
|
||||
if(datumLength < sizeof(long))
|
||||
break;
|
||||
}
|
||||
else
|
||||
result += string((char *) &datum, sizeof(long));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Debug::formatToAddress(unsigned long long address)
|
||||
{
|
||||
char addressString[17];
|
||||
|
||||
snprintf(addressString, 17, "%016llx", address);
|
||||
|
||||
return string("0x") + addressString;
|
||||
}
|
||||
|
||||
void Debug::logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName)
|
||||
{
|
||||
const static uint8_t maxStringLength = 32;
|
||||
string message = "syscall: ";
|
||||
|
||||
string sysCallName;
|
||||
if(sysCallId < MAX_NUMBER_OF_SYSCALLS)
|
||||
sysCallName = syscalls[sysCallId];
|
||||
if(sysCallName.empty())
|
||||
sysCallName = "syscall" + std::to_string(sysCallId);
|
||||
|
||||
|
||||
switch(sysCallId)
|
||||
{
|
||||
case SYS_read:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == -1 ? "-1" : std::to_string((size_t) result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", \"" + (returnedFromSysCall ? __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength))) : "")
|
||||
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_write:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == -1 ? "-1" : std::to_string((size_t) result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
|
||||
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_close:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_stat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(\"" + peekData(shellPid, (char *) firstArgument, -1) + "\""
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_fstat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_openat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + ((int) firstArgument == AT_FDCWD ? string("AT_FDCWD") : std::to_string((int) firstArgument))
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, -1)) + "\""
|
||||
+ ", " + std::to_string((int) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_rt_sigprocmask:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ", " + formatToAddress(thirdArgument)
|
||||
+ ", " + std::to_string((size_t) fourthArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_access:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + peekData(shellPid, (char *) firstArgument, -1)
|
||||
+ ", " + std::to_string((int) secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_dup2:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + std::to_string((int) secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_fcntl:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + (secondArgument == 0 ? string("F_DUP_FD") : std::to_string((int) secondArgument))
|
||||
+ ", …"
|
||||
+ ")";
|
||||
break;
|
||||
/* case SYS_fstat:
|
||||
struct stat statbuf;
|
||||
message += "(" + std::to_string(firstArgument)
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
|
||||
+ (maxStringLength < thirdArgument ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
*/ default:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == -1 ? "-1" : std::to_string(result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + formatToAddress(firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ", " + formatToAddress(thirdArgument)
|
||||
+ ", " + formatToAddress(fourthArgument)
|
||||
+ ")";
|
||||
}
|
||||
// source: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||
|
||||
log(message, fileName, lineNo, functionName);
|
||||
}
|
||||
|
||||
Debug *Debug::getInstance()
|
||||
{
|
||||
static Debug *debug = new Debug();
|
||||
return debug;
|
||||
}
|
||||
|
||||
const char *Debug::syscalls[MAX_NUMBER_OF_SYSCALLS] = {[0] = "read",[1] = "write",
|
||||
[2] = "open",[3] = "close",
|
||||
[4] = "stat",[5] = "fstat",
|
||||
[6] = "lstat",[7] = "poll",
|
||||
[8] = "lseek",[9] = "mmap",
|
||||
[10] = "mprotect",[11] = "munmap",
|
||||
[12] = "brk",[13] = "rt_sigaction",
|
||||
[14] = "rt_sigprocmask",[15] = "rt_sigreturn",
|
||||
[16] = "ioctl",[17] = "pread64",
|
||||
[18] = "pwrite64",[19] = "readv",
|
||||
[20] = "writev",[21] = "access",
|
||||
[22] = "pipe",[23] = "select",
|
||||
[24] = "sched_yield",[25] = "mremap",
|
||||
[26] = "msync",[27] = "mincore",
|
||||
[28] = "madvise",[29] = "shmget",
|
||||
[30] = "shmat",[31] = "shmctl",
|
||||
[32] = "dup",[33] = "dup2",
|
||||
[34] = "pause",[35] = "nanosleep",
|
||||
[36] = "getitimer",[37] = "alarm",
|
||||
[38] = "setitimer",[39] = "getpid",
|
||||
[40] = "sendfile",[41] = "socket",
|
||||
[42] = "connect",[43] = "accept",
|
||||
[44] = "sendto",[45] = "recvfrom",
|
||||
[46] = "sendmsg",[47] = "recvmsg",
|
||||
[48] = "shutdown",[49] = "bind",
|
||||
[50] = "listen",[51] = "getsockname",
|
||||
[52] = "getpeername",[53] = "socketpair",
|
||||
[54] = "setsockopt",[55] = "getsockopt",
|
||||
[56] = "clone",[57] = "fork",
|
||||
[58] = "vfork",[59] = "execve",
|
||||
[60] = "exit",[61] = "wait4",
|
||||
[62] = "kill",[63] = "uname",
|
||||
[64] = "semget",[65] = "semop",
|
||||
[66] = "semctl",[67] = "shmdt",
|
||||
[68] = "msgget",[69] = "msgsnd",
|
||||
[70] = "msgrcv",[71] = "msgctl",
|
||||
[72] = "fcntl",[73] = "flock",
|
||||
[74] = "fsync",[75] = "fdatasync",
|
||||
[76] = "truncate",[77] = "ftruncate",
|
||||
[78] = "getdents",[79] = "getcwd",
|
||||
[80] = "chdir",[81] = "fchdir",
|
||||
[82] = "rename",[83] = "mkdir",
|
||||
[84] = "rmdir",[85] = "creat",
|
||||
[86] = "link",[87] = "unlink",
|
||||
[88] = "symlink",[89] = "readlink",
|
||||
[90] = "chmod",[91] = "fchmod",
|
||||
[92] = "chown",[93] = "fchown",
|
||||
[94] = "lchown",[95] = "umask",
|
||||
[96] = "gettimeofday",[97] = "getrlimit",
|
||||
[98] = "getrusage",[99] = "sysinfo",
|
||||
[100] = "times",[101] = "ptrace",
|
||||
[102] = "getuid",[103] = "syslog",
|
||||
[104] = "getgid",[105] = "setuid",
|
||||
[106] = "setgid",[107] = "geteuid",
|
||||
[108] = "getegid",[109] = "setpgid",
|
||||
[110] = "getppid",[111] = "getpgrp",
|
||||
[112] = "setsid",[113] = "setreuid",
|
||||
[114] = "setregid",[115] = "getgroups",
|
||||
[116] = "setgroups",[117] = "setresuid",
|
||||
[118] = "getresuid",[119] = "setresgid",
|
||||
[120] = "getresgid",[121] = "getpgid",
|
||||
[122] = "setfsuid",[123] = "setfsgid",
|
||||
[124] = "getsid",[125] = "capget",
|
||||
[126] = "capset",[127] = "rt_sigpending",
|
||||
[128] = "rt_sigtimedwait",[129] = "rt_sigqueueinfo",
|
||||
[130] = "rt_sigsuspend",[131] = "sigaltstack",
|
||||
[132] = "utime",[133] = "mknod",
|
||||
[134] = "uselib",[135] = "personality",
|
||||
[136] = "ustat",[137] = "statfs",
|
||||
[138] = "fstatfs",[139] = "sysfs",
|
||||
[140] = "getpriority",[141] = "setpriority",
|
||||
[142] = "sched_setparam",[143] = "sched_getparam",
|
||||
[144] = "sched_setscheduler",[145] = "sched_getscheduler",
|
||||
[146] = "sched_get_priority_max",[147] = "sched_get_priority_min",
|
||||
[148] = "sched_rr_get_interval",[149] = "mlock",
|
||||
[150] = "munlock",[151] = "mlockall",
|
||||
[152] = "munlockall",[153] = "vhangup",
|
||||
[154] = "modify_ldt",[155] = "pivot_root",
|
||||
[156] = "",[157] = "prctl",
|
||||
[158] = "arch_prctl",[159] = "adjtimex",
|
||||
[160] = "setrlimit",[161] = "chroot",
|
||||
[162] = "sync",[163] = "acct",
|
||||
[164] = "settimeofday",[165] = "mount",
|
||||
[166] = "",[167] = "swapon",
|
||||
[168] = "swapoff",[169] = "reboot",
|
||||
[170] = "sethostname",[171] = "setdomainname",
|
||||
[172] = "iopl",[173] = "ioperm",
|
||||
[174] = "",[175] = "init_module",
|
||||
[176] = "delete_module",[177] = "",
|
||||
[178] = "",[179] = "quotactl",
|
||||
[180] = "",[181] = "",
|
||||
[182] = "",[183] = "",
|
||||
[184] = "",[185] = "",
|
||||
[186] = "gettid",[187] = "readahead",
|
||||
[188] = "setxattr",[189] = "lsetxattr",
|
||||
[190] = "fsetxattr",[191] = "getxattr",
|
||||
[192] = "lgetxattr",[193] = "fgetxattr",
|
||||
[194] = "listxattr",[195] = "llistxattr",
|
||||
[196] = "flistxattr",[197] = "removexattr",
|
||||
[198] = "lremovexattr",[199] = "fremovexattr",
|
||||
[200] = "tkill",[201] = "time",
|
||||
[202] = "futex",[203] = "sched_setaffinity",
|
||||
[204] = "sched_getaffinity",[205] = "set_thread_area",
|
||||
[206] = "io_setup",[207] = "io_destroy",
|
||||
[208] = "io_getevents",[209] = "io_submit",
|
||||
[210] = "io_cancel",[211] = "get_thread_area",
|
||||
[212] = "",[213] = "epoll_create",
|
||||
[214] = "",[215] = "",
|
||||
[216] = "remap_file_pages",[217] = "getdents64",
|
||||
[218] = "set_tid_address",[219] = "restart_syscall",
|
||||
[220] = "semtimedop",[221] = "fadvise64",
|
||||
[222] = "timer_create",[223] = "timer_settime",
|
||||
[224] = "timer_gettime",[225] = "timer_getoverrun",
|
||||
[226] = "timer_delete",[227] = "clock_settime",
|
||||
[228] = "clock_gettime",[229] = "clock_getres",
|
||||
[230] = "clock_nanosleep",[231] = "exit_group",
|
||||
[232] = "epoll_wait",[233] = "epoll_ctl",
|
||||
[234] = "tgkill",[235] = "utimes",
|
||||
[236] = "",[237] = "mbind",
|
||||
[238] = "set_mempolicy",[239] = "get_mempolicy",
|
||||
[240] = "mq_open",[241] = "mq_unlink",
|
||||
[242] = "mq_timedsend",[243] = "mq_timedreceive",
|
||||
[244] = "mq_notify",[245] = "mq_getsetattr",
|
||||
[246] = "kexec_load",[247] = "waitid",
|
||||
[248] = "add_key",[249] = "request_key",
|
||||
[250] = "keyctl",[251] = "ioprio_set",
|
||||
[252] = "ioprio_get",[253] = "inotify_init",
|
||||
[254] = "inotify_add_watch",[255] = "inotify_rm_watch",
|
||||
[256] = "migrate_pages",[257] = "openat",
|
||||
[258] = "mkdirat",[259] = "mknodat",
|
||||
[260] = "fchownat",[261] = "futimesat",
|
||||
[262] = "newfstatat",[263] = "unlinkat",
|
||||
[264] = "renameat",[265] = "linkat",
|
||||
[266] = "symlinkat",[267] = "readlinkat",
|
||||
[268] = "fchmodat",[269] = "faccessat",
|
||||
[270] = "pselect6",[271] = "ppoll",
|
||||
[272] = "unshare",[273] = "set_robust_list",
|
||||
[274] = "get_robust_list",[275] = "splice",
|
||||
[276] = "tee",[277] = "sync_file_range",
|
||||
[278] = "vmsplice",[279] = "move_pages",
|
||||
[280] = "utimensat",[281] = "epoll_pwait",
|
||||
[282] = "signalfd",[283] = "timerfd_create",
|
||||
[284] = "eventfd",[285] = "fallocate",
|
||||
[286] = "timerfd_settime",[287] = "timerfd_gettime",
|
||||
[288] = "accept4",[289] = "signalfd4",
|
||||
[290] = "eventfd2",[291] = "epoll_create1",
|
||||
[292] = "dup3",[293] = "pipe2",
|
||||
[294] = "inotify_init1",[295] = "preadv",
|
||||
[296] = "pwritev",[297] = "rt_tgsigqueueinfo",
|
||||
[298] = "perf_event_open",[299] = "recvmmsg",
|
||||
[300] = "fanotify_init",[301] = "fanotify_mark",
|
||||
[302] = "prlimit64",[303] = "name_to_handle_at",
|
||||
[304] = "open_by_handle_at",[305] = "clock_adjtime",
|
||||
[306] = "syncfs",[307] = "sendmmsg",
|
||||
[308] = "setns",[309] = "getcpu",
|
||||
[310] = "process_vm_readv",[311] = "process_vm_writev",
|
||||
[312] = "kcmp",[313] = "finit_module",
|
||||
[314] = "sched_setattr",[315] = "sched_getattr",
|
||||
[316] = "renameat2",[317] = "seccomp",
|
||||
[318] = "getrandom",[319] = "memfd_create",
|
||||
[320] = "kexec_file_load",[321] = "bpf",
|
||||
[322] = "execveat",[323] = "userfaultfd",
|
||||
[324] = "membarrier",[325] = "mlock2",
|
||||
[326] = "copy_file_range",[327] = "preadv2",
|
||||
[328] = "pwritev2",[329] = "pkey_mprotect",
|
||||
[330] = "pkey_alloc",[331] = "pkey_free",
|
||||
[332] = "statx",[333] = "io_pgetevents",
|
||||
[334] = "rseq",[335] = "",
|
||||
[336] = "",[337] = "",
|
||||
[338] = "",[339] = "",
|
||||
[340] = "",[341] = "",
|
||||
[342] = "",[343] = "",
|
||||
[344] = "",[345] = "",
|
||||
[346] = "",[347] = "",
|
||||
[348] = "",[349] = "",
|
||||
[350] = "",[351] = "",
|
||||
[352] = "",[353] = "",
|
||||
[354] = "",[355] = "",
|
||||
[356] = "",[357] = "",
|
||||
[358] = "",[359] = "",
|
||||
[360] = "",[361] = "",
|
||||
[362] = "",[363] = "",
|
||||
[364] = "",[365] = "",
|
||||
[366] = "",[367] = "",
|
||||
[368] = "",[369] = "",
|
||||
[370] = "",[371] = "",
|
||||
[372] = "",[373] = "",
|
||||
[374] = "",[375] = "",
|
||||
[376] = "",[377] = "",
|
||||
[378] = "",[379] = "",
|
||||
[380] = "",[381] = "",
|
||||
[382] = "",[383] = "",
|
||||
[384] = "",[385] = "",
|
||||
[386] = "",[387] = "",
|
||||
[388] = "",[389] = "",
|
||||
[390] = "",[391] = "",
|
||||
[392] = "",[393] = "",
|
||||
[394] = "",[395] = "",
|
||||
[396] = "",[397] = "",
|
||||
[398] = "",[399] = "",
|
||||
[400] = "",[401] = "",
|
||||
[402] = "",[403] = "",
|
||||
[404] = "",[405] = "",
|
||||
[406] = "",[407] = "",
|
||||
[408] = "",[409] = "",
|
||||
[410] = "",[411] = "",
|
||||
[412] = "",[413] = "",
|
||||
[414] = "",[415] = "",
|
||||
[416] = "",[417] = "",
|
||||
[418] = "",[419] = "",
|
||||
[420] = "",[421] = "",
|
||||
[422] = "",[423] = "",
|
||||
[424] = "pidfd_send_signal",[425] = "io_uring_setup",
|
||||
[426] = "io_uring_enter",[427] = "io_uring_register",
|
||||
[428] = "open_tree",[429] = "move_mount",
|
||||
[430] = "fsopen",[431] = "fsconfig",
|
||||
[432] = "fsmount",[433] = "fspick",
|
||||
[434] = "pidfd_open",[435] = "clone3" };
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
|
||||
Following command in part thx to:
|
||||
https://unix.stackexchange.com/questions/445507/syscall-number-%E2%86%92-name-mapping-at-runtime
|
||||
|
||||
Command to create mapping of system call number to system call name:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
awk 'BEGIN { print "#include <sys/syscall.h>" }
|
||||
/p_syscall_meta/ { syscall = substr($NF, 19);
|
||||
printf "[SYS_%s] = \"%s\", \n", syscall, syscall }' /proc/kallsyms \
|
||||
| gcc -E -P - \
|
||||
| sort -V \
|
||||
| grep "\[[0-9]" \
|
||||
| awk 'BEGIN {expectedIndex = 0;}
|
||||
{ actualIndex = $1;
|
||||
gsub(/[\[\]]/, "", actualIndex);
|
||||
if (actualIndex != expectedIndex)
|
||||
for (; expectedIndex < actualIndex; expectedIndex++)
|
||||
print "[" expectedIndex "] = \"\",";
|
||||
print $0; expectedIndex++ }' \
|
||||
| tr -d "\n" \
|
||||
| sed -e "s/^/const char *syscalls[1024] = {/; s/,$/ };/" \
|
||||
| sed -e 's/,/,\n /2;P;D' \
|
||||
| cat - <(echo) # new line at the end
|
||||
*/
|
|
@ -3,7 +3,8 @@
|
|||
**WARNING**: This is a prototype. Things will probably break; in spectacular
|
||||
ways.
|
||||
|
||||
`NCursesPtyWindow` provides a pseudo terminal in a ncurses window.
|
||||
`kNCurses` extends the existing C++ library shipping with ncurses and provides a
|
||||
tiling window manager and a pseudo terminal in a ncurses window.
|
||||
|
||||
## Building
|
||||
|
||||
|
|
345
Window.cpp
345
Window.cpp
|
@ -1,345 +0,0 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "Window.hpp"
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <gsl/gsl>
|
||||
#include <cctype>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace krikkel::NCursesPtyWindow
|
||||
{
|
||||
using gsl::narrow;
|
||||
using std::lock_guard;
|
||||
|
||||
Window::Window(std::mutex *writeToNCursesMutex, int lines, int columns, int y, int x)
|
||||
: writeToNCursesMutex(writeToNCursesMutex), NCursesWindow(lines, columns, y, x)
|
||||
{
|
||||
// to get the original terminal we need to shutdown ncurses for a moment
|
||||
/// @todo maybe try `reset_prog_mode()` and `reset_shell_mode()` instead
|
||||
::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);
|
||||
vterm_set_utf8(pseudoTerminal, true);
|
||||
pseudoTerminalScreen = vterm_obtain_screen(pseudoTerminal);
|
||||
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);
|
||||
|
||||
//raw(); //— cbreak might suffice
|
||||
//noecho(); — already set
|
||||
//nodelay(true); — @todo needs some reprogramming
|
||||
keypad(true);
|
||||
nonl();
|
||||
|
||||
/// @todo block all signals, this thread does not handle any
|
||||
readPtyClientThread =
|
||||
std::thread(&Window::readFromPtyClientThreadMethod, this);
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
close(fdPtyHost);
|
||||
close(fdPtyClient);
|
||||
vterm_free(pseudoTerminal);
|
||||
}
|
||||
|
||||
int Window::getFdPtyClient() const
|
||||
{
|
||||
return fdPtyClient;
|
||||
}
|
||||
|
||||
void Window::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 Window::writeUnicodeCharToClient(wint_t character)
|
||||
{
|
||||
vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE);
|
||||
}
|
||||
|
||||
void Window::writeKeyToClient(VTermKey key)
|
||||
{
|
||||
__debug_log("writing key: " + std::to_string(key));
|
||||
vterm_keyboard_key(pseudoTerminal, key, VTERM_MOD_NONE);
|
||||
}
|
||||
|
||||
SingleUserInput Window::readSingleUserInput()
|
||||
{
|
||||
wint_t input;
|
||||
int result = get_wch(&input);
|
||||
return SingleUserInput(result, input);
|
||||
}
|
||||
|
||||
void Window::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 Window::readFromPtyClient()
|
||||
{
|
||||
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)) + '\'');
|
||||
}
|
||||
|
||||
VTermScreenCallbacks Window::screenCallbacks =
|
||||
{
|
||||
.damage = staticHandlerDamage,
|
||||
.moverect = staticHandlerMoveRect,
|
||||
.movecursor = staticHandlerMoveCursor,
|
||||
.settermprop = staticHandlerSetTermProp,
|
||||
.bell = staticHandlerBell,
|
||||
.resize = staticHandlerResize,
|
||||
.sb_pushline = staticHandlerPushLine,
|
||||
.sb_popline = staticHandlerPopLine,
|
||||
};
|
||||
|
||||
int Window::handlerDamage(VTermRect rect)
|
||||
{
|
||||
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);
|
||||
|
||||
refresh();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::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 Window::handlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible)
|
||||
{
|
||||
/// @todo maybe use `mvcur()` instead?
|
||||
cursorX = pos.col;
|
||||
cursorY = pos.row;
|
||||
refresh();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::handlerSetTermProp(VTermProp prop, VTermValue *val)
|
||||
{
|
||||
/// @todo maybe use "vterm_get_prop_type() —> bool, number, string"?
|
||||
__debug_stringify_switch_begin(handlerSetTermProp, prop, val);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORVISIBLE);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORBLINK);
|
||||
__debug_stringify_switch_case(VTERM_PROP_ALTSCREEN);
|
||||
__debug_stringify_switch_case(VTERM_PROP_REVERSE);
|
||||
__debug_stringify_switch_case_end_bool(val->boolean);
|
||||
__debug_stringify_switch_case(VTERM_PROP_TITLE);
|
||||
__debug_stringify_switch_case(VTERM_PROP_ICONNAME);
|
||||
__debug_stringify_switch_case_end_string(val->string);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORSHAPE);
|
||||
__debug_stringify_switch_case(VTERM_PROP_MOUSE);
|
||||
__debug_stringify_switch_case_end_number(val->number);
|
||||
__debug_stringify_switch_end(prop);
|
||||
|
||||
__debug_log(std::string("unimplemented handler called: ")
|
||||
+ __debug_stringify_get_string(handlerSetTermProp)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::handlerBell()
|
||||
{
|
||||
beep();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::handlerResize(int rows, int cols)
|
||||
{
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::handlerPushLine(int cols, const VTermScreenCell *cells)
|
||||
{
|
||||
__debug_log("(unimplemented) push line with " + std::to_string(cols) + " columns");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Window::handlerPopLine(int cols, VTermScreenCell *cells)
|
||||
{
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
attr_t Window::extractAttributesFromVTermCell(VTermScreenCell vTermCell)
|
||||
{
|
||||
attr_t result = A_NORMAL;
|
||||
//__debug_log("unimplemented method called");
|
||||
return result;
|
||||
}
|
||||
|
||||
short Window::extractColorFromVTermCell(VTermScreenCell vTermCell)
|
||||
{
|
||||
//__debug_log("unimplemented method called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Window::copyPtyCellToNCursesWindow(int x, int y)
|
||||
{
|
||||
VTermPos cellPosition = {y, x};
|
||||
VTermScreenCell vTermCell;
|
||||
cchar_t converted;
|
||||
attr_t formatting;
|
||||
short colorPair;
|
||||
wchar_t character;
|
||||
|
||||
vterm_screen_get_cell(pseudoTerminalScreen, cellPosition, &vTermCell);
|
||||
formatting = extractAttributesFromVTermCell(vTermCell);
|
||||
colorPair = extractColorFromVTermCell(vTermCell);
|
||||
|
||||
if(vTermCell.chars[0] == 0)
|
||||
character = ' ';
|
||||
else
|
||||
character = *vTermCell.chars;
|
||||
|
||||
{
|
||||
lock_guard nCursesLock(*writeToNCursesMutex);
|
||||
setcchar(&converted, &character, formatting, colorPair, NULL);
|
||||
move(cellPosition.row, cellPosition.col);
|
||||
chgat(1, formatting, colorPair, NULL);
|
||||
add_wch(&converted);
|
||||
}
|
||||
}
|
||||
|
||||
int Window::add_wch(const cchar_t *character)
|
||||
{
|
||||
return ::wadd_wch(w, character);
|
||||
}
|
||||
|
||||
int Window::get_wch(wint_t *character)
|
||||
{
|
||||
return ::wget_wch(w, character);
|
||||
}
|
||||
|
||||
int Window::refresh()
|
||||
{
|
||||
lock_guard nCursesLock(*writeToNCursesMutex);
|
||||
move(cursorY, cursorX);
|
||||
return NCursesWindow::refresh();
|
||||
}
|
||||
|
||||
/// @todo potential racing condition where drawing into terminal while
|
||||
/// resizing?
|
||||
int Window::wresize(int rows, int cols)
|
||||
{
|
||||
lock_guard nCursesLock(*writeToNCursesMutex);
|
||||
lock_guard writeLock(writeToPseudoTerminalMutex);
|
||||
winsize windowSize =
|
||||
{
|
||||
.ws_row = narrow<unsigned short>(rows)
|
||||
, .ws_col = narrow<unsigned short>(cols)
|
||||
};
|
||||
ioctl(fdPtyHost, TIOCSWINSZ, &windowSize);
|
||||
vterm_set_size(pseudoTerminal, rows, cols);
|
||||
|
||||
return NCursesWindow::wresize(rows, cols);
|
||||
}
|
||||
|
||||
int Window::staticHandlerDamage(VTermRect rect, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerDamage(rect);
|
||||
}
|
||||
|
||||
int Window::staticHandlerMoveRect(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerMoveRect(dest, src);
|
||||
}
|
||||
|
||||
int Window::staticHandlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerMoveCursor(pos, oldpos, visible);
|
||||
}
|
||||
|
||||
int Window::staticHandlerSetTermProp(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerSetTermProp(prop, val);
|
||||
}
|
||||
|
||||
int Window::staticHandlerBell(void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerBell();
|
||||
}
|
||||
|
||||
int Window::staticHandlerResize(int rows, int cols, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerResize(rows, cols);
|
||||
}
|
||||
|
||||
int Window::staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerPushLine(cols, cells);
|
||||
}
|
||||
|
||||
int Window::staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
return instance->handlerPopLine(cols, cells);
|
||||
}
|
||||
|
||||
void Window::staticHandlerOutput(const char *s, size_t len, void *user)
|
||||
{
|
||||
Window *instance = static_cast<Window *>(user);
|
||||
__debug_log("output handler writing to client: '" + __debug_make_bytes_printable(std::string(s, len)) + "'");
|
||||
instance->writeToClient(s, len);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
##
|
||||
# @brief Includes Microsoft's "C++ Guidelines Standard Library"
|
||||
# @author Christian Burger <christian@krikkel.de>
|
||||
|
||||
find_path(GSL_INCLUDE_DIR "gsl")
|
||||
if(GSL_INCLUDE_DIR STREQUAL "GSL_INCLUDE_DIR-NOTFOUND")
|
||||
find_path(GSL_INCLUDE_DIR "gsl/gsl" REQUIRED)
|
||||
if(NOT GSL_INCLUDE_DIR)
|
||||
message(SEND_ERROR "Microsoft GSL not found.")
|
||||
else()
|
||||
message(STATUS "Microsoft GSL found.")
|
||||
message(STATUS "Found Microsoft C++ GSL.")
|
||||
include_directories(SYSTEM ${GSL_INCLUDE_DIR})
|
||||
endif()
|
|
@ -1,17 +1,31 @@
|
|||
# @author Christian Burger <christian@krikkel.de>
|
||||
# @brief searches for libvterm and builds it if not found
|
||||
##
|
||||
# @brief Includes `libvterm` (builds it if not found on the system).
|
||||
#
|
||||
# provides LIBVTERM_LIBRARY and include path
|
||||
# dependency must be added like this:
|
||||
# if(EXISTS libvtermProject)
|
||||
# add_dependencies(<targetDependingOn> libvtermProject)
|
||||
# endif()
|
||||
# Provides ${LIBVTERM_LIBRARY} and implicitly adds path for correct
|
||||
# `#include<>`.
|
||||
#
|
||||
# @warning dependency must be added like this:
|
||||
# if(TARGET libvtermProject)
|
||||
# add_dependencies(<targetDependingOn> libvtermProject)
|
||||
# endif()
|
||||
#
|
||||
# @author Christian Burger <christian@krikkel.de>
|
||||
|
||||
|
||||
find_library(LIBVTERM_LIBRARY NAMES libvterm.so.0.0.2 vterm)
|
||||
find_library(LIBVTERM_LIBRARY NAMES "libvterm.so.0.0.2" "vterm")
|
||||
find_path(LIBVTERM_INCLUDE "vterm.h")
|
||||
|
||||
if(LIBVTERM_LIBRARY STREQUAL "LIBVTERM_LIBRARY-NOTFOUND")
|
||||
message(STATUS "libvterm not found — building it")
|
||||
if(NOT LIBVTERM_LIBRARY)
|
||||
message(STATUS "Did not find `libvterm.so`.")
|
||||
set(LIBVTERM_BUILD_IT TRUE)
|
||||
endif()
|
||||
if(NOT LIBVTERM_INCLUDE)
|
||||
message(STATUS "Did not find `vterm.h`.")
|
||||
set(LIBVTERM_BUILD_IT TRUE)
|
||||
endif()
|
||||
if(LIBVTERM_BUILD_IT)
|
||||
include(ExternalProject)
|
||||
message(STATUS "Did not find libvterm (see previous error) — building it.")
|
||||
ExternalProject_Add(
|
||||
libvtermProject
|
||||
DOWNLOAD_COMMAND URL "https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/libvterm/0.1.2-2/libvterm_0.1.2.orig.tar.gz"
|
||||
|
@ -22,4 +36,6 @@ if(LIBVTERM_LIBRARY STREQUAL "LIBVTERM_LIBRARY-NOTFOUND")
|
|||
ExternalProject_Get_property(libvtermProject SOURCE_DIR)
|
||||
set(LIBVTERM_LIBRARY "${SOURCE_DIR}/.libs/libvterm.a")
|
||||
include_directories(SYSTEM "${SOURCE_DIR}/include")
|
||||
else()
|
||||
message(STATUS "Found vterm library.")
|
||||
endif()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
##
|
||||
# @brief Includes the UTF-8 C++ version of the `ncurses` library.
|
||||
# @author Christian Burger <christian@krikkel.de>
|
||||
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
|
@ -6,8 +8,8 @@ find_package(Curses 6.2 REQUIRED)
|
|||
include_directories(SYSTEM ${CURSES_INCLUDE_DIRS})
|
||||
|
||||
# find C++ interface for ncurses with unicode support
|
||||
find_library(CURSES_CPP_WIDE_LIBRARY NAMES ncurses++w REQUIRED)
|
||||
find_library(CURSES_CPP_WIDE_LIBRARY NAMES ncurses++w)
|
||||
if(NOT CURSES_CPP_WIDE_LIBRARY)
|
||||
message(FATAL_ERROR "C++ interface for ncurses (wide/unicode) not found.")
|
||||
message(SEND_ERROR "C++ interface for ncurses (wide/unicode) not found.")
|
||||
endif()
|
||||
list(APPEND CURSES_LIBRARIES ${CURSES_CPP_WIDE_LIBRARY})
|
77
cmake/version.cmake
Normal file
77
cmake/version.cmake
Normal file
|
@ -0,0 +1,77 @@
|
|||
##
|
||||
# @brief a ${SEMANTIC_VERSION} based on Git tags (or a `VERSION` file) à la
|
||||
# `v<major>.<minor>.<patch>` is provided
|
||||
#
|
||||
# Versions have the format "v<major>.<minor>.<patch>" in accordance with
|
||||
# Semantic Versioning. If a version is provided by a file named `VERSION`, the
|
||||
# full version must be given. If a version is provided by Git tags, only part of
|
||||
# the version must be given, the format is then: "v<major>.<minor>". The <patch>
|
||||
# version is automatically deduced from the distance in commits to the latest
|
||||
# Git tag. The version set in the file overrides the tags in the Git repository.
|
||||
#
|
||||
# based upon Semantic Versioning: https://semver.org/
|
||||
#
|
||||
# @attention The <patch> version-part is a bit flaky; it gives the distance in
|
||||
# commits to the <major>.<minor> version. Due to the non-linear
|
||||
# nature of the commit history (because of branches), it is ambigous
|
||||
# (i. e. multiple commits can be found at the same distance). On top
|
||||
# of that, it is your job to make sure, that patches only fix bugs
|
||||
# and do not change the API (no new functionalty; or old removed).
|
||||
#
|
||||
# One option to address both issues is: Stay in one branch for
|
||||
# releases — for example named 'release' — and cherry-pick new
|
||||
# versions into the branch (alphas, betas, candidates and releases).
|
||||
# Optional: squash cherry-picked commits, so that the <patch> part of
|
||||
# the version is only incremented by one.
|
||||
# @warning To update the version you have to run CMake again.
|
||||
#
|
||||
# @todo escape/quote paths
|
||||
# @todo test cases for CMake file components?
|
||||
# @todo make variables local (see: unset()) and put them in a "namespace"
|
||||
# @author Christian Burger <christian@krikkel.de>
|
||||
find_package(Git)
|
||||
|
||||
function(get_semantic_version)
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/VERSION")
|
||||
message(STATUS "using file `VERSION` to determine version")
|
||||
file(READ "VERSION" REPOSITORY_VERSION)
|
||||
elseif(GIT_FOUND)
|
||||
message(STATUS "using Git to determine project version")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} describe --always --match v[0-9]*.[0-9]*
|
||||
OUTPUT_VARIABLE REPOSITORY_VERSION)
|
||||
else()
|
||||
message(WARNING "We need Git or a file \"VERSION\" to determine a version (e. g. \"v1.0\").")
|
||||
endif()
|
||||
|
||||
# test cases
|
||||
#set(REPOSITORY_VERSION "v0.1") # = 0.1.0
|
||||
#set(REPOSITORY_VERSION "v0.1-1-g12345678") # = 0.1.1
|
||||
#set(REPOSITORY_VERSION "v0.1") # = 0.0.0
|
||||
#set(REPOSITORY_VERSION "1234abcd") # = 0.0.0
|
||||
#set(REPOSITORY_VERSION "") # = 0.1.0
|
||||
|
||||
string(REGEX MATCH "^v([0-9]+)\.([0-9]+)([-\\.]([0-9]+))?" SEMANTIC_VERSION_PREPROCESSED "${REPOSITORY_VERSION}")
|
||||
|
||||
if("${SEMANTIC_VERSION_PREPROCESSED}" STREQUAL "")
|
||||
message(STATUS "Found no version tag (e. g. \"v1.0\").")
|
||||
endif()
|
||||
|
||||
set(SEMANTIC_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(SEMANTIC_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(SEMANTIC_VERSION_PATCH ${CMAKE_MATCH_4})
|
||||
|
||||
if("${SEMANTIC_VERSION_MAJOR}" STREQUAL "")
|
||||
set(SEMANTIC_VERSION "0.0.0")
|
||||
else()
|
||||
if("${SEMANTIC_VERSION_PATCH}" STREQUAL "")
|
||||
set(SEMANTIC_VERSION_PATCH "0")
|
||||
endif()
|
||||
set(SEMANTIC_VERSION "${SEMANTIC_VERSION_MAJOR}.${SEMANTIC_VERSION_MINOR}.${SEMANTIC_VERSION_PATCH}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Project version is: ${SEMANTIC_VERSION}")
|
||||
|
||||
set(SEMANTIC_VERSION "${SEMANTIC_VERSION}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
get_semantic_version()
|
18
default.nix
Normal file
18
default.nix
Normal file
|
@ -0,0 +1,18 @@
|
|||
{ debug ? false }:
|
||||
|
||||
with import <nixpkgs> { };
|
||||
|
||||
let
|
||||
ncursesPkg = ncurses.overrideAttrs (oldAttrs: rec { configureFlags = oldAttrs.configureFlags ++ [ "--with-cxx-shared" ];});
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "kNCurses";
|
||||
src = ./.;
|
||||
|
||||
buildInputs = [ libvterm-neovim ncursesPkg microsoft_gsl];
|
||||
nativeBuildInputs = [ cmake ];
|
||||
cmakeBuildType = if debug then "Debug" else "Release";
|
||||
makeFlags = lib.lists.optional debug "VERBOSE=1";
|
||||
hardeningDisable = lib.lists.optionals debug [ "fortify" ];
|
||||
dontStrip = debug;
|
||||
}
|
31
include/kNCurses/HorizontalTilingWindowManager.hpp
Normal file
31
include/kNCurses/HorizontalTilingWindowManager.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @brief Window manager
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef B7BCF793_2FAB_49CC_9E00_CDEA370D38F9
|
||||
#define B7BCF793_2FAB_49CC_9E00_CDEA370D38F9
|
||||
|
||||
#include "TilingWindowManager.hpp"
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class Window;
|
||||
class SingleUserInput;
|
||||
|
||||
class HorizontalTilingWindowManager : public TilingWindowManager
|
||||
{
|
||||
public:
|
||||
HorizontalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex);
|
||||
|
||||
void resizeAndMoveWindow(Window *window
|
||||
, windowDimension dimension
|
||||
, windowPosition position) override;
|
||||
windowDimension getAvailableSpace() override;
|
||||
void windowBorder() override;
|
||||
void moveCursor(windowPosition position) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif /* B7BCF793_2FAB_49CC_9E00_CDEA370D38F9 */
|
|
@ -1,12 +1,13 @@
|
|||
/**
|
||||
* @brief `ncurses` window displaying contents of a pseudo terminal
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
* @todo remove all invalid constructors
|
||||
*/
|
||||
|
||||
#ifndef __WINDOW_H__
|
||||
#define __WINDOW_H__
|
||||
|
||||
#include "SingleUserInput.hpp"
|
||||
#include "Window.hpp"
|
||||
|
||||
#include <cursesw.h>
|
||||
#include <pty.h>
|
||||
|
@ -15,44 +16,27 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef add_wch
|
||||
inline void UNDEF(add_wch)(const cchar_t *character) { add_wch(character); }
|
||||
#undef add_wch
|
||||
#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
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class Window : public NCursesWindow
|
||||
class PtyWindow : public Window
|
||||
{
|
||||
public:
|
||||
Window(std::mutex *writeToNCursesMutex, int lines, int columns, int y, int x);
|
||||
~Window();
|
||||
PtyWindow(std::recursive_mutex *ncursesMutex, int lines, int columns, int y, int x);
|
||||
~PtyWindow();
|
||||
|
||||
int getFdPtyClient() const;
|
||||
void writeToClient(const char * string, size_t length);
|
||||
void writeUnicodeCharToClient(wint_t character);
|
||||
void writeKeyToClient(VTermKey key);
|
||||
void writeFunctionKeyToClient(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);
|
||||
int resize(int rows, int cols) override;
|
||||
|
||||
private:
|
||||
int fdPtyHost, fdPtyClient;
|
||||
struct termios terminalParameters;
|
||||
VTerm *pseudoTerminal;
|
||||
std::mutex writeToPseudoTerminalMutex;
|
||||
std::mutex *writeToNCursesMutex;
|
||||
std::recursive_mutex ptyMutex;
|
||||
std::recursive_mutex *ncursesMutex;
|
||||
VTermScreen *pseudoTerminalScreen;
|
||||
static VTermScreenCallbacks screenCallbacks;
|
||||
/// @todo one line is at most 4096 chars long
|
||||
|
@ -63,6 +47,7 @@ namespace krikkel::NCursesPtyWindow
|
|||
std::thread readPtyClientThread;
|
||||
void readFromPtyClientThreadMethod();
|
||||
void readFromPtyClient();
|
||||
void writeToPtyClient(const char * string, size_t length);
|
||||
|
||||
int handlerDamage(VTermRect rect);
|
||||
int handlerMoveRect(VTermRect dest, VTermRect src);
|
4
include/kNCurses/README.md
Normal file
4
include/kNCurses/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
## Rational
|
||||
|
||||
The include files are part of the exposed part of the library. Thus they are
|
||||
kept separate from the actual source.
|
|
@ -9,7 +9,7 @@
|
|||
#include <cursesw.h>
|
||||
#include <vterm_keycodes.h>
|
||||
|
||||
namespace krikkel::NCursesPtyWindow
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class SingleUserInput
|
||||
{
|
65
include/kNCurses/TilingWindowManager.hpp
Normal file
65
include/kNCurses/TilingWindowManager.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* @brief Window manager for tiling (template pattern, abstract parent)
|
||||
*
|
||||
* A window manager contains windows of a given size, lays them out and can hide
|
||||
* or show them. There is no concrete window manager for tiling horizontally and
|
||||
* vertically at the same time. Instead nesting the window managers is adviced,
|
||||
* since the window manager classes are derived from windows in the end.
|
||||
*
|
||||
* This is an abstract class used by the concrete classes
|
||||
* `VerticalTilingWindowManager` and `HorizontalTilingWindowManager`. Both
|
||||
* specify the virtual and protected, declared but undefined methods used by the
|
||||
* public interface to facilitate the function depending on the orientation
|
||||
* (horizontally or vertically).
|
||||
*
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef C51BA18F_0915_43B9_BD5D_129F0CDBC1CD
|
||||
#define C51BA18F_0915_43B9_BD5D_129F0CDBC1CD
|
||||
|
||||
#include "Window.hpp"
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class SingleUserInput;
|
||||
|
||||
class TilingWindowManager : public Window
|
||||
{
|
||||
public:
|
||||
/// @todo figure out more appropiate names …
|
||||
typedef int16_t windowDimension;
|
||||
typedef uint16_t windowPosition;
|
||||
typedef std::pair<Window *, windowDimension> WindowStackElement;
|
||||
|
||||
TilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex);
|
||||
std::recursive_mutex *getNCursesMutex();
|
||||
void addWindow(Window *window, windowDimension size = -1);
|
||||
|
||||
int resize(int rows, int cols) override;
|
||||
int refresh() override;
|
||||
void updateLayout();
|
||||
|
||||
void hideWindow(Window *window);
|
||||
void showWindow(Window *window);
|
||||
void invertWindowsVisibility();
|
||||
|
||||
protected:
|
||||
/// @todo rename to `nCursesMutex`?
|
||||
std::recursive_mutex *ncursesMutex;
|
||||
std::list<WindowStackElement> stack, visibleStack;
|
||||
|
||||
windowDimension getRelativeUnitSizeForVisibleWindows(windowDimension spaceAvailable);
|
||||
virtual void resizeAndMoveWindow(Window *window
|
||||
, windowDimension dimension
|
||||
, windowPosition position) = 0;
|
||||
virtual void windowBorder() = 0;
|
||||
virtual windowDimension getAvailableSpace() = 0;
|
||||
virtual void moveCursor(windowPosition position) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* C51BA18F_0915_43B9_BD5D_129F0CDBC1CD */
|
31
include/kNCurses/VerticalTilingWindowManager.hpp
Normal file
31
include/kNCurses/VerticalTilingWindowManager.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @brief Window manager
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef C10F5DF3_1DB4_4714_A84D_115F492F5CDC
|
||||
#define C10F5DF3_1DB4_4714_A84D_115F492F5CDC
|
||||
|
||||
#include "TilingWindowManager.hpp"
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class Window;
|
||||
class SingleUserInput;
|
||||
|
||||
class VerticalTilingWindowManager : public TilingWindowManager
|
||||
{
|
||||
public:
|
||||
VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex);
|
||||
|
||||
void resizeAndMoveWindow(Window *window
|
||||
, windowDimension dimension
|
||||
, windowPosition position) override;
|
||||
windowDimension getAvailableSpace() override;
|
||||
void windowBorder() override;
|
||||
void moveCursor(windowPosition position) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif /* C10F5DF3_1DB4_4714_A84D_115F492F5CDC */
|
65
include/kNCurses/Window.hpp
Normal file
65
include/kNCurses/Window.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* @brief Providing wide character methods missing in NCursesWindow.
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef A2C3FDB7_7A85_4527_BC85_366E14149EB8
|
||||
#define A2C3FDB7_7A85_4527_BC85_366E14149EB8
|
||||
|
||||
#include "SingleUserInput.hpp"
|
||||
|
||||
#include <ncursesw/cursesw.h>
|
||||
|
||||
#ifdef addnwstr
|
||||
inline int UNDEF(addnwstr)(const wchar_t *wstr, int n) { return addnwstr(wstr, n); }
|
||||
#undef addnwstr
|
||||
#define addnwstr UNDEF(addnwstr)
|
||||
#endif
|
||||
|
||||
#ifdef add_wch
|
||||
inline int UNDEF(add_wch)(const cchar_t *character) { return add_wch(character); }
|
||||
#undef add_wch
|
||||
#define add_wch UNDEF(add_wch)
|
||||
#endif
|
||||
|
||||
#ifdef ins_wch
|
||||
inline int UNDEF(ins_wch)(cchar_t *character) { return ins_wch(character); }
|
||||
#undef ins_wch
|
||||
#define ins_wch UNDEF(ins_wch)
|
||||
#endif
|
||||
|
||||
#ifdef get_wch
|
||||
inline int UNDEF(get_wch)(wint_t *character) { return get_wch(character); }
|
||||
#undef get_wch
|
||||
#define get_wch UNDEF(get_wch)
|
||||
#endif
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class TilingWindowManager;
|
||||
|
||||
class Window : public NCursesWindow
|
||||
{
|
||||
friend class TilingWindowManager;
|
||||
|
||||
public:
|
||||
Window();
|
||||
Window(TilingWindowManager *windowManager);
|
||||
Window(const NCursesWindow &window);
|
||||
Window(int lines, int columns, int y, int x);
|
||||
int addnwstr(const wchar_t *wstr, int n);
|
||||
int add_wch(const cchar_t *character);
|
||||
int ins_wch(const cchar_t *character);
|
||||
int get_wch(wint_t *character);
|
||||
|
||||
bool isHidden();
|
||||
SingleUserInput readSingleUserInput();
|
||||
virtual int resize(int rows, int cols);
|
||||
virtual int refresh() override;
|
||||
|
||||
protected:
|
||||
bool hidden = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* A2C3FDB7_7A85_4527_BC85_366E14149EB8 */
|
506
src/krikkel/Debug.cpp
Normal file
506
src/krikkel/Debug.cpp
Normal file
|
@ -0,0 +1,506 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
* @todo Switch over to <seccomp.h>? For resolving system call numbers?
|
||||
* Maybe keep the current solution as a fallback?
|
||||
* @todo catch `out of range` exception from stoi()
|
||||
*
|
||||
* Contains mapping of system calls numbers to names from original dev system:
|
||||
* "Linux 5.13.0-28-generic #31~20.04.1-Ubuntu SMP \
|
||||
* Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux"
|
||||
* See comment at the end for the actual command to gather mapping information.
|
||||
*/
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
#include <syscall.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define BUFFER_SIZE 128
|
||||
|
||||
namespace krikkel
|
||||
{
|
||||
using std::string;
|
||||
using std::fstream;
|
||||
using std::filesystem::is_directory;
|
||||
using std::endl;
|
||||
using std::time;
|
||||
using std::localtime;
|
||||
using std::strftime;
|
||||
using std::min;
|
||||
|
||||
Debug::Debug()
|
||||
{
|
||||
string directory = ".";
|
||||
if(is_directory("sandbox"))
|
||||
directory = "./sandbox";
|
||||
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
|
||||
logFile << endl << endl << endl << endl << "New instance of class Debug." << endl;
|
||||
logFile << getUname();
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
string Debug::getUname()
|
||||
{
|
||||
string result = "";
|
||||
char buffer[BUFFER_SIZE];
|
||||
FILE *unameProcess = popen("uname -srvmpio", "r");
|
||||
|
||||
if(unameProcess && fgets(buffer, BUFFER_SIZE, unameProcess))
|
||||
result = buffer;
|
||||
pclose(unameProcess);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string Debug::getTimestamp()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
tm *localNow = localtime(&now);
|
||||
static char formattedLocalNow[32];
|
||||
strftime(formattedLocalNow, 32, "%c", localNow);
|
||||
|
||||
return formattedLocalNow;
|
||||
}
|
||||
|
||||
void Debug::log(string message, string fileName, int lineNo, string functionName)
|
||||
{
|
||||
string output = "";
|
||||
size_t position = message.find("sysCall(");
|
||||
|
||||
if(position != string::npos)
|
||||
{
|
||||
size_t end = message.find_first_not_of("0123456789", position + 8);
|
||||
unsigned long long systemCallNumber = std::stoi(message.substr(position + 8, end - position - 8));
|
||||
string systemCallName = strlen(syscalls[systemCallNumber]) != 0 ? syscalls[systemCallNumber]
|
||||
: "unknown_system_call";
|
||||
if(systemCallNumber < MAX_NUMBER_OF_SYSCALLS)
|
||||
output = message.replace(position, end - position + 1, systemCallName + "(");
|
||||
}
|
||||
if(output == "")
|
||||
output = message;
|
||||
|
||||
logFile << getTimestamp() << ": " << output << " (in "
|
||||
<< fileName.substr(fileName.rfind('/') + 1) << ":"
|
||||
<< lineNo << " in " << functionName << "())" << endl;
|
||||
logFile.flush();
|
||||
}
|
||||
|
||||
std::string Debug::peekData(pid_t shellPid, void *data, size_t length)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for(size_t index = 0; index < length / sizeof(long); ++index)
|
||||
{
|
||||
long datum = ptrace(PTRACE_PEEKDATA, shellPid, ((char *) data) + index * sizeof(long));
|
||||
if(length == static_cast<size_t>(-1))
|
||||
{
|
||||
size_t datumLength = strnlen((char *) &datum, sizeof(long));
|
||||
result += string((char *) &datum, datumLength);
|
||||
if(datumLength < sizeof(long))
|
||||
break;
|
||||
}
|
||||
else
|
||||
result += string((char *) &datum, sizeof(long));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Debug::formatToAddress(unsigned long long address)
|
||||
{
|
||||
char addressString[17];
|
||||
|
||||
snprintf(addressString, 17, "%016llx", address);
|
||||
|
||||
return string("0x") + addressString;
|
||||
}
|
||||
|
||||
void Debug::logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName)
|
||||
{
|
||||
const static uint8_t maxStringLength = 32;
|
||||
string message = "syscall: ";
|
||||
|
||||
string sysCallName;
|
||||
if(sysCallId < MAX_NUMBER_OF_SYSCALLS)
|
||||
sysCallName = syscalls[sysCallId];
|
||||
if(sysCallName.empty())
|
||||
sysCallName = "syscall" + std::to_string(sysCallId);
|
||||
|
||||
|
||||
// good overviews for syscall arguments:
|
||||
// https://filippo.io/linux-syscall-table/
|
||||
// https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||
switch(sysCallId)
|
||||
{
|
||||
case SYS_read:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == static_cast<unsigned long long>(-1) ? "-1" : std::to_string((size_t) result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", \"" + (returnedFromSysCall ? __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength))) : "")
|
||||
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_write:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == static_cast<unsigned long long>(-1) ? "-1" : std::to_string((size_t) result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
|
||||
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_close:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_stat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(\"" + peekData(shellPid, (char *) firstArgument, -1) + "\""
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_fstat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_openat:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + ((int) firstArgument == AT_FDCWD ? string("AT_FDCWD") : std::to_string((int) firstArgument))
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, -1)) + "\""
|
||||
+ ", " + std::to_string((int) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_rt_sigprocmask:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ", " + formatToAddress(thirdArgument)
|
||||
+ ", " + std::to_string((size_t) fourthArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_access:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + peekData(shellPid, (char *) firstArgument, -1)
|
||||
+ ", " + std::to_string((int) secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_dup2:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + std::to_string((int) secondArgument)
|
||||
+ ")";
|
||||
break;
|
||||
case SYS_fcntl:
|
||||
if(returnedFromSysCall)
|
||||
message += std::to_string((int) result) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + std::to_string((int) firstArgument)
|
||||
+ ", " + (secondArgument == 0 ? string("F_DUP_FD") : std::to_string((int) secondArgument))
|
||||
+ ", …"
|
||||
+ ")";
|
||||
break;
|
||||
/* case SYS_fstat:
|
||||
struct stat statbuf;
|
||||
message += "(" + std::to_string(firstArgument)
|
||||
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
|
||||
+ (maxStringLength < thirdArgument ? "[…]" : "") + "\""
|
||||
+ ", " + std::to_string((size_t) thirdArgument)
|
||||
+ ")";
|
||||
break;
|
||||
*/ default:
|
||||
if(returnedFromSysCall)
|
||||
message += (result == (unsigned long long) -1 ? "-1" : std::to_string(result)) + " = ";
|
||||
message += sysCallName
|
||||
+ "(" + formatToAddress(firstArgument)
|
||||
+ ", " + formatToAddress(secondArgument)
|
||||
+ ", " + formatToAddress(thirdArgument)
|
||||
+ ", " + formatToAddress(fourthArgument)
|
||||
+ ")";
|
||||
}
|
||||
// source: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||
|
||||
log(message, fileName, lineNo, functionName);
|
||||
}
|
||||
|
||||
Debug *Debug::getInstance()
|
||||
{
|
||||
static Debug *debug = new Debug();
|
||||
return debug;
|
||||
}
|
||||
|
||||
const char *Debug::syscalls[MAX_NUMBER_OF_SYSCALLS] = {/*[0] =*/ "read",/*[1] =*/ "write",
|
||||
/*[2] =*/ "open",/*[3] =*/ "close",
|
||||
/*[4] =*/ "stat",/*[5] =*/ "fstat",
|
||||
/*[6] =*/ "lstat",/*[7] =*/ "poll",
|
||||
/*[8] =*/ "lseek",/*[9] =*/ "mmap",
|
||||
/*[10] =*/ "mprotect",/*[11] =*/ "munmap",
|
||||
/*[12] =*/ "brk",/*[13] =*/ "rt_sigaction",
|
||||
/*[14] =*/ "rt_sigprocmask",/*[15] =*/ "rt_sigreturn",
|
||||
/*[16] =*/ "ioctl",/*[17] =*/ "pread64",
|
||||
/*[18] =*/ "pwrite64",/*[19] =*/ "readv",
|
||||
/*[20] =*/ "writev",/*[21] =*/ "access",
|
||||
/*[22] =*/ "pipe",/*[23] =*/ "select",
|
||||
/*[24] =*/ "sched_yield",/*[25] =*/ "mremap",
|
||||
/*[26] =*/ "msync",/*[27] =*/ "mincore",
|
||||
/*[28] =*/ "madvise",/*[29] =*/ "shmget",
|
||||
/*[30] =*/ "shmat",/*[31] =*/ "shmctl",
|
||||
/*[32] =*/ "dup",/*[33] =*/ "dup2",
|
||||
/*[34] =*/ "pause",/*[35] =*/ "nanosleep",
|
||||
/*[36] =*/ "getitimer",/*[37] =*/ "alarm",
|
||||
/*[38] =*/ "setitimer",/*[39] =*/ "getpid",
|
||||
/*[40] =*/ "sendfile",/*[41] =*/ "socket",
|
||||
/*[42] =*/ "connect",/*[43] =*/ "accept",
|
||||
/*[44] =*/ "sendto",/*[45] =*/ "recvfrom",
|
||||
/*[46] =*/ "sendmsg",/*[47] =*/ "recvmsg",
|
||||
/*[48] =*/ "shutdown",/*[49] =*/ "bind",
|
||||
/*[50] =*/ "listen",/*[51] =*/ "getsockname",
|
||||
/*[52] =*/ "getpeername",/*[53] =*/ "socketpair",
|
||||
/*[54] =*/ "setsockopt",/*[55] =*/ "getsockopt",
|
||||
/*[56] =*/ "clone",/*[57] =*/ "fork",
|
||||
/*[58] =*/ "vfork",/*[59] =*/ "execve",
|
||||
/*[60] =*/ "exit",/*[61] =*/ "wait4",
|
||||
/*[62] =*/ "kill",/*[63] =*/ "uname",
|
||||
/*[64] =*/ "semget",/*[65] =*/ "semop",
|
||||
/*[66] =*/ "semctl",/*[67] =*/ "shmdt",
|
||||
/*[68] =*/ "msgget",/*[69] =*/ "msgsnd",
|
||||
/*[70] =*/ "msgrcv",/*[71] =*/ "msgctl",
|
||||
/*[72] =*/ "fcntl",/*[73] =*/ "flock",
|
||||
/*[74] =*/ "fsync",/*[75] =*/ "fdatasync",
|
||||
/*[76] =*/ "truncate",/*[77] =*/ "ftruncate",
|
||||
/*[78] =*/ "getdents",/*[79] =*/ "getcwd",
|
||||
/*[80] =*/ "chdir",/*[81] =*/ "fchdir",
|
||||
/*[82] =*/ "rename",/*[83] =*/ "mkdir",
|
||||
/*[84] =*/ "rmdir",/*[85] =*/ "creat",
|
||||
/*[86] =*/ "link",/*[87] =*/ "unlink",
|
||||
/*[88] =*/ "symlink",/*[89] =*/ "readlink",
|
||||
/*[90] =*/ "chmod",/*[91] =*/ "fchmod",
|
||||
/*[92] =*/ "chown",/*[93] =*/ "fchown",
|
||||
/*[94] =*/ "lchown",/*[95] =*/ "umask",
|
||||
/*[96] =*/ "gettimeofday",/*[97] =*/ "getrlimit",
|
||||
/*[98] =*/ "getrusage",/*[99] =*/ "sysinfo",
|
||||
/*[100] =*/ "times",/*[101] =*/ "ptrace",
|
||||
/*[102] =*/ "getuid",/*[103] =*/ "syslog",
|
||||
/*[104] =*/ "getgid",/*[105] =*/ "setuid",
|
||||
/*[106] =*/ "setgid",/*[107] =*/ "geteuid",
|
||||
/*[108] =*/ "getegid",/*[109] =*/ "setpgid",
|
||||
/*[110] =*/ "getppid",/*[111] =*/ "getpgrp",
|
||||
/*[112] =*/ "setsid",/*[113] =*/ "setreuid",
|
||||
/*[114] =*/ "setregid",/*[115] =*/ "getgroups",
|
||||
/*[116] =*/ "setgroups",/*[117] =*/ "setresuid",
|
||||
/*[118] =*/ "getresuid",/*[119] =*/ "setresgid",
|
||||
/*[120] =*/ "getresgid",/*[121] =*/ "getpgid",
|
||||
/*[122] =*/ "setfsuid",/*[123] =*/ "setfsgid",
|
||||
/*[124] =*/ "getsid",/*[125] =*/ "capget",
|
||||
/*[126] =*/ "capset",/*[127] =*/ "rt_sigpending",
|
||||
/*[128] =*/ "rt_sigtimedwait",/*[129] =*/ "rt_sigqueueinfo",
|
||||
/*[130] =*/ "rt_sigsuspend",/*[131] =*/ "sigaltstack",
|
||||
/*[132] =*/ "utime",/*[133] =*/ "mknod",
|
||||
/*[134] =*/ "uselib",/*[135] =*/ "personality",
|
||||
/*[136] =*/ "ustat",/*[137] =*/ "statfs",
|
||||
/*[138] =*/ "fstatfs",/*[139] =*/ "sysfs",
|
||||
/*[140] =*/ "getpriority",/*[141] =*/ "setpriority",
|
||||
/*[142] =*/ "sched_setparam",/*[143] =*/ "sched_getparam",
|
||||
/*[144] =*/ "sched_setscheduler",/*[145] =*/ "sched_getscheduler",
|
||||
/*[146] =*/ "sched_get_priority_max",/*[147] =*/ "sched_get_priority_min",
|
||||
/*[148] =*/ "sched_rr_get_interval",/*[149] =*/ "mlock",
|
||||
/*[150] =*/ "munlock",/*[151] =*/ "mlockall",
|
||||
/*[152] =*/ "munlockall",/*[153] =*/ "vhangup",
|
||||
/*[154] =*/ "modify_ldt",/*[155] =*/ "pivot_root",
|
||||
/*[156] =*/ "",/*[157] =*/ "prctl",
|
||||
/*[158] =*/ "arch_prctl",/*[159] =*/ "adjtimex",
|
||||
/*[160] =*/ "setrlimit",/*[161] =*/ "chroot",
|
||||
/*[162] =*/ "sync",/*[163] =*/ "acct",
|
||||
/*[164] =*/ "settimeofday",/*[165] =*/ "mount",
|
||||
/*[166] =*/ "",/*[167] =*/ "swapon",
|
||||
/*[168] =*/ "swapoff",/*[169] =*/ "reboot",
|
||||
/*[170] =*/ "sethostname",/*[171] =*/ "setdomainname",
|
||||
/*[172] =*/ "iopl",/*[173] =*/ "ioperm",
|
||||
/*[174] =*/ "",/*[175] =*/ "init_module",
|
||||
/*[176] =*/ "delete_module",/*[177] =*/ "",
|
||||
/*[178] =*/ "",/*[179] =*/ "quotactl",
|
||||
/*[180] =*/ "",/*[181] =*/ "",
|
||||
/*[182] =*/ "",/*[183] =*/ "",
|
||||
/*[184] =*/ "",/*[185] =*/ "",
|
||||
/*[186] =*/ "gettid",/*[187] =*/ "readahead",
|
||||
/*[188] =*/ "setxattr",/*[189] =*/ "lsetxattr",
|
||||
/*[190] =*/ "fsetxattr",/*[191] =*/ "getxattr",
|
||||
/*[192] =*/ "lgetxattr",/*[193] =*/ "fgetxattr",
|
||||
/*[194] =*/ "listxattr",/*[195] =*/ "llistxattr",
|
||||
/*[196] =*/ "flistxattr",/*[197] =*/ "removexattr",
|
||||
/*[198] =*/ "lremovexattr",/*[199] =*/ "fremovexattr",
|
||||
/*[200] =*/ "tkill",/*[201] =*/ "time",
|
||||
/*[202] =*/ "futex",/*[203] =*/ "sched_setaffinity",
|
||||
/*[204] =*/ "sched_getaffinity",/*[205] =*/ "set_thread_area",
|
||||
/*[206] =*/ "io_setup",/*[207] =*/ "io_destroy",
|
||||
/*[208] =*/ "io_getevents",/*[209] =*/ "io_submit",
|
||||
/*[210] =*/ "io_cancel",/*[211] =*/ "get_thread_area",
|
||||
/*[212] =*/ "",/*[213] =*/ "epoll_create",
|
||||
/*[214] =*/ "",/*[215] =*/ "",
|
||||
/*[216] =*/ "remap_file_pages",/*[217] =*/ "getdents64",
|
||||
/*[218] =*/ "set_tid_address",/*[219] =*/ "restart_syscall",
|
||||
/*[220] =*/ "semtimedop",/*[221] =*/ "fadvise64",
|
||||
/*[222] =*/ "timer_create",/*[223] =*/ "timer_settime",
|
||||
/*[224] =*/ "timer_gettime",/*[225] =*/ "timer_getoverrun",
|
||||
/*[226] =*/ "timer_delete",/*[227] =*/ "clock_settime",
|
||||
/*[228] =*/ "clock_gettime",/*[229] =*/ "clock_getres",
|
||||
/*[230] =*/ "clock_nanosleep",/*[231] =*/ "exit_group",
|
||||
/*[232] =*/ "epoll_wait",/*[233] =*/ "epoll_ctl",
|
||||
/*[234] =*/ "tgkill",/*[235] =*/ "utimes",
|
||||
/*[236] =*/ "",/*[237] =*/ "mbind",
|
||||
/*[238] =*/ "set_mempolicy",/*[239] =*/ "get_mempolicy",
|
||||
/*[240] =*/ "mq_open",/*[241] =*/ "mq_unlink",
|
||||
/*[242] =*/ "mq_timedsend",/*[243] =*/ "mq_timedreceive",
|
||||
/*[244] =*/ "mq_notify",/*[245] =*/ "mq_getsetattr",
|
||||
/*[246] =*/ "kexec_load",/*[247] =*/ "waitid",
|
||||
/*[248] =*/ "add_key",/*[249] =*/ "request_key",
|
||||
/*[250] =*/ "keyctl",/*[251] =*/ "ioprio_set",
|
||||
/*[252] =*/ "ioprio_get",/*[253] =*/ "inotify_init",
|
||||
/*[254] =*/ "inotify_add_watch",/*[255] =*/ "inotify_rm_watch",
|
||||
/*[256] =*/ "migrate_pages",/*[257] =*/ "openat",
|
||||
/*[258] =*/ "mkdirat",/*[259] =*/ "mknodat",
|
||||
/*[260] =*/ "fchownat",/*[261] =*/ "futimesat",
|
||||
/*[262] =*/ "newfstatat",/*[263] =*/ "unlinkat",
|
||||
/*[264] =*/ "renameat",/*[265] =*/ "linkat",
|
||||
/*[266] =*/ "symlinkat",/*[267] =*/ "readlinkat",
|
||||
/*[268] =*/ "fchmodat",/*[269] =*/ "faccessat",
|
||||
/*[270] =*/ "pselect6",/*[271] =*/ "ppoll",
|
||||
/*[272] =*/ "unshare",/*[273] =*/ "set_robust_list",
|
||||
/*[274] =*/ "get_robust_list",/*[275] =*/ "splice",
|
||||
/*[276] =*/ "tee",/*[277] =*/ "sync_file_range",
|
||||
/*[278] =*/ "vmsplice",/*[279] =*/ "move_pages",
|
||||
/*[280] =*/ "utimensat",/*[281] =*/ "epoll_pwait",
|
||||
/*[282] =*/ "signalfd",/*[283] =*/ "timerfd_create",
|
||||
/*[284] =*/ "eventfd",/*[285] =*/ "fallocate",
|
||||
/*[286] =*/ "timerfd_settime",/*[287] =*/ "timerfd_gettime",
|
||||
/*[288] =*/ "accept4",/*[289] =*/ "signalfd4",
|
||||
/*[290] =*/ "eventfd2",/*[291] =*/ "epoll_create1",
|
||||
/*[292] =*/ "dup3",/*[293] =*/ "pipe2",
|
||||
/*[294] =*/ "inotify_init1",/*[295] =*/ "preadv",
|
||||
/*[296] =*/ "pwritev",/*[297] =*/ "rt_tgsigqueueinfo",
|
||||
/*[298] =*/ "perf_event_open",/*[299] =*/ "recvmmsg",
|
||||
/*[300] =*/ "fanotify_init",/*[301] =*/ "fanotify_mark",
|
||||
/*[302] =*/ "prlimit64",/*[303] =*/ "name_to_handle_at",
|
||||
/*[304] =*/ "open_by_handle_at",/*[305] =*/ "clock_adjtime",
|
||||
/*[306] =*/ "syncfs",/*[307] =*/ "sendmmsg",
|
||||
/*[308] =*/ "setns",/*[309] =*/ "getcpu",
|
||||
/*[310] =*/ "process_vm_readv",/*[311] =*/ "process_vm_writev",
|
||||
/*[312] =*/ "kcmp",/*[313] =*/ "finit_module",
|
||||
/*[314] =*/ "sched_setattr",/*[315] =*/ "sched_getattr",
|
||||
/*[316] =*/ "renameat2",/*[317] =*/ "seccomp",
|
||||
/*[318] =*/ "getrandom",/*[319] =*/ "memfd_create",
|
||||
/*[320] =*/ "kexec_file_load",/*[321] =*/ "bpf",
|
||||
/*[322] =*/ "execveat",/*[323] =*/ "userfaultfd",
|
||||
/*[324] =*/ "membarrier",/*[325] =*/ "mlock2",
|
||||
/*[326] =*/ "copy_file_range",/*[327] =*/ "preadv2",
|
||||
/*[328] =*/ "pwritev2",/*[329] =*/ "pkey_mprotect",
|
||||
/*[330] =*/ "pkey_alloc",/*[331] =*/ "pkey_free",
|
||||
/*[332] =*/ "statx",/*[333] =*/ "io_pgetevents",
|
||||
/*[334] =*/ "rseq",/*[335] =*/ "",
|
||||
/*[336] =*/ "",/*[337] =*/ "",
|
||||
/*[338] =*/ "",/*[339] =*/ "",
|
||||
/*[340] =*/ "",/*[341] =*/ "",
|
||||
/*[342] =*/ "",/*[343] =*/ "",
|
||||
/*[344] =*/ "",/*[345] =*/ "",
|
||||
/*[346] =*/ "",/*[347] =*/ "",
|
||||
/*[348] =*/ "",/*[349] =*/ "",
|
||||
/*[350] =*/ "",/*[351] =*/ "",
|
||||
/*[352] =*/ "",/*[353] =*/ "",
|
||||
/*[354] =*/ "",/*[355] =*/ "",
|
||||
/*[356] =*/ "",/*[357] =*/ "",
|
||||
/*[358] =*/ "",/*[359] =*/ "",
|
||||
/*[360] =*/ "",/*[361] =*/ "",
|
||||
/*[362] =*/ "",/*[363] =*/ "",
|
||||
/*[364] =*/ "",/*[365] =*/ "",
|
||||
/*[366] =*/ "",/*[367] =*/ "",
|
||||
/*[368] =*/ "",/*[369] =*/ "",
|
||||
/*[370] =*/ "",/*[371] =*/ "",
|
||||
/*[372] =*/ "",/*[373] =*/ "",
|
||||
/*[374] =*/ "",/*[375] =*/ "",
|
||||
/*[376] =*/ "",/*[377] =*/ "",
|
||||
/*[378] =*/ "",/*[379] =*/ "",
|
||||
/*[380] =*/ "",/*[381] =*/ "",
|
||||
/*[382] =*/ "",/*[383] =*/ "",
|
||||
/*[384] =*/ "",/*[385] =*/ "",
|
||||
/*[386] =*/ "",/*[387] =*/ "",
|
||||
/*[388] =*/ "",/*[389] =*/ "",
|
||||
/*[390] =*/ "",/*[391] =*/ "",
|
||||
/*[392] =*/ "",/*[393] =*/ "",
|
||||
/*[394] =*/ "",/*[395] =*/ "",
|
||||
/*[396] =*/ "",/*[397] =*/ "",
|
||||
/*[398] =*/ "",/*[399] =*/ "",
|
||||
/*[400] =*/ "",/*[401] =*/ "",
|
||||
/*[402] =*/ "",/*[403] =*/ "",
|
||||
/*[404] =*/ "",/*[405] =*/ "",
|
||||
/*[406] =*/ "",/*[407] =*/ "",
|
||||
/*[408] =*/ "",/*[409] =*/ "",
|
||||
/*[410] =*/ "",/*[411] =*/ "",
|
||||
/*[412] =*/ "",/*[413] =*/ "",
|
||||
/*[414] =*/ "",/*[415] =*/ "",
|
||||
/*[416] =*/ "",/*[417] =*/ "",
|
||||
/*[418] =*/ "",/*[419] =*/ "",
|
||||
/*[420] =*/ "",/*[421] =*/ "",
|
||||
/*[422] =*/ "",/*[423] =*/ "",
|
||||
/*[424] =*/ "pidfd_send_signal",/*[425] =*/ "io_uring_setup",
|
||||
/*[426] =*/ "io_uring_enter",/*[427] =*/ "io_uring_register",
|
||||
/*[428] =*/ "open_tree",/*[429] =*/ "move_mount",
|
||||
/*[430] =*/ "fsopen",/*[431] =*/ "fsconfig",
|
||||
/*[432] =*/ "fsmount",/*[433] =*/ "fspick",
|
||||
/*[434] =*/ "pidfd_open",/*[435] =*/ "clone3" };
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
|
||||
Following command in part thx to:
|
||||
https://unix.stackexchange.com/questions/445507/syscall-number-%E2%86%92-name-mapping-at-runtime
|
||||
|
||||
Command to create mapping of system call number to system call name:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
awk 'BEGIN { print "#include <sys/syscall.h>" }
|
||||
/p_syscall_meta/ { syscall = substr($NF, 19);
|
||||
printf "[SYS_%s] = \"%s\", \n", syscall, syscall }' /proc/kallsyms \
|
||||
| gcc -E -P - \
|
||||
| sort -V \
|
||||
| grep "\[[0-9]" \
|
||||
| awk 'BEGIN {expectedIndex = 0;}
|
||||
{ actualIndex = $1;
|
||||
gsub(/[\[\]]/, "", actualIndex);
|
||||
if (actualIndex != expectedIndex)
|
||||
for (; expectedIndex < actualIndex; expectedIndex++)
|
||||
print "[" expectedIndex "] = \"\",";
|
||||
print $0; expectedIndex++ }' \
|
||||
| tr -d "\n" \
|
||||
| sed -e "s/^/const char *syscalls[1024] = {/; s/,$/ };/" \
|
||||
| sed -e 's/,/,\n /2;P;D' \
|
||||
| cat - <(echo) # new line at the end
|
||||
*/
|
|
@ -53,14 +53,21 @@ namespace krikkel
|
|||
{
|
||||
#define __debug_stringify_switch_case(caseTestValue) \
|
||||
case caseTestValue: \
|
||||
propertyName = (propertyName.empty() ? #caseTestValue : propertyName)
|
||||
#define __debug_stringify_switch_case_end_bool(value) \
|
||||
propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \
|
||||
[[fallthrough]]
|
||||
#define __debug_stringify_switch_last_case_bool(caseTestValue, value) \
|
||||
case caseTestValue: \
|
||||
propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \
|
||||
valueString = (value ? "true" : "false"); \
|
||||
break
|
||||
#define __debug_stringify_switch_case_end_string(value) \
|
||||
#define __debug_stringify_switch_last_case_string(caseTestValue, value) \
|
||||
case caseTestValue: \
|
||||
propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \
|
||||
valueString = "\"" + std::string(value) + "\""; \
|
||||
break
|
||||
#define __debug_stringify_switch_case_end_number(value) \
|
||||
#define __debug_stringify_switch_last_case_number(caseTestValue, value) \
|
||||
case caseTestValue: \
|
||||
propertyName = (propertyName.empty() ? #caseTestValue : propertyName); \
|
||||
valueString = std::to_string(value); \
|
||||
break;
|
||||
#define __debug_stringify_switch_end(__prop) \
|
||||
|
@ -120,9 +127,9 @@ namespace krikkel
|
|||
#define __debug_log(message)
|
||||
#define __debug_stringify_switch_begin(closureName, __prop, __value)
|
||||
#define __debug_stringify_switch_case(caseTestValue)
|
||||
#define __debug_stringify_switch_case_end_bool(value)
|
||||
#define __debug_stringify_switch_case_end_string(value)
|
||||
#define __debug_stringify_switch_case_end_number(value)
|
||||
#define __debug_stringify_switch_last_case_bool(caseTestValue, value)
|
||||
#define __debug_stringify_switch_last_case_string(caseTestValue, value)
|
||||
#define __debug_stringify_switch_last_case_number(caseTestValue, value)
|
||||
#define __debug_stringify_switch_end(__prop)
|
||||
#define __debug_make_bytes_printable_table(__bytes)
|
||||
#define __debug_make_bytes_printable(__bytes)
|
147
src/krikkel/NCurses/DemoApp.cpp
Normal file
147
src/krikkel/NCurses/DemoApp.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "DemoApp.hpp"
|
||||
#include "kNCurses/VerticalTilingWindowManager.hpp"
|
||||
#include "kNCurses/HorizontalTilingWindowManager.hpp"
|
||||
#include "kNCurses/Window.hpp"
|
||||
#include "kNCurses/PtyWindow.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
using std::scoped_lock;
|
||||
|
||||
DemoApp::DemoApp() : NCursesApplication(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int DemoApp::run()
|
||||
{
|
||||
setUpWindows();
|
||||
forkShell();
|
||||
mainLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DemoApp::setUpWindows()
|
||||
{
|
||||
windowManager
|
||||
= new VerticalTilingWindowManager(Root_Window, &ncursesMutex);
|
||||
topWindowManager
|
||||
= new HorizontalTilingWindowManager(new Window(), &ncursesMutex);
|
||||
bottomWindowManager
|
||||
= new HorizontalTilingWindowManager(new Window(), &ncursesMutex);
|
||||
|
||||
windowManager->addWindow(topWindowManager);
|
||||
ptyWindow = new PtyWindow(&ncursesMutex, 0, 0, 0, 0);
|
||||
windowManager->addWindow(ptyWindow);
|
||||
windowManager->addWindow(bottomWindowManager);
|
||||
windowManager->updateLayout();
|
||||
windowManager->refresh();
|
||||
|
||||
dummyWindowTopLeft = new Window();
|
||||
topWindowManager->addWindow(dummyWindowTopLeft, 1);
|
||||
dummyWindowTopLeft->addstr("t\no\np\n \nl\ne\nf\nt\n w\ni\nn\nd\no\nw");
|
||||
dummyWindowTopRight = new Window(topWindowManager);
|
||||
dummyWindowTopRight->addstr("top right window");
|
||||
|
||||
topWindowManager->updateLayout();
|
||||
topWindowManager->refresh();
|
||||
|
||||
dummyWindowBottomLeft = new Window(bottomWindowManager);
|
||||
dummyWindowBottomLeft->addstr("bottom left window");
|
||||
dummyWindowBottomRight = new Window(bottomWindowManager);
|
||||
dummyWindowBottomRight->addstr("bottom right window");
|
||||
|
||||
bottomWindowManager->updateLayout();
|
||||
bottomWindowManager->refresh();
|
||||
}
|
||||
|
||||
void DemoApp::forkShell()
|
||||
{
|
||||
if(fork() == 0)
|
||||
{
|
||||
const char *shellPath = getenv("SHELL");
|
||||
if(!shellPath)
|
||||
shellPath = "/bin/bash";
|
||||
|
||||
login_tty(ptyWindow->getFdPtyClient());
|
||||
const int sizeArg = 2;
|
||||
const char *arg[sizeArg] = {shellPath, NULL};
|
||||
fprintf(stderr, "<CTRL>+C exits shell.\n\n");
|
||||
execv(shellPath, const_cast<char * const *>(arg));
|
||||
fprintf(stderr, "Well, well, well … could not start a shell. We "
|
||||
"tried `%s`. Maybe set `SHELL` environment variable"
|
||||
" to a working value?", shellPath);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void DemoApp::mainLoop()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
SingleUserInput input = windowManager->readSingleUserInput();
|
||||
if(input.isNormalKey())
|
||||
{
|
||||
ptyWindow->writeUnicodeCharToClient(input.getRawInput());
|
||||
{
|
||||
scoped_lock lock(ncursesMutex);
|
||||
dummyWindowTopRight->addch(input.getRawInput());
|
||||
dummyWindowTopRight->refresh();
|
||||
}
|
||||
}
|
||||
if(input.isFunctionKey())
|
||||
{
|
||||
switch(input.getRawInput())
|
||||
{
|
||||
case KEY_RESIZE:
|
||||
windowManager->updateLayout();
|
||||
break;
|
||||
case KEY_F(1):
|
||||
if(topWindowManager->isHidden())
|
||||
windowManager->showWindow(topWindowManager);
|
||||
else
|
||||
windowManager->hideWindow(topWindowManager);
|
||||
windowManager->updateLayout();
|
||||
windowManager->refresh();
|
||||
break;
|
||||
case KEY_F(2):
|
||||
if(ptyWindow->isHidden())
|
||||
windowManager->showWindow(ptyWindow);
|
||||
else
|
||||
windowManager->hideWindow(ptyWindow);
|
||||
windowManager->updateLayout();
|
||||
windowManager->refresh();
|
||||
break;
|
||||
case KEY_F(3):
|
||||
if(bottomWindowManager->isHidden())
|
||||
windowManager->showWindow(bottomWindowManager);
|
||||
else
|
||||
windowManager->hideWindow(bottomWindowManager);
|
||||
windowManager->updateLayout();
|
||||
windowManager->refresh();
|
||||
break;
|
||||
case KEY_F(4):
|
||||
windowManager->invertWindowsVisibility();
|
||||
windowManager->updateLayout();
|
||||
windowManager->refresh();
|
||||
break;
|
||||
case KEY_F(5):
|
||||
windowManager->refresh();
|
||||
break;
|
||||
default:
|
||||
ptyWindow->writeFunctionKeyToClient(input.mapKeyNcursesToVTerm());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/krikkel/NCurses/DemoApp.hpp
Normal file
39
src/krikkel/NCurses/DemoApp.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @brief demo application for the library
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#ifndef A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
#define A3B2AE4E_0A39_468C_8CCA_E6508166702A
|
||||
|
||||
#include <cursesapp.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
class VerticalTilingWindowManager;
|
||||
class HorizontalTilingWindowManager;
|
||||
class Window;
|
||||
class PtyWindow;
|
||||
|
||||
class DemoApp : public NCursesApplication
|
||||
{
|
||||
public:
|
||||
DemoApp();
|
||||
|
||||
private:
|
||||
VerticalTilingWindowManager *windowManager;
|
||||
HorizontalTilingWindowManager *topWindowManager, *bottomWindowManager;
|
||||
Window *dummyWindowTopLeft, *dummyWindowTopRight
|
||||
, *dummyWindowBottomLeft, *dummyWindowBottomRight;
|
||||
PtyWindow *ptyWindow;
|
||||
std::recursive_mutex ncursesMutex;
|
||||
|
||||
int run() override;
|
||||
void setUpWindows();
|
||||
void forkShell();
|
||||
void mainLoop();
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* A3B2AE4E_0A39_468C_8CCA_E6508166702A */
|
41
src/krikkel/NCurses/HorizontalTilingWindowManager.cpp
Normal file
41
src/krikkel/NCurses/HorizontalTilingWindowManager.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "kNCurses/HorizontalTilingWindowManager.hpp"
|
||||
#include "kNCurses/Window.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
using std::recursive_mutex;
|
||||
|
||||
HorizontalTilingWindowManager::HorizontalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex)
|
||||
: TilingWindowManager(rootWindow, ncursesMutex)
|
||||
{}
|
||||
|
||||
void HorizontalTilingWindowManager::resizeAndMoveWindow(Window *window, windowDimension dimension, windowPosition position)
|
||||
{
|
||||
__debug_log("(" + std::to_string(position + begx()) + ", " + std::to_string(0 + begy()) + ", " + std::to_string(dimension) + ", " + std::to_string(height()) + ")");
|
||||
window->resize(height(), dimension);
|
||||
window->mvwin(0 + begy(), position + begx());
|
||||
}
|
||||
|
||||
TilingWindowManager::windowDimension HorizontalTilingWindowManager::getAvailableSpace()
|
||||
{
|
||||
return width();
|
||||
}
|
||||
|
||||
void HorizontalTilingWindowManager::windowBorder()
|
||||
{
|
||||
vline(height());
|
||||
}
|
||||
|
||||
void HorizontalTilingWindowManager::moveCursor(TilingWindowManager::windowPosition position)
|
||||
{
|
||||
move(0, position);
|
||||
}
|
||||
}
|
367
src/krikkel/NCurses/PtyWindow.cpp
Normal file
367
src/krikkel/NCurses/PtyWindow.cpp
Normal file
|
@ -0,0 +1,367 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "kNCurses/PtyWindow.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <gsl/gsl>
|
||||
#include <cctype>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <exception>
|
||||
#include <cerrno>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
using gsl::narrow;
|
||||
using std::scoped_lock;
|
||||
using std::recursive_mutex;
|
||||
using std::system_error;
|
||||
using std::system_category;
|
||||
using std::string;
|
||||
|
||||
PtyWindow::PtyWindow(recursive_mutex *ncursesMutex, int lines, int columns, int y, int x)
|
||||
: Window(lines, columns, y, x), ncursesMutex(ncursesMutex)
|
||||
{
|
||||
// to get the original terminal we need to shutdown ncurses for a moment
|
||||
/// @todo maybe try `reset_prog_mode()` and `reset_shell_mode()` instead
|
||||
::endwin();
|
||||
tcgetattr(STDIN_FILENO, &terminalParameters);
|
||||
::refresh();
|
||||
|
||||
winsize windowSize =
|
||||
{
|
||||
/*.ws_row =*/ narrow<short unsigned int>(this->height())
|
||||
, /*.ws_col =*/ narrow<short unsigned int>(this->width())
|
||||
, /*.y =*/ 0
|
||||
, /*.x =*/ 0
|
||||
};
|
||||
openpty(&fdPtyHost, &fdPtyClient, NULL, &terminalParameters, &windowSize);
|
||||
|
||||
pseudoTerminal = vterm_new(windowSize.ws_row, windowSize.ws_col);
|
||||
vterm_set_utf8(pseudoTerminal, true);
|
||||
pseudoTerminalScreen = vterm_obtain_screen(pseudoTerminal);
|
||||
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);
|
||||
|
||||
//raw(); // — cbreak might suffice
|
||||
//noecho(); // — already set by NCursesWindow
|
||||
keypad(true); /// — already set by NCursesWindow
|
||||
//meta(true); // — already set by NCursesWindow
|
||||
//nodelay(true); // — @todo would need some programming, not sure if useful
|
||||
nonl();
|
||||
|
||||
/// @todo block all signals, this thread does not handle any
|
||||
readPtyClientThread =
|
||||
std::thread(&PtyWindow::readFromPtyClientThreadMethod, this);
|
||||
}
|
||||
|
||||
PtyWindow::~PtyWindow()
|
||||
{
|
||||
close(fdPtyHost);
|
||||
close(fdPtyClient);
|
||||
vterm_free(pseudoTerminal);
|
||||
}
|
||||
|
||||
int PtyWindow::getFdPtyClient() const
|
||||
{
|
||||
return fdPtyClient;
|
||||
}
|
||||
|
||||
void PtyWindow::writeToPtyClient(const char *output, size_t length)
|
||||
{
|
||||
size_t bytesWritten = write(fdPtyHost, output, length);
|
||||
/// @todo this is a bit too simple; partial write is possible
|
||||
if(bytesWritten != length)
|
||||
{
|
||||
string basicError("Could not write to PTY client (i. e. the shell).");
|
||||
throw system_error(errno, system_category(), basicError);
|
||||
}
|
||||
//__debug_log("written to PTY client: '" + __debug_make_bytes_printable(std::string(output, length)) + '\'');
|
||||
}
|
||||
|
||||
void PtyWindow::writeUnicodeCharToClient(wint_t character)
|
||||
{
|
||||
scoped_lock lock(ptyMutex);
|
||||
vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE);
|
||||
}
|
||||
|
||||
void PtyWindow::writeFunctionKeyToClient(VTermKey key)
|
||||
{
|
||||
scoped_lock lock(ptyMutex);
|
||||
//__debug_log("writing key: " + std::to_string(key));
|
||||
vterm_keyboard_key(pseudoTerminal, key, VTERM_MOD_NONE);
|
||||
}
|
||||
|
||||
void PtyWindow::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 PtyWindow::readFromPtyClient()
|
||||
{
|
||||
size_t bytesRead = read(fdPtyHost, ptyClientOutputBuffer, PTY_CLIENT_OUTPUT_BUFFER_SIZE);
|
||||
if(bytesRead != (size_t) -1 && bytesRead != 0)
|
||||
{
|
||||
scoped_lock lock(ptyMutex, *ncursesMutex);
|
||||
vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead);
|
||||
}
|
||||
//__debug_log("read from PTY client: '" + __debug_make_bytes_printable(std::string(ptyClientOutputBuffer, bytesRead)) + '\'');
|
||||
}
|
||||
|
||||
VTermScreenCallbacks PtyWindow::screenCallbacks =
|
||||
{
|
||||
/*.damage =*/ staticHandlerDamage
|
||||
, /*.moverect =*/ staticHandlerMoveRect
|
||||
, /*.movecursor =*/ staticHandlerMoveCursor
|
||||
, /*.settermprop =*/ staticHandlerSetTermProp
|
||||
, /*.bell =*/ staticHandlerBell
|
||||
, /*.resize =*/ staticHandlerResize
|
||||
, /*.sb_pushline =*/ staticHandlerPushLine
|
||||
, /*.sb_popline =*/ staticHandlerPopLine
|
||||
};
|
||||
|
||||
int PtyWindow::handlerDamage(VTermRect rect)
|
||||
{
|
||||
scoped_lock lock(ptyMutex, *ncursesMutex);
|
||||
|
||||
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);
|
||||
|
||||
refresh();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerMoveRect(VTermRect dest, VTermRect src)
|
||||
{
|
||||
(void) dest;
|
||||
(void) 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 PtyWindow::handlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible)
|
||||
{
|
||||
(void) oldpos;
|
||||
(void) visible;
|
||||
|
||||
scoped_lock lock(*ncursesMutex);
|
||||
cursorX = pos.col;
|
||||
cursorY = pos.row;
|
||||
refresh();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerSetTermProp(VTermProp prop, VTermValue *val)
|
||||
{
|
||||
(void) prop;
|
||||
(void) val;
|
||||
|
||||
/// @todo maybe use "vterm_get_prop_type() —> bool, number, string"?
|
||||
__debug_stringify_switch_begin(handlerSetTermProp, prop, val);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORVISIBLE);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORBLINK);
|
||||
__debug_stringify_switch_case(VTERM_PROP_ALTSCREEN);
|
||||
__debug_stringify_switch_last_case_bool(VTERM_PROP_REVERSE, val->boolean);
|
||||
__debug_stringify_switch_case(VTERM_PROP_TITLE);
|
||||
__debug_stringify_switch_last_case_string(VTERM_PROP_ICONNAME, val->string);
|
||||
__debug_stringify_switch_case(VTERM_PROP_CURSORSHAPE);
|
||||
__debug_stringify_switch_last_case_number(VTERM_PROP_MOUSE, val->number);
|
||||
__debug_stringify_switch_end(prop);
|
||||
|
||||
__debug_log(std::string("unimplemented handler called: ")
|
||||
+ __debug_stringify_get_string(handlerSetTermProp)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerBell()
|
||||
{
|
||||
beep();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerResize(int rows, int cols)
|
||||
{
|
||||
(void) rows;
|
||||
(void) cols;
|
||||
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerPushLine(int cols, const VTermScreenCell *cells)
|
||||
{
|
||||
(void) cols;
|
||||
(void) cells;
|
||||
|
||||
__debug_log("(unimplemented) push line with " + std::to_string(cols) + " columns");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PtyWindow::handlerPopLine(int cols, VTermScreenCell *cells)
|
||||
{
|
||||
(void) cols;
|
||||
(void) cells;
|
||||
|
||||
__debug_log("unimplemented handler called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
attr_t PtyWindow::extractAttributesFromVTermCell(VTermScreenCell vTermCell)
|
||||
{
|
||||
(void) vTermCell;
|
||||
|
||||
attr_t result = A_NORMAL;
|
||||
//__debug_log("unimplemented method called");
|
||||
return result;
|
||||
}
|
||||
|
||||
short PtyWindow::extractColorFromVTermCell(VTermScreenCell vTermCell)
|
||||
{
|
||||
(void) vTermCell;
|
||||
|
||||
//__debug_log("unimplemented method called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PtyWindow::copyPtyCellToNCursesWindow(int x, int y)
|
||||
{
|
||||
VTermPos cellPosition = {y, x};
|
||||
VTermScreenCell vTermCell;
|
||||
cchar_t converted;
|
||||
attr_t formatting;
|
||||
short colorPair;
|
||||
wchar_t character;
|
||||
|
||||
vterm_screen_get_cell(pseudoTerminalScreen, cellPosition, &vTermCell);
|
||||
formatting = extractAttributesFromVTermCell(vTermCell);
|
||||
colorPair = extractColorFromVTermCell(vTermCell);
|
||||
|
||||
if(vTermCell.chars[0] == 0)
|
||||
character = ' ';
|
||||
else
|
||||
character = *vTermCell.chars;
|
||||
|
||||
//__debug_log(std::string("written '") + (char) character + std::string("' ") + std::to_string(x) + ", " + std::to_string(y));
|
||||
setcchar(&converted, &character, formatting, colorPair, NULL);
|
||||
{
|
||||
move(cellPosition.row, cellPosition.col);
|
||||
chgat(1, formatting, colorPair, NULL);
|
||||
add_wch(&converted);
|
||||
}
|
||||
}
|
||||
|
||||
int PtyWindow::refresh()
|
||||
{
|
||||
//__debug_log("refreshing");
|
||||
scoped_lock lock(*ncursesMutex);
|
||||
move(cursorY, cursorX);
|
||||
return Window::refresh();
|
||||
}
|
||||
|
||||
/// @todo potential racing condition where drawing into terminal while
|
||||
/// resizing?
|
||||
int PtyWindow::resize(int rows, int cols)
|
||||
{
|
||||
scoped_lock lock(ptyMutex, *ncursesMutex);
|
||||
|
||||
winsize windowSize =
|
||||
{
|
||||
/*.ws_row =*/ narrow<unsigned short>(rows)
|
||||
, /*.ws_col =*/ narrow<unsigned short>(cols)
|
||||
, 0
|
||||
, 0
|
||||
};
|
||||
ioctl(fdPtyHost, TIOCSWINSZ, &windowSize);
|
||||
vterm_set_size(pseudoTerminal, rows, cols);
|
||||
|
||||
return Window::resize(rows, cols);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerDamage(VTermRect rect, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerDamage(rect);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerMoveRect(VTermRect dest, VTermRect src, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerMoveRect(dest, src);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerMoveCursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerMoveCursor(pos, oldpos, visible);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerSetTermProp(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerSetTermProp(prop, val);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerBell(void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerBell();
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerResize(int rows, int cols, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerResize(rows, cols);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerPushLine(int cols, const VTermScreenCell *cells, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerPushLine(cols, cells);
|
||||
}
|
||||
|
||||
int PtyWindow::staticHandlerPopLine(int cols, VTermScreenCell *cells, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
return instance->handlerPopLine(cols, cells);
|
||||
}
|
||||
|
||||
void PtyWindow::staticHandlerOutput(const char *s, size_t len, void *user)
|
||||
{
|
||||
PtyWindow *instance = static_cast<PtyWindow *>(user);
|
||||
instance->writeToPtyClient(s, len);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "SingleUserInput.hpp"
|
||||
#include "Debug.hpp"
|
||||
#include "kNCurses/SingleUserInput.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <vterm.h>
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
|||
#define KEY_ALT_BACKSPACE 127
|
||||
#define KEY_ALT_ALT_BACKSPACE '\b'
|
||||
|
||||
namespace krikkel::NCursesPtyWindow
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
SingleUserInput::SingleUserInput(int _resultGetWchCall, wint_t _input)
|
||||
: input(_input), resultGetWchCall(_resultGetWchCall)
|
||||
|
@ -42,7 +42,7 @@ namespace krikkel::NCursesPtyWindow
|
|||
// it is only processing user input, it is not time critical enough for
|
||||
// the wasted space
|
||||
|
||||
// @tode unmapped keys: keys erase
|
||||
// @todo unmapped keys: keys erase
|
||||
if(resultGetWchCall == OK)
|
||||
switch(input)
|
||||
{
|
170
src/krikkel/NCurses/TilingWindowManager.cpp
Normal file
170
src/krikkel/NCurses/TilingWindowManager.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "kNCurses/TilingWindowManager.hpp"
|
||||
#include "kNCurses/Window.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
using std::list;
|
||||
using std::pair;
|
||||
using std::recursive_mutex;
|
||||
using std::scoped_lock;
|
||||
|
||||
TilingWindowManager::TilingWindowManager(NCursesWindow *rootWindow, recursive_mutex *ncursesMutex)
|
||||
: Window(*rootWindow), ncursesMutex(ncursesMutex)
|
||||
{}
|
||||
|
||||
void TilingWindowManager::addWindow(Window *window, windowDimension size)
|
||||
{
|
||||
WindowStackElement stackElement(window, size);
|
||||
stack.push_back(stackElement);
|
||||
visibleStack.push_back(stackElement);
|
||||
}
|
||||
|
||||
std::recursive_mutex *TilingWindowManager::getNCursesMutex()
|
||||
{
|
||||
return ncursesMutex;
|
||||
}
|
||||
|
||||
void TilingWindowManager::hideWindow(Window *window)
|
||||
{
|
||||
if(window->hidden)
|
||||
return;
|
||||
|
||||
visibleStack.remove_if(
|
||||
[window](WindowStackElement element)
|
||||
{
|
||||
return element.first == window;
|
||||
});
|
||||
window->hidden = true;
|
||||
}
|
||||
|
||||
void TilingWindowManager::showWindow(Window *window)
|
||||
{
|
||||
if(!window->hidden)
|
||||
return;
|
||||
|
||||
list<WindowStackElement>::iterator currentVisibleWindowElement = visibleStack.begin();
|
||||
for(WindowStackElement currentWindowElement : stack)
|
||||
{
|
||||
if(currentWindowElement.first == window)
|
||||
{
|
||||
visibleStack.insert(currentVisibleWindowElement, currentWindowElement);
|
||||
window->hidden = false;
|
||||
break;
|
||||
}
|
||||
if(currentWindowElement != (*currentVisibleWindowElement))
|
||||
continue;
|
||||
++currentVisibleWindowElement;
|
||||
}
|
||||
}
|
||||
|
||||
void TilingWindowManager::invertWindowsVisibility()
|
||||
{
|
||||
list<WindowStackElement> originallyVisibleStack(visibleStack);
|
||||
list<WindowStackElement>::iterator originallyVisibleStackIterator = originallyVisibleStack.begin();
|
||||
visibleStack.clear();
|
||||
|
||||
for(WindowStackElement currentWindowElement : stack)
|
||||
if(originallyVisibleStackIterator != originallyVisibleStack.end()
|
||||
&& currentWindowElement == *originallyVisibleStackIterator)
|
||||
{
|
||||
currentWindowElement.first->hidden = true;
|
||||
++originallyVisibleStackIterator;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentWindowElement.first->hidden = false;
|
||||
visibleStack.push_back(currentWindowElement);
|
||||
}
|
||||
}
|
||||
|
||||
int TilingWindowManager::resize(int rows, int cols)
|
||||
{
|
||||
int result = Window::resize(rows, cols);
|
||||
updateLayout();
|
||||
return result;
|
||||
}
|
||||
|
||||
int TilingWindowManager::refresh()
|
||||
{
|
||||
scoped_lock lock(*ncursesMutex);
|
||||
|
||||
int result = Window::refresh();
|
||||
|
||||
for(WindowStackElement stackElement : visibleStack)
|
||||
// @todo there are return values; compound them?
|
||||
stackElement.first->refresh();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TilingWindowManager::updateLayout()
|
||||
{
|
||||
scoped_lock lock(*ncursesMutex);
|
||||
|
||||
size_t stackSize = visibleStack.size();
|
||||
if(stackSize == 0)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int unitSize = getRelativeUnitSizeForVisibleWindows(getAvailableSpace()) - 1;
|
||||
windowPosition position = 0;
|
||||
auto lastButNotLeast = prev(visibleStack.end());
|
||||
for(auto stackElementIterator = visibleStack.begin()
|
||||
; stackElementIterator != lastButNotLeast
|
||||
; ++stackElementIterator)
|
||||
{
|
||||
WindowStackElement stackElement = *stackElementIterator;
|
||||
Window *window = stackElement.first;
|
||||
if(!window->isHidden())
|
||||
{
|
||||
int windowShift;
|
||||
int windowSize = stackElement.second;
|
||||
if(windowSize < 0)
|
||||
windowShift = unitSize * (-windowSize);
|
||||
else
|
||||
windowShift = windowSize;
|
||||
|
||||
resizeAndMoveWindow(window, windowShift, position);
|
||||
position += windowShift;
|
||||
__debug_log("drawing line at " + std::to_string(position));
|
||||
moveCursor(position++);
|
||||
windowBorder();
|
||||
}
|
||||
}
|
||||
resizeAndMoveWindow((*lastButNotLeast).first, getAvailableSpace() - position, position);
|
||||
}
|
||||
|
||||
|
||||
TilingWindowManager::windowDimension TilingWindowManager::getRelativeUnitSizeForVisibleWindows(windowDimension spaceAvailable)
|
||||
{
|
||||
uint16_t numberOfRelativeUnits = 0;
|
||||
windowDimension absoluteSum = 0;
|
||||
|
||||
for(WindowStackElement stackElement : visibleStack)
|
||||
{
|
||||
windowDimension size = stackElement.second;
|
||||
if(size < 0)
|
||||
numberOfRelativeUnits -= size;
|
||||
else
|
||||
absoluteSum += size;
|
||||
}
|
||||
|
||||
windowDimension relativeUnitSize = (spaceAvailable - absoluteSum) / numberOfRelativeUnits;
|
||||
windowDimension remainder = (spaceAvailable - absoluteSum - relativeUnitSize * numberOfRelativeUnits);
|
||||
if(remainder > 0 && remainder * 2 < numberOfRelativeUnits)
|
||||
++relativeUnitSize;
|
||||
|
||||
return relativeUnitSize;
|
||||
}
|
||||
}
|
42
src/krikkel/NCurses/VerticalTilingWindowManager.cpp
Normal file
42
src/krikkel/NCurses/VerticalTilingWindowManager.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "kNCurses/VerticalTilingWindowManager.hpp"
|
||||
#include "kNCurses/Window.hpp"
|
||||
#include "../Debug.hpp"
|
||||
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
using std::recursive_mutex;
|
||||
|
||||
VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex)
|
||||
: TilingWindowManager(rootWindow, ncursesMutex)
|
||||
{}
|
||||
|
||||
void VerticalTilingWindowManager::resizeAndMoveWindow(Window *window, windowDimension dimension, windowPosition position)
|
||||
{
|
||||
__debug_log("(" + std::to_string(0 + begx()) + ", " + std::to_string(position + begy()) + ", " + std::to_string(width()) + ", " + std::to_string(dimension) + ")");
|
||||
window->resize(dimension, width());
|
||||
window->mvwin(position + begy(), 0 + begx());
|
||||
}
|
||||
|
||||
TilingWindowManager::windowDimension VerticalTilingWindowManager::getAvailableSpace()
|
||||
{
|
||||
return height();
|
||||
}
|
||||
|
||||
void VerticalTilingWindowManager::windowBorder()
|
||||
{
|
||||
hline(width());
|
||||
}
|
||||
|
||||
void VerticalTilingWindowManager::moveCursor(TilingWindowManager::windowPosition position)
|
||||
{
|
||||
move(position, 0);
|
||||
}
|
||||
|
||||
}
|
71
src/krikkel/NCurses/Window.cpp
Normal file
71
src/krikkel/NCurses/Window.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "kNCurses/Window.hpp"
|
||||
#include "kNCurses/VerticalTilingWindowManager.hpp"
|
||||
|
||||
namespace krikkel::NCurses
|
||||
{
|
||||
Window::Window() : NCursesWindow(0, 0, 0, 0)
|
||||
{}
|
||||
|
||||
Window::Window(TilingWindowManager *windowManager)
|
||||
: NCursesWindow(0, 0, 0, 0)
|
||||
{
|
||||
windowManager->addWindow(this);
|
||||
windowManager->updateLayout();
|
||||
}
|
||||
|
||||
Window::Window(const NCursesWindow &window)
|
||||
: NCursesWindow(window)
|
||||
{}
|
||||
|
||||
Window::Window(int lines, int columns, int y, int x)
|
||||
: NCursesWindow(lines, columns, y, x)
|
||||
{}
|
||||
|
||||
int Window::addnwstr(const wchar_t *wstr, int n)
|
||||
{
|
||||
return ::waddnwstr(w, wstr, n);
|
||||
}
|
||||
|
||||
int Window::add_wch(const cchar_t *character)
|
||||
{
|
||||
return ::wadd_wch(w, character);
|
||||
}
|
||||
|
||||
int Window::ins_wch(const cchar_t *character)
|
||||
{
|
||||
return ::wins_wch(w, character);
|
||||
}
|
||||
|
||||
int Window::get_wch(wint_t *character)
|
||||
{
|
||||
return ::wget_wch(w, character);
|
||||
}
|
||||
|
||||
bool Window::isHidden()
|
||||
{
|
||||
return hidden;
|
||||
}
|
||||
|
||||
SingleUserInput Window::readSingleUserInput()
|
||||
{
|
||||
wint_t input;
|
||||
int result = get_wch(&input);
|
||||
return SingleUserInput(result, input);
|
||||
}
|
||||
|
||||
int Window::resize(int rows, int cols)
|
||||
{
|
||||
return NCursesWindow::wresize(rows, cols);
|
||||
}
|
||||
|
||||
int Window::refresh()
|
||||
{
|
||||
if(!hidden)
|
||||
return NCursesWindow::refresh();
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,6 @@
|
|||
* @author Christian Burger (christian@krikkel.de)
|
||||
*/
|
||||
|
||||
#include "App.hpp"
|
||||
#include "DemoApp.hpp"
|
||||
|
||||
krikkel::NCursesPtyWindow::App cursesPtyApp;
|
||||
krikkel::NCurses::DemoApp demoApp;
|
Loading…
Reference in a new issue