From 73743039b1531915268aba03d3a5c5880d988ce9 Mon Sep 17 00:00:00 2001 From: Alex Alabuzhev Date: Thu, 18 Jul 2024 01:24:42 +0100 Subject: [PATCH] Use console pages (DECCRA) to preserve content, not supported by ReadConsoleOutput See microsoft/terminal#10810 for details --- far/cmdline.cpp | 7 ++-- far/colormix.cpp | 2 + far/console.cpp | 81 ++++++++++++++++++++++++++++++++++++++++- far/console.hpp | 3 ++ far/console_session.cpp | 14 +++---- far/ctrlobj.cpp | 30 --------------- far/ctrlobj.hpp | 1 - far/execute.cpp | 1 + far/far.natvis | 36 ++++++++---------- far/grabber.cpp | 1 + far/interf.cpp | 7 ---- far/interf.hpp | 1 - far/main.cpp | 25 ++++++++++++- far/plugin.hpp | 3 ++ far/scrbuf.cpp | 55 +++++----------------------- far/scrbuf.hpp | 1 - 16 files changed, 148 insertions(+), 120 deletions(-) diff --git a/far/cmdline.cpp b/far/cmdline.cpp index a4c2d39d3e..bd73663050 100644 --- a/far/cmdline.cpp +++ b/far/cmdline.cpp @@ -1046,9 +1046,10 @@ void CommandLine::ExecString(execute_info& Info) ExecutionContext->DrawCommand(Info.DisplayCommand.empty()? Info.Command : Info.DisplayCommand); ExecutionContext->DoPrologue(); + ExecutionContext->Consolise(); - if (DoConsolise) - ExecutionContext->Consolise(); + if (Info.Echo) + std::wcout << std::endl; }; if (Info.Command.empty()) @@ -1286,7 +1287,7 @@ bool CommandLine::ProcessOSCommands(string_view const CmdLine, function_ref ForeignBlocks; + std::optional ForeignBlock; + + const auto queue_block = [&] + { + if (!ForeignBlock) + return; + + for (auto& Block: ForeignBlocks) + { + if ( + Block.left == ForeignBlock->left && + Block.right == ForeignBlock->right && + Block.bottom == ForeignBlock->top - 1 + ) + { + Block.bottom = ForeignBlock->bottom; + ForeignBlock.reset(); + return; + } + } + + ForeignBlocks.emplace_back(*ForeignBlock); + ForeignBlock.reset(); + }; + for (const auto i: std::views::iota(SubRect.top + SubrectOffset, std::min(SubRect.top + SubrectOffset + ViewportSize.y, SubRect.bottom + 1))) { if (i != SubRect.top + SubrectOffset) @@ -1653,7 +1679,8 @@ namespace console_detail LastColor = colors::default_color(); } - make_vt_sequence(Buffer[i].subspan(SubRect.left, SubRect.width()), Str, LastColor); + const auto BlockRow = Buffer[i].subspan(SubRect.left, SubRect.width()); + make_vt_sequence(BlockRow, Str, LastColor); if (SubRect.right == ScrX && i != ScrY) { @@ -1663,13 +1690,40 @@ namespace console_detail // Surprisingly, it also fixes terminal#15153. Str += L'\n'; } + + for (const auto& Cell: BlockRow) + { + const auto CellIndex = &Cell - BlockRow.data(); + + if ( + Cell.Attributes.Flags & FCF_FOREIGN && + colors::is_transparent(Cell.Attributes.ForegroundColor) && + colors::is_transparent(Cell.Attributes.BackgroundColor) + ) + { + if (!ForeignBlock) + ForeignBlock.emplace(SubRect.left + CellIndex, i, SubRect.left + CellIndex, i); + else + ++ForeignBlock->right; + + continue; + } + + queue_block(); + } + + queue_block(); } + queue_block(); + if (!::console.Write(Str)) return false; - Str.clear(); + for (const auto& Block: ForeignBlocks) + ::console.unstash_output(Block); + Str.clear(); } return ::console.Write(CSI L"m"sv); @@ -2661,6 +2715,29 @@ namespace console_detail send_vt_command(far::format(OSC(L"9;4;{};{}"), state_to_vt(State), Percent)); } +// I'd prefer a more obscure number, but looks like only 1-6 are supported +#define SERVICE_PAGE_NUMBER "3" + + void console::stash_output() const + { + send_vt_command(CSI L";;;;1;;;" SERVICE_PAGE_NUMBER "$v"sv); + } + + void console::unstash_output(rectangle const Coordinates) const + { + send_vt_command(far::format( + CSI L"{};{};{};{};" SERVICE_PAGE_NUMBER ";{};{};1$v"sv, + 1 + Coordinates.top, + 1 + Coordinates.left, + 1 + Coordinates.bottom, + 1 + Coordinates.right, + 1 + Coordinates.top, + 1 + Coordinates.left + )); + } + +#undef SERVICE_PAGE_NUMBER + bool console::GetCursorRealPosition(point& Position) const { CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; diff --git a/far/console.hpp b/far/console.hpp index aebeb55797..d9813b8abe 100644 --- a/far/console.hpp +++ b/far/console.hpp @@ -199,6 +199,9 @@ namespace console_detail void set_progress_state(TBPFLAG State) const; void set_progress_value(TBPFLAG State, size_t Percent) const; + void stash_output() const; + void unstash_output(rectangle Coordinates) const; + [[nodiscard]] short GetDelta() const; diff --git a/far/console_session.cpp b/far/console_session.cpp index 90c4d71bf8..c1dd5ac6aa 100644 --- a/far/console_session.cpp +++ b/far/console_session.cpp @@ -88,10 +88,8 @@ class context final: noncopyable, public i_context void DrawCommand(string_view const Command) override { Global->CtrlObject->CmdLine()->DrawFakeCommand(Command); - ScrollScreen(1); m_Command = Command; - m_ShowCommand = true; } void Consolise(bool SetTextColour) override @@ -155,7 +153,8 @@ class context final: noncopyable, public i_context if (Scroll && DoWeReallyHaveToScroll(Global->Opt->ShowKeyBar? 3 : 2)) { - ScrollScreen(1); + std::wcout << std::endl; + Global->ScrBuf->FillBuf(); } console.ResetViewportPosition(); @@ -173,7 +172,6 @@ class context final: noncopyable, public i_context private: string m_Command; - bool m_ShowCommand{}; bool m_Activated{}; bool m_Finalised{}; bool m_Consolised{}; @@ -210,13 +208,13 @@ void console_session::LeavePluginContext(bool Scroll) // FCTL_SETUSERSCREEN without corresponding FCTL_GETUSERSCREEN // Old (1.x) behaviour emulation: if (Global->Opt->ShowKeyBar) - { + std::wcout << L'\n'; + + if (Scroll) std::wcout << L'\n'; - } + std::wcout.flush(); Global->ScrBuf->FillBuf(); - if (Scroll) - ScrollScreen(1); Global->WindowManager->Desktop()->TakeSnapshot(); } diff --git a/far/ctrlobj.cpp b/far/ctrlobj.cpp index a98e970360..208df28d0b 100644 --- a/far/ctrlobj.cpp +++ b/far/ctrlobj.cpp @@ -66,12 +66,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ControlObject::ControlObject() { - SetColor(COL_COMMANDLINEUSERSCREEN); - GotoXY(0, ScrY - 3); - ShowVersion(false); - GotoXY(0, ScrY - 2); - MoveCursor({ 0, ScrY - 1 }); - Global->WindowManager->InitDesktop(); filters::InitFilters(); @@ -144,30 +138,6 @@ void ControlObject::close() Plugins->UnloadPlugins(); } - -void ControlObject::ShowVersion(bool const Direct) -{ - if (Direct) - { - std::wcout << build::version_string() << L'\n' << build::copyright() << L'\n' << std::endl; - return; - } - - point Size; - console.GetSize(Size); - point CursorPosition; - console.GetCursorPosition(CursorPosition); - const auto FreeSpace = Size.y - CursorPosition.y - 1; - - if (FreeSpace < 5 && DoWeReallyHaveToScroll(5)) - ScrollScreen(5-FreeSpace); - - GotoXY(0,ScrY-4); - Text(build::version_string()); - GotoXY(0,ScrY-3); - Text(build::copyright()); -} - FilePanels* ControlObject::Cp() const { return FPanels.get(); diff --git a/far/ctrlobj.hpp b/far/ctrlobj.hpp index 80047e12b5..ff743261e3 100644 --- a/far/ctrlobj.hpp +++ b/far/ctrlobj.hpp @@ -72,7 +72,6 @@ class ControlObject: noncopyable FilePanels *Cp() const; window_ptr Panels() const; void CreateDummyFilePanels(); - static void ShowVersion(bool Direct = true); KeyMacro Macro; std::unique_ptr HiFiles; diff --git a/far/execute.cpp b/far/execute.cpp index cb87d6f600..123226d0f5 100644 --- a/far/execute.cpp +++ b/far/execute.cpp @@ -476,6 +476,7 @@ static void detach(point const& ConsoleSize, rectangle const& ConsoleWindowRect) console.Allocate(); InitConsole(); + Global->ScrBuf->FillBuf(); console.SetSize(ConsoleSize); console.SetWindowRect(ConsoleWindowRect); diff --git a/far/far.natvis b/far/far.natvis index 5c8c4e29e5..4a819ca154 100644 --- a/far/far.natvis +++ b/far/far.natvis @@ -87,34 +87,28 @@ - - - - - empty - size={size()}, dynamic={dynamic()} + - size() - dynamic() - - size() - data() - + m_Buffer._Head + m_Buffer._Tail._Head + + static + dynamic + + m_Size - - - + - - {(array_ptr<char,$T2>&)*this} - - (array_ptr<char,$T2>&)*this - + *($T1*)m_Buffer._Head._Elems + *($T1*)m_Buffer._Tail._Head._Mypair._Myval2 + + static + dynamic - ($T1*)data() + m_Size diff --git a/far/grabber.cpp b/far/grabber.cpp index 469af48056..2e15a03433 100644 --- a/far/grabber.cpp +++ b/far/grabber.cpp @@ -299,6 +299,7 @@ void Grabber::DisplayObject() colors::make_invert(Destination.ForegroundColor, Destination.IsFgIndex()); colors::make_invert(Destination.BackgroundColor, Destination.IsBgIndex()); colors::make_invert(Destination.UnderlineColor, Destination.IsUnderlineIndex()); + flags::clear(Destination.Flags, FCF_FOREIGN); } } diff --git a/far/interf.cpp b/far/interf.cpp index 8db1c52925..bb392d0d64 100644 --- a/far/interf.cpp +++ b/far/interf.cpp @@ -392,7 +392,6 @@ void InitConsole() SetPalette(); UpdateScreenSize(); - Global->ScrBuf->FillBuf(); consoleicons::instance().update_icon(); } @@ -1197,12 +1196,6 @@ const FarColor& GetColor() return CurColor; } - -void ScrollScreen(int Count) -{ - Global->ScrBuf->Scroll(Count); -} - bool DoWeReallyHaveToScroll(short Rows) { /* diff --git a/far/interf.hpp b/far/interf.hpp index a223760402..e4d6b5d8bb 100644 --- a/far/interf.hpp +++ b/far/interf.hpp @@ -158,7 +158,6 @@ void ShowCursor(); void SetInitialCursorType(); void GetCursorType(bool& Visible, size_t& Size); void MoveRealCursor(int X,int Y); -void ScrollScreen(int Count); bool DoWeReallyHaveToScroll(short Rows); struct position_parser_state diff --git a/far/main.cpp b/far/main.cpp index 936faab408..28ab24ccb3 100644 --- a/far/main.cpp +++ b/far/main.cpp @@ -417,6 +417,26 @@ static bool is_arg(string_view const Str) return !Str.empty() && any_of(Str.front(), L'-', L'/'); } +static void ShowVersion(bool const Direct) +{ + bool EnoughSpace{}; + + if (!Direct) + { + // Version, copyright, empty line, command line, keybar + if (const auto SpaceNeeded = 5; !DoWeReallyHaveToScroll(SpaceNeeded)) + { + EnoughSpace = true; + console.SetCursorPosition({ 0, ScrY - (SpaceNeeded - 1) }); + } + } + + std::wcout << + build::version_string() << L'\n' << + build::copyright() << L"\n\n"sv.substr(Direct || EnoughSpace? 1 : 0) << + std::endl; +} + static std::optional ProcessServiceModes(std::span const Args) { const auto isArg = [&](string_view const Name) @@ -456,7 +476,7 @@ static std::optional ProcessServiceModes(std::span co if (Args.size() == 1 && (isArg(L"?") || isArg(L"h"))) { - ControlObject::ShowVersion(); + ShowVersion(true); show_help(); return EXIT_SUCCESS; } @@ -887,6 +907,9 @@ static int mainImpl(std::span const Args) } InitConsole(); + Global->ScrBuf->FillBuf(); + ShowVersion(false); + Global->ScrBuf->FillBuf(); SCOPE_EXIT { diff --git a/far/plugin.hpp b/far/plugin.hpp index 6322867e62..3938902d73 100644 --- a/far/plugin.hpp +++ b/far/plugin.hpp @@ -119,6 +119,9 @@ FAR_INLINE_CONSTANT FARCOLORFLAGS FCF_INHERIT_STYLE = 0x0000000000000004ULL, FCF_RAWATTR_MASK = 0x000000000000FF00ULL, // stored console attributes +#ifdef FAR_USE_INTERNALS + FCF_FOREIGN = 0x0000000000010000ULL, +#endif // END FAR_USE_INTERNALS FCF_FG_BOLD = 0x1000000000000000ULL, FCF_FG_ITALIC = 0x2000000000000000ULL, diff --git a/far/scrbuf.cpp b/far/scrbuf.cpp index a1849586c9..79e5016586 100644 --- a/far/scrbuf.cpp +++ b/far/scrbuf.cpp @@ -152,6 +152,16 @@ void ScreenBuf::FillBuf() rectangle const ReadRegion{ 0, 0, static_cast(Buf.width() - 1), static_cast(Buf.height() - 1) }; console.ReadOutput(Buf, ReadRegion); + + for (auto& i: Buf.vector()) + { + colors::make_transparent(i.Attributes.ForegroundColor); + colors::make_transparent(i.Attributes.BackgroundColor); + i.Attributes.Flags |= FCF_FOREIGN; + } + + console.stash_output(); + Shadow = Buf; point CursorPosition; console.GetCursorPosition(CursorPosition); @@ -815,51 +825,6 @@ void ScreenBuf::RestoreElevationChar() } } -// проскроллировать буфер вверх. -void ScreenBuf::Scroll(size_t Count) -{ - assert(Count); - - SCOPED_ACTION(std::scoped_lock)(CS); - - const FAR_CHAR_INFO Fill{ L' ', {}, {}, colors::PaletteColorToFarColor(COL_COMMANDLINEUSERSCREEN) }; - - if (Global->Opt->WindowMode) - { - if (console.IsScrollbackPresent()) - { - rectangle Region{ 0, 0, ScrX, static_cast(Count - 1) }; - - // TODO: matrix_view to avoid copying - matrix BufferBlock(Count, ScrX + 1); - Read(Region, BufferBlock); - - console.ScrollNonClientArea(Count, Fill); - - Region.top = -static_cast(Count); - Region.bottom = -1; - console.WriteOutput(BufferBlock, Region); - } - else - { - // Even if there's no scrollback there might be the right area - console.ScrollNonClientArea(Count, Fill); - } - } - - if (Count && Count < Buf.height()) - { - auto& RawBuf = Buf.vector(); - const auto size = RawBuf.size(); - RawBuf.erase(RawBuf.begin(), RawBuf.begin() + Count * Buf.width()); - RawBuf.resize(size, Fill); - - SBFlags.Clear(SBFLAGS_FLUSHED); - } - - debug_flush(); -} - void ScreenBuf::SetClearTypeFix(int const ClearTypeFix) { m_ClearTypeFix = ClearTypeFix; diff --git a/far/scrbuf.hpp b/far/scrbuf.hpp index bfe71db13a..4a52279381 100644 --- a/far/scrbuf.hpp +++ b/far/scrbuf.hpp @@ -92,7 +92,6 @@ class ScreenBuf: noncopyable void ApplyColor(rectangle Where, const FarColor& Color); void FillRect(rectangle Where, const FAR_CHAR_INFO& Info); - void Scroll(size_t Count); void Invalidate(flush_type FlushType = flush_type::all); void Flush(flush_type FlushType = flush_type::all);