diff --git a/ChangeLog b/ChangeLog index 283411e6..84b2e5e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2023-11-11 Markus Gans + * Optimized mouse string parser + 2023-11-03 Markus Gans * Reorganize the data structure of widget color theme diff --git a/final/input/fmouse.cpp b/final/input/fmouse.cpp index 4282cb59..f8802971 100644 --- a/final/input/fmouse.cpp +++ b/final/input/fmouse.cpp @@ -662,10 +662,7 @@ void FMouseX11::processEvent (const TimeValue& time) setMoveState (mouse_position, btn); setButtonState (btn & button_mask, time); - if ( mouse_position == getNewPos() - && ! isWheelUp() && ! isWheelDown() - && ! isWheelLeft() && ! isWheelRight() - && x11_button_state == uChar(btn) ) + if ( noChanges(mouse_position, uChar(btn)) ) { clearEvent(); x11_mouse[0] = '\0'; // Delete already interpreted data @@ -707,7 +704,7 @@ void FMouseX11::setMoveState (const FPoint& mouse_position, int btn) noexcept } //---------------------------------------------------------------------- -bool FMouseX11::isMouseClickButton (const int btn) const noexcept +inline bool FMouseX11::isMouseClickButton (const int btn) const noexcept { return btn == button1_pressed || btn == button2_pressed @@ -718,7 +715,7 @@ bool FMouseX11::isMouseClickButton (const int btn) const noexcept } //---------------------------------------------------------------------- -bool FMouseX11::isMouseWheelButton (const int btn) const noexcept +inline bool FMouseX11::isMouseWheelButton (const int btn) const noexcept { return btn == button_up || btn == button_down @@ -726,6 +723,17 @@ bool FMouseX11::isMouseWheelButton (const int btn) const noexcept || btn == button_right; } +//---------------------------------------------------------------------- +inline auto FMouseX11::noChanges (const FPoint& mouse_position, uChar btn) const noexcept -> bool +{ + return mouse_position == getNewPos() + && ! isWheelUp() + && ! isWheelDown() + && ! isWheelLeft() + && ! isWheelRight() + && btn == x11_button_state; +} + //---------------------------------------------------------------------- void FMouseX11::handleMouseClickButton ( int btn , const TimeValue& time) noexcept @@ -900,55 +908,26 @@ void FMouseSGR::setRawData (FKeyboard::keybuffer& fifo_buf) noexcept void FMouseSGR::processEvent (const TimeValue& time) { const auto& mouse_position = getPos(); - sInt16 x{0}; - sInt16 y{0}; - int btn{0}; - - // Parse the SGR mouse string - const char* p = sgr_mouse.data(); - - // Parse button - if ( ! parseNumberIf (p, btn, [] (char ch) { return ch != ';'; }) ) - { - clearEvent(); - sgr_mouse[0] = '\0'; // Delete already interpreted data - return; - } + Tokens token{}; - if ( *p ) p++; // ship one character after the number - - // Parse x-value - if ( ! parseNumberIf (p, x, [] (char ch) { return ch != ';'; }) ) + if ( parseSGRMouseString(token) ) { clearEvent(); sgr_mouse[0] = '\0'; // Delete already interpreted data return; } - if ( *p ) p++; // ship one character after the number - - // Parse y-value - if ( ! parseNumberIf (p, y, [] (char ch) { return ch != 'M' && ch != 'm'; }) ) - { - clearEvent(); - sgr_mouse[0] = '\0'; // Delete already interpreted data - return; - } - - setNewPos (x, y); // Update the mouse state + setNewPos (token.x, token.y); // Update the mouse state clearButtonState(); - setKeyState (btn); - setMoveState (mouse_position, btn); + setKeyState (token.btn); + setMoveState (mouse_position, token.btn); - if ( *p == pressed ) - setPressedButtonState (btn & button_mask, time); - else // *p == released - setReleasedButtonState (btn & button_mask); + if ( *token.p == pressed ) + setPressedButtonState (token.btn & button_mask, time); + else // *token.p == released + setReleasedButtonState (token.btn & button_mask); - if ( mouse_position == getNewPos() - && ! isWheelUp() && ! isWheelDown() - && ! isWheelLeft() && ! isWheelRight() - && sgr_button_state == uChar(((*p & 0x20) << 2) + btn) ) + if ( noChanges(mouse_position, uChar(((*token.p & 0x20) << 2) + token.btn)) ) { clearEvent(); sgr_mouse[0] = '\0'; // Delete already interpreted data @@ -958,7 +937,7 @@ void FMouseSGR::processEvent (const TimeValue& time) setEvent(); useNewPos(); // Get the button state from string - sgr_button_state = uChar(((*p & 0x20) << 2) + btn); + sgr_button_state = uChar(((*token.p & 0x20) << 2) + token.btn); // Delete already interpreted data sgr_mouse[0] = '\0'; } @@ -989,7 +968,7 @@ void FMouseSGR::setMoveState (const FPoint& mouse_position, int btn) noexcept } //---------------------------------------------------------------------- -bool FMouseSGR::isMouseClickButton (const int btn) const noexcept +inline bool FMouseSGR::isMouseClickButton (const int btn) const noexcept { return btn == button1 || btn == button2 @@ -1000,7 +979,7 @@ bool FMouseSGR::isMouseClickButton (const int btn) const noexcept } //---------------------------------------------------------------------- -bool FMouseSGR::isMouseWheelButton (const int btn) const noexcept +inline bool FMouseSGR::isMouseWheelButton (const int btn) const noexcept { return btn == button_up || btn == button_down @@ -1008,6 +987,41 @@ bool FMouseSGR::isMouseWheelButton (const int btn) const noexcept || btn == button_right; } +//---------------------------------------------------------------------- +inline bool FMouseSGR::parseSGRMouseString (FMouseSGR::Tokens& token) const noexcept +{ + // Parse the SGR mouse string + token.p = sgr_mouse.data(); + + // Parse button + if ( ! parseNumberIf (token.p, token.btn, [] (char ch) { return ch != ';'; }) ) + return true; + + if ( *token.p ) + token.p++; // ship one character after the number + + // Parse x-value + if ( ! parseNumberIf (token.p, token.x, [] (char ch) { return ch != ';'; }) ) + return true; + + if ( *token.p ) + token.p++; // ship one character after the number + + // Parse y-value + return ! parseNumberIf (token.p, token.y, [] (char ch) { return ch != 'M' && ch != 'm'; }); +} + +//---------------------------------------------------------------------- +inline auto FMouseSGR::noChanges (const FPoint& mouse_position, uChar btn) const noexcept -> bool +{ + return mouse_position == getNewPos() + && ! isWheelUp() + && ! isWheelDown() + && ! isWheelLeft() + && ! isWheelRight() + && btn == sgr_button_state; +} + //---------------------------------------------------------------------- void FMouseSGR::handleMouseClickButton ( int btn , const TimeValue& time) noexcept @@ -1183,69 +1197,22 @@ void FMouseUrxvt::processEvent (const TimeValue& time) // Parse and interpret the X11 xterm mouse string (Urxvt-Mode) const auto& mouse_position = getPos(); - sInt16 x{0}; - sInt16 y{0}; - int btn{0}; - - // Parse the Urxvt mouse string - const char* p = urxvt_mouse.data(); - bool x_neg{false}; - bool y_neg{false}; - - // Parse button - if ( ! parseNumberIf (p, btn, [] (char ch) { return ch != ';'; }) ) - { - clearEvent(); - urxvt_mouse[0] = '\0'; // Delete already interpreted data - return; - } + Tokens token{}; - if ( *++p == '-' ) - { - p++; - x_neg = true; - } - - // Parse x-value - if ( ! parseNumberIf (p, x, [] (char ch) { return ch != ';'; }) ) - { - clearEvent(); - urxvt_mouse[0] = '\0'; // Delete already interpreted data - return; - } - - if ( *++p == '-' ) - { - p++; - y_neg = true; - } - - // Parse y-value - if ( ! parseNumberIf (p, y, [] (char ch) { return ch != 'M'; }) ) + if ( parseUrxvtMouseString(token) ) { clearEvent(); urxvt_mouse[0] = '\0'; // Delete already interpreted data return; } - if ( x_neg || x == 0 ) - x = 1; - - if ( y_neg || y == 0 ) - y = 1; - - x = std::min(x, sInt16(getMaxWidth())); - y = std::min(y, sInt16(getMaxHeight())); - setNewPos (x, y); + adjustAndSetPosition (token); clearButtonState(); - setKeyState (btn); - setMoveState (mouse_position, btn); - setButtonState (btn & button_mask, time); + setKeyState (token.btn); + setMoveState (mouse_position, token.btn); + setButtonState (token.btn & button_mask, time); - if ( mouse_position == getNewPos() - && ! isWheelUp() && ! isWheelDown() - && ! isWheelLeft() && ! isWheelRight() - && urxvt_button_state == uChar(btn) ) + if ( noChanges(mouse_position, uChar(token.btn)) ) { clearEvent(); urxvt_mouse[0] = '\0'; // Delete already interpreted data @@ -1254,7 +1221,7 @@ void FMouseUrxvt::processEvent (const TimeValue& time) setEvent(); useNewPos(); - urxvt_button_state = uChar(btn); + urxvt_button_state = uChar(token.btn); // Delete already interpreted data urxvt_mouse[0] = '\0'; } @@ -1286,7 +1253,7 @@ void FMouseUrxvt::setMoveState (const FPoint& mouse_position, int btn) noexcept } //---------------------------------------------------------------------- -bool FMouseUrxvt::isMouseClickButton (const int btn) const noexcept +inline bool FMouseUrxvt::isMouseClickButton (const int btn) const noexcept { return btn == button1_pressed || btn == button2_pressed @@ -1297,7 +1264,7 @@ bool FMouseUrxvt::isMouseClickButton (const int btn) const noexcept } //---------------------------------------------------------------------- -bool FMouseUrxvt::isMouseWheelButton (const int btn) const noexcept +inline bool FMouseUrxvt::isMouseWheelButton (const int btn) const noexcept { return btn == button_up || btn == button_down @@ -1305,6 +1272,62 @@ bool FMouseUrxvt::isMouseWheelButton (const int btn) const noexcept || btn == button_right; } +//---------------------------------------------------------------------- +inline bool FMouseUrxvt::parseUrxvtMouseString (FMouseUrxvt::Tokens& token) const noexcept +{ + + // Parse the Urxvt mouse string + token.p = urxvt_mouse.data(); + + // Parse button + if ( ! parseNumberIf (token.p, token.btn, [] (char ch) { return ch != ';'; }) ) + return true; + + if ( *++token.p == '-' ) + { + token.p++; + token.x_neg = true; + } + + // Parse x-value + if ( ! parseNumberIf (token.p, token.x, [] (char ch) { return ch != ';'; }) ) + return true; + + if ( *++token.p == '-' ) + { + token.p++; + token.y_neg = true; + } + + // Parse y-value + return ! parseNumberIf (token.p, token.y, [] (char ch) { return ch != 'M'; }); +} + +//---------------------------------------------------------------------- +inline void FMouseUrxvt::adjustAndSetPosition (FMouseUrxvt::Tokens& token) +{ + if ( token.x_neg || token.x == 0 ) + token.x = 1; + + if ( token.y_neg || token.y == 0 ) + token.y = 1; + + token.x = std::min(token.x, sInt16(getMaxWidth())); + token.y = std::min(token.y, sInt16(getMaxHeight())); + setNewPos(token.x, token.y); +} + +//---------------------------------------------------------------------- +inline auto FMouseUrxvt::noChanges (const FPoint& mouse_position, uChar btn) const noexcept -> bool +{ + return mouse_position == getNewPos() + && ! isWheelUp() + && ! isWheelDown() + && ! isWheelLeft() + && ! isWheelRight() + && urxvt_button_state == uChar(btn); +} + //---------------------------------------------------------------------- void FMouseUrxvt::handleMouseClickButton ( int btn , const TimeValue& time) noexcept diff --git a/final/input/fmouse.h b/final/input/fmouse.h index b387b5c4..3eb76a92 100644 --- a/final/input/fmouse.h +++ b/final/input/fmouse.h @@ -370,6 +370,7 @@ class FMouseX11 final : public FMouse void setMoveState (const FPoint&, int) noexcept; bool isMouseClickButton (const int) const noexcept; bool isMouseWheelButton (const int) const noexcept; + auto noChanges (const FPoint&, uChar) const noexcept -> bool; void handleMouseClickButton (int, const TimeValue&) noexcept; void handleMouseWheelButton (int) noexcept; void setButtonState (const int, const TimeValue&) noexcept; @@ -403,6 +404,14 @@ class FMouseSGR final : public FMouse void processEvent (const TimeValue&) override; private: + struct Tokens + { + sInt16 x{0}; + sInt16 y{0}; + int btn{0}; + const char* p{nullptr}; // Current read position + }; + // Enumeration enum x11_ext_btn_states { @@ -433,6 +442,8 @@ class FMouseSGR final : public FMouse void setMoveState (const FPoint&, int) noexcept; bool isMouseClickButton (const int) const noexcept; bool isMouseWheelButton (const int) const noexcept; + bool parseSGRMouseString (Tokens&) const noexcept; + auto noChanges (const FPoint&, uChar) const noexcept -> bool; void handleMouseClickButton (int, const TimeValue&) noexcept; void handleMouseWheelButton (int) noexcept; void setPressedButtonState (const int, const TimeValue&) noexcept; @@ -466,6 +477,16 @@ class FMouseUrxvt final : public FMouse void processEvent (const TimeValue&) override; private: + struct Tokens + { + sInt16 x{0}; + sInt16 y{0}; + int btn{0}; + bool x_neg{false}; + bool y_neg{false}; + const char* p{nullptr}; // Current read position + }; + // Enumeration enum urxvt_btn_states { @@ -495,6 +516,9 @@ class FMouseUrxvt final : public FMouse void setMoveState (const FPoint&, int) noexcept; bool isMouseClickButton (const int) const noexcept; bool isMouseWheelButton (const int) const noexcept; + bool parseUrxvtMouseString (Tokens&) const noexcept; + void adjustAndSetPosition (Tokens&); + auto noChanges (const FPoint&, uChar) const noexcept -> bool; void handleMouseClickButton (int, const TimeValue&) noexcept; void handleButtonRelease() noexcept; void handleMouseWheelButton (int) noexcept; diff --git a/final/output/tty/fterm_functions.cpp b/final/output/tty/fterm_functions.cpp index 86bf137b..4ad3e4b9 100644 --- a/final/output/tty/fterm_functions.cpp +++ b/final/output/tty/fterm_functions.cpp @@ -565,8 +565,7 @@ auto getColumnWidth (const FString& s, std::size_t end_pos) -> std::size_t } catch (const std::out_of_range& ex) { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; + handleOutOfRangeError(ex); } } diff --git a/final/util/flog.cpp b/final/util/flog.cpp index 5210dbf7..1989e8b0 100644 --- a/final/util/flog.cpp +++ b/final/util/flog.cpp @@ -83,4 +83,13 @@ auto FLog::sync() -> int return 0; } + +// FLog non-member operators +//---------------------------------------------------------------------- +void handleOutOfRangeError (const std::out_of_range& ex) +{ + std::clog << FLog::LogLevel::Error + << "Out of Range error: " << ex.what() << std::endl; +} + } // namespace finalcut diff --git a/final/util/flog.h b/final/util/flog.h index 557c9a7b..72e507e4 100644 --- a/final/util/flog.h +++ b/final/util/flog.h @@ -116,6 +116,10 @@ class FLog : public std::stringbuf friend auto operator << (std::ostream&, LogLevel) -> std::ostream&; }; +// non-member function forward declarations +//---------------------------------------------------------------------- +void handleOutOfRangeError (const std::out_of_range& ex); + // FLog inline functions //---------------------------------------------------------------------- template diff --git a/final/widget/flineedit.cpp b/final/widget/flineedit.cpp index 9938c95e..02d89217 100644 --- a/final/widget/flineedit.cpp +++ b/final/widget/flineedit.cpp @@ -807,6 +807,36 @@ inline auto FLineEdit::isPasswordField() const -> bool return input_type == InputType::Password; } +//---------------------------------------------------------------------- +inline auto FLineEdit::isFullwidthChar (std::size_t pos) const -> bool +{ + try + { + return getColumnWidth(print_text[pos]) == 2; // pos is always > 0 + } + catch (const std::out_of_range& ex) + { + handleOutOfRangeError(ex); + return false; + } +} + +//---------------------------------------------------------------------- +inline auto FLineEdit::getColumnWidthWithErrorHandling + ( FString::const_reference cref_string + , std::size_t fallback_size ) const -> std::size_t +{ + try + { + return getColumnWidth(cref_string); + } + catch (const std::out_of_range& ex) + { + handleOutOfRangeError(ex); + return fallback_size; + } +} + //---------------------------------------------------------------------- inline auto FLineEdit::endPosToOffset (std::size_t pos) -> offsetPair { @@ -819,17 +849,8 @@ inline auto FLineEdit::endPosToOffset (std::size_t pos) -> offsetPair while ( pos > 0 && input_width > 0 ) { - std::size_t char_width{}; - - try - { - char_width = getColumnWidth(print_text[pos]); - } - catch (const std::out_of_range& ex) - { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; - } + std::size_t char_width = \ + getColumnWidthWithErrorHandling (print_text[pos]); if ( input_width >= char_width ) input_width -= char_width; @@ -839,21 +860,10 @@ inline auto FLineEdit::endPosToOffset (std::size_t pos) -> offsetPair if ( input_width == 1) { - if ( char_width == 1 ) + if ( char_width == 1 && isFullwidthChar(pos - 1) ) { - try - { - if ( getColumnWidth(print_text[pos - 1]) == 2 ) // pos is always > 0 - { - fullwidth_char_offset = 1; - break; - } - } - catch (const std::out_of_range& ex) - { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; - } + fullwidth_char_offset = 1; + break; } if ( char_width == 2 ) @@ -879,18 +889,8 @@ auto FLineEdit::clickPosToCursorPos (std::size_t pos) -> std::size_t while ( click_width < pos && idx < len ) { - std::size_t char_width{}; - - try - { - char_width = getColumnWidth(print_text[idx]); - } - catch (const std::out_of_range& ex) - { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; - } - + std::size_t char_width = \ + getColumnWidthWithErrorHandling (print_text[idx]); idx++; click_width += char_width; @@ -915,28 +915,14 @@ void FLineEdit::adjustTextOffset() if ( cursor_pos < len ) { - try - { - cursor_char_width = getColumnWidth(print_text[cursor_pos]); - } - catch (const std::out_of_range& ex) - { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; - } + cursor_char_width = \ + getColumnWidthWithErrorHandling (print_text[cursor_pos], 1); } if ( len > 0 ) { - try - { - first_char_width = getColumnWidth(print_text[0]); - } - catch (const std::out_of_range& ex) - { - std::clog << FLog::LogLevel::Error - << "Out of Range error: " << ex.what() << std::endl; - } + first_char_width = \ + getColumnWidthWithErrorHandling (print_text[0]); } // Text alignment right for long lines diff --git a/final/widget/flineedit.h b/final/widget/flineedit.h index b48f2a72..1e65bf25 100644 --- a/final/widget/flineedit.h +++ b/final/widget/flineedit.h @@ -194,6 +194,9 @@ class FLineEdit : public FWidget auto getCursorColumnPos() const -> std::size_t; auto getPasswordText() const -> FString; auto isPasswordField() const -> bool; + auto isFullwidthChar (std::size_t) const -> bool; + auto getColumnWidthWithErrorHandling ( FString::const_reference + , std::size_t = 0 ) const ->std::size_t; auto endPosToOffset (std::size_t) -> offsetPair; auto clickPosToCursorPos (std::size_t) -> std::size_t; void adjustTextOffset();