From b278c9247dae8f165fa8ebb3137f254c08abc4f6 Mon Sep 17 00:00:00 2001 From: Yaraslau Tamashevich Date: Tue, 16 Apr 2024 17:44:56 +0300 Subject: [PATCH 1/2] Fix crash when status line is enabled --- metainfo.xml | 1 + src/vtbackend/Terminal.cpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/metainfo.xml b/metainfo.xml index 2598338176..7cfc84746d 100644 --- a/metainfo.xml +++ b/metainfo.xml @@ -127,6 +127,7 @@
  • Fixes `CSI 8 ; (COLS) ; (ROWS) t` to resize the terminal with respect to High-DPI
  • Fixes screen sampling with multiple monitors (#940)
  • Fixes bell sound in spawned window in same process (#1515)
  • +
  • Fixes status line crush (#1511)
  • diff --git a/src/vtbackend/Terminal.cpp b/src/vtbackend/Terminal.cpp index 8226ef2ba4..e0a10be662 100644 --- a/src/vtbackend/Terminal.cpp +++ b/src/vtbackend/Terminal.cpp @@ -1272,20 +1272,24 @@ struct LocalSequenceHandler // {{{ instructionCounter = 0; targetScreen.processSequence(seq); } - void writeText(char32_t codepoint) { instructionCounter++; targetScreen.writeText(codepoint); } - [[nodiscard]] size_t writeText(std::string_view chars, size_t cellCount) + [[nodiscard]] size_t writeText(std::string_view chars, size_t) { - assert(!chars.empty()); - instructionCounter += chars.size(); - targetScreen.writeText(chars, cellCount); - return terminal.settings().pageSize.columns.as() - - terminal.currentScreen().cursor().position.column.as(); + // implementation of targetScreen.writeText(chars, cellCount) + // is buggy and does not work correctly + // so we do not use optimization for + // ParserEvents::print(chars,cellCount) + // but write char by char + // TODO fix targetScreen.writeText(chars, cellCount) + for (auto c: chars) + targetScreen.writeText(c); + + return 0; } void writeTextEnd() { targetScreen.writeTextEnd(); } From 93fec671e062c5b929c773000464493cd79747af Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Wed, 19 Jun 2024 21:13:41 +0200 Subject: [PATCH 2/2] [vtbackend] Refactor Sequencer into SequenceBuilder to increase code share. Also, SequenceBuilder is the way better and more intuitive name than Sequencer :) Signed-off-by: Christian Parpart --- src/vtbackend/CMakeLists.txt | 3 +- src/vtbackend/Metrics.h | 2 - src/vtbackend/Screen.cpp | 1 + src/vtbackend/Screen.h | 124 +++++++++++++- src/vtbackend/Sequence.h | 10 ++ src/vtbackend/SequenceBuilder.h | 197 +++++++++++++++++++++++ src/vtbackend/Sequencer.cpp | 154 ------------------ src/vtbackend/Sequencer.h | 239 --------------------------- src/vtbackend/Terminal.cpp | 267 +++---------------------------- src/vtbackend/Terminal.h | 37 ++++- src/vtbackend/bench-headless.cpp | 2 + 11 files changed, 386 insertions(+), 650 deletions(-) create mode 100644 src/vtbackend/SequenceBuilder.h delete mode 100644 src/vtbackend/Sequencer.cpp delete mode 100644 src/vtbackend/Sequencer.h diff --git a/src/vtbackend/CMakeLists.txt b/src/vtbackend/CMakeLists.txt index 8c830b7d89..f4abfccf1e 100644 --- a/src/vtbackend/CMakeLists.txt +++ b/src/vtbackend/CMakeLists.txt @@ -36,7 +36,7 @@ set(vtbackend_HEADERS Screen.h Selector.h Sequence.h - Sequencer.h + SequenceBuilder.h SixelParser.h StatusLineBuilder.h Terminal.h @@ -67,7 +67,6 @@ set(vtbackend_SOURCES Screen.cpp Selector.cpp Sequence.cpp - Sequencer.cpp SixelParser.cpp StatusLineBuilder.cpp Terminal.cpp diff --git a/src/vtbackend/Metrics.h b/src/vtbackend/Metrics.h index 1cba3df155..b1a62e45a5 100644 --- a/src/vtbackend/Metrics.h +++ b/src/vtbackend/Metrics.h @@ -1,8 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include // Sequence - #include #include #include diff --git a/src/vtbackend/Screen.cpp b/src/vtbackend/Screen.cpp index 8c6078e3ae..cf91dc4dd3 100644 --- a/src/vtbackend/Screen.cpp +++ b/src/vtbackend/Screen.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/vtbackend/Screen.h b/src/vtbackend/Screen.h index 5637588c04..b70eed6c4c 100644 --- a/src/vtbackend/Screen.h +++ b/src/vtbackend/Screen.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -39,9 +38,90 @@ namespace vtbackend { +class SixelImageBuilder; class Terminal; struct Settings; +// {{{ TODO: move me somewhere more appropriate +// XTSMGRAPHICS (xterm extension) +// CSI ? Pi ; Pa ; Pv S +namespace XtSmGraphics +{ + enum class Item + { + NumberOfColorRegisters = 1, + SixelGraphicsGeometry = 2, + ReGISGraphicsGeometry = 3, + }; + + enum class Action + { + Read = 1, + ResetToDefault = 2, + SetToValue = 3, + ReadLimit = 4 + }; + + using Value = std::variant; +} // namespace XtSmGraphics + +/// TBC - Tab Clear +/// +/// This control function clears tab stops. +enum class HorizontalTabClear +{ + /// Ps = 0 (default) + AllTabs, + + /// Ps = 3 + UnderCursor, +}; + +/// Input: CSI 16 t +/// +/// Input: CSI 14 t (for text area size) +/// Input: CSI 14; 2 t (for full window size) +/// Output: CSI 14 ; width ; height ; t +enum class RequestPixelSize // TODO: rename RequestPixelSize to RequestArea? +{ + CellArea, + TextArea, + WindowArea, +}; + +/// DECRQSS - Request Status String +enum class RequestStatusString +{ + SGR, + DECSCL, + DECSCUSR, + DECSCA, + DECSTBM, + DECSLRM, + DECSLPP, + DECSCPP, + DECSNLS, + DECSASD, + DECSSDT, +}; + +inline std::string setDynamicColorValue( + RGBColor const& color) // TODO: yet another helper. maybe SemanticsUtils static class? +{ + auto const r = static_cast(static_cast(color.red) / 255.0f * 0xFFFF); + auto const g = static_cast(static_cast(color.green) / 255.0f * 0xFFFF); + auto const b = static_cast(static_cast(color.blue) / 255.0f * 0xFFFF); + return fmt::format("rgb:{:04X}/{:04X}/{:04X}", r, g, b); +} + +enum class ApplyResult +{ + Ok, + Invalid, + Unsupported, +}; +// }}} + /** * Terminal Screen. * @@ -594,3 +674,45 @@ inline bool Screen::isContiguousToCurrentLine(std::string_view continuatio } } // namespace vtbackend + +// {{{ fmt formatter +template <> +struct fmt::formatter: formatter +{ + auto format(vtbackend::RequestStatusString value, + format_context& ctx) noexcept -> format_context::iterator + { + string_view name; + switch (value) + { + case vtbackend::RequestStatusString::SGR: name = "SGR"; break; + case vtbackend::RequestStatusString::DECSCL: name = "DECSCL"; break; + case vtbackend::RequestStatusString::DECSCUSR: name = "DECSCUSR"; break; + case vtbackend::RequestStatusString::DECSCA: name = "DECSCA"; break; + case vtbackend::RequestStatusString::DECSTBM: name = "DECSTBM"; break; + case vtbackend::RequestStatusString::DECSLRM: name = "DECSLRM"; break; + case vtbackend::RequestStatusString::DECSLPP: name = "DECSLPP"; break; + case vtbackend::RequestStatusString::DECSCPP: name = "DECSCPP"; break; + case vtbackend::RequestStatusString::DECSNLS: name = "DECSNLS"; break; + case vtbackend::RequestStatusString::DECSASD: name = "DECSASD"; break; + case vtbackend::RequestStatusString::DECSSDT: name = "DECSSDT"; break; + } + return formatter::format(name, ctx); + } +}; + +template <> +struct fmt::formatter +{ + template + constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + template + auto format(vtbackend::Sequence const& seq, FormatContext& ctx) + { + return fmt::format_to(ctx.out(), "{}", seq.text()); + } +}; +// }}} diff --git a/src/vtbackend/Sequence.h b/src/vtbackend/Sequence.h index 89134ccfa7..7ebc8d60c7 100644 --- a/src/vtbackend/Sequence.h +++ b/src/vtbackend/Sequence.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -362,4 +363,13 @@ class SequenceHandler virtual void writeTextEnd() = 0; }; +template +concept SequenceHandlerConcept = requires(T t) { + { t.executeControlCode('\x00') } -> std::same_as; + { t.processSequence(Sequence {}) } -> std::same_as; + { t.writeText(U'a') } -> std::same_as; + { t.writeText("a", size_t(1)) } -> std::same_as; + { t.writeTextEnd() } -> std::same_as; +}; + } // namespace vtbackend diff --git a/src/vtbackend/SequenceBuilder.h b/src/vtbackend/SequenceBuilder.h new file mode 100644 index 0000000000..10e2188b0e --- /dev/null +++ b/src/vtbackend/SequenceBuilder.h @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace vtbackend +{ + +template +concept InstructionCounterConcept = requires(T t, size_t n) { + { t() } -> std::same_as; + { t(n) } -> std::same_as; +}; + +struct NoOpInstructionCounter +{ + void operator()(size_t /*increment*/ = 1) const noexcept {} +}; + +/// SequenceBuilder - The semantic VT analyzer layer. +/// +/// SequenceBuilder implements the translation from VT parser events, forming a higher level Sequence, +/// that can be matched against FunctionDefinition objects and then handled on the currently active Screen. +template +class SequenceBuilder +{ + public: + explicit SequenceBuilder(Handler handler, IncrementInstructionCounter incrementInstructionCounter): + _sequence {}, + _parameterBuilder { _sequence.parameters() }, + _incrementInstructionCounter { std::move(incrementInstructionCounter) }, + _handler { std::move(handler) } + { + } + + // {{{ ParserEvents interface + void error(std::string_view errorString) + { + if (vtParserLog) + vtParserLog()("Parser error: {}", errorString); + } + void print(char32_t codepoint) + { + _incrementInstructionCounter(); + _handler.writeText(codepoint); + } + + size_t print(std::string_view chars, size_t cellCount) + { + assert(!chars.empty()); + + _incrementInstructionCounter(cellCount); + _handler.writeText(chars, cellCount); + return _handler.maxBulkTextSequenceWidth(); + } + + void printEnd() { _handler.writeTextEnd(); } + + void execute(char controlCode) { _handler.executeControlCode(controlCode); } + + void clear() noexcept + { + _sequence.clearExceptParameters(); + _parameterBuilder.reset(); + } + + void collect(char ch) { _sequence.intermediateCharacters().push_back(ch); } + + void collectLeader(char leader) noexcept { _sequence.setLeader(leader); } + + void param(char ch) noexcept + { + switch (ch) + { + case ';': paramSeparator(); break; + case ':': paramSubSeparator(); break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': paramDigit(ch); break; + default: crispy::unreachable(); + } + } + + void paramDigit(char ch) noexcept + { + _parameterBuilder.multiplyBy10AndAdd(static_cast(ch - '0')); + } + + void paramSeparator() noexcept { _parameterBuilder.nextParameter(); } + void paramSubSeparator() noexcept { _parameterBuilder.nextSubParameter(); } + + void dispatchESC(char finalChar) + { + _sequence.setCategory(FunctionCategory::ESC); + _sequence.setFinalChar(finalChar); + handleSequence(); + } + + void dispatchCSI(char finalChar) + { + _sequence.setCategory(FunctionCategory::CSI); + _sequence.setFinalChar(finalChar); + handleSequence(); + } + + void startOSC() { _sequence.setCategory(FunctionCategory::OSC); } + + void putOSC(char ch) + { + if (_sequence.intermediateCharacters().size() + 1 < Sequence::MaxOscLength) + _sequence.intermediateCharacters().push_back(ch); + } + + void dispatchOSC() + { + auto const [code, skipCount] = vtparser::extractCodePrefix(_sequence.intermediateCharacters()); + _parameterBuilder.set(static_cast(code)); + _sequence.intermediateCharacters().erase(0, skipCount); + handleSequence(); + clear(); + } + + void hook(char finalChar) + { + _incrementInstructionCounter(); + _sequence.setCategory(FunctionCategory::DCS); + _sequence.setFinalChar(finalChar); + + handleSequence(); + } + void put(char ch) + { + if (_hookedParser) + _hookedParser->pass(ch); + } + void unhook() + { + if (_hookedParser) + { + _hookedParser->finalize(); + _hookedParser.reset(); + } + } + void startAPC() {} + void putAPC(char) {} + void dispatchAPC() {} + void startPM() {} + void putPM(char) {} + void dispatchPM() {} + + void hookParser(std::unique_ptr parserExtension) noexcept + { + _hookedParser = std::move(parserExtension); + } + + [[nodiscard]] size_t maxBulkTextSequenceWidth() const noexcept + { + return _handler.maxBulkTextSequenceWidth(); + } + // }}} + + private: + void handleSequence() + { + _parameterBuilder.fixiate(); + _handler.processSequence(_sequence); + } + + Sequence _sequence {}; + SequenceParameterBuilder _parameterBuilder; + IncrementInstructionCounter _incrementInstructionCounter; + Handler _handler; + + std::unique_ptr _hookedParser {}; +}; + +template +SequenceBuilder(Handler&, + IncrementInstructionCounter) -> SequenceBuilder; + +} // namespace vtbackend diff --git a/src/vtbackend/Sequencer.cpp b/src/vtbackend/Sequencer.cpp deleted file mode 100644 index 9862a439d0..0000000000 --- a/src/vtbackend/Sequencer.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std::string_view_literals; - -namespace vtbackend -{ - -Sequencer::Sequencer(Terminal& terminal): _terminal { terminal }, _parameterBuilder { _sequence.parameters() } -{ -} - -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) -void Sequencer::error(std::string_view errorString) -{ - if (vtParserLog) - vtParserLog()("Parser error: {}", errorString); -} - -void Sequencer::print(char32_t codepoint) -{ - _terminal.incrementInstructionCounter(); - _terminal.sequenceHandler().writeText(codepoint); -} - -size_t Sequencer::print(std::string_view chars, size_t cellCount) -{ - assert(!chars.empty()); - - _terminal.incrementInstructionCounter(chars.size()); - _terminal.sequenceHandler().writeText(chars, cellCount); - - return _terminal.settings().pageSize.columns.as() - - _terminal.currentScreen().cursor().position.column.as(); -} - -void Sequencer::printEnd() noexcept -{ - _terminal.sequenceHandler().writeTextEnd(); -} - -void Sequencer::execute(char controlCode) -{ - _terminal.sequenceHandler().executeControlCode(controlCode); -} - -void Sequencer::collect(char ch) -{ - _sequence.intermediateCharacters().push_back(ch); -} - -void Sequencer::collectLeader(char leader) noexcept -{ - _sequence.setLeader(leader); -} - -void Sequencer::param(char ch) noexcept -{ - switch (ch) - { - case ';': paramSeparator(); break; - case ':': paramSubSeparator(); break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': paramDigit(ch); break; - default: crispy::unreachable(); - } -} - -void Sequencer::dispatchESC(char finalChar) -{ - _sequence.setCategory(FunctionCategory::ESC); - _sequence.setFinalChar(finalChar); - handleSequence(); -} - -void Sequencer::dispatchCSI(char finalChar) -{ - _sequence.setCategory(FunctionCategory::CSI); - _sequence.setFinalChar(finalChar); - handleSequence(); -} - -void Sequencer::startOSC() noexcept -{ - _sequence.setCategory(FunctionCategory::OSC); -} - -void Sequencer::putOSC(char ch) -{ - if (_sequence.intermediateCharacters().size() + 1 < Sequence::MaxOscLength) - _sequence.intermediateCharacters().push_back(ch); -} - -void Sequencer::dispatchOSC() -{ - auto const [code, skipCount] = vtparser::extractCodePrefix(_sequence.intermediateCharacters()); - _parameterBuilder.set(static_cast(code)); - _sequence.intermediateCharacters().erase(0, skipCount); - handleSequence(); - clear(); -} - -void Sequencer::hook(char finalChar) -{ - _terminal.incrementInstructionCounter(); - _sequence.setCategory(FunctionCategory::DCS); - _sequence.setFinalChar(finalChar); - - handleSequence(); -} - -void Sequencer::put(char ch) -{ - if (_hookedParser) - _hookedParser->pass(ch); -} - -void Sequencer::unhook() -{ - if (_hookedParser) - { - _hookedParser->finalize(); - _hookedParser.reset(); - } -} - -size_t Sequencer::maxBulkTextSequenceWidth() const noexcept -{ - return _terminal.maxBulkTextSequenceWidth(); -} - -void Sequencer::handleSequence() -{ - _parameterBuilder.fixiate(); - _terminal.sequenceHandler().processSequence(_sequence); -} - -} // namespace vtbackend diff --git a/src/vtbackend/Sequencer.h b/src/vtbackend/Sequencer.h deleted file mode 100644 index 149e0f8c6d..0000000000 --- a/src/vtbackend/Sequencer.h +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -namespace vtbackend -{ - -// {{{ TODO: refactor me -// XTSMGRAPHICS (xterm extension) -// CSI ? Pi ; Pa ; Pv S -namespace XtSmGraphics -{ - enum class Item - { - NumberOfColorRegisters = 1, - SixelGraphicsGeometry = 2, - ReGISGraphicsGeometry = 3, - }; - - enum class Action - { - Read = 1, - ResetToDefault = 2, - SetToValue = 3, - ReadLimit = 4 - }; - - using Value = std::variant; -} // namespace XtSmGraphics - -/// TBC - Tab Clear -/// -/// This control function clears tab stops. -enum class HorizontalTabClear -{ - /// Ps = 0 (default) - AllTabs, - - /// Ps = 3 - UnderCursor, -}; - -/// Input: CSI 16 t -/// -/// Input: CSI 14 t (for text area size) -/// Input: CSI 14; 2 t (for full window size) -/// Output: CSI 14 ; width ; height ; t -enum class RequestPixelSize // TODO: rename RequestPixelSize to RequestArea? -{ - CellArea, - TextArea, - WindowArea, -}; - -/// DECRQSS - Request Status String -enum class RequestStatusString -{ - SGR, - DECSCL, - DECSCUSR, - DECSCA, - DECSTBM, - DECSLRM, - DECSLPP, - DECSCPP, - DECSNLS, - DECSASD, - DECSSDT, -}; - -/// DECSIXEL - Sixel Graphics Image. -struct SixelImage -{ // TODO: this struct is only used internally in Sequencer, make it private - /// Size in pixels for this image - ImageSize size; - - /// RGBA buffer of the image to be rendered - Image::Data rgba; -}; - -inline std::string setDynamicColorValue( - RGBColor const& color) // TODO: yet another helper. maybe SemanticsUtils static class? -{ - auto const r = static_cast(static_cast(color.red) / 255.0f * 0xFFFF); - auto const g = static_cast(static_cast(color.green) / 255.0f * 0xFFFF); - auto const b = static_cast(static_cast(color.blue) / 255.0f * 0xFFFF); - return fmt::format("rgb:{:04X}/{:04X}/{:04X}", r, g, b); -} - -enum class ApplyResult -{ - Ok, - Invalid, - Unsupported, -}; -// }}} - -class Terminal; - -/// Sequencer - The semantic VT analyzer layer. -/// -/// Sequencer implements the translation from VT parser events, forming a higher level Sequence, -/// that can be matched against actions to perform on the target Screen. -class Sequencer -{ - public: - /// Constructs the sequencer stage. - explicit Sequencer(Terminal& terminal); - - // ParserEvents - // - void error(std::string_view errorString); - void print(char32_t codepoint); - size_t print(std::string_view chars, size_t cellCount); - void printEnd() noexcept; - void execute(char controlCode); - void clear() noexcept; - void collect(char ch); - void collectLeader(char leader) noexcept; - void param(char ch) noexcept; - void paramDigit(char ch) noexcept; - void paramSeparator() noexcept; - void paramSubSeparator() noexcept; - void dispatchESC(char finalChar); - void dispatchCSI(char finalChar); - void startOSC() noexcept; - void putOSC(char ch); - void dispatchOSC(); - void hook(char finalChar); - void put(char ch); - void unhook(); - void startAPC() {} - void putAPC(char) {} - void dispatchAPC() {} - void startPM() {} - void putPM(char) {} - void dispatchPM() {} - - void hookParser(std::unique_ptr parserExtension) noexcept - { - _hookedParser = std::move(parserExtension); - } - - [[nodiscard]] size_t maxBulkTextSequenceWidth() const noexcept; - - private: - void handleSequence(); - - // private data - // - Terminal& _terminal; - Sequence _sequence {}; - SequenceParameterBuilder _parameterBuilder; - - std::unique_ptr _hookedParser; - std::unique_ptr _sixelImageBuilder; -}; - -// {{{ inlines -inline void Sequencer::clear() noexcept -{ - _sequence.clearExceptParameters(); - _parameterBuilder.reset(); -} - -inline void Sequencer::paramDigit(char ch) noexcept -{ - _parameterBuilder.multiplyBy10AndAdd(static_cast(ch - '0')); -} - -inline void Sequencer::paramSeparator() noexcept -{ - _parameterBuilder.nextParameter(); -} - -inline void Sequencer::paramSubSeparator() noexcept -{ - _parameterBuilder.nextSubParameter(); -} -// }}} - -} // namespace vtbackend - -// {{{ fmt formatter -template <> -struct fmt::formatter: formatter -{ - auto format(vtbackend::RequestStatusString value, format_context& ctx) noexcept - -> format_context::iterator - { - string_view name; - switch (value) - { - case vtbackend::RequestStatusString::SGR: name = "SGR"; break; - case vtbackend::RequestStatusString::DECSCL: name = "DECSCL"; break; - case vtbackend::RequestStatusString::DECSCUSR: name = "DECSCUSR"; break; - case vtbackend::RequestStatusString::DECSCA: name = "DECSCA"; break; - case vtbackend::RequestStatusString::DECSTBM: name = "DECSTBM"; break; - case vtbackend::RequestStatusString::DECSLRM: name = "DECSLRM"; break; - case vtbackend::RequestStatusString::DECSLPP: name = "DECSLPP"; break; - case vtbackend::RequestStatusString::DECSCPP: name = "DECSCPP"; break; - case vtbackend::RequestStatusString::DECSNLS: name = "DECSNLS"; break; - case vtbackend::RequestStatusString::DECSASD: name = "DECSASD"; break; - case vtbackend::RequestStatusString::DECSSDT: name = "DECSSDT"; break; - } - return formatter::format(name, ctx); - } -}; - -template <> -struct fmt::formatter -{ - template - constexpr auto parse(ParseContext& ctx) - { - return ctx.begin(); - } - template - auto format(vtbackend::Sequence const& seq, FormatContext& ctx) - { - return fmt::format_to(ctx.out(), "{}", seq.text()); - } -}; -// }}} diff --git a/src/vtbackend/Terminal.cpp b/src/vtbackend/Terminal.cpp index e0a10be662..762fd4b9cd 100644 --- a/src/vtbackend/Terminal.cpp +++ b/src/vtbackend/Terminal.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -156,8 +156,8 @@ Terminal::Terminal(Events& eventListener, discardImage(*image); } }, _hyperlinks { HyperlinkCache { 1024 } }, - _sequencer { *this }, - _parser { std::ref(_sequencer) }, + _sequenceBuilder { ModeDependantSequenceHandler { *this }, TerminalInstructionCounter { *this } }, + _parser { std::ref(_sequenceBuilder) }, _viCommands { *this }, _inputHandler { _viCommands, ViMode::Insert } { @@ -1019,221 +1019,6 @@ string_view Terminal::lockedWriteToPtyBuffer(string_view data) return string_view(ref.data(), ref.size()); } -// {{{ SequenceBuilder -// SequenceBuilder implements ParserEvents interface to handle parsed VT sequences. -template -class SequenceBuilder -{ - public: - explicit SequenceBuilder(Handler& handler): - _sequence {}, _parameterBuilder { _sequence.parameters() }, _handler { handler } - { - } - - // {{{ ParserEvents interface - void error(std::string_view errorString); - void print(char32_t codepoint); - size_t print(std::string_view chars, size_t cellCount); - void printEnd(); - void execute(char controlCode); - void clear() noexcept; - void collect(char ch); - void collectLeader(char leader) noexcept; - void param(char ch) noexcept; - void paramDigit(char ch) noexcept; - void paramSeparator() noexcept; - void paramSubSeparator() noexcept; - void dispatchESC(char finalChar); - void dispatchCSI(char finalChar); - void startOSC(); - void putOSC(char ch); - void dispatchOSC(); - void hook(char finalChar); - void put(char ch); - void unhook(); - void startAPC() {} - void putAPC(char) {} - void dispatchAPC() {} - void startPM() {} - void putPM(char) {} - void dispatchPM() {} - - [[nodiscard]] size_t maxBulkTextSequenceWidth() const noexcept; - // }}} - - private: - void handleSequence(); - - Sequence _sequence {}; - SequenceParameterBuilder _parameterBuilder; - Handler& _handler; - - std::unique_ptr _hookedParser {}; - std::unique_ptr _sixelImageBuilder {}; -}; - -template -inline void SequenceBuilder::clear() noexcept -{ - _sequence.clearExceptParameters(); - _parameterBuilder.reset(); -} - -template -inline void SequenceBuilder::paramDigit(char ch) noexcept -{ - _parameterBuilder.multiplyBy10AndAdd(static_cast(ch - '0')); -} - -template -inline void SequenceBuilder::paramSeparator() noexcept -{ - _parameterBuilder.nextParameter(); -} - -template -inline void SequenceBuilder::paramSubSeparator() noexcept -{ - _parameterBuilder.nextSubParameter(); -} - -template -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) -void SequenceBuilder::error(std::string_view errorString) -{ - if (vtParserLog) - vtParserLog()("Parser error: {}", errorString); -} - -template -void SequenceBuilder::print(char32_t codepoint) -{ - _handler.writeText(codepoint); -} - -template -size_t SequenceBuilder::print(string_view chars, size_t cellCount) -{ - return _handler.writeText(chars, cellCount); -} - -template -void SequenceBuilder::printEnd() -{ - _handler.writeTextEnd(); -} - -template -void SequenceBuilder::execute(char controlCode) -{ - _handler.executeControlCode(controlCode); -} - -template -void SequenceBuilder::collect(char ch) -{ - _sequence.intermediateCharacters().push_back(ch); -} - -template -void SequenceBuilder::collectLeader(char leader) noexcept -{ - _sequence.setLeader(leader); -} - -template -void SequenceBuilder::param(char ch) noexcept -{ - switch (ch) - { - case ';': paramSeparator(); break; - case ':': paramSubSeparator(); break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': paramDigit(ch); break; - default: crispy::unreachable(); - } -} - -template -void SequenceBuilder::dispatchESC(char finalChar) -{ - _sequence.setCategory(FunctionCategory::ESC); - _sequence.setFinalChar(finalChar); - handleSequence(); -} - -template -void SequenceBuilder::dispatchCSI(char finalChar) -{ - _sequence.setCategory(FunctionCategory::CSI); - _sequence.setFinalChar(finalChar); - handleSequence(); -} - -template -void SequenceBuilder::startOSC() -{ - _sequence.setCategory(FunctionCategory::OSC); -} - -template -void SequenceBuilder::putOSC(char ch) -{ - if (_sequence.intermediateCharacters().size() + 1 < Sequence::MaxOscLength) - _sequence.intermediateCharacters().push_back(ch); -} - -template -void SequenceBuilder::dispatchOSC() -{ - auto const [code, skipCount] = vtparser::extractCodePrefix(_sequence.intermediateCharacters()); - _parameterBuilder.set(static_cast(code)); - _sequence.intermediateCharacters().erase(0, skipCount); - handleSequence(); - clear(); -} - -template -void SequenceBuilder::hook(char finalChar) -{ - _handler.hook(finalChar); - _sequence.setCategory(FunctionCategory::DCS); - _sequence.setFinalChar(finalChar); - - handleSequence(); -} - -template -void SequenceBuilder::put(char ch) -{ - if (_hookedParser) - _hookedParser->pass(ch); -} - -template -void SequenceBuilder::unhook() -{ - if (_hookedParser) - { - _hookedParser->finalize(); - _hookedParser.reset(); - } -} - -template -size_t SequenceBuilder::maxBulkTextSequenceWidth() const noexcept -{ - return _handler.maxBulkTextSequenceWidth(); -} - size_t Terminal::maxBulkTextSequenceWidth() const noexcept { if (!isPrimaryScreen()) @@ -1247,38 +1032,26 @@ size_t Terminal::maxBulkTextSequenceWidth() const noexcept return unbox(_mainScreenMargin.horizontal.to - _currentScreen->cursor().position.column); } -template -void SequenceBuilder::handleSequence() -{ - _parameterBuilder.fixiate(); - _handler.processSequence(_sequence); -} -// }}} - -struct LocalSequenceHandler // {{{ +// {{{ SimpleSequenceHandler +// This simple sequence handler is used to write to the screen +// without any optimizations (and no parser hooking). +// We use this for rendering the status line. +struct SimpleSequenceHandler { - Terminal& terminal; Screen& targetScreen; - uint64_t instructionCounter = 0; - void executeControlCode(char controlCode) - { - instructionCounter++; - targetScreen.executeControlCode(controlCode); - } + void executeControlCode(char controlCode) { targetScreen.executeControlCode(controlCode); } void processSequence(Sequence const& seq) { - instructionCounter = 0; + // NB: We might want to check for some VT sequences that should not be processed here. + // We might make use of Terminal::activeSequences() here somehow. targetScreen.processSequence(seq); } - void writeText(char32_t codepoint) - { - instructionCounter++; - targetScreen.writeText(codepoint); - } - [[nodiscard]] size_t writeText(std::string_view chars, size_t) + void writeText(char32_t codepoint) { targetScreen.writeText(codepoint); } + + void writeText(std::string_view chars, size_t /*cellCount*/) { // implementation of targetScreen.writeText(chars, cellCount) // is buggy and does not work correctly @@ -1288,25 +1061,23 @@ struct LocalSequenceHandler // {{{ // TODO fix targetScreen.writeText(chars, cellCount) for (auto c: chars) targetScreen.writeText(c); - - return 0; } void writeTextEnd() { targetScreen.writeTextEnd(); } - void hook(char /*finalChar*/) { instructionCounter++; } - [[nodiscard]] size_t maxBulkTextSequenceWidth() const noexcept { - return terminal.maxBulkTextSequenceWidth(); + // Returning 0 here, because we do not make use of the performance optimized bulk write above. + return 0; } }; // }}} void Terminal::writeToScreenInternal(Screen& screen, std::string_view vtStream) { - auto sequenceHandler = LocalSequenceHandler { *this, screen }; - auto sequenceBuilder = SequenceBuilder { sequenceHandler }; + auto sequenceHandler = SimpleSequenceHandler { screen }; + auto sequenceBuilder = SequenceBuilder { sequenceHandler, NoOpInstructionCounter() }; + // auto sequenceBuilder = SequenceBuilder { *this, NoOpInstructionCounter() }; auto parser = vtparser::Parser { sequenceBuilder }; parser.parseFragment(vtStream); diff --git a/src/vtbackend/Terminal.h b/src/vtbackend/Terminal.h index 757125cb38..dd45bfd76a 100644 --- a/src/vtbackend/Terminal.h +++ b/src/vtbackend/Terminal.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -911,7 +911,7 @@ class Terminal void hookParser(std::unique_ptr parserExtension) noexcept { - _sequencer.hookParser(std::move(parserExtension)); + _sequenceBuilder.hookParser(std::move(parserExtension)); } constexpr void resetInstructionCounter() noexcept { _instructionCounter = 0; } @@ -1148,8 +1148,37 @@ class Terminal std::string _windowTitle {}; std::stack _savedWindowTitles {}; - Sequencer _sequencer; - vtparser::Parser _parser; + struct ModeDependantSequenceHandler + { + Terminal& terminal; + void executeControlCode(char controlCode) + { + terminal.sequenceHandler().executeControlCode(controlCode); + } + void processSequence(Sequence const& sequence) + { + terminal.sequenceHandler().processSequence(sequence); + } + void writeText(char32_t codepoint) { terminal.sequenceHandler().writeText(codepoint); } + void writeText(std::string_view codepoints, size_t cellCount) + { + terminal.sequenceHandler().writeText(codepoints, cellCount); + } + void writeTextEnd() { terminal.sequenceHandler().writeTextEnd(); } + size_t maxBulkTextSequenceWidth() const noexcept { return terminal.maxBulkTextSequenceWidth(); } + }; + + struct TerminalInstructionCounter + { + Terminal& terminal; + void operator()() noexcept { terminal.incrementInstructionCounter(); } + void operator()(size_t increment) noexcept { terminal.incrementInstructionCounter(increment); } + }; + + using StandardSequenceBuilder = SequenceBuilder; + + StandardSequenceBuilder _sequenceBuilder; + vtparser::Parser _parser; uint64_t _instructionCounter = 0; InputGenerator _inputGenerator {}; diff --git a/src/vtbackend/bench-headless.cpp b/src/vtbackend/bench-headless.cpp index 96add5bd2b..996153b0fb 100644 --- a/src/vtbackend/bench-headless.cpp +++ b/src/vtbackend/bench-headless.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include