hide and show windows in window manager

* switching to recursive mutexes for now; easier to implement
* need to read user input from the window manager; hidden windows are
drawn when reading input from there
* note: occasional dead lock between PTY and ncurses mutex
* fixed type in class `SingleUserInput`
master
Christian Burger 2022-05-06 14:02:18 +02:00
parent 0462a68c54
commit b9e32941fb
9 changed files with 134 additions and 37 deletions

View File

@ -54,7 +54,9 @@
"stdexcept": "cpp", "stdexcept": "cpp",
"typeinfo": "cpp", "typeinfo": "cpp",
"pointers": "cpp", "pointers": "cpp",
"list": "cpp" "list": "cpp",
"condition_variable": "cpp",
"mutex": "cpp"
}, },
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.includePath": [ "C_Cpp.default.includePath": [

47
App.cpp
View File

@ -22,10 +22,11 @@ namespace krikkel::NCurses
int DemoApp::run() int DemoApp::run()
{ {
std::mutex ncursesMutex; std::recursive_mutex ncursesMutex;
VerticalTilingWindowManager windowManager(Root_Window, &ncursesMutex); VerticalTilingWindowManager windowManager(Root_Window, &ncursesMutex);
new Window(&windowManager); Window *dummyWindow = new Window(&windowManager);
dummyWindow->move(0, 0);
PtyWindow *ptyWindow = new PtyWindow(&ncursesMutex, 0, 0, 0, 0); PtyWindow *ptyWindow = new PtyWindow(&ncursesMutex, 0, 0, 0, 0);
windowManager.addWindow(ptyWindow); windowManager.addWindow(ptyWindow);
new Window(&windowManager); new Window(&windowManager);
@ -51,15 +52,47 @@ namespace krikkel::NCurses
while(true) while(true)
{ {
SingleUserInput input = ptyWindow->readSingleUserInput(); SingleUserInput input = windowManager.readSingleUserInput();
if(input.isNormalKey()) if(input.isNormalKey())
{
std::lock_guard lock(ncursesMutex);
ptyWindow->writeUnicodeCharToClient(input.getRawInput()); ptyWindow->writeUnicodeCharToClient(input.getRawInput());
dummyWindow->addch(input.getRawInput());
dummyWindow->refresh();
}
if(input.isFunctionKey()) if(input.isFunctionKey())
{ {
if(input.getRawInput() == KEY_RESIZE) switch(input.getRawInput())
ptyWindow->wresize(Root_Window->lines(), Root_Window->cols()); {
else case KEY_RESIZE:
ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm()); windowManager.updateLayout();
break;
case KEY_F(1):
windowManager.hideWindow(ptyWindow);
windowManager.updateLayout();
windowManager.refresh();
break;
case KEY_F(2):
windowManager.showWindow(ptyWindow);
windowManager.updateLayout();
windowManager.refresh();
break;
case KEY_F(3):
windowManager.hideWindow(dummyWindow);
windowManager.updateLayout();
windowManager.refresh();
break;
case KEY_F(4):
windowManager.showWindow(dummyWindow);
windowManager.updateLayout();
windowManager.refresh();
break;
case KEY_F(5):
windowManager.refresh();
break;
default:
ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm());
}
} }
} }

View File

