From 1d3522f35bc57d624ad9fd1c2c2f83034e46d0b6 Mon Sep 17 00:00:00 2001 From: Markus Gans Date: Wed, 4 Nov 2020 16:19:02 +0100 Subject: [PATCH] Elimination of unnecessary terminal flushes --- ChangeLog | 3 ++ examples/mouse.cpp | 3 ++ src/fapplication.cpp | 17 ++++++-- src/fdialog.cpp | 4 +- src/fkeyboard.cpp | 14 +++---- src/flineedit.cpp | 16 ++++++-- src/flistbox.cpp | 22 ++++++---- src/flistview.cpp | 22 ++++++---- src/fmenu.cpp | 5 ++- src/fmenubar.cpp | 11 +++-- src/fmenuitem.cpp | 5 ++- src/fprogressbar.cpp | 3 +- src/fscrollbar.cpp | 10 ++++- src/fscrollview.cpp | 4 +- src/ftextview.cpp | 7 +++- src/fvterm.cpp | 56 ++++++++++++------------- src/fwidget.cpp | 6 ++- src/include/final/fapplication.h | 4 +- src/include/final/fkeyboard.h | 2 +- src/include/final/fvterm.h | 70 ++++++++++++++++++-------------- 20 files changed, 170 insertions(+), 114 deletions(-) diff --git a/ChangeLog b/ChangeLog index 64b588a8..4fb106ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2020-11-04 Markus Gans + * Elimination of unnecessary terminal flushes + 2020-11-03 Markus Gans * Use FIONREAD to get the number of characters available for reading on stdin diff --git a/examples/mouse.cpp b/examples/mouse.cpp index e876a06e..99730553 100644 --- a/examples/mouse.cpp +++ b/examples/mouse.cpp @@ -521,6 +521,9 @@ void MouseDraw::drawCanvas() } printarea->has_changes = true; + + if ( updateTerminal() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/fapplication.cpp b/src/fapplication.cpp index 2f3a7353..01878a30 100644 --- a/src/fapplication.cpp +++ b/src/fapplication.cpp @@ -73,6 +73,7 @@ FMouseControl* FApplication::mouse {nullptr}; // mouse control int FApplication::loop_level {0}; // event loop level int FApplication::quit_code {EXIT_SUCCESS}; bool FApplication::quit_now {false}; +bool FApplication::pending_updates {false}; uInt64 FApplication::next_event_wait {5000}; // 5 ms (200 Hz) struct timeval FApplication::time_last_event {}; @@ -845,7 +846,6 @@ void FApplication::queuingKeyboardInput() const findKeyboardWidget(); keyboard->escapeKeyHandling(); // special case: Esc key keyboard->clearKeyBufferOnTimeout(); - std::fflush(stdout); if ( isKeyPressed() ) keyboard->fetchKeyCode(); @@ -1234,11 +1234,18 @@ void FApplication::sendWheelEvent ( const FMouseData& md //---------------------------------------------------------------------- inline void FApplication::flushTerminal() { - if ( flush_count == 0 || flush_count % 4 != 0 ) + if ( ! pending_updates ) return; + if ( flush_count < 4 ) + { + flush_count++; + return; + } + flush(); flush_count = 0; + pending_updates = false; } //---------------------------------------------------------------------- @@ -1321,9 +1328,11 @@ bool FApplication::processNextEvent() processMouseEvent(); processResizeEvent(); processCloseWidget(); - processTerminalUpdate(); // after terminal changes + + if ( processTerminalUpdate() ) // after terminal changes + pending_updates = true; + flushTerminal(); - flush_count++; processLogger(); } diff --git a/src/fdialog.cpp b/src/fdialog.cpp index ef36ef9b..558ed411 100644 --- a/src/fdialog.cpp +++ b/src/fdialog.cpp @@ -270,7 +270,9 @@ void FDialog::setPos (const FPoint& pos, bool) restoreOverlaidWindows(); FWindow::adjustSize(); setCursorToFocusWidget(); - updateTerminal(); + + if ( updateTerminal() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/fkeyboard.cpp b/src/fkeyboard.cpp index a27f5c97..426eb2bb 100644 --- a/src/fkeyboard.cpp +++ b/src/fkeyboard.cpp @@ -467,15 +467,13 @@ FKey FKeyboard::UTF8decode (const char utf8[]) const //---------------------------------------------------------------------- inline ssize_t FKeyboard::readKey() { - setNonBlockingInput(); int len{0}; - if ( ioctl(FTermios::getStdIn(), FIONREAD, &len) >= 0 && len > int(FIFO_BUF_SIZE) ) - len = int(FIFO_BUF_SIZE); - else - len = 1; + if ( ioctl(FTermios::getStdIn(), FIONREAD, &len) < 0 || len == 0 ) + return 0; - const ssize_t bytes = read(FTermios::getStdIn(), &read_character, std::size_t(len)); + setNonBlockingInput(); + const ssize_t bytes = read(FTermios::getStdIn(), &read_character, 1); unsetNonBlockingInput(); return bytes; } @@ -492,7 +490,7 @@ void FKeyboard::parseKeyBuffer() if ( bytesread + fifo_offset <= int(FIFO_BUF_SIZE) ) { - fifo_buf[fifo_offset] = char(read_character); + fifo_buf[fifo_offset] = read_character; fifo_offset++; fifo_in_use = true; } @@ -527,8 +525,6 @@ void FKeyboard::parseKeyBuffer() if ( fkey_queue.size() >= MAX_QUEUE_SIZE ) break; } - - read_character = 0; } //---------------------------------------------------------------------- diff --git a/src/flineedit.cpp b/src/flineedit.cpp index ea880301..77e1dcf1 100644 --- a/src/flineedit.cpp +++ b/src/flineedit.cpp @@ -357,7 +357,9 @@ void FLineEdit::onKeyPress (FKeyEvent* ev) && key != fc::Fkey_enter ) { drawInputField(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } } @@ -397,7 +399,9 @@ void FLineEdit::onMouseDown (FMouseEvent* ev) adjustTextOffset(); drawInputField(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } } @@ -431,7 +435,9 @@ void FLineEdit::onMouseMove (FMouseEvent* ev) adjustTextOffset(); drawInputField(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } // auto-scrolling when dragging mouse outside the widget @@ -534,7 +540,9 @@ void FLineEdit::onTimer (FTimerEvent*) adjustTextOffset(); drawInputField(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/flistbox.cpp b/src/flistbox.cpp index 2764a4ef..23589e7e 100644 --- a/src/flistbox.cpp +++ b/src/flistbox.cpp @@ -352,7 +352,8 @@ void FListBox::onMouseDown (FMouseEvent* ev) if ( yoffset_before != yoffset ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } @@ -425,8 +426,8 @@ void FListBox::onMouseMove (FMouseEvent* ev) if ( yoffset_before != yoffset ) vbar->drawBar(); - processTerminalUpdate(); - flush(); + if ( processTerminalUpdate() ) + flush(); } // Auto-scrolling when dragging mouse outside the widget @@ -503,7 +504,8 @@ void FListBox::onTimer (FTimerEvent*) if ( yoffset_before != yoffset ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -545,7 +547,8 @@ void FListBox::onWheel (FWheelEvent* ev) if ( yoffset_before != yoffset ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -1079,7 +1082,8 @@ inline void FListBox::updateDrawing (bool draw_vbar, bool draw_hbar) if ( draw_hbar ) hbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -1799,7 +1803,8 @@ void FListBox::cb_vbarChange (const FWidget*) if ( yoffset_before != yoffset ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } @@ -1867,7 +1872,8 @@ void FListBox::cb_hbarChange (const FWidget*) if ( xoffset_before != xoffset ) hbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } diff --git a/src/flistview.cpp b/src/flistview.cpp index d4c02669..59c36790 100644 --- a/src/flistview.cpp +++ b/src/flistview.cpp @@ -1116,7 +1116,8 @@ void FListView::onMouseDown (FMouseEvent* ev) if ( first_line_position_before != first_visible_line.getPosition() ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } } @@ -1220,8 +1221,8 @@ void FListView::onMouseMove (FMouseEvent* ev) if ( first_line_position_before != first_visible_line.getPosition() ) vbar->drawBar(); - processTerminalUpdate(); - flush(); + if ( processTerminalUpdate() ) + flush(); } // auto-scrolling when dragging mouse outside the widget @@ -1307,7 +1308,8 @@ void FListView::onTimer (FTimerEvent*) if ( first_line_position_before != first_visible_line.getPosition() ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -1345,7 +1347,8 @@ void FListView::onWheel (FWheelEvent* ev) if ( first_line_position_before != first_visible_line.getPosition() ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -2149,7 +2152,8 @@ void FListView::updateDrawing (bool draw_vbar, bool draw_hbar) if ( draw_hbar ) hbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -2868,7 +2872,8 @@ void FListView::cb_vbarChange (const FWidget*) if ( first_line_position_before != first_visible_line.getPosition() ) vbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } @@ -2933,7 +2938,8 @@ void FListView::cb_hbarChange (const FWidget*) if ( xoffset_before != xoffset ) hbar->drawBar(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } } diff --git a/src/fmenu.cpp b/src/fmenu.cpp index 96238bc8..b6bab235 100644 --- a/src/fmenu.cpp +++ b/src/fmenu.cpp @@ -311,8 +311,9 @@ void FMenu::onMouseMove (FMouseEvent* ev) else if ( ms.hide_sub_menu ) { closeOpenedSubMenu(); - processTerminalUpdate(); - flush(); + + if ( processTerminalUpdate() ) + flush(); } } diff --git a/src/fmenubar.cpp b/src/fmenubar.cpp index 889b656f..f3248059 100644 --- a/src/fmenubar.cpp +++ b/src/fmenubar.cpp @@ -223,8 +223,10 @@ void FMenuBar::onAccel (FAccelEvent* ev) getStatusBar()->drawMessage(); redraw(); - processTerminalUpdate(); - flush(); + + if ( processTerminalUpdate() ) + flush(); + ev->accept(); } @@ -920,8 +922,9 @@ void FMenuBar::mouseMoveOverList (const FMouseEvent&& ev) if ( focus_changed ) { redraw(); - processTerminalUpdate(); - flush(); + + if ( processTerminalUpdate() ) + flush(); } } diff --git a/src/fmenuitem.cpp b/src/fmenuitem.cpp index 3c78e4f6..e0287873 100644 --- a/src/fmenuitem.cpp +++ b/src/fmenuitem.cpp @@ -428,8 +428,9 @@ void FMenuItem::onAccel (FAccelEvent* ev) mbar->drop_down = false; } - processTerminalUpdate(); - flush(); + if ( processTerminalUpdate() ) + flush(); + ev->accept(); } diff --git a/src/fprogressbar.cpp b/src/fprogressbar.cpp index 3a7c9da7..42c5553d 100644 --- a/src/fprogressbar.cpp +++ b/src/fprogressbar.cpp @@ -144,7 +144,8 @@ void FProgressbar::draw() if ( getFlags().shadow ) drawShadow(this); - flush(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/fscrollbar.cpp b/src/fscrollbar.cpp index 2d025df7..d40922ca 100644 --- a/src/fscrollbar.cpp +++ b/src/fscrollbar.cpp @@ -352,7 +352,10 @@ void FScrollbar::onMouseMove (FMouseEvent* ev) { setValue(new_val); drawBar(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); + processScroll(); } } @@ -757,7 +760,10 @@ void FScrollbar::jumpToClickPos (int x, int y) { setValue(new_val); drawBar(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); + scroll_type = FScrollbar::scrollJump; processScroll(); } diff --git a/src/fscrollview.cpp b/src/fscrollview.cpp index a2027d74..37fa8863 100644 --- a/src/fscrollview.cpp +++ b/src/fscrollview.cpp @@ -411,7 +411,9 @@ void FScrollView::scrollTo (int x, int y) viewport->has_changes = true; copy2area(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/ftextview.cpp b/src/ftextview.cpp index c8fe6598..42a04e51 100644 --- a/src/ftextview.cpp +++ b/src/ftextview.cpp @@ -184,7 +184,9 @@ void FTextView::scrollTo (int x, int y) } drawText(); - processTerminalUpdate(); + + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- @@ -475,7 +477,8 @@ void FTextView::onWheel (FWheelEvent* ev) if ( isShown() ) drawText(); - processTerminalUpdate(); + if ( processTerminalUpdate() ) + flush(); } //---------------------------------------------------------------------- diff --git a/src/fvterm.cpp b/src/fvterm.cpp index 7fbacbf1..821b199b 100644 --- a/src/fvterm.cpp +++ b/src/fvterm.cpp @@ -62,7 +62,7 @@ uInt FVTerm::clr_bol_length{}; uInt FVTerm::clr_eol_length{}; uInt FVTerm::cursor_address_length{}; struct timeval FVTerm::last_term_size_check{}; -std::queue* FVTerm::output_buffer{nullptr}; +std::vector* FVTerm::output_buffer{nullptr}; FPoint* FVTerm::term_pos{nullptr}; const FVTerm* FVTerm::init_object{nullptr}; FSystem* FVTerm::fsystem{nullptr}; @@ -266,7 +266,7 @@ void FVTerm::putVTerm() const } //---------------------------------------------------------------------- -void FVTerm::updateTerminal() const +bool FVTerm::updateTerminal() const { // Updates pending changes to the terminal @@ -275,7 +275,7 @@ void FVTerm::updateTerminal() const if ( no_terminal_updates || FApplication::isQuit() || ! (hasPendingUpdates(vterm) && draw_completed) ) { - return; + return false; } std::size_t changedlines = 0; @@ -287,12 +287,12 @@ void FVTerm::updateTerminal() const changedlines++; if ( changedlines % check_interval == 0 - && (keyboard->hasUnprocessedInput() || keyboard->isKeyPressed(0)) + && (keyboard->hasUnprocessedInput() || keyboard->isKeyPressed(0) ) && skipped_terminal_update <= max_skip ) { // Skipping terminal updates if there is unprocessed inputs skipped_terminal_update++; - return; + return false; } } @@ -301,6 +301,7 @@ void FVTerm::updateTerminal() const // sets the new input cursor position updateTerminalCursor(); + return changedlines > 0; } //---------------------------------------------------------------------- @@ -613,20 +614,18 @@ void FVTerm::flush() { // Flush the output buffer - if ( ! output_buffer ) + if ( ! output_buffer || output_buffer->empty() ) return; - while ( ! output_buffer->empty() ) - { - static const FTerm::defaultPutChar& FTermPutchar = FTerm::putchar(); + static const FTerm::defaultPutChar& FTermPutchar = FTerm::putchar(); - if ( FTermPutchar ) - { - FTermPutchar (output_buffer->front()); - output_buffer->pop(); - } - } + if ( ! FTermPutchar ) + return; + for (auto&& ch : *output_buffer) + FTermPutchar(ch); + + output_buffer->clear(); std::fflush(stdout); } @@ -1263,26 +1262,26 @@ void FVTerm::clearArea (FTermArea* area, int fillchar) const } //---------------------------------------------------------------------- -void FVTerm::processTerminalUpdate() const +bool FVTerm::processTerminalUpdate() const { const auto& data = FTerm::getFTermData(); // Checks if the resizing of the terminal is not finished if ( data && data->hasTermResized() ) - return; + return false; // Monitor whether the terminal size has changed if ( isTermSizeChanged() ) { raise (SIGWINCH); // Send SIGWINCH - return; + return false; } // Update data on VTerm updateVTerm(); // Update the visible terminal - updateTerminal(); + return updateTerminal(); } //---------------------------------------------------------------------- @@ -1845,7 +1844,7 @@ void FVTerm::init() { fterm = new FTerm(); term_pos = new FPoint(-1, -1); - output_buffer = new std::queue; + output_buffer = new std::vector; } catch (const std::bad_alloc&) { @@ -1857,6 +1856,9 @@ void FVTerm::init() // The final setting is made later in FTerm::init_locale(). std::setlocale (LC_ALL, ""); + // Reserve memory on the terminal output buffer + output_buffer->reserve(TERMINAL_OUTPUT_BUFFER_SIZE + 256); + // term_attribute stores the current state of the terminal term_attribute.ch = '\0'; term_attribute.fg_color = fc::Default; @@ -3072,23 +3074,17 @@ inline void FVTerm::characterFilter (FChar& next_char) } //---------------------------------------------------------------------- -inline void FVTerm::appendOutputBuffer (const std::string& s) +inline void FVTerm::appendOutputBuffer (const std::string& str) { - const auto& c_string = s.c_str(); - FTermcap::paddingPrint (c_string, 1, appendOutputBuffer); -} - -//---------------------------------------------------------------------- -inline void FVTerm::appendOutputBuffer (const char s[]) -{ - FTermcap::paddingPrint (s, 1, appendOutputBuffer); + for (auto&& ch : str) + FVTerm::appendOutputBuffer(int(ch)); } //---------------------------------------------------------------------- int FVTerm::appendOutputBuffer (int ch) { // append method for unicode character - output_buffer->push(ch); + output_buffer->push_back(ch); if ( output_buffer->size() >= TERMINAL_OUTPUT_BUFFER_SIZE ) flush(); diff --git a/src/fwidget.cpp b/src/fwidget.cpp index 5873c9d0..c7a1242d 100644 --- a/src/fwidget.cpp +++ b/src/fwidget.cpp @@ -981,8 +981,10 @@ void FWidget::show() if ( show_root_widget && show_root_widget == this ) { finishDrawing(); - processTerminalUpdate(); - flush(); + + if ( processTerminalUpdate() ) + flush(); + show_root_widget = nullptr; } diff --git a/src/include/final/fapplication.h b/src/include/final/fapplication.h index 1b4e7623..bb6dacd2 100644 --- a/src/include/final/fapplication.h +++ b/src/include/final/fapplication.h @@ -243,10 +243,10 @@ class FApplication : public FWidget int flush_count{0}; static uInt64 next_event_wait; static timeval time_last_event; + static int loop_level; static int quit_code; static bool quit_now; - static int loop_level; - static bool process_timer_event; + static bool pending_updates; static FMouseControl* mouse; static FKeyboard* keyboard; static FWidget* keyboard_widget; diff --git a/src/include/final/fkeyboard.h b/src/include/final/fkeyboard.h index 1be3f0ae..567a7e2c 100644 --- a/src/include/final/fkeyboard.h +++ b/src/include/final/fkeyboard.h @@ -197,7 +197,7 @@ class FKeyboard final std::queue fkey_queue{}; FKey fkey{0}; FKey key{0}; - uChar read_character{}; + char read_character{}; char fifo_buf[FIFO_BUF_SIZE]{'\0'}; int fifo_offset{0}; int stdin_status_flags{0}; diff --git a/src/include/final/fvterm.h b/src/include/final/fvterm.h index e59093dc..acd78558 100644 --- a/src/include/final/fvterm.h +++ b/src/include/final/fvterm.h @@ -50,7 +50,6 @@ #include // need for timeval (cygwin) -#include #include #include #include @@ -255,7 +254,7 @@ class FVTerm void createVTerm (const FSize&); void resizeVTerm (const FSize&) const; void putVTerm() const; - void updateTerminal() const; + bool updateTerminal() const; virtual void addPreprocessingHandler ( const FVTerm* , const FPreprocessingFunction& ); virtual void delPreprocessingHandler (const FVTerm*); @@ -318,7 +317,7 @@ class FVTerm void scrollAreaForward (FTermArea*) const; void scrollAreaReverse (FTermArea*) const; void clearArea (FTermArea*, int = ' ') const; - void processTerminalUpdate() const; + bool processTerminalUpdate() const; static void startDrawing(); static void finishDrawing(); virtual void initTerminal(); @@ -420,37 +419,38 @@ class FVTerm void appendLowerRight (FChar&) const; static void characterFilter (FChar&); static void appendOutputBuffer (const std::string&); - static void appendOutputBuffer (const char[]); + template + static void appendOutputBuffer (const char (&)[N]); static int appendOutputBuffer (int); // Data members - FTermArea* print_area{nullptr}; // print area for this object - FTermArea* child_print_area{nullptr}; // print area for children - FTermArea* vwin{nullptr}; // virtual window - static const FVTerm* init_object; // Global FVTerm object - static FSystem* fsystem; - static FTerm* fterm; - static FTermArea* vterm; // virtual terminal - static FTermArea* vdesktop; // virtual desktop - static FTermArea* active_area; // active area - static std::queue* output_buffer; - static FChar term_attribute; - static FChar next_attribute; - static FChar s_ch; // shadow character - static FChar i_ch; // inherit background character - static FPoint* term_pos; // terminal cursor position - static FKeyboard* keyboard; - static timeval last_term_size_check; - static bool draw_completed; - static bool no_terminal_updates; - static uInt64 term_size_check_timeout; - static int skipped_terminal_update; - static uInt erase_char_length; - static uInt repeat_char_length; - static uInt clr_bol_length; - static uInt clr_eol_length; - static uInt cursor_address_length; - static bool cursor_hideable; + FTermArea* print_area{nullptr}; // print area for this object + FTermArea* child_print_area{nullptr}; // print area for children + FTermArea* vwin{nullptr}; // virtual window + static const FVTerm* init_object; // Global FVTerm object + static FSystem* fsystem; + static FTerm* fterm; + static FTermArea* vterm; // virtual terminal + static FTermArea* vdesktop; // virtual desktop + static FTermArea* active_area; // active area + static std::vector* output_buffer; + static FChar term_attribute; + static FChar next_attribute; + static FChar s_ch; // shadow character + static FChar i_ch; // inherit background character + static FPoint* term_pos; // terminal cursor position + static FKeyboard* keyboard; + static timeval last_term_size_check; + static bool draw_completed; + static bool no_terminal_updates; + static uInt64 term_size_check_timeout; + static int skipped_terminal_update; + static uInt erase_char_length; + static uInt repeat_char_length; + static uInt clr_bol_length; + static uInt clr_eol_length; + static uInt cursor_address_length; + static bool cursor_hideable; }; @@ -988,6 +988,14 @@ inline bool FVTerm::isCursorHideable() const inline void FVTerm::hideVTermCursor() const { vterm->input_cursor_visible = false; } +//---------------------------------------------------------------------- +template +inline void FVTerm::appendOutputBuffer (const char (&str)[N]) +{ + for (auto&& ch : str) + FVTerm::appendOutputBuffer(int(ch)); +} + } // namespace finalcut #endif // FVTERM_H