From b9e32941fbac0f810eb26015f97945ea88f60e0a Mon Sep 17 00:00:00 2001 From: Christian Burger Date: Fri, 6 May 2022 14:02:18 +0200 Subject: [PATCH] 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` --- .vscode/settings.json | 4 +- App.cpp | 47 +++++++++++++--- PtyWindow.cpp | 28 +++++----- SingleUserInput.cpp | 2 +- VerticalTilingWindowManager.cpp | 54 +++++++++++++++---- Window.cpp | 10 ++++ include/NCurses/PtyWindow.hpp | 6 +-- .../NCurses/VerticalTilingWindowManager.hpp | 13 +++-- include/NCurses/Window.hpp | 7 +++ 9 files changed, 134 insertions(+), 37 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0109953..dce84ad 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,7 +54,9 @@ "stdexcept": "cpp", "typeinfo": "cpp", "pointers": "cpp", - "list": "cpp" + "list": "cpp", + "condition_variable": "cpp", + "mutex": "cpp" }, "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", "C_Cpp.default.includePath": [ diff --git a/App.cpp b/App.cpp index d2d14bd..3d0882e 100644 --- a/App.cpp +++ b/App.cpp @@ -22,10 +22,11 @@ namespace krikkel::NCurses int DemoApp::run() { - std::mutex ncursesMutex; + std::recursive_mutex 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); windowManager.addWindow(ptyWindow); new Window(&windowManager); @@ -51,15 +52,47 @@ namespace krikkel::NCurses while(true) { - SingleUserInput input = ptyWindow->readSingleUserInput(); + SingleUserInput input = windowManager.readSingleUserInput(); if(input.isNormalKey()) + { + std::lock_guard lock(ncursesMutex); ptyWindow->writeUnicodeCharToClient(input.getRawInput()); + dummyWindow->addch(input.getRawInput()); + dummyWindow->refresh(); + } if(input.isFunctionKey()) { - if(input.getRawInput() == KEY_RESIZE) - ptyWindow->wresize(Root_Window->lines(), Root_Window->cols()); - else - ptyWindow->writeKeyToClient(input.mapKeyNcursesToVTerm()); + switch(input.getRawInput()) + { + case KEY_RESIZE: + 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()); + } } } diff --git a/PtyWindow.cpp b/PtyWindow.cpp index 96095a4..fee9cf4 100644 --- a/PtyWindow.cpp +++ b/PtyWindow.cpp @@ -17,8 +17,9 @@ namespace krikkel::NCurses { using gsl::narrow; 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) { // 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) { - lock_guard writeLock(writeToPseudoTerminalMutex); + lock_guard lock(writeToPseudoTerminalMutex); 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) { + lock_guard lock(writeToPseudoTerminalMutex); vterm_keyboard_unichar(pseudoTerminal, character, VTERM_MOD_NONE); } 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); } @@ -108,7 +111,7 @@ namespace krikkel::NCurses lock_guard writeLock(writeToPseudoTerminalMutex); 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 = @@ -238,9 +241,10 @@ namespace krikkel::NCurses 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); { - lock_guard nCursesLock(*ncursesMutex); - setcchar(&converted, &character, formatting, colorPair, NULL); + lock_guard lock(*ncursesMutex); move(cellPosition.row, cellPosition.col); chgat(1, formatting, colorPair, NULL); add_wch(&converted); @@ -249,9 +253,10 @@ namespace krikkel::NCurses int PtyWindow::refresh() { - lock_guard nCursesLock(*ncursesMutex); + //__debug_log("refreshing"); + lock_guard lock(*ncursesMutex); move(cursorY, cursorX); - return NCursesWindow::refresh(); + return Window::refresh(); } /// @todo potential racing condition where drawing into terminal while @@ -269,8 +274,8 @@ namespace krikkel::NCurses vterm_set_size(pseudoTerminal, rows, cols); } { - lock_guard nCursesLock(*ncursesMutex); - return Window::wresize(rows, cols); + lock_guard lock(*ncursesMutex); + return Window::resize(rows, cols); } } @@ -325,7 +330,6 @@ namespace krikkel::NCurses void PtyWindow::staticHandlerOutput(const char *s, size_t len, void *user) { PtyWindow *instance = static_cast(user); - __debug_log("output handler writing to client: '" + __debug_make_bytes_printable(std::string(s, len)) + "'"); instance->writeToClient(s, len); } } \ No newline at end of file diff --git a/SingleUserInput.cpp b/SingleUserInput.cpp index f10a8fa..12d8420 100644 --- a/SingleUserInput.cpp +++ b/SingleUserInput.cpp @@ -42,7 +42,7 @@ namespace krikkel::NCurses // 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) { diff --git a/VerticalTilingWindowManager.cpp b/VerticalTilingWindowManager.cpp index ccd9878..a759467 100644 --- a/VerticalTilingWindowManager.cpp +++ b/VerticalTilingWindowManager.cpp @@ -10,44 +10,80 @@ namespace krikkel::NCurses { using std::list; - using std::mutex; + using std::recursive_mutex; using std::lock_guard; using std::find; - VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, mutex *ncursesMutex) - : rootWindow(rootWindow), ncursesMutex(ncursesMutex) + VerticalTilingWindowManager::VerticalTilingWindowManager(NCursesWindow *rootWindow, recursive_mutex *ncursesMutex) + : rootWindow(new Window(*rootWindow)), ncursesMutex(ncursesMutex) {} void VerticalTilingWindowManager::addWindow(Window *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::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() { + lock_guard lock(*ncursesMutex); + rootWindow->refresh(); - for(Window *window : stack) + for(Window *window : visibleStack) window->refresh(); } + SingleUserInput VerticalTilingWindowManager::readSingleUserInput() + { + return rootWindow->readSingleUserInput(); + } + void VerticalTilingWindowManager::updateLayout() { + lock_guard lock(*ncursesMutex); int availableWidth = rootWindow->width(); int availableHeight = rootWindow->height(); - int windowHeight = availableHeight / stack.size() - 1; - if((windowHeight + 1) * stack.size() < availableHeight) + size_t stackSize = visibleStack.size(); + int windowHeight = availableHeight / stackSize - 1; + + if((windowHeight + 1) * stackSize < availableHeight) ++windowHeight; - list::iterator it = stack.begin(); uint16_t y = 0; + list::iterator it = visibleStack.begin(); for(size_t index = 0 - ; index < stack.size() - 1 + ; index < visibleStack.size() - 1 ; ++index) { resizeWindowInStack(*it++, y, windowHeight, availableWidth); y += windowHeight; { - lock_guard lock(*ncursesMutex); rootWindow->move(y++, 0); rootWindow->hline(availableWidth); } diff --git a/Window.cpp b/Window.cpp index 4629571..b00e31e 100644 --- a/Window.cpp +++ b/Window.cpp @@ -14,6 +14,10 @@ namespace krikkel::NCurses windowManager->updateLayout(); } + Window::Window(const NCursesWindow &window) + : NCursesWindow(window) + {} + Window::Window(int lines, int columns, int y, int x) : NCursesWindow(lines, columns, y, x) {} @@ -50,4 +54,10 @@ namespace krikkel::NCurses return NCursesWindow::wresize(rows, cols); } + int Window::refresh() + { + if(!hidden) + return NCursesWindow::refresh(); + return 0; + } } \ No newline at end of file diff --git a/include/NCurses/PtyWindow.hpp b/include/NCurses/PtyWindow.hpp index 4c0456b..bedccfc 100644 --- a/include/NCurses/PtyWindow.hpp +++ b/include/NCurses/PtyWindow.hpp @@ -21,7 +21,7 @@ namespace krikkel::NCurses class PtyWindow : public Window { 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(); int getFdPtyClient() const; @@ -36,8 +36,8 @@ namespace krikkel::NCurses int fdPtyHost, fdPtyClient; struct termios terminalParameters; VTerm *pseudoTerminal; - std::mutex writeToPseudoTerminalMutex; - std::mutex *ncursesMutex; + std::recursive_mutex writeToPseudoTerminalMutex; + std::recursive_mutex *ncursesMutex; VTermScreen *pseudoTerminalScreen; static VTermScreenCallbacks screenCallbacks; /// @todo one line is at most 4096 chars long diff --git a/include/NCurses/VerticalTilingWindowManager.hpp b/include/NCurses/VerticalTilingWindowManager.hpp index 3171dd5..0d16f53 100644 --- a/include/NCurses/VerticalTilingWindowManager.hpp +++ b/include/NCurses/VerticalTilingWindowManager.hpp @@ -13,19 +13,24 @@ namespace krikkel::NCurses { class Window; + class SingleUserInput; class VerticalTilingWindowManager { public: - VerticalTilingWindowManager(NCursesWindow *rootWindow, std::mutex *ncursesMutex); + VerticalTilingWindowManager(NCursesWindow *rootWindow, std::recursive_mutex *ncursesMutex); void addWindow(Window *window); void refresh(); void updateLayout(); + void hideWindow(Window *window); + void showWindow(Window *window); + SingleUserInput readSingleUserInput(); + private: - NCursesWindow *rootWindow; - std::mutex *ncursesMutex; - std::list stack; + Window *rootWindow; + std::recursive_mutex *ncursesMutex; + std::list stack, visibleStack; void resizeWindowInStack(Window *window, uint16_t y, uint16_t height, uint16_t width); }; diff --git a/include/NCurses/Window.hpp b/include/NCurses/Window.hpp index 73b383d..8115004 100644 --- a/include/NCurses/Window.hpp +++ b/include/NCurses/Window.hpp @@ -40,8 +40,11 @@ namespace krikkel::NCurses class Window : public NCursesWindow { + friend class VerticalTilingWindowManager; + public: Window(VerticalTilingWindowManager *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); @@ -50,6 +53,10 @@ namespace krikkel::NCurses SingleUserInput readSingleUserInput(); virtual int resize(int rows, int cols); + virtual int refresh() override; + + protected: + bool hidden = false; }; }