@ -17,8 +17,9 @@ namespace krikkel::NCurses
{ {
using gsl::narrow; using gsl::narrow;
using std::lock_guard; using std::lock_guard;
using std::recursive_mutex;
PtyWindow::PtyWindow(std::mutex *ncursesMutex, int lines, int columns, int y, int x) PtyWindow::PtyWindow(recursive_mutex *ncursesMutex, int lines, int columns, int y, int x)
: ncursesMutex(ncursesMutex), Window(lines, columns, y, x) : ncursesMutex(ncursesMutex), Window(lines, columns, y, x)
{ {
// to get the original terminal we need to shutdown ncurses for a moment // to get the original terminal we need to shutdown ncurses for a moment
@ -67,19 +68,21 @@ namespace krikkel::NCurses
void PtyWindow::writeToClient(const char *output, size_t length) void PtyWindow::writeToClient(const char *output, size_t length)
{ {
lock_guard writeLock(writeToPseudoTerminalMutex); lock_guard lock(writeToPseudoTerminalMutex);
write(fdPtyHost, output, length); write(fdPtyHost, output, length);
__debug_log("written to PTY client: '" + __debug_make_bytes_printable(std::string(output, length)) + '\''); //__debug_log("written to PTY client: '" + __debug_make_bytes_printable(std::string(output, length)) + '\'');
} }
void PtyWindow::writeUnicodeCharToClient(wint_t character) void PtyWindow::writeUnicodeCharToClient(wint_t character)
{ {
lock_guard lock(writeToPseudoTerminalMutex);
vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE); vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE);
} }
void PtyWindow::writeKeyToClient(VTermKey key) void PtyWindow::writeKeyToClient(VTermKey key)
{ {
__debug_log("writing key: " + std::to_string(key)); lock_guard lock(writeToPseudoTerminalMutex);
//__debug_log("writing key: " + std::to_string(key));
vterm_keyboard_key(pseudoTerminal, key, VTERM_MOD_NONE); vterm_keyboard_key(pseudoTerminal, key, VTERM_MOD_NONE);
} }
@ -108,7 +111,7 @@ namespace krikkel::NCurses
lock_guard writeLock(writeToPseudoTerminalMutex); lock_guard writeLock(writeToPseudoTerminalMutex);
vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead); vterm_input_write(pseudoTerminal, ptyClientOutputBuffer, bytesRead);
} }
__debug_log("read from PTY client: '" + __debug_make_bytes_printable(std::string(ptyClientOutputBuffer, bytesRead)) + '\''); //__debug_log("read from PTY client: '" + __debug_make_bytes_printable(std::string(ptyClientOutputBuffer, bytesRead)) + '\'');
} }
VTermScreenCallbacks PtyWindow::screenCallbacks = VTermScreenCallbacks PtyWindow::screenCallbacks =
@ -238,9 +241,10 @@ namespace krikkel::NCurses
else else
character = *vTermCell.chars; 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);
{ {
lock_guard nCursesLock(*ncursesMutex); lock_guard lock(*ncursesMutex);
setcchar(&converted, &character, formatting, colorPair, NULL);
move(cellPosition.row, cellPosition.col); move(cellPosition.row, cellPosition.col);
chgat(1, formatting, colorPair, NULL); chgat(1, formatting, colorPair, NULL);
add_wch(&converted); add_wch(&converted);
@ -249,9 +253,10 @@ namespace krikkel::NCurses
int PtyWindow::refresh() int PtyWindow::refresh()
{ {
lock_guard nCursesLock(*ncursesMutex); //__debug_log("refreshing");
lock_guard lock(*ncursesMutex);
move(cursorY, cursorX); move(cursorY, cursorX);
return NCursesWindow::refresh(); return Window::refresh();
} }
/// @todo potential racing condition where drawing into terminal while /// @todo potential racing condition where drawing into terminal while
@ -269,8 +274,8 @@ namespace krikkel::NCurses
vterm_set_size(pseudoTerminal, rows, cols); vterm_set_size(pseudoTerminal, rows, cols);
} }
{ {
lock_guard nCursesLock(*ncursesMutex); lock_guard lock(*ncursesMutex);
return Window::wresize(rows, cols); return Window::resize(rows, cols);
} }
} }
@ -325,7 +330,6 @@ namespace krikkel::NCurses
void PtyWindow::staticHandlerOutput(const char *s, size_t len, void *user) void PtyWindow::staticHandlerOutput(const char *s, size_t len, void *user)
{ {
PtyWindow *instance = static_cast<PtyWindow *>(user); PtyWindow *instance = static_cast<PtyWindow *>(user);
__debug_log("output handler writing to client: '" + __debug_make_bytes_printable(std::string(s, len)) + "'");
instance->writeToClient(s, len); instance->writeToClient(s, len);
} }
} }

