Repository :
https://github.com/FarGroup/FarManager
On branch : master
Link :
https://github.com/FarGroup/FarManager/commit/d3ea94b94084930a3664ad6e3b9c270c6985015d
>---------------------------------------------------------------
commit d3ea94b94084930a3664ad6e3b9c270c6985015d
Author: Alex Alabuzhev <
alab...@gmail.com>
Date: Thu Apr 2 20:07:15 2026 +0100
Refactoring
>---------------------------------------------------------------
d3ea94b94084930a3664ad6e3b9c270c6985015d
far/console.cpp | 44 +++++++++----------
far/console.hpp | 4 +-
far/far.vcxproj | 1 +
far/far.vcxproj.filters | 3 ++
far/log.cpp | 57 ++++++++++++++-----------
far/main.cpp | 111 ++++++++++++++++++++++++++++++++++++------------
far/main.hpp | 67 +++++++++++++++++++++++++++++
far/testing.cpp | 2 +
8 files changed, 210 insertions(+), 79 deletions(-)
diff --git a/far/console.cpp b/far/console.cpp
index 3a0f219e9..d452ee5ee 100644
--- a/far/console.cpp
+++ b/far/console.cpp
@@ -47,12 +47,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "encoding.hpp"
#include "char_width.hpp"
#include "log.hpp"
+#include "main.hpp"
// Platform:
#include "platform.version.hpp"
// Common:
-#include "common.hpp"
#include "common/2d/algorithm.hpp"
#include "common/algorithm.hpp"
#include "common/enum_substrings.hpp"
@@ -298,10 +298,11 @@ static short GetDelta(CONSOLE_SCREEN_BUFFER_INFO const& csbi)
namespace console_detail
{
- // пишем/читаем порциями по 32 K, иначе проблемы.
+ // Old Windows versions apparently have some static buffers inside Read|WriteConsoleOutputW.
+ // Going over this limit can produce funny visual artefacts.
const unsigned int MAXSIZE = 32768;
- class external_console
+ class console::external_console
{
public:
NONCOPYABLE(external_console);
@@ -515,9 +516,6 @@ protected:
stream_buffer_overrider m_In, m_Out, m_Err, m_Log;
};
- static nifty_counter::buffer<external_console> Storage;
- static auto& ExternalConsole = reinterpret_cast<external_console&>(Storage);
-
class hide_cursor
{
public:
@@ -728,12 +726,8 @@ protected:
m_StreamBuf(std::make_unique<consolebuf>(GetStdHandle(STD_OUTPUT_HANDLE))),
m_StreamBuffersOverrider(std::make_unique<stream_buffers_overrider>())
{
- placement::construct(ExternalConsole);
- }
-
- console::~console()
- {
- placement::destruct(ExternalConsole);
+ if (get_run_mode() == run_mode::interactive)
+ m_ExternalConsole = std::make_unique<external_console>();
}
bool console::Allocate() const
@@ -1416,11 +1410,11 @@ protected:
bool console::ReadOutput(matrix<FAR_CHAR_INFO>& Buffer, point const BufferCoord, rectangle const& ReadRegionRelative) const
{
- if (ExternalConsole.Imports.pReadOutput)
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pReadOutput)
{
const COORD BufferSize{ static_cast<short>(Buffer.width()), static_cast<short>(Buffer.height()) };
auto ReadRegion = make_rect(ReadRegionRelative);
- return ExternalConsole.Imports.pReadOutput(Buffer.data(), BufferSize, make_coord(BufferCoord), &ReadRegion) != FALSE;
+ return m_ExternalConsole->Imports.pReadOutput(Buffer.data(), BufferSize, make_coord(BufferCoord), &ReadRegion) != FALSE;
}
const int Delta = sWindowMode? GetDelta() : 0;
@@ -2378,11 +2372,11 @@ protected:
return implementation::WriteOutputVT(Buffer, BufferCoord, WriteRegion);
}
- if (ExternalConsole.Imports.pWriteOutput)
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pWriteOutput)
{
const COORD BufferSize{ static_cast<short>(Buffer.width()), static_cast<short>(Buffer.height()) };
auto WriteRegion = make_rect(WriteRegionRelative);
- return ExternalConsole.Imports.pWriteOutput(Buffer.data(), BufferSize, make_coord(BufferCoord), &WriteRegion) != FALSE;
+ return m_ExternalConsole->Imports.pWriteOutput(Buffer.data(), BufferSize, make_coord(BufferCoord), &WriteRegion) != FALSE;
}
const int Delta = sWindowMode? GetDelta() : 0;
@@ -2473,8 +2467,8 @@ protected:
bool console::Commit() const
{
- if (ExternalConsole.Imports.pCommit)
- return ExternalConsole.Imports.pCommit() != FALSE;
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pCommit)
+ return m_ExternalConsole->Imports.pCommit() != FALSE;
// reserved
return true;
@@ -2482,8 +2476,8 @@ protected:
bool console::GetTextAttributes(FarColor& Attributes) const
{
- if (ExternalConsole.Imports.pGetTextAttributes)
- return ExternalConsole.Imports.pGetTextAttributes(&Attributes) != FALSE;
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pGetTextAttributes)
+ return m_ExternalConsole->Imports.pGetTextAttributes(&Attributes) != FALSE;
CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
if (!get_console_screen_buffer_info(GetOutputHandle(), &ConsoleScreenBufferInfo))
@@ -2495,8 +2489,8 @@ protected:
bool console::SetTextAttributes(const FarColor& Attributes) const
{
- if (ExternalConsole.Imports.pSetTextAttributes)
- return ExternalConsole.Imports.pSetTextAttributes(&Attributes) != FALSE;
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pSetTextAttributes)
+ return m_ExternalConsole->Imports.pSetTextAttributes(&Attributes) != FALSE;
return (IsVtActive()? implementation::SetTextAttributesVT : implementation::SetTextAttributesNT)(Attributes);
}
@@ -2764,8 +2758,8 @@ protected:
bool console::ClearExtraRegions(const FarColor& Color, int Mode) const
{
- if (ExternalConsole.Imports.pClearExtraRegions)
- return ExternalConsole.Imports.pClearExtraRegions(&Color, Mode) != FALSE;
+ if (m_ExternalConsole && m_ExternalConsole->Imports.pClearExtraRegions)
+ return m_ExternalConsole->Imports.pClearExtraRegions(&Color, Mode) != FALSE;
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (!get_console_screen_buffer_info(GetOutputHandle(), &csbi))
@@ -3070,7 +3064,7 @@ protected:
bool console::ExternalRendererLoaded() const
{
- return ExternalConsole.Imports.pWriteOutput.operator bool();
+ return m_ExternalConsole && m_ExternalConsole->Imports.pWriteOutput;
}
size_t console::GetWidthPreciseExpensive(string_view const Str)
diff --git a/far/console.hpp b/far/console.hpp
index 2106a2d2f..b1c8371e9 100644
--- a/far/console.hpp
+++ b/far/console.hpp
@@ -69,7 +69,6 @@ namespace console_detail
NONCOPYABLE(console);
console();
- ~console();
bool Allocate() const;
bool Free() const;
@@ -258,6 +257,9 @@ namespace console_detail
os::handle m_WidthTestScreen;
KEY_EVENT_RECORD mutable m_QueuedKeys{};
+
+ class external_console;
+ std::unique_ptr<external_console> m_ExternalConsole;
};
}
diff --git a/far/far.vcxproj b/far/far.vcxproj
index c8ab0594e..4034de77a 100644
--- a/far/far.vcxproj
+++ b/far/far.vcxproj
@@ -380,6 +380,7 @@ cl /nologo /c /Fo"$(IntDir)%(Filename)_c++.testobj" /TP api_test.c
<ClInclude Include="macroapi.hpp" />
<ClInclude Include="macroopcode.hpp" />
<ClInclude Include="macrovalues.hpp" />
+ <ClInclude Include="main.hpp" />
<ClInclude Include="manager.hpp" />
<ClInclude Include="map_file.hpp" />
<ClInclude Include="memcheck.hpp" />
diff --git a/far/far.vcxproj.filters b/far/far.vcxproj.filters
index 0bc25a322..707ae3024 100644
--- a/far/far.vcxproj.filters
+++ b/far/far.vcxproj.filters
@@ -934,6 +934,9 @@
<ClInclude Include="macrovalues.hpp">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="main.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="manager.hpp">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/far/log.cpp b/far/log.cpp
index 8bb363a96..58e3a307f 100644
--- a/far/log.cpp
+++ b/far/log.cpp
@@ -46,6 +46,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "imports.hpp"
#include "interf.hpp"
#include "keyboard.hpp"
+#include "main.hpp"
#include "palette.hpp"
#include "pathmix.hpp"
#include "pipe.hpp"
@@ -230,12 +231,6 @@ namespace
}
};
- template<bool Value>
- struct discardable
- {
- static constexpr auto is_discardable = Value;
- };
-
struct no_config
{
static void configure_impl(string_view)
@@ -243,7 +238,7 @@ namespace
}
};
- class sink_null: public no_config, public discardable<true>
+ class sink_null: public no_config
{
public:
static void handle_impl(message const&)
@@ -253,7 +248,7 @@ namespace
static constexpr auto name = L"null"sv;
};
- class sink_debug: public no_config, public discardable<false>
+ class sink_debug: public no_config
{
public:
void handle_impl(message const& Message) const
@@ -274,7 +269,7 @@ namespace
static constexpr auto name = L"debug"sv;
};
- class sink_console: public discardable<true>
+ class sink_console
{
public:
sink_console()
@@ -282,6 +277,15 @@ namespace
initialize_ui();
}
+ ~sink_console()
+ {
+ if (get_run_mode() != run_mode::interactive)
+ {
+ // Borrowed handle, can't close it
+ m_Buffer.release();
+ }
+ }
+
static void process(HANDLE Buffer, message const& Message)
{
class console_color
@@ -392,6 +396,13 @@ namespace
private:
void initialize_ui()
{
+ if (get_run_mode() != run_mode::interactive)
+ {
+ // No UI, we can use stdout
+ m_Buffer.reset(console.GetOutputHandle());
+ return;
+ }
+
m_Buffer.reset(CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, {}, CONSOLE_TEXTMODE_BUFFER, {}));
if (!m_Buffer)
return;
@@ -406,7 +417,7 @@ namespace
os::handle m_Buffer;
};
- class sink_stdout: public no_config, public discardable<true>
+ class sink_stdout: public no_config
{
public:
void handle_impl(message const& Message)
@@ -424,7 +435,7 @@ namespace
static constexpr auto name = L"stdout"sv;
};
- class sink_file: public no_config, public discardable<false>
+ class sink_file: public no_config
{
public:
explicit sink_file():
@@ -502,7 +513,7 @@ namespace
string_view m_Eol{ eol::win.str() };
};
- class sink_pipe: public no_config, public discardable<true>
+ class sink_pipe: public no_config
{
public:
void handle_impl(message const& Message)
@@ -601,9 +612,8 @@ namespace
class async_impl
{
protected:
- explicit async_impl(std::function<void(message const&)> Out, bool const IsDiscardable, string_view const Name):
+ explicit async_impl(std::function<void(message const&)> Out, string_view const Name):
m_Out(std::move(Out)),
- m_IsDiscardable(IsDiscardable),
m_Thread(&async_impl::poll, this, Name)
{
}
@@ -645,12 +655,7 @@ namespace
return;
for (auto Messages = m_Messages.pop_all(); !Messages.empty(); Messages.pop())
- {
- if (m_IsDiscardable && m_FinishEvent.is_signaled())
- return;
-
m_Out(Messages.front());
- }
}
},
[](DWORD const ExceptionCode)
@@ -663,7 +668,6 @@ namespace
os::event m_MessageEvent { os::event::type::automatic, os::event::state::nonsignaled };
os::event m_FinishEvent { os::event::type::manual, os::event::state::nonsignaled };
std::function<void(message const&)> m_Out;
- bool m_IsDiscardable;
os::thread m_Thread;
};
@@ -679,8 +683,8 @@ namespace
public:
static constexpr auto mode = sink_mode::async;
- explicit async(bool const IsDiscardable):
- synchronized_impl([this](message const& Message){ this->sink_boilerplate<sink_type>::handle_impl(Message); }, IsDiscardable, sink_type::name)
+ explicit async():
+ synchronized_impl([this](message const& Message){ this->sink_boilerplate<sink_type>::handle_impl(Message); }, sink_type::name)
{
}
@@ -711,7 +715,7 @@ namespace
std::unique_ptr<sink> create_sink(string_view SinkName, sink_mode Mode);
- class sink_composite: public discardable<false>
+ class sink_composite
{
public:
sink_composite()
@@ -774,7 +778,7 @@ namespace
if (Mode == sink_mode::sync)
return std::make_unique<sync<T>>();
else
- return std::make_unique<async<T>>(T::is_discardable);
+ return std::make_unique<async<T>>();
}
template<typename... args>
@@ -818,7 +822,10 @@ namespace logging
~engine()
{
- LOGINFO(L"Logger exit"sv);
+ // If it's still incomplete, we haven't logged anything in this session,
+ // so no need to construct the whole thing now just to log the exit.
+ if (m_Status != engine_status::incomplete)
+ LOGINFO(L"Logger exit"sv);
if (m_VectoredHandler && imports.RemoveVectoredExceptionHandler)
imports.RemoveVectoredExceptionHandler(m_VectoredHandler);
diff --git a/far/main.cpp b/far/main.cpp
index fa0ed74ff..6a5733976 100644
--- a/far/main.cpp
+++ b/far/main.cpp
@@ -1,5 +1,4 @@
-// validator: no-self-include
-/*
+/*
main.cpp
Функция main.
@@ -35,6 +34,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// BUGBUG
#include "platform.headers.hpp"
+// Self:
+#include "main.hpp"
+
// Internal:
#include "keys.hpp"
#include "farcolor.hpp"
@@ -78,7 +80,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "platform.debug.hpp"
#include "platform.env.hpp"
#include "platform.memory.hpp"
-#include "platform.process.hpp"
#include "platform.security.hpp"
// Common:
@@ -448,7 +449,7 @@ static void ShowVersion(bool const Direct)
std::endl;
}
-static std::optional<int> ProcessServiceModes(std::span<const wchar_t* const> const Args)
+static run_mode get_run_mode_impl(std::span<const wchar_t* const> const Args)
{
const auto isArg = [&](string_view const Name)
{
@@ -456,43 +457,95 @@ static std::optional<int> ProcessServiceModes(std::span<const wchar_t* const> co
};
if (Args.size() == 4 && IsElevationArgument(Args[0])) // /service:elevation {UUID} PID UsePrivileges
- {
- return ElevationMain(Args[1], from_string<DWORD>(Args[2]), *Args[3] == L'1');
- }
+ return run_mode::elevation;
if (in_closed_range(2u, Args.size(), 5u) && (isArg(L"export"sv) || isArg(L"import"sv)))
- {
- const auto Export = isArg(L"export"sv);
- string strProfilePath(Args.size() > 2? Args[2] : L""sv), strLocalProfilePath(Args.size() > 3 ? Args[3] : L""), strTemplatePath(Args.size() > 4 ? Args[4] : L"");
- InitTemplateProfile(strTemplatePath);
- InitProfile(strProfilePath, strLocalProfilePath);
- Global->m_ConfigProvider = std::make_unique<config_provider>(Export? config_provider::mode::m_export : config_provider::mode::m_import);
- ConfigProvider().ServiceMode(Args[1]);
- return EXIT_SUCCESS;
- }
+ return isArg(L"export"sv)? run_mode::config_export : run_mode::config_import;
if (in_closed_range(1u, Args.size(), 3u) && isArg(L"clearcache"sv))
- {
- string strProfilePath(Args.size() > 1? Args[1] : L""sv);
- string strLocalProfilePath(Args.size() > 2? Args[2] : L""sv);
- InitProfile(strProfilePath, strLocalProfilePath);
- (void)config_provider{config_provider::clear_cache{}};
- return EXIT_SUCCESS;
- }
+ return run_mode::clear_cache;
if (Args.size() == 2 && logging::is_log_argument(Args[0]))
+ return run_mode::logger;
+
+ if (Args.size() == 1 && (isArg(L"?") || isArg(L"h")))
+ return run_mode::help;
+
+ return run_mode::interactive;
+}
+
+static run_mode s_RunMode = run_mode::unknown;
+
+static run_mode get_run_mode(std::span<const wchar_t* const> const Args)
+{
+ if (s_RunMode == run_mode::unknown)
+ s_RunMode = get_run_mode_impl(Args);
+
+ return s_RunMode;
+}
+
+run_mode get_run_mode()
+{
+ if (s_RunMode == run_mode::unknown)
{
- return logging::main(Args[1]);
+ int Argc = 0;
+ os::memory::local::ptr<wchar_t const* const> const Argv(CommandLineToArgvW(GetCommandLine(), &Argc));
+ std::span const AllArgs(Argv.get(), Argc), Args(AllArgs.subspan(1));
+
+ s_RunMode = get_run_mode_impl(Args);
}
- if (Args.size() == 1 && (isArg(L"?") || isArg(L"h")))
+ return s_RunMode;
+}
+
+#ifdef ENABLE_TESTS
+void set_test_mode()
+{
+ s_RunMode = run_mode::tests;
+}
+#endif
+
+static std::optional<int> ProcessServiceModes(run_mode const RunMode, std::span<const wchar_t* const> const Args)
+{
+ switch (RunMode)
{
+ case run_mode::interactive:
+ return {};
+
+ case run_mode::elevation:
+ return ElevationMain(Args[1], from_string<DWORD>(Args[2]), *Args[3] == L'1'); // /service:elevation {UUID} PID UsePrivileges
+
+ case run_mode::config_import:
+ case run_mode::config_export:
+ {
+ string strProfilePath(Args.size() > 2? Args[2] : L""sv), strLocalProfilePath(Args.size() > 3 ? Args[3] : L""), strTemplatePath(Args.size() > 4 ? Args[4] : L"");
+ InitTemplateProfile(strTemplatePath);
+ InitProfile(strProfilePath, strLocalProfilePath);
+ Global->m_ConfigProvider = std::make_unique<config_provider>(RunMode == run_mode::config_export? config_provider::mode::m_export : config_provider::mode::m_import);
+ ConfigProvider().ServiceMode(Args[1]);
+ return EXIT_SUCCESS;
+ }
+
+ case run_mode::clear_cache:
+ {
+ string strProfilePath(Args.size() > 1? Args[1] : L""sv);
+ string strLocalProfilePath(Args.size() > 2? Args[2] : L""sv);
+ InitProfile(strProfilePath, strLocalProfilePath);
+ (void)config_provider{config_provider::clear_cache{}};
+ return EXIT_SUCCESS;
+ }
+
+ case run_mode::logger:
+ return logging::main(Args[1]);
+
+ case run_mode::help:
ShowVersion(true);
show_help();
return EXIT_SUCCESS;
- }
- return {};
+ default:
+ std::unreachable();
+ }
}
#ifdef _M_IX86
@@ -811,6 +864,8 @@ static int mainImpl(std::span<const wchar_t* const> const Args)
{
setlocale(LC_ALL, "");
+ const auto RunMode = get_run_mode(Args);
+
if (FarColor InitAttributes; console.GetTextAttributes(InitAttributes))
colors::store_default_color(InitAttributes);
@@ -847,7 +902,7 @@ static int mainImpl(std::span<const wchar_t* const> const Args)
else
os::env::del(FarAdminMode);
- if (const auto Result = ProcessServiceModes(Args))
+ if (const auto Result = ProcessServiceModes(RunMode, Args))
return *Result;
SCOPED_ACTION(listener)(update_environment, [] { if (Global->Opt->UpdateEnvironment) ReloadEnvironment(); });
diff --git a/far/main.hpp b/far/main.hpp
new file mode 100644
index 000000000..5cb2b4029
--- /dev/null
+++ b/far/main.hpp
@@ -0,0 +1,67 @@
+#ifndef MAIN_HPP_E29E6548_A41A_4F7F_9E57_356BE15FC9A7
+#define MAIN_HPP_E29E6548_A41A_4F7F_9E57_356BE15FC9A7
+#pragma once
+
+/*
+main.hpp
+
+*/
+/*
+Copyright © 2026 Far Group
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// Internal:
+
+// Platform:
+
+// Common:
+
+// External:
+
+//----------------------------------------------------------------------------
+
+enum class run_mode
+{
+ unknown,
+ interactive,
+ elevation,
+ config_import,
+ config_export,
+ clear_cache,
+ logger,
+ help,
+#ifdef ENABLE_TESTS
+ tests,
+#endif
+};
+
+run_mode get_run_mode();
+
+#ifdef ENABLE_TESTS
+void set_test_mode();
+#endif
+
+#endif // MAIN_HPP_E29E6548_A41A_4F7F_9E57_356BE15FC9A7
diff --git a/far/testing.cpp b/far/testing.cpp
index d0fcb3499..86861cd22 100644
--- a/far/testing.cpp
+++ b/far/testing.cpp
@@ -43,6 +43,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "components.hpp"
#include "locale.hpp"
#include "log.hpp"
+#include "main.hpp"
// Platform:
@@ -95,6 +96,7 @@ static bool s_IsTestRun;
static int run_tests(std::span<wchar_t const* const> const Args)
{
s_IsTestRun = true;
+ set_test_mode();
return Catch::Session().run(static_cast<int>(Args.size()), Args.data());
}