View File

@ -42,7 +42,7 @@ namespace krikkel::NCurses
// it is only processing user input, it is not time critical enough for // it is only processing user input, it is not time critical enough for
// the wasted space // the wasted space
// @tode unmapped keys: keys erase // @todo unmapped keys: keys erase
if(resultGetWchCall == OK) if(resultGetWchCall == OK)
switch(input) switch(input)
{ {

View File

@ -10,44 +10,80 @@
namespace krikkel::NCurses namespace krikkel::NCurses
{ {
using std::list; using std::list;
using std::mutex; using std::recursive_mutex;
using std::lock_guard; using std::lock_guard;
using std::find; using std::find;
VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, mutex *ncursesMutex) VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, recursive_mutex *ncursesMutex)
: rootWindow(rootWindow), ncursesMutex(ncursesMutex) : rootWindow(new Window(*rootWindow)), ncursesMutex(ncursesMutex)
{} {}
void VerticalTilingWindowManager::addWindow(Window *window) void VerticalTilingWindowManager::addWindow(Window *window)
{ {
stack.push_back(window); stack.push_back(window);
visibleStack.push_back(window);
}
void VerticalTilingWindowManager::hideWindow(Window *window)
{
visibleStack.remove(window);
window->hidden = true;
}
void VerticalTilingWindowManager::showWindow(Window *window)
{
if(!window->hidden)
return;
list<Window *>::iterator currentVisibleWindow = visibleStack.begin();
for(Window *currentWindow : stack)
{
if(currentWindow == window)
{
visibleStack.insert(currentVisibleWindow, window);
window->hidden = false;
break;
}
if(currentWindow != (*currentVisibleWindow))
continue;
++currentVisibleWindow;
}
} }
void VerticalTilingWindowManager::refresh() void VerticalTilingWindowManager::refresh()
{ {
lock_guard lock(*ncursesMutex);
rootWindow->refresh(); rootWindow->refresh();
for(Window *window : stack) for(Window *window : visibleStack)
window->refresh(); window->refresh();
} }
SingleUserInput VerticalTilingWindowManager::readSingleUserInput()
{
return rootWindow->readSingleUserInput();
}
void VerticalTilingWindowManager::updateLayout() void VerticalTilingWindowManager::updateLayout()
{ {
lock_guard lock(*ncursesMutex);
int availableWidth = rootWindow->width(); int availableWidth = rootWindow->width();
int availableHeight = rootWindow->height(); int availableHeight = rootWindow->height();
int windowHeight = availableHeight / stack.size() - 1; size_t stackSize = visibleStack.size();
if((windowHeight + 1) * stack.size() < availableHeight) int windowHeight = availableHeight / stackSize - 1;
if((windowHeight + 1) * stackSize < availableHeight)
++windowHeight; ++windowHeight;
list<Window *>::iterator it = stack.begin();
uint16_t y = 0; uint16_t y = 0;
list<Window *>::iterator it = visibleStack.begin();
for(size_t index = 0 for(size_t index = 0
; index < stack.size() - 1 ; index < visibleStack.size() - 1
; ++index) ; ++index)
{ {
resizeWindowInStack(*it++, y, windowHeight, availableWidth); resizeWindowInStack(*it++, y, windowHeight, availableWidth);
y += windowHeight; y += windowHeight;
{ {
lock_guard lock(*ncursesMutex);
rootWindow->move(y++, 0); rootWindow->move(y++, 0);
rootWindow->hline(availableWidth); rootWindow->hline(availableWidth);
} }

View File

@ -14,6 +14,10 @@ namespace krikkel::NCurses
windowManager->updateLayout(); windowManager->updateLayout();
} }
Window::Window(const NCursesWindow &window)
: NCursesWindow(window)
{}
Window::Window(int lines, int columns, int y, int x) Window::Window(int lines, int columns, int y, int x)
: NCursesWindow(lines, columns, y, x) : NCursesWindow(lines, columns, y, x)
{} {}
@ -50,4 +54,10 @@ namespace krikkel::NCurses
return NCursesWindow::wresize(rows, cols); return NCursesWindow::wresize(rows, cols);
} }
int Window::refresh()
{
if(!hidden)
return NCursesWindow::refresh();
return 0;
}
} }

View File

@ -21,7 +21,7 @@ namespace krikkel::NCurses
class PtyWindow : public Window class PtyWindow : public Window
{ {
public: public:
PtyWindow(std::mutex *ncursesMutex, int lines, int columns, int y, int x); PtyWindow(std::recursive_mutex *ncursesMutex, int lines, int columns, int y, int x);
~PtyWindow(); ~PtyWindow();
int getFdPtyClient() const; int getFdPtyClient() const;
@ -36,8 +36,8 @@ namespace krikkel::NCurses
int fdPtyHost, fdPtyClient; int fdPtyHost, fdPtyClient;
struct termios terminalParameters; struct termios terminalParameters;
VTerm *pseudoTerminal; VTerm *pseudoTerminal;
std::mutex writeToPseudoTerminalMutex; std::recursive_mutex writeToPseudoTerminalMutex;
std::mutex *ncursesMutex; std::recursive_mutex *ncursesMutex;
VTermScreen *pseudoTerminalScreen; VTermScreen *pseudoTerminalScreen;
static VTermScreenCallbacks screenCallbacks; static VTermScreenCallbacks screenCallbacks;
/// @todo one line is at most 4096 chars long /// @todo one line is at most 4096 chars long

View File

@ -13,19 +13,24 @@
namespace krikkel::NCurses namespace krikkel::NCurses
{ {
class Window; class Window;
class SingleUserInput;
class VerticalTilingWindowManager class VerticalTilingWindowManager
{ {
public: public:
VerticalTilingWindowManager(NCursesWindow *rootWindow, std::mutex *ncursesMutex); VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex);
void addWindow(Window *window); void addWindow(Window *window);
void refresh(); void refresh();
void updateLayout(); void updateLayout();
void hideWindow(Window *window);
void showWindow(Window *window);
SingleUserInput readSingleUserInput();
private: private:
NCursesWindow *rootWindow; Window *rootWindow;
std::mutex *ncursesMutex; std::recursive_mutex *ncursesMutex;
std::list<Window *> stack; std::list<Window *> stack, visibleStack;
void resizeWindowInStack(Window *window, uint16_t y, uint16_t height, uint16_t width); void resizeWindowInStack(Window *window, uint16_t y, uint16_t height, uint16_t width);
}; };

View File

@ -40,8 +40,11 @@ namespace krikkel::NCurses
class Window : public NCursesWindow class Window : public NCursesWindow
{ {
friend class VerticalTilingWindowManager;
public: public:
Window(VerticalTilingWindowManager *windowManager); Window(VerticalTilingWindowManager *windowManager);
Window(const NCursesWindow &window);
Window(int lines, int columns, int y, int x); Window(int lines, int columns, int y, int x);
int addnwstr(const wchar_t *wstr, int n); int addnwstr(const wchar_t *wstr, int n);
int add_wch(const cchar_t *character); int add_wch(const cchar_t *character);
@ -50,6 +53,10 @@ namespace krikkel::NCurses
SingleUserInput readSingleUserInput(); SingleUserInput readSingleUserInput();
virtual int resize(int rows, int cols); virtual int resize(int rows, int cols);
virtual int refresh() override;
protected:
bool hidden = false;
}; };
} }