From 4980efa9b7b7ab2f794b4f396655a0c2d71d7e75 Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 11 Aug 2026 14:53:16 +0200 Subject: [PATCH] refactor(subsystems&ime): RAII lifecycle --- src/common/assert.cpp | 2 +- src/common/commonSubsystem.cpp | 21 -- src/common/commonSubsystem.h | 12 - src/common/emulatorConfig.cpp | 8 +- src/common/emulatorConfig.h | 10 +- src/common/logging/log.cpp | 10 +- src/common/logging/log.h | 11 +- src/common/profiler.cpp | 19 +- src/common/profiler.h | 11 +- src/common/subsystems.cpp | 239 ++------------ src/common/subsystems.h | 111 +++---- src/common/threads.cpp | 6 +- src/common/threads.h | 3 +- src/emulator.cpp | 54 +-- src/graphics/presentation/imeOverlay.cpp | 142 ++------ .../presentation/window/swapchain.cpp | 1 - .../presentation/window/vulkanWindow.cpp | 1 - src/kernel/fileSystem.cpp | 14 +- src/kernel/fileSystem.h | 12 +- src/kernel/memory.cpp | 6 +- src/kernel/memory.h | 10 +- src/kernel/pthread.cpp | 6 +- src/kernel/pthread.h | 8 +- src/libs/agc.cpp | 6 +- src/libs/agc.h | 10 +- src/libs/audio.cpp | 24 +- src/libs/audio.h | 11 +- src/libs/controller.cpp | 9 +- src/libs/controller.h | 11 +- src/libs/ime.cpp | 311 +++++------------- src/libs/ime.h | 101 +----- src/libs/imeCommon.cpp | 259 +++++++++++++++ src/libs/imeCommon.h | 159 +++++++++ src/libs/imeDialog.cpp | 224 ++----------- src/libs/imeDialog.h | 124 ++----- src/libs/network.cpp | 9 +- src/libs/network.h | 11 +- src/loader/timer.cpp | 9 - src/loader/timer.h | 9 +- src/main.cpp | 24 +- tests/ShaderRecompilerComputeTests.cpp | 10 +- tests/VirtualMemoryAllocationTests.cpp | 26 +- tests/shaderCfgTests.cpp | 8 +- 43 files changed, 816 insertions(+), 1256 deletions(-) delete mode 100644 src/common/commonSubsystem.cpp delete mode 100644 src/common/commonSubsystem.h create mode 100644 src/libs/imeCommon.cpp create mode 100644 src/libs/imeCommon.h diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 8d466f8..c5329d9 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -35,7 +35,7 @@ static std::string BuildFatalReport(const char* title, std::string_view text, co static int DbgReport(const char* title, std::string_view text, const char* file, int line) { Log::WriteFatal(BuildFatalReport(title, text, file, line)); - SubsystemsListSingleton::Instance()->ShutdownAll(); + Subsystems::EmergencyShutdownActive(); return 1; } diff --git a/src/common/commonSubsystem.cpp b/src/common/commonSubsystem.cpp deleted file mode 100644 index 1733c44..0000000 --- a/src/common/commonSubsystem.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "common/commonSubsystem.h" - -#include "common/byteBuffer.h" // IWYU pragma: associated -#include "common/common.h" // IWYU pragma: associated -#include "common/debug.h" // IWYU pragma: associated -#include "common/hash.h" // IWYU pragma: associated -#include "common/magicEnum.h" // IWYU pragma: associated -#include "common/singleton.h" // IWYU pragma: associated -#include "common/virtualMemory.h" - -namespace Common { - -KYTY_SUBSYSTEM_INIT(Common) { - VirtualMemory::Init(); -} - -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Common) {} - -KYTY_SUBSYSTEM_DESTROY(Common) {} - -} // namespace Common diff --git a/src/common/commonSubsystem.h b/src/common/commonSubsystem.h deleted file mode 100644 index a774161..0000000 --- a/src/common/commonSubsystem.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef KYTY_COMMON_COMMON_SUBSYSTEM_H_ -#define KYTY_COMMON_COMMON_SUBSYSTEM_H_ - -#include "common/subsystems.h" - -namespace Common { - -KYTY_SUBSYSTEM_DEFINE(Common); - -} // namespace Common - -#endif /* KYTY_COMMON_COMMON_SUBSYSTEM_H_ */ diff --git a/src/common/emulatorConfig.cpp b/src/common/emulatorConfig.cpp index 7f115da..fb79c79 100644 --- a/src/common/emulatorConfig.cpp +++ b/src/common/emulatorConfig.cpp @@ -9,15 +9,15 @@ namespace Config { static std::unique_ptr g_config; -KYTY_SUBSYSTEM_INIT(Config) { +void Initialize() { EXIT_IF(g_config != nullptr); g_config = std::make_unique(); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {} - -KYTY_SUBSYSTEM_DESTROY(Config) {} +void Shutdown() { + g_config.reset(); +} void Load(const ConfigOptions& cfg) { EXIT_IF(g_config == nullptr); diff --git a/src/common/emulatorConfig.h b/src/common/emulatorConfig.h index 241c2ee..77e3926 100644 --- a/src/common/emulatorConfig.h +++ b/src/common/emulatorConfig.h @@ -2,7 +2,6 @@ #define KYTY_COMMON_EMULATOR_CONFIG_H_ #include "common/common.h" -#include "common/subsystems.h" #include #include @@ -10,7 +9,14 @@ namespace Config { -KYTY_SUBSYSTEM_DEFINE(Config); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Config"; + static constexpr auto initialize = Config::Initialize; + static constexpr auto shutdown = Config::Shutdown; +}; enum class ShaderOptimizationType { None, Size, Performance }; diff --git a/src/common/logging/log.cpp b/src/common/logging/log.cpp index 27bbe48..a44a6ee 100644 --- a/src/common/logging/log.cpp +++ b/src/common/logging/log.cpp @@ -149,7 +149,7 @@ void WriteFatal(fmt::text_style style, std::string_view text) { Flush(); } -KYTY_SUBSYSTEM_INIT(Log) { +void Initialize() { g_initialized = true; switch (Config::GetPrintfDirection()) { case Config::OutputDirection::Silent: g_direction = Direction::Silent; break; @@ -161,13 +161,7 @@ KYTY_SUBSYSTEM_INIT(Log) { SetupLogger(); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Log) { - Flush(); - std::lock_guard lock(g_logger_mutex); - g_logger.reset(); -} - -KYTY_SUBSYSTEM_DESTROY(Log) { +void Shutdown() { Flush(); std::lock_guard lock(g_logger_mutex); g_logger.reset(); diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 3ce29cf..bbe2d2c 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -2,7 +2,6 @@ #define KYTY_COMMON_LOGGING_LOG_H_ #include "common/common.h" -#include "common/subsystems.h" #include #include @@ -10,7 +9,15 @@ namespace Log { -KYTY_SUBSYSTEM_DEFINE(Log); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Log"; + static constexpr auto initialize = Log::Initialize; + static constexpr auto shutdown = Log::Shutdown; + static constexpr auto emergency_shutdown = Log::Shutdown; +}; enum class Direction { Silent, Console, File }; diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index d061c46..11a08f4 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp @@ -1,7 +1,6 @@ #include "common/profiler.h" #include "common/emulatorConfig.h" -#include "common/subsystems.h" #include #include @@ -55,13 +54,7 @@ void SetThreadName(const char* name) { } } -void Close() { - if (tracy::ProfilerAvailable()) { - tracy::ShutdownProfiler(); - } -} - -KYTY_SUBSYSTEM_INIT(Profiler) { +void Initialize() { switch (Config::GetProfilerDirection()) { case Config::ProfilerDirection::Network: if (!tracy::ProfilerAvailable()) { @@ -78,12 +71,10 @@ KYTY_SUBSYSTEM_INIT(Profiler) { } } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Profiler) { - Close(); -} - -KYTY_SUBSYSTEM_DESTROY(Profiler) { - Close(); +void Shutdown() { + if (tracy::ProfilerAvailable()) { + tracy::ShutdownProfiler(); + } } } // namespace Profiler diff --git a/src/common/profiler.h b/src/common/profiler.h index a84e308..1ebbb5e 100644 --- a/src/common/profiler.h +++ b/src/common/profiler.h @@ -2,7 +2,6 @@ #define KYTY_COMMON_PROFILER_H_ #include "common/common.h" -#include "common/subsystems.h" #include #include @@ -42,7 +41,15 @@ private: void EndBlock(); void SetThreadName(const char* name); -KYTY_SUBSYSTEM_DEFINE(Profiler); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Profiler"; + static constexpr auto initialize = Profiler::Initialize; + static constexpr auto shutdown = Profiler::Shutdown; + static constexpr auto emergency_shutdown = Profiler::Shutdown; +}; } // namespace Profiler diff --git a/src/common/subsystems.cpp b/src/common/subsystems.cpp index 6b0d009..5263b7c 100644 --- a/src/common/subsystems.cpp +++ b/src/common/subsystems.cpp @@ -1,232 +1,43 @@ #include "common/subsystems.h" -#include "common/assert.h" - -#include -#include -#include -#include -#include - namespace Common { -class SubsystemPrivate { -public: - SubsystemPrivate() = default; - virtual ~SubsystemPrivate() = default; +static Subsystems* g_active_subsystems = nullptr; - KYTY_CLASS_NO_COPY(SubsystemPrivate); +Subsystems::Subsystems(bool print): m_print(print) { + m_active.reserve(16); + g_active_subsystems = this; +} - bool failed {false}; - std::string fail_msg; -}; - -class SubsystemsListPrivate { -public: - explicit SubsystemsListPrivate(SubsystemsList& p): parent(p) {} - - virtual ~SubsystemsListPrivate() = default; - - void SetArgs(int argc, char** argv) { - this->m_argc = argc; - this->m_argv = argv; +Subsystems::~Subsystems() { + Destroy(); + if (g_active_subsystems == this) { + g_active_subsystems = nullptr; } +} - void Add(Subsystem* s, std::initializer_list deps) { - EXIT_IF(!s); - - const char* name = s->Id(); - - EXIT_IF(FindByName(name) != nullptr); - - auto nl = std::make_unique(); - - nl->s = s; - nl->name = name; - nl->deps = nullptr; - nl->next = std::move(list); - nl->prev_init = nullptr; - - auto* node = nl.get(); - list = std::move(nl); - - for (auto* dep: deps) { - const char* str = dep->Id(); - - auto l = std::make_unique(); - l->dep_name = str; - l->next = std::move(node->deps); - - node->deps = std::move(l); +void Subsystems::Destroy() { + for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) { + if (it->shutdown != nullptr) { + it->shutdown(); } - - node->initialized = false; } + m_active.clear(); +} - bool InitAll(bool print_msg) { - while (SubsListStruct* n = FindNextToInitialize()) { - n->s->Init(&parent); - - if (n->s->m_p->failed) { - fail_msg = n->s->m_p->fail_msg.c_str(); - fail_name = n->name; - return false; - } - - if (print_msg) { - printf("Initialized: %s\n", n->name); - } - - n->initialized = true; - - SubsListStruct* last = last_init; - last_init = n; - n->prev_init = last; +void Subsystems::EmergencyShutdown() { + for (auto it = m_active.rbegin(); it != m_active.rend(); ++it) { + if (it->emergency_shutdown != nullptr) { + it->emergency_shutdown(); } - - return true; } + m_active.clear(); +} - void DestroyAll(bool print_msg) { - for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) { - n->s->Destroy(&parent); - n->initialized = false; - - if (print_msg) { - printf("Destroyed: %s\n", n->name); - } - } - - last_init = nullptr; +void Subsystems::EmergencyShutdownActive() { + if (g_active_subsystems != nullptr) { + g_active_subsystems->EmergencyShutdown(); } - - void ShutdownAll() { - for (SubsListStruct* n = last_init; n != nullptr; n = n->prev_init) { - n->s->UnexpectedShutdown(&parent); - n->initialized = false; - } - - last_init = nullptr; - } - - struct DepsListStruct { - const char* dep_name; - std::unique_ptr next; - }; - - struct SubsListStruct { - Subsystem* s; - const char* name; - std::unique_ptr deps; - std::unique_ptr next; - SubsListStruct* prev_init; - bool initialized; - }; - - [[nodiscard]] SubsListStruct* FindByName(const char* name) const { - for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) { - if (std::strcmp(n->name, name) == 0) { - return n; - } - } - - return nullptr; - } - - [[nodiscard]] SubsListStruct* FindNextToInitialize() const { - for (SubsListStruct* n = list.get(); n != nullptr; n = n->next.get()) { - if (n->initialized) { - continue; - } - - DepsListStruct* d = n->deps.get(); - for (; d != nullptr; d = d->next.get()) { - SubsListStruct* s = FindByName(d->dep_name); - if ((s == nullptr) || !s->initialized) { - break; - } - } - - if (d == nullptr) { - return n; - } - } - - return nullptr; - } - - KYTY_CLASS_NO_COPY(SubsystemsListPrivate); - - std::unique_ptr list; - SubsListStruct* last_init = nullptr; - int m_argc = 0; - char** m_argv = nullptr; - const char* fail_msg = nullptr; - const char* fail_name = nullptr; - SubsystemsList& parent; -}; - -SubsystemsList::SubsystemsList(): m_p(std::make_unique(*this)) {} - -SubsystemsList::~SubsystemsList() = default; - -void SubsystemsList::Add(Subsystem* s, std::initializer_list deps) { - m_p->Add(s, deps); -} - -bool SubsystemsList::InitAll(bool print_msg) { - return m_p->InitAll(print_msg); -} - -void SubsystemsList::DestroyAll(bool print_msg) { - m_p->DestroyAll(print_msg); -} - -int* SubsystemsList::GetArgc() { - return &m_p->m_argc; -} - -char** SubsystemsList::GetArgv() { - return m_p->m_argv; -} - -Subsystem::Subsystem(): m_p(std::make_unique()) {} - -Subsystem::~Subsystem() = default; - -void Subsystem::Fail(const char* format, ...) { - va_list args {}; - va_start(args, format); - - va_list args_copy {}; - va_copy(args_copy, args); - int len = std::vsnprintf(nullptr, 0, format, args_copy); - va_end(args_copy); - - if (len > 0) { - std::string msg(static_cast(len) + 1, '\0'); - std::vsnprintf(msg.data(), msg.size(), format, args); - m_p->fail_msg = msg.c_str(); - m_p->failed = true; - } - - va_end(args); -} - -const char* SubsystemsList::GetFailName() const { - return m_p->fail_name; -} - -const char* SubsystemsList::GetFailMsg() const { - return m_p->fail_msg; -} - -void SubsystemsList::SetArgs(int argc, char* argv[]) { - m_p->SetArgs(argc, argv); -} - -void SubsystemsList::ShutdownAll() { - m_p->ShutdownAll(); } } // namespace Common diff --git a/src/common/subsystems.h b/src/common/subsystems.h index d183dfc..e049082 100644 --- a/src/common/subsystems.h +++ b/src/common/subsystems.h @@ -2,90 +2,61 @@ #define KYTY_COMMON_SUBSYSTEMS_H_ #include "common/common.h" -#include "common/singleton.h" -#include -#include +#include +#include namespace Common { -class Subsystem; -class SubsystemsListPrivate; -class SubsystemPrivate; - -class SubsystemsList { +class Subsystems { public: - SubsystemsList(); - virtual ~SubsystemsList(); - void SetArgs(int argc, char* argv[]); + using Callback = void (*)(); - // void Add(Subsystem* s, const char* name, ...); - void Add(Subsystem* s, std::initializer_list deps); + explicit Subsystems(bool print = false); + ~Subsystems(); - bool InitAll(bool print_msg = false); - void DestroyAll(bool print_msg = false); + template + void Initialize() { + Lifecycle::initialize(); + m_active.push_back({ShutdownCallback(), EmergencyCallback()}); + if (m_print) { + std::printf("Initialized: %s\n", Lifecycle::name); + } + } - int* GetArgc(); - char** GetArgv(); + void Destroy(); + void EmergencyShutdown(); - [[nodiscard]] const char* GetFailName() const; - [[nodiscard]] const char* GetFailMsg() const; + static void EmergencyShutdownActive(); - void ShutdownAll(); - - static SubsystemsList* Instance() { return Common::Singleton::Instance(); } - - KYTY_CLASS_NO_COPY(SubsystemsList); + KYTY_CLASS_NO_COPY(Subsystems); private: - std::unique_ptr m_p; + template + static consteval Callback ShutdownCallback() { + if constexpr (requires { Lifecycle::shutdown; }) { + return Lifecycle::shutdown; + } + return nullptr; + } + + template + static consteval Callback EmergencyCallback() { + if constexpr (requires { Lifecycle::emergency_shutdown; }) { + return Lifecycle::emergency_shutdown; + } + return nullptr; + } + + struct Entry { + Callback shutdown; + Callback emergency_shutdown; + }; + + std::vector m_active; + bool m_print; }; -using SubsystemsListSingleton = Common::Singleton; - -class Subsystem { -public: - Subsystem(); - virtual ~Subsystem(); - - virtual const char* Id() = 0; - virtual void Init(SubsystemsList* parent) = 0; - virtual void Destroy(SubsystemsList* parent) = 0; - virtual void UnexpectedShutdown(SubsystemsList* parent) = 0; - - friend class SubsystemsListPrivate; - - KYTY_CLASS_NO_COPY(Subsystem); - -protected: - void Fail(const char* format, ...) KYTY_FORMAT_PRINTF(2, 3); - -private: - std::unique_ptr m_p; -}; - -#define KYTY_SUBSYSTEM_DEFINE(s) \ - class s##Subsystem: public Common::Subsystem { \ - public: \ - static Subsystem* Instance() { return Common::Singleton::Instance(); } \ - const char* Id() { return #s; } \ - void Init(Common::SubsystemsList* parent); \ - void Destroy(Common::SubsystemsList* parent); \ - void UnexpectedShutdown(Common::SubsystemsList* parent); \ - }; \ - typedef Common::Singleton s##SubsystemSingleton; - -#define KYTY_SUBSYSTEM_INIT(s) \ - void s##Subsystem::Init([[maybe_unused]] Common::SubsystemsList* parent) -#define KYTY_SUBSYSTEM_DESTROY(s) \ - void s##Subsystem::Destroy([[maybe_unused]] Common::SubsystemsList* parent) -#define KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(s) \ - void s##Subsystem::UnexpectedShutdown([[maybe_unused]] Common::SubsystemsList* parent) - -// #define KYTY_SUBSYSTEM_ADD(list, s, ...) list.push_back(s##SubsystemSingleton::Instance(), #s, -// __VA_ARGS__); #define KYTY_SUBSYSTEM_ADD2(list, ...) list.Add2(s##SubsystemSingleton::Instance(), -// __VA_ARGS__); - } // namespace Common #endif /* KYTY_COMMON_SUBSYSTEMS_H_ */ diff --git a/src/common/threads.cpp b/src/common/threads.cpp index c0b4bbe..4f2c382 100644 --- a/src/common/threads.cpp +++ b/src/common/threads.cpp @@ -252,15 +252,11 @@ static thread_id_t g_main_thread; static int g_main_thread_int; static std::atomic g_thread_counter = 0; -KYTY_SUBSYSTEM_INIT(Threads) { +void InitializeThreads() { g_main_thread = std::this_thread::get_id(); g_main_thread_int = Thread::GetThreadIdUnique(); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {} - -KYTY_SUBSYSTEM_DESTROY(Threads) {} - Thread::Thread(thread_func_t func, void* arg) : m_thread(std::make_unique(func, arg)) { while (!m_thread->started) { diff --git a/src/common/threads.h b/src/common/threads.h index 5f0c222..f9c6f98 100644 --- a/src/common/threads.h +++ b/src/common/threads.h @@ -2,14 +2,13 @@ #define KYTY_COMMON_THREADS_H_ #include "common/common.h" -#include "common/subsystems.h" #include #include namespace Common { -KYTY_SUBSYSTEM_DEFINE(Threads); +void InitializeThreads(); using thread_func_t = void (*)(void*); using wait_poll_func_t = void (*)(); diff --git a/src/emulator.cpp b/src/emulator.cpp index cea35f6..71bbe0a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -2,7 +2,6 @@ #include "common/abi.h" #include "common/assert.h" -#include "common/commonSubsystem.h" #include "common/emulatorConfig.h" #include "common/file.h" #include "common/logging/log.h" @@ -43,7 +42,7 @@ static void KytyClose() { LOGF("done!\n"); - Common::SubsystemsListSingleton::Instance()->ShutdownAll(); + Common::Subsystems::EmergencyShutdownActive(); } static void MountOrCreateDir(const std::filesystem::path& dir, const std::string& point) { @@ -105,30 +104,13 @@ static void ClearDebugTextureFolder() { } } -static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json) { +static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& param_json, + Common::Subsystems& subsystems) { EXIT_IF(!Common::Thread::IsMainThread()); - auto* slist = Common::SubsystemsList::Instance(); - - auto* audio = Libs::Audio::AudioSubsystem::Instance(); - auto* config = Config::ConfigSubsystem::Instance(); - auto* controller = Libs::Controller::ControllerSubsystem::Instance(); - auto* core = Common::CommonSubsystem::Instance(); - auto* file_system = Libs::LibKernel::FileSystem::FileSystemSubsystem::Instance(); - auto* graphics = Libs::Graphics::GraphicsSubsystem::Instance(); - auto* log = Log::LogSubsystem::Instance(); - auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance(); - auto* network = Libs::Network::NetworkSubsystem::Instance(); - auto* profiler = Profiler::ProfilerSubsystem::Instance(); - auto* pthread = Libs::LibKernel::PthreadSubsystem::Instance(); - auto* timer = Loader::Timer::TimerSubsystem::Instance(); - - slist->Add(config, {core}); - slist->InitAll(true); - + subsystems.Initialize(); Config::Load(cfg); - slist->Add(log, {core, config}); - slist->InitAll(true); + subsystems.Initialize(); if (Common::File::IsFileExisting(param_json)) { Loader::SystemContentLoadParamSfo(param_json); @@ -138,17 +120,16 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path& } } - slist->Add(audio, {core, log, pthread, memory}); - slist->Add(controller, {core, log, config}); - slist->Add(file_system, {core, log, pthread}); - slist->Add(graphics, {core, log, pthread, memory, config, profiler, controller}); - slist->Add(memory, {core, log}); - slist->Add(network, {core, log, pthread}); - slist->Add(profiler, {core, config}); - slist->Add(pthread, {core, log, timer}); - slist->Add(timer, {core, log}); - - slist->InitAll(true); + // Initialization order is explicit; destruction is automatic and reversed. + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); + subsystems.Initialize(); } static void LoadElf(const std::filesystem::path& elf, bool dbg_print_reloc = false, @@ -189,8 +170,9 @@ void Run(const RunOptions& options) { EXIT("ELF is required\n"); } - const auto param_json = options.app0_dir / "sce_sys" / "param.json"; - Init(options.config, param_json); + const auto param_json = options.app0_dir / "sce_sys" / "param.json"; + Common::Subsystems subsystems(true); + Init(options.config, param_json, subsystems); ClearDebugTextureFolder(); diff --git a/src/graphics/presentation/imeOverlay.cpp b/src/graphics/presentation/imeOverlay.cpp index 3c95faf..374e818 100644 --- a/src/graphics/presentation/imeOverlay.cpp +++ b/src/graphics/presentation/imeOverlay.cpp @@ -32,64 +32,21 @@ namespace DialogIme = Libs::Dialog::ImeDialog; namespace Ime { -enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 }; -enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 }; -enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 }; -enum class ExternalAction : uint8_t { - None, - Text, - Backspace, - MoveLeft, - MoveRight, - Cancel, - Accept, - Newline, -}; +using Type = ImeCommon::Type; +using EnterLabel = ImeCommon::EnterLabel; +using Alignment = ImeCommon::Alignment; +using ExternalAction = ImeCommon::ExternalAction; +using ExternalInput = ImeCommon::ExternalInput; +using HostSnapshot = ImeCommon::HostSnapshot; -struct Keycode { - uint16_t keycode = 0; - char16_t character = u'\0'; - uint32_t status = 0; - uint32_t type = 0; - int32_t user_id = 0; - uint32_t resource_id = 0; - uint64_t timestamp = 0; -}; - -struct ExternalInput { - Keycode key; - ExternalAction action = ExternalAction::None; - std::u16string text; -}; - -struct HostSnapshot { - uint64_t generation = 0; - Type type = Type::Default; - EnterLabel enter_label = EnterLabel::Default; - uint32_t option = 0; - uint32_t max_text_length = 0; - uint32_t cursor = 0; - uint32_t disable_device = 0; - bool key_panel_visible = true; - float posx = 0.0f; - float posy = 0.0f; - Alignment horizontal_alignment = Alignment::Start; - Alignment vertical_alignment = Alignment::Start; - uint32_t panel_width = 0; - uint32_t panel_height = 0; - std::u16string text; - std::u16string title; - std::u16string placeholder; -}; - -constexpr uint32_t OPTION_MULTILINE = 0x00000001; -constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = 0x00000002; -constexpr uint32_t OPTION_PASSWORD = 0x00000004; -constexpr uint32_t OPTION_FIXED_POSITION = 0x00000040; -constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = 0x00000800; -constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000; -constexpr uint32_t DISABLE_DEVICE_CONTROLLER = 0x00000001; -constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002; +constexpr uint32_t OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE; +constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = ImeCommon::OPTION_NO_AUTO_CAPITALIZE; +constexpr uint32_t OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD; +constexpr uint32_t OPTION_FIXED_POSITION = ImeCommon::OPTION_FIXED_POSITION; +constexpr uint32_t OPTION_DISABLE_POSITION_ADJ = ImeCommon::OPTION_DISABLE_POSITION_ADJUST; +constexpr uint32_t OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K; +constexpr uint32_t DISABLE_DEVICE_CONTROLLER = ImeCommon::DISABLE_DEVICE_CONTROLLER; +constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = ImeCommon::DISABLE_DEVICE_EXT_KEYBOARD; constexpr uint64_t CORE_GENERATION_BIT = uint64_t {1} << 63; uint64_t PackCoreGeneration(uint64_t generation) { @@ -108,49 +65,11 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { if (snapshot == nullptr) { return false; } - CoreIme::HostSnapshot core; - if (CoreIme::GetHostSnapshot(&core)) { - snapshot->generation = PackCoreGeneration(core.generation); - snapshot->type = static_cast(core.type); - snapshot->enter_label = static_cast(core.enter_label); - snapshot->option = core.option; - snapshot->max_text_length = core.max_text_length; - snapshot->cursor = core.cursor; - snapshot->disable_device = core.disable_device; - snapshot->key_panel_visible = core.key_panel_visible; - snapshot->posx = core.posx; - snapshot->posy = core.posy; - snapshot->horizontal_alignment = static_cast(core.horizontal_alignment); - snapshot->vertical_alignment = static_cast(core.vertical_alignment); - snapshot->panel_width = core.panel_width; - snapshot->panel_height = core.panel_height; - snapshot->text = std::move(core.text); - snapshot->title.clear(); - snapshot->placeholder.clear(); + if (CoreIme::GetHostSnapshot(snapshot)) { + snapshot->generation = PackCoreGeneration(snapshot->generation); return true; } - DialogIme::HostSnapshot dialog; - if (!DialogIme::GetHostSnapshot(&dialog)) { - return false; - } - snapshot->generation = dialog.generation; - snapshot->type = static_cast(dialog.type); - snapshot->enter_label = static_cast(dialog.enter_label); - snapshot->option = dialog.option; - snapshot->max_text_length = dialog.max_text_length; - snapshot->cursor = dialog.cursor; - snapshot->disable_device = dialog.disable_device; - snapshot->key_panel_visible = dialog.key_panel_visible; - snapshot->posx = dialog.posx; - snapshot->posy = dialog.posy; - snapshot->horizontal_alignment = static_cast(dialog.horizontal_alignment); - snapshot->vertical_alignment = static_cast(dialog.vertical_alignment); - snapshot->panel_width = dialog.panel_width; - snapshot->panel_height = dialog.panel_height; - snapshot->text = std::move(dialog.text); - snapshot->title = std::move(dialog.title); - snapshot->placeholder = std::move(dialog.placeholder); - return true; + return DialogIme::GetHostSnapshot(snapshot); } bool HostInsertText(uint64_t generation, std::u16string_view text) { @@ -175,30 +94,9 @@ bool HostCancel(uint64_t generation) { } bool HostQueueExternalInput(uint64_t generation, ExternalInput input) { - if (!IsCoreGeneration(generation)) { - DialogIme::ExternalInput dialog; - dialog.key.keycode = input.key.keycode; - dialog.key.character = input.key.character; - dialog.key.status = input.key.status; - dialog.key.type = input.key.type; - dialog.key.user_id = input.key.user_id; - dialog.key.resource_id = input.key.resource_id; - dialog.key.timestamp = input.key.timestamp; - dialog.action = static_cast(input.action); - dialog.text = std::move(input.text); - return DialogIme::HostQueueExternalInput(generation, std::move(dialog)); - } - CoreIme::ExternalInput core; - core.key.keycode = input.key.keycode; - core.key.character = input.key.character; - core.key.status = input.key.status; - core.key.type = input.key.type; - core.key.user_id = input.key.user_id; - core.key.resource_id = input.key.resource_id; - core.key.timestamp = input.key.timestamp; - core.action = static_cast(input.action); - core.text = std::move(input.text); - return CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(core)); + return IsCoreGeneration(generation) + ? CoreIme::HostQueueExternalInput(UnpackGeneration(generation), std::move(input)) + : DialogIme::HostQueueExternalInput(generation, std::move(input)); } } // namespace Ime diff --git a/src/graphics/presentation/window/swapchain.cpp b/src/graphics/presentation/window/swapchain.cpp index 931030d..c1467a6 100644 --- a/src/graphics/presentation/window/swapchain.cpp +++ b/src/graphics/presentation/window/swapchain.cpp @@ -22,7 +22,6 @@ #include "common/logging/log.h" #include "common/profiler.h" #include "common/stringUtils.h" -#include "common/subsystems.h" #include "common/systemInfo.h" #include "common/threads.h" #include "common/timer.h" diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index c5f5da7..1a6d322 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -22,7 +22,6 @@ #include "common/logging/log.h" #include "common/profiler.h" #include "common/stringUtils.h" -#include "common/subsystems.h" #include "common/systemInfo.h" #include "common/threads.h" #include "common/timer.h" diff --git a/src/kernel/fileSystem.cpp b/src/kernel/fileSystem.cpp index 8b5b98b..f2e9c0f 100644 --- a/src/kernel/fileSystem.cpp +++ b/src/kernel/fileSystem.cpp @@ -337,21 +337,23 @@ std::filesystem::path MountPoints::GetRealDirectory(const std::string& mounted_d return mounted_directory; } -KYTY_SUBSYSTEM_INIT(FileSystem) { +void Initialize() { g_mount_points = new MountPoints; g_files = new FileDescriptors; } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(FileSystem) { +void EmergencyShutdown() { if (g_files != nullptr) { g_files->CloseAll(); } } -KYTY_SUBSYSTEM_DESTROY(FileSystem) { - if (g_files != nullptr) { - g_files->CloseAll(); - } +void Shutdown() { + EmergencyShutdown(); + delete g_files; + delete g_mount_points; + g_files = nullptr; + g_mount_points = nullptr; } void Mount(const std::filesystem::path& folder, const std::string& point) { diff --git a/src/kernel/fileSystem.h b/src/kernel/fileSystem.h index bc73d18..17b22fe 100644 --- a/src/kernel/fileSystem.h +++ b/src/kernel/fileSystem.h @@ -4,7 +4,6 @@ #include "common/abi.h" #include "common/common.h" #include "common/stringUtils.h" -#include "common/subsystems.h" #include "kernel/pthread.h" #include @@ -33,7 +32,16 @@ struct FileStat { unsigned int: (8 / 2) * (16 - static_cast(sizeof(KernelTimespec))); }; -KYTY_SUBSYSTEM_DEFINE(FileSystem); +void Initialize(); +void Shutdown(); +void EmergencyShutdown(); + +struct Lifecycle { + static constexpr const char* name = "FileSystem"; + static constexpr auto initialize = Libs::LibKernel::FileSystem::Initialize; + static constexpr auto shutdown = Libs::LibKernel::FileSystem::Shutdown; + static constexpr auto emergency_shutdown = Libs::LibKernel::FileSystem::EmergencyShutdown; +}; void Mount(const std::filesystem::path& folder, const std::string& point); void Umount(const std::string& folder_or_point); diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp index 9b575be..ce84cd4 100644 --- a/src/kernel/memory.cpp +++ b/src/kernel/memory.cpp @@ -976,7 +976,7 @@ static bool SelfTestSub64SharedPlaceholderAlias() { static bool ReplaceFixedRangeWithReserved(uint64_t start, uint64_t size); -KYTY_SUBSYSTEM_INIT(Memory) { +void Initialize() { g_flexible_memory_size_frozen = true; VirtualMemory::Init(); g_guest_address_space = std::make_unique(PhysicalMemory::TotalSize()); @@ -988,9 +988,7 @@ KYTY_SUBSYSTEM_INIT(Memory) { EXIT_IF(!SelfTestSub64SharedPlaceholderAlias()); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {} - -KYTY_SUBSYSTEM_DESTROY(Memory) { +void Shutdown() { g_pooled_memory.reset(); g_flexible_memory.reset(); g_physical_memory.reset(); diff --git a/src/kernel/memory.h b/src/kernel/memory.h index 775192e..45017ba 100644 --- a/src/kernel/memory.h +++ b/src/kernel/memory.h @@ -3,7 +3,6 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" #include "common/virtualMemory.h" namespace Libs::Graphics { @@ -13,7 +12,14 @@ enum class PageFaultAccess; namespace Libs::LibKernel::Memory { -KYTY_SUBSYSTEM_DEFINE(Memory); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Memory"; + static constexpr auto initialize = Libs::LibKernel::Memory::Initialize; + static constexpr auto shutdown = Libs::LibKernel::Memory::Shutdown; +}; using callback_func_t = void (*)(uintptr_t addr, size_t size); diff --git a/src/kernel/pthread.cpp b/src/kernel/pthread.cpp index 0a12885..cbd6687 100644 --- a/src/kernel/pthread.cpp +++ b/src/kernel/pthread.cpp @@ -1124,7 +1124,7 @@ void* PthreadCreateMainGuestStack() { return stack_top; } -KYTY_SUBSYSTEM_INIT(Pthread) { +void Initialize() { PRINT_NAME_ENABLE(false); EXIT_IF(g_pthread_context != nullptr); @@ -1157,10 +1157,6 @@ KYTY_SUBSYSTEM_INIT(Pthread) { thread.Detach(); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Pthread) {} - -KYTY_SUBSYSTEM_DESTROY(Pthread) {} - static int PthreadAttrCopy(PthreadAttr* dst, const PthreadAttr* src) { if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) { return KERNEL_ERROR_EINVAL; diff --git a/src/kernel/pthread.h b/src/kernel/pthread.h index 3b39581..24a83a9 100644 --- a/src/kernel/pthread.h +++ b/src/kernel/pthread.h @@ -3,7 +3,6 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" // IWYU pragma: no_include @@ -19,7 +18,12 @@ namespace Libs { namespace LibKernel { -KYTY_SUBSYSTEM_DEFINE(Pthread); +void Initialize(); + +struct PthreadLifecycle { + static constexpr const char* name = "Pthread"; + static constexpr auto initialize = Libs::LibKernel::Initialize; +}; struct PthreadAttrPrivate; struct PthreadPrivate; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 6bef6ed..160aee2 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -43,7 +43,7 @@ namespace Libs::Graphics { static RenderContext* g_renderer = nullptr; -KYTY_SUBSYSTEM_INIT(Graphics) { +void Initialize() { // Some games lock up if this is not called first if (Config::RenderDocEnabled()) { RenderDocInit(); @@ -59,9 +59,7 @@ KYTY_SUBSYSTEM_INIT(Graphics) { ShaderInit(); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Graphics) {} - -KYTY_SUBSYSTEM_DESTROY(Graphics) { +void Shutdown() { EXIT_IF(g_renderer == nullptr); g_renderer->ShutdownGpu(); VideoOut::VideoOutShutdown(); diff --git a/src/libs/agc.h b/src/libs/agc.h index a9d18eb..4890916 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -3,7 +3,6 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" #include "kernel/eventQueue.h" namespace Libs::Graphics { @@ -21,7 +20,14 @@ struct MemoryRange { uint64_t m_size; }; -KYTY_SUBSYSTEM_DEFINE(Graphics); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Graphics"; + static constexpr auto initialize = Libs::Graphics::Initialize; + static constexpr auto shutdown = Libs::Graphics::Shutdown; +}; void GraphicsDbgDumpDcb(const char* type, uint32_t num_dw, uint32_t* cmd_buffer); diff --git a/src/libs/audio.cpp b/src/libs/audio.cpp index 2011023..6a4c74d 100644 --- a/src/libs/audio.cpp +++ b/src/libs/audio.cpp @@ -181,15 +181,13 @@ uint32_t AudioOutOutputs(const OutputParam* params, uint32_t num, bool blocking) } // namespace AudioInternal -KYTY_SUBSYSTEM_INIT(Audio) { +void Initialize() { EXIT_IF(g_audio != nullptr); g_audio = new Audio; } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Audio) {} - -KYTY_SUBSYSTEM_DESTROY(Audio) { +void Shutdown() { delete g_audio; g_audio = nullptr; } @@ -1461,14 +1459,14 @@ struct Ngs2CustomSamplerRackOption { }; union Ngs2RackOptionUnion { - Ngs2RackOption common; - Ngs2SamplerRackOption sampler; - Ngs2MasteringRackOption mastering; - Ngs2SubmixerRackOption submixer; - Ngs2ReverbRackOption reverb; - Ngs2CustomSubmixerRackOption custom_submixer; + Ngs2RackOption common; + Ngs2SamplerRackOption sampler; + Ngs2MasteringRackOption mastering; + Ngs2SubmixerRackOption submixer; + Ngs2ReverbRackOption reverb; + Ngs2CustomSubmixerRackOption custom_submixer; Ngs2CustomMasteringRackOption custom_mastering; - Ngs2CustomSamplerRackOption custom_sampler; + Ngs2CustomSamplerRackOption custom_sampler; }; struct Ngs2ContextBufferInfo { @@ -2653,8 +2651,8 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat switch (voice->rack->type) { case Ngs2RackType::Submixer: { EXIT_NOT_IMPLEMENTED(state_size != sizeof(Ngs2SubmixerVoiceState)); - auto* submixer = reinterpret_cast(state); - *submixer = {}; + auto* submixer = reinterpret_cast(state); + *submixer = {}; submixer->voice_state.state_flags = Ngs2GetStateFlags(voice); LOGF("\t state_flags = %u\n", submixer->voice_state.state_flags); break; diff --git a/src/libs/audio.h b/src/libs/audio.h index 7218bdd..168d9b8 100644 --- a/src/libs/audio.h +++ b/src/libs/audio.h @@ -3,11 +3,16 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" - namespace Libs::Audio { -KYTY_SUBSYSTEM_DEFINE(Audio); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Audio"; + static constexpr auto initialize = Libs::Audio::Initialize; + static constexpr auto shutdown = Libs::Audio::Shutdown; +}; namespace AudioOut { diff --git a/src/libs/controller.cpp b/src/libs/controller.cpp index 0d86875..4e57056 100644 --- a/src/libs/controller.cpp +++ b/src/libs/controller.cpp @@ -112,16 +112,17 @@ static void pad_fill_data(PadData* data, const ControllerState& state, bool conn data->device_unique_data_len = 0; } -KYTY_SUBSYSTEM_INIT(Controller) { +void Initialize() { EXIT_IF(g_controller != nullptr); g_controller = new GameController; g_controller->Connect(HOST_INPUT_CONTROLLER_ID); } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Controller) {} - -KYTY_SUBSYSTEM_DESTROY(Controller) {} +void Shutdown() { + delete g_controller; + g_controller = nullptr; +} void GameController::Connect(int id) { Common::LockGuard lock(m_mutex); diff --git a/src/libs/controller.h b/src/libs/controller.h index 631b112..3403f6b 100644 --- a/src/libs/controller.h +++ b/src/libs/controller.h @@ -3,11 +3,16 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" - namespace Libs::Controller { -KYTY_SUBSYSTEM_DEFINE(Controller); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Controller"; + static constexpr auto initialize = Libs::Controller::Initialize; + static constexpr auto shutdown = Libs::Controller::Shutdown; +}; constexpr int HOST_INPUT_CONTROLLER_ID = -1000; diff --git a/src/libs/ime.cpp b/src/libs/ime.cpp index d843015..2879d8d 100644 --- a/src/libs/ime.cpp +++ b/src/libs/ime.cpp @@ -55,13 +55,13 @@ constexpr uint32_t VALID_KEYBOARD_OPTIONS = 0x0000003f; constexpr uint32_t VALID_KEYBOARD_MODE = 0x0000007f; constexpr uint64_t VALID_LANGUAGES = 0x00000001ff1fffffULL; -constexpr uint32_t OPTION_MULTILINE = 0x00000001; -constexpr uint32_t OPTION_PASSWORD = 0x00000004; -constexpr uint32_t OPTION_EXT_KEYBOARD = 0x00000010; -constexpr uint32_t OPTION_EXPANDED_PREEDIT = 0x00001000; -constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000; +constexpr uint32_t OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE; +constexpr uint32_t OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD; +constexpr uint32_t OPTION_EXT_KEYBOARD = ImeCommon::OPTION_EXT_KEYBOARD; +constexpr uint32_t OPTION_EXPANDED_PREEDIT = ImeCommon::OPTION_EXPANDED_PREEDIT; +constexpr uint32_t OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K; -constexpr uint32_t EXT_OPTION_HIDE_KEY_PANEL = 0x00000400; +constexpr uint32_t EXT_OPTION_HIDE_KEY_PANEL = ImeCommon::EXT_OPTION_HIDE_KEY_PANEL; constexpr size_t EVENT_QUEUE_CAPACITY = 128; struct QueuedEvent { @@ -71,18 +71,15 @@ struct QueuedEvent { }; struct State { - bool open = false; - bool event_overflow = false; - uint64_t generation = 0; - uint64_t revision = 0; - uint32_t option = 0; - uint32_t max_text_length = 0; - uint32_t cursor = 0; + bool open = false; + bool event_overflow = false; + uint64_t generation = 0; + uint64_t revision = 0; Param param {}; ExtendedParam extended {}; void* arg = nullptr; EventHandler handler = nullptr; - std::u16string text; + ImeCommon::TextEditEngine editor; std::deque events; std::vector external_inputs; }; @@ -130,97 +127,6 @@ bool ValidateText(const char16_t* text, uint32_t length, bool multiline, uint32_ return true; } -bool IsAllowedInput(char16_t value, Type type, uint32_t option) { - if (value == u'\r' || value == u'\0') { - return false; - } - if (value == u'\n') { - return (option & OPTION_MULTILINE) != 0; - } - if (type == Type::Number) { - return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.'; - } - if (type == Type::BasicLatin) { - return value >= u' ' && value <= u'~'; - } - return true; -} - -char16_t HidCharacter(uint16_t keycode, uint32_t status) { - const bool shift = (status & 0x00002200) != 0; - const bool caps = (status & 0x00020000) != 0; - if (keycode >= 4 && keycode <= 29) { - return static_cast(((shift != caps) ? u'A' : u'a') + keycode - 4); - } - if (keycode >= 30 && keycode <= 39) { - static constexpr char16_t plain[] = u"1234567890"; - static constexpr char16_t shifted[] = u"!@#$%^&*()"; - return (shift ? shifted : plain)[keycode - 30]; - } - if (keycode == 44) { - return u' '; - } - if (keycode >= 45 && keycode <= 56) { - static constexpr char16_t plain[] = u"-=[]\\#;'`,./"; - static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?"; - return (shift ? shifted : plain)[keycode - 45]; - } - if (keycode >= 89 && keycode <= 98) { - static constexpr char16_t keypad[] = u"1234567890"; - return keypad[keycode - 89]; - } - return keycode == 99 ? u'.' : u'\0'; -} - -bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status, - bool multiline) { - constexpr uint32_t KEYCODE_VALID = 0x00000001; - constexpr uint32_t CHARACTER_VALID = 0x00000002; - if (keycode == input->key.keycode && status == input->key.status) { - return true; - } - if ((status & KEYCODE_VALID) == 0 || keycode == 0) { - return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0; - } - input->key.keycode = keycode; - input->key.status = status; - input->text.clear(); - switch (keycode) { - case 40: - case 88: - case 158: - input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept; - break; - case 41: input->action = ExternalAction::Cancel; break; - case 42: - case 187: input->action = ExternalAction::Backspace; break; - case 43: - case 186: input->action = ExternalAction::None; break; - case 79: input->action = ExternalAction::MoveRight; break; - case 80: input->action = ExternalAction::MoveLeft; break; - default: { - const char16_t character = HidCharacter(keycode, status); - if (character == u'\0') { - return false; - } - input->action = ExternalAction::Text; - input->key.character = character; - input->text.push_back(character); - break; - } - } - return true; -} - -uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) { - cursor = std::min(cursor, static_cast(text.size())); - if (cursor > 0 && cursor < text.size() && text[cursor - 1] >= 0xd800 && - text[cursor - 1] <= 0xdbff && text[cursor] >= 0xdc00 && text[cursor] <= 0xdfff) { - cursor++; - } - return cursor; -} - void NotifyVisibility(bool visible, uint64_t generation) { if (const auto callback = g_visibility_callback.load(std::memory_order_acquire); callback != nullptr) { @@ -247,7 +153,7 @@ void SyncGuestText(char16_t* work, char16_t* input, uint32_t max_length, std::u1 void SyncTextBuffersLocked() { SyncGuestText(static_cast(g_state.param.work), g_state.param.input_text_buffer, - g_state.max_text_length, g_state.text); + g_state.editor.GetMaxLength(), g_state.editor.GetText()); } bool QueueEventLocked(QueuedEvent event) { @@ -263,53 +169,33 @@ QueuedEvent MakeTextEventLocked(uint32_t id, uint32_t edit_index, int32_t edit_l QueuedEvent queued; queued.event.id = id; queued.event.param.text.str = static_cast(g_state.param.work); - queued.event.param.text.caret_index = g_state.cursor; + queued.event.param.text.caret_index = g_state.editor.GetCursor(); queued.event.param.text.area_num = 1; queued.event.param.text.text_area[0] = {TextAreaMode::Edit, edit_index, edit_length}; queued.text_payload = true; - queued.text = g_state.text; + queued.text = g_state.editor.GetText(); return queued; } bool CommitFilteredText(uint64_t generation, uint64_t revision, std::u16string old_text, - std::u16string candidate, uint32_t cursor, TextFilter filter, - uint32_t max_length, bool multiline) { + ImeCommon::TextEditEngine candidate, TextFilter filter, bool multiline) { if (filter != nullptr) { - std::vector output(max_length + 1, u'\0'); - uint32_t output_length = max_length; - if (filter(output.data(), &output_length, candidate.c_str(), - static_cast(candidate.size())) == 0 && - output_length <= max_length) { - uint32_t actual_length = 0; - if (ValidateText(output.data(), output_length, multiline, &actual_length) && - actual_length == output_length) { - candidate.assign(output.data(), output.data() + output_length); - } + std::u16string filtered; + if (ImeCommon::RunTextFilter(filter, candidate.GetText(), candidate.GetMaxLength(), + &filtered) && + ImeCommon::IsValidInputText(filtered, multiline)) { + candidate.ReplaceText(std::move(filtered), candidate.GetCursor()); } } std::scoped_lock lock(g_mutex); if (!g_state.open || g_state.generation != generation || g_state.revision != revision || - g_state.text != old_text || candidate == old_text) { + g_state.editor.GetText() != old_text || candidate.GetText() == old_text) { return false; } - size_t prefix = 0; - while (prefix < old_text.size() && prefix < candidate.size() && - old_text[prefix] == candidate[prefix]) { - prefix++; - } - size_t old_tail = old_text.size(); - size_t new_tail = candidate.size(); - while (old_tail > prefix && new_tail > prefix && - old_text[old_tail - 1] == candidate[new_tail - 1]) { - old_tail--; - new_tail--; - } - const int32_t edit_length = - static_cast(new_tail - prefix) - static_cast(old_tail - prefix); - g_state.text = std::move(candidate); - g_state.cursor = NormalizeCursor(g_state.text, cursor); - QueueEventLocked(MakeTextEventLocked(1, static_cast(prefix), edit_length)); + const auto edit = ImeCommon::ComputeEditDelta(old_text, candidate.GetText()); + g_state.editor = std::move(candidate); + QueueEventLocked(MakeTextEventLocked(1, edit.index, edit.length)); UpdateRevisionLocked(); return true; } @@ -475,16 +361,13 @@ int KYTY_SYSV_ABI ImeOpen(const Param* param, const ExtendedParam* extended) { g_state.open = true; g_state.generation = next_generation; g_state.revision = next_revision; - g_state.option = param->option; - g_state.max_text_length = param->max_text_length; - g_state.cursor = static_cast(text.size()); g_state.param = *param; if (extended != nullptr) { g_state.extended = *extended; } g_state.arg = param->arg; g_state.handler = param->handler; - g_state.text = std::move(text); + g_state.editor.Reset(param->type, param->option, param->max_text_length, std::move(text)); std::memset(param->work, 0, WORK_BUFFER_SIZE); SyncTextBuffersLocked(); @@ -550,7 +433,7 @@ int KYTY_SYSV_ABI ImeUpdate(EventHandler handler) { while (!g_state.events.empty()) { dispatch.push_back({g_state.arg, std::move(g_state.events.front()), static_cast(g_state.param.work), - g_state.param.input_text_buffer, g_state.max_text_length, + g_state.param.input_text_buffer, g_state.editor.GetMaxLength(), g_state.generation}); g_state.events.pop_front(); } @@ -607,14 +490,14 @@ int KYTY_SYSV_ABI ImeSetText(const char16_t* text, uint32_t length) { if (text == nullptr) { return ERROR_INVALID_ADDRESS; } - const uint32_t clamped_length = std::min(length, g_state.max_text_length); + const uint32_t clamped_length = std::min(length, g_state.editor.GetMaxLength()); uint32_t actual_length = 0; - if (!ValidateText(text, clamped_length, (g_state.option & OPTION_MULTILINE) != 0, + if (!ValidateText(text, clamped_length, (g_state.editor.GetOption() & OPTION_MULTILINE) != 0, &actual_length)) { return ERROR_INVALID_TEXT; } - g_state.text.assign(text, text + actual_length); - g_state.cursor = NormalizeCursor(g_state.text, g_state.cursor); + g_state.editor.ReplaceText(std::u16string(text, text + actual_length), + g_state.editor.GetCursor()); SyncTextBuffersLocked(); UpdateRevisionLocked(); return OK; @@ -628,10 +511,10 @@ int KYTY_SYSV_ABI ImeSetCaret(const Caret* caret) { if (caret == nullptr) { return ERROR_INVALID_ADDRESS; } - if (caret->index > g_state.text.size()) { + if (caret->index > g_state.editor.GetText().size()) { return ERROR_INVALID_PARAM; } - g_state.cursor = NormalizeCursor(g_state.text, caret->index); + g_state.editor.SetCursor(caret->index); UpdateRevisionLocked(); return OK; } @@ -644,7 +527,7 @@ int KYTY_SYSV_ABI ImeSetTextGeometry(TextAreaMode mode, const TextGeometry* geom if (geometry == nullptr) { return ERROR_INVALID_ADDRESS; } - const bool over_2k = (g_state.option & OPTION_USE_OVER_2K) != 0; + const bool over_2k = (g_state.editor.GetOption() & OPTION_USE_OVER_2K) != 0; const float max_x = over_2k ? 3840.0f : 1920.0f; const float max_y = over_2k ? 2160.0f : 1080.0f; if (!std::isfinite(geometry->x) || !std::isfinite(geometry->y) || geometry->x < 0.0f || @@ -754,11 +637,11 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { snapshot->generation = g_state.generation; snapshot->type = g_state.param.type; snapshot->enter_label = g_state.param.enter_label; - snapshot->option = g_state.option; - snapshot->max_text_length = g_state.max_text_length; - snapshot->cursor = g_state.cursor; + snapshot->option = g_state.editor.GetOption(); + snapshot->max_text_length = g_state.editor.GetMaxLength(); + snapshot->cursor = g_state.editor.GetCursor(); snapshot->disable_device = g_state.extended.disable_device; - snapshot->key_panel_visible = (g_state.option & OPTION_EXT_KEYBOARD) == 0 || + snapshot->key_panel_visible = (g_state.editor.GetOption() & OPTION_EXT_KEYBOARD) == 0 || (g_state.extended.option & EXT_OPTION_HIDE_KEY_PANEL) == 0; snapshot->posx = g_state.param.posx; snapshot->posy = g_state.param.posy; @@ -766,92 +649,65 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { snapshot->vertical_alignment = g_state.param.vertical_alignment; snapshot->panel_width = g_state.param.type == Type::Number ? 370 : 793; snapshot->panel_height = g_state.param.type == Type::Number ? 402 : 408; - if ((g_state.option & OPTION_USE_OVER_2K) != 0) { + if ((g_state.editor.GetOption() & OPTION_USE_OVER_2K) != 0) { snapshot->panel_width *= 2; snapshot->panel_height *= 2; } - snapshot->text = g_state.text; + snapshot->text = g_state.editor.GetText(); + snapshot->title.clear(); + snapshot->placeholder.clear(); return true; } static bool ApplyInsertText(uint64_t generation, std::u16string_view text) { - uint64_t revision = 0; - uint32_t cursor = 0; - uint32_t max_length = 0; - bool multiline = false; - TextFilter filter = nullptr; - std::u16string old_text; - std::u16string candidate; + uint64_t revision = 0; + bool multiline = false; + TextFilter filter = nullptr; + std::u16string old_text; + ImeCommon::TextEditEngine candidate; { std::scoped_lock lock(g_mutex); if (!g_state.open || g_state.generation != generation || text.empty()) { return false; } - uint32_t valid_length = 0; - multiline = (g_state.option & OPTION_MULTILINE) != 0; - if (!ValidateText(text.data(), static_cast(text.size()), multiline, - &valid_length) || - valid_length != text.size()) { + multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0; + if (!ImeCommon::IsValidInputText(text, multiline)) { return false; } - std::u16string allowed; - for (const char16_t value: text) { - if (IsAllowedInput(value, g_state.param.type, g_state.option)) { - allowed.push_back(value); - } - } - const size_t available = g_state.max_text_length - g_state.text.size(); - if (allowed.empty() || available == 0) { + candidate = g_state.editor; + if (!candidate.Insert(text)) { return false; } - allowed.resize(std::min(allowed.size(), available)); - if (!allowed.empty() && allowed.back() >= 0xd800 && allowed.back() <= 0xdbff) { - allowed.pop_back(); - } - if (allowed.empty()) { - return false; - } - revision = g_state.revision; - max_length = g_state.max_text_length; - filter = g_state.param.filter; - old_text = g_state.text; - candidate = old_text; - candidate.insert(g_state.cursor, allowed); - cursor = g_state.cursor + static_cast(allowed.size()); + revision = g_state.revision; + filter = g_state.param.filter; + old_text = g_state.editor.GetText(); } return CommitFilteredText(generation, revision, std::move(old_text), std::move(candidate), - cursor, filter, max_length, multiline); + filter, multiline); } static bool ApplyBackspace(uint64_t generation) { - uint64_t revision = 0; - uint32_t cursor = 0; - uint32_t max_length = 0; - bool multiline = false; - TextFilter filter = nullptr; - std::u16string old_text; - std::u16string candidate; + uint64_t revision = 0; + bool multiline = false; + TextFilter filter = nullptr; + std::u16string old_text; + ImeCommon::TextEditEngine candidate; { std::scoped_lock lock(g_mutex); - if (!g_state.open || g_state.generation != generation || g_state.cursor == 0) { + if (!g_state.open || g_state.generation != generation) { return false; } - uint32_t first = g_state.cursor - 1; - if (first > 0 && g_state.text[first] >= 0xdc00 && g_state.text[first] <= 0xdfff && - g_state.text[first - 1] >= 0xd800 && g_state.text[first - 1] <= 0xdbff) { - first--; + candidate = g_state.editor; + if (!candidate.Backspace()) { + return false; } - revision = g_state.revision; - max_length = g_state.max_text_length; - multiline = (g_state.option & OPTION_MULTILINE) != 0; - filter = g_state.param.filter; - old_text = g_state.text; - candidate = old_text; - candidate.erase(first, g_state.cursor - first); - cursor = first; + revision = g_state.revision; + multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0; + filter = g_state.param.filter; + old_text = g_state.editor.GetText(); } return CommitFilteredText(generation, revision, std::move(old_text), std::move(candidate), - cursor, filter, max_length, multiline); + filter, multiline); } static bool ApplyMoveCursor(uint64_t generation, int delta) { @@ -859,25 +715,14 @@ static bool ApplyMoveCursor(uint64_t generation, int delta) { if (!g_state.open || g_state.generation != generation || delta == 0) { return false; } - const uint32_t old_cursor = g_state.cursor; - int next = - std::clamp(static_cast(old_cursor) + delta, 0, static_cast(g_state.text.size())); - if (delta < 0 && next > 0 && next < static_cast(g_state.text.size()) && - g_state.text[next] >= 0xdc00 && g_state.text[next] <= 0xdfff && - g_state.text[next - 1] >= 0xd800 && g_state.text[next - 1] <= 0xdbff) { - next--; - } else if (delta > 0 && next > 0 && next < static_cast(g_state.text.size()) && - g_state.text[next - 1] >= 0xd800 && g_state.text[next - 1] <= 0xdbff && - g_state.text[next] >= 0xdc00 && g_state.text[next] <= 0xdfff) { - next++; - } - g_state.cursor = static_cast(next); - if (g_state.cursor == old_cursor) { + const uint32_t old_cursor = g_state.editor.GetCursor(); + if (!g_state.editor.MoveCursor(delta)) { return false; } - const uint32_t direction = g_state.cursor > old_cursor ? 2 : 1; - const uint32_t steps = static_cast( - std::abs(static_cast(g_state.cursor) - static_cast(old_cursor))); + const uint32_t new_cursor = g_state.editor.GetCursor(); + const uint32_t direction = new_cursor > old_cursor ? 2 : 1; + const uint32_t steps = static_cast( + std::abs(static_cast(new_cursor) - static_cast(old_cursor))); for (uint32_t i = 0; i < steps; i++) { QueuedEvent event; event.event.id = 2; @@ -962,7 +807,7 @@ void ApplyExternalInputs() { generation = g_state.generation; filter = g_state.extended.ext_keyboard_filter; user_id = g_state.param.user_id; - multiline = (g_state.option & OPTION_MULTILINE) != 0; + multiline = (g_state.editor.GetOption() & OPTION_MULTILINE) != 0; inputs.swap(g_state.external_inputs); } for (auto& input: inputs) { @@ -978,7 +823,7 @@ void ApplyExternalInputs() { uint16_t keycode = input.key.keycode; uint32_t status = input.key.status; if (filter(&input.key, &keycode, &status, nullptr) == 0) { - accepted = ApplyKeyboardFilterOutput(&input, keycode, status, multiline); + accepted = ImeCommon::ApplyKeyboardFilterOutput(&input, keycode, status, multiline); } } if (!accepted) { @@ -999,6 +844,8 @@ void ApplyExternalInputs() { } // namespace +#if !defined(KYTY_IME_TESTS) + LIB_VERSION("Ime", 1, "Ime", 1, 1); LIB_DEFINE(InitPlatform_1_Ime) { @@ -1017,4 +864,6 @@ LIB_DEFINE(InitPlatform_1_Ime) { LIB_FUNC("ua+13Hk9kKs", ImeKeyboardSetMode); } +#endif + } // namespace Libs::Ime diff --git a/src/libs/ime.h b/src/libs/ime.h index 195bafe..c53e1d7 100644 --- a/src/libs/ime.h +++ b/src/libs/ime.h @@ -1,7 +1,7 @@ #ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_ #define EMULATOR_INCLUDE_EMULATOR_LIBS_IME_H_ -#include "common/abi.h" +#include "libs/imeCommon.h" #include #include @@ -11,28 +11,19 @@ namespace Libs::Ime { constexpr uint32_t WORK_BUFFER_SIZE = 20 * 1024; -constexpr uint32_t MAX_TEXT_LENGTH = 2048; +constexpr uint32_t MAX_TEXT_LENGTH = ImeCommon::MAX_TEXT_LENGTH; -enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 }; -enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 }; -enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 }; +using Type = ImeCommon::Type; +using EnterLabel = ImeCommon::EnterLabel; +using Alignment = ImeCommon::Alignment; struct Event; -struct Keycode; -using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length, - const char16_t* source_text, - uint32_t source_text_length); +using Keycode = ImeCommon::Keycode; +using TextFilter = ImeCommon::TextFilter; using EventHandler = void(KYTY_SYSV_ABI*)(void* arg, const Event* event); -using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode, - uint32_t* out_status, void* reserved); - -struct Color { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; +using ExtKeyboardFilter = ImeCommon::ExtKeyboardFilter; +using Color = ImeCommon::Color; struct Caret { float x; @@ -75,16 +66,6 @@ struct KeyboardResourceIdArray { uint32_t resource_id[5]; }; -struct Keycode { - uint16_t keycode; - char16_t character; - uint32_t status; - uint32_t type; - int32_t user_id; - uint32_t resource_id; - uint64_t timestamp; -}; - union EventParam { Rect rect; EditText text; @@ -119,24 +100,7 @@ struct Param { int8_t reserved[8]; }; -struct ExtendedParam { - uint32_t option; - Color color_base; - Color color_line; - Color color_text_field; - Color color_preedit; - Color color_button_default; - Color color_button_function; - Color color_button_symbol; - Color color_text; - Color color_special; - uint32_t priority; - const char* additional_dictionary_path; - ExtKeyboardFilter ext_keyboard_filter; - uint32_t disable_device; - uint32_t ext_keyboard_mode; - int8_t reserved[60]; -}; +using ExtendedParam = ImeCommon::ExtendedParam; struct KeyboardParam { uint32_t option; @@ -156,22 +120,8 @@ struct KeyboardInfo { int8_t reserved[12]; }; -enum class ExternalAction : uint8_t { - None, - Text, - Backspace, - MoveLeft, - MoveRight, - Cancel, - Accept, - Newline, -}; - -struct ExternalInput { - Keycode key; - ExternalAction action; - std::u16string text; -}; +using ExternalAction = ImeCommon::ExternalAction; +using ExternalInput = ImeCommon::ExternalInput; static_assert(sizeof(Caret) == 0x10); static_assert(sizeof(TextGeometry) == 0x10); @@ -192,30 +142,9 @@ static_assert(sizeof(KeyboardResourceIdArray) == 0x18); static_assert(sizeof(KeyboardInfo) == 0x24); static_assert(sizeof(Keycode) == 0x20); -struct VisualState { - bool active; - uint64_t revision; -}; - -struct HostSnapshot { - uint64_t generation; - Type type; - EnterLabel enter_label; - uint32_t option; - uint32_t max_text_length; - uint32_t cursor; - uint32_t disable_device; - bool key_panel_visible; - float posx; - float posy; - Alignment horizontal_alignment; - Alignment vertical_alignment; - uint32_t panel_width; - uint32_t panel_height; - std::u16string text; -}; - -using VisibilityCallback = void (*)(bool visible, uint64_t generation); +using VisualState = ImeCommon::VisualState; +using HostSnapshot = ImeCommon::HostSnapshot; +using VisibilityCallback = ImeCommon::VisibilityCallback; void KYTY_SYSV_ABI ImeParamInit(Param* param); int KYTY_SYSV_ABI ImeGetPanelSize(const Param* param, uint32_t* width, uint32_t* height); diff --git a/src/libs/imeCommon.cpp b/src/libs/imeCommon.cpp new file mode 100644 index 0000000..61ff36c --- /dev/null +++ b/src/libs/imeCommon.cpp @@ -0,0 +1,259 @@ +#include "libs/imeCommon.h" + +#include +#include + +namespace Libs::ImeCommon { +namespace { + +constexpr bool IsHighSurrogate(char16_t value) { + return value >= 0xd800 && value <= 0xdbff; +} + +constexpr bool IsLowSurrogate(char16_t value) { + return value >= 0xdc00 && value <= 0xdfff; +} + +char16_t HidCharacter(uint16_t keycode, uint32_t status) { + const bool shift = (status & 0x00002200) != 0; + const bool caps = (status & 0x00020000) != 0; + if (keycode >= 4 && keycode <= 29) { + return static_cast(((shift != caps) ? u'A' : u'a') + keycode - 4); + } + if (keycode >= 30 && keycode <= 39) { + static constexpr char16_t plain[] = u"1234567890"; + static constexpr char16_t shifted[] = u"!@#$%^&*()"; + return (shift ? shifted : plain)[keycode - 30]; + } + if (keycode == 44) { + return u' '; + } + if (keycode >= 45 && keycode <= 56) { + static constexpr char16_t plain[] = u"-=[]\\#;'`,./"; + static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?"; + return (shift ? shifted : plain)[keycode - 45]; + } + if (keycode >= 89 && keycode <= 98) { + static constexpr char16_t keypad[] = u"1234567890"; + return keypad[keycode - 89]; + } + return keycode == 99 ? u'.' : u'\0'; +} + +} // namespace + +bool IsValidUtf16(std::u16string_view text) { + for (size_t i = 0; i < text.size(); i++) { + if (IsHighSurrogate(text[i])) { + if (++i >= text.size() || !IsLowSurrogate(text[i])) { + return false; + } + } else if (IsLowSurrogate(text[i])) { + return false; + } + } + return true; +} + +bool IsValidInputText(std::u16string_view text, bool multiline) { + if (!IsValidUtf16(text)) { + return false; + } + return std::none_of(text.begin(), text.end(), [multiline](char16_t value) { + return value == u'\0' || (!multiline && (value == u'\n' || value == u'\r')); + }); +} + +bool IsAllowedInput(char16_t value, Type type, uint32_t option) { + if (value == u'\r' || value == u'\0') { + return false; + } + if (value == u'\n') { + return (option & OPTION_MULTILINE) != 0; + } + if (type == Type::Number) { + return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.'; + } + if (type == Type::BasicLatin) { + return value >= u' ' && value <= u'~'; + } + return true; +} + +uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) { + cursor = std::min(cursor, static_cast(text.size())); + if (cursor > 0 && cursor < text.size() && IsHighSurrogate(text[cursor - 1]) && + IsLowSurrogate(text[cursor])) { + cursor++; + } + return cursor; +} + +void ClampText(std::u16string* text, uint32_t limit) { + if (text->size() <= limit) { + return; + } + text->resize(limit); + if (!text->empty() && IsHighSurrogate(text->back())) { + text->pop_back(); + } +} + +EditDelta ComputeEditDelta(std::u16string_view before, std::u16string_view after) { + size_t prefix = 0; + while (prefix < before.size() && prefix < after.size() && before[prefix] == after[prefix]) { + prefix++; + } + size_t before_tail = before.size(); + size_t after_tail = after.size(); + while (before_tail > prefix && after_tail > prefix && + before[before_tail - 1] == after[after_tail - 1]) { + before_tail--; + after_tail--; + } + return {static_cast(prefix), + static_cast(after_tail - prefix) - static_cast(before_tail - prefix)}; +} + +bool RunTextFilter(TextFilter filter, const std::u16string& source, uint32_t output_capacity, + std::u16string* output) { + if (filter == nullptr || output == nullptr) { + return false; + } + std::vector buffer(static_cast(output_capacity) + 1, u'\0'); + uint32_t output_length = output_capacity; + if (filter(buffer.data(), &output_length, source.c_str(), + static_cast(source.size())) != 0 || + output_length > output_capacity || + !IsValidUtf16(std::u16string_view(buffer.data(), output_length))) { + return false; + } + output->assign(buffer.data(), buffer.data() + output_length); + return true; +} + +bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status, + bool multiline) { + constexpr uint32_t KEYCODE_VALID = 0x00000001; + constexpr uint32_t CHARACTER_VALID = 0x00000002; + if (keycode == input->key.keycode && status == input->key.status) { + return true; + } + if ((status & KEYCODE_VALID) == 0 || keycode == 0) { + return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0; + } + input->key.keycode = keycode; + input->key.status = status; + input->text.clear(); + switch (keycode) { + case 40: + case 88: + case 158: + input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept; + break; + case 41: input->action = ExternalAction::Cancel; break; + case 42: + case 187: input->action = ExternalAction::Backspace; break; + case 43: + case 186: input->action = ExternalAction::None; break; + case 79: input->action = ExternalAction::MoveRight; break; + case 80: input->action = ExternalAction::MoveLeft; break; + default: { + const char16_t character = HidCharacter(keycode, status); + if (character == u'\0') { + return false; + } + input->action = ExternalAction::Text; + input->key.character = character; + input->text.push_back(character); + break; + } + } + return true; +} + +void TextEditEngine::Reset(Type type, uint32_t option, uint32_t max_length, std::u16string text) { + m_type = type; + m_option = option; + m_max_length = max_length; + ClampText(&text, max_length); + m_text = std::move(text); + m_cursor = static_cast(m_text.size()); +} + +bool TextEditEngine::Insert(std::u16string_view text, EditDelta* edit) { + if (text.empty() || !IsValidUtf16(text)) { + return false; + } + std::u16string allowed; + allowed.reserve(text.size()); + for (const char16_t value: text) { + if (IsAllowedInput(value, m_type, m_option)) { + allowed.push_back(value); + } + } + const size_t available = m_max_length - m_text.size(); + if (allowed.empty() || available == 0) { + return false; + } + allowed.resize(std::min(allowed.size(), available)); + if (!allowed.empty() && IsHighSurrogate(allowed.back())) { + allowed.pop_back(); + } + if (allowed.empty()) { + return false; + } + if (edit != nullptr) { + *edit = {m_cursor, static_cast(allowed.size())}; + } + m_text.insert(m_cursor, allowed); + m_cursor += static_cast(allowed.size()); + return true; +} + +bool TextEditEngine::Backspace(EditDelta* edit) { + if (m_cursor == 0) { + return false; + } + uint32_t first = m_cursor - 1; + if (first > 0 && IsLowSurrogate(m_text[first]) && IsHighSurrogate(m_text[first - 1])) { + first--; + } + if (edit != nullptr) { + *edit = {first, -static_cast(m_cursor - first)}; + } + m_text.erase(first, m_cursor - first); + m_cursor = first; + return true; +} + +bool TextEditEngine::MoveCursor(int delta) { + if (delta == 0) { + return false; + } + int next = std::clamp(static_cast(m_cursor) + delta, 0, static_cast(m_text.size())); + if (delta < 0 && next > 0 && next < static_cast(m_text.size()) && + IsLowSurrogate(m_text[next]) && IsHighSurrogate(m_text[next - 1])) { + next--; + } else if (delta > 0 && next > 0 && next < static_cast(m_text.size()) && + IsHighSurrogate(m_text[next - 1]) && IsLowSurrogate(m_text[next])) { + next++; + } + if (next == static_cast(m_cursor)) { + return false; + } + m_cursor = static_cast(next); + return true; +} + +void TextEditEngine::ReplaceText(std::u16string text, uint32_t cursor) { + ClampText(&text, m_max_length); + m_text = std::move(text); + m_cursor = NormalizeCursor(m_text, cursor); +} + +void TextEditEngine::SetCursor(uint32_t cursor) { + m_cursor = NormalizeCursor(m_text, cursor); +} + +} // namespace Libs::ImeCommon diff --git a/src/libs/imeCommon.h b/src/libs/imeCommon.h new file mode 100644 index 0000000..c527b69 --- /dev/null +++ b/src/libs/imeCommon.h @@ -0,0 +1,159 @@ +#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_ +#define EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_ + +#include "common/abi.h" + +#include +#include +#include +#include + +namespace Libs::ImeCommon { + +constexpr uint32_t MAX_TEXT_LENGTH = 2048; +constexpr uint32_t OPTION_MULTILINE = 0x00000001; +constexpr uint32_t OPTION_NO_AUTO_CAPITALIZE = 0x00000002; +constexpr uint32_t OPTION_PASSWORD = 0x00000004; +constexpr uint32_t OPTION_EXT_KEYBOARD = 0x00000010; +constexpr uint32_t OPTION_FIXED_POSITION = 0x00000040; +constexpr uint32_t OPTION_DISABLE_POSITION_ADJUST = 0x00000800; +constexpr uint32_t OPTION_EXPANDED_PREEDIT = 0x00001000; +constexpr uint32_t OPTION_USE_OVER_2K = 0x00004000; +constexpr uint32_t EXT_OPTION_HIDE_KEY_PANEL = 0x00000400; +constexpr uint32_t DISABLE_DEVICE_CONTROLLER = 0x00000001; +constexpr uint32_t DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002; + +enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 }; +enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 }; +enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 }; + +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +struct Keycode { + uint16_t keycode; + char16_t character; + uint32_t status; + uint32_t type; + int32_t user_id; + uint32_t resource_id; + uint64_t timestamp; +}; + +using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length, + const char16_t* source_text, + uint32_t source_text_length); +using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode, + uint32_t* out_status, void* reserved); + +struct ExtendedParam { + uint32_t option; + Color color_base; + Color color_line; + Color color_text_field; + Color color_preedit; + Color color_button_default; + Color color_button_function; + Color color_button_symbol; + Color color_text; + Color color_special; + uint32_t priority; + const char* additional_dictionary_path; + ExtKeyboardFilter ext_keyboard_filter; + uint32_t disable_device; + uint32_t ext_keyboard_mode; + int8_t reserved[60]; +}; + +enum class ExternalAction : uint8_t { + None, + Text, + Backspace, + MoveLeft, + MoveRight, + Cancel, + Accept, + Newline, +}; + +struct ExternalInput { + Keycode key {}; + ExternalAction action = ExternalAction::None; + std::u16string text; +}; + +struct VisualState { + bool active; + uint64_t revision; +}; + +struct HostSnapshot { + uint64_t generation = 0; + Type type = Type::Default; + EnterLabel enter_label = EnterLabel::Default; + uint32_t option = 0; + uint32_t max_text_length = 0; + uint32_t cursor = 0; + uint32_t disable_device = 0; + bool key_panel_visible = true; + float posx = 0.0f; + float posy = 0.0f; + Alignment horizontal_alignment = Alignment::Start; + Alignment vertical_alignment = Alignment::Start; + uint32_t panel_width = 0; + uint32_t panel_height = 0; + std::u16string text; + std::u16string title; + std::u16string placeholder; +}; + +using VisibilityCallback = void (*)(bool visible, uint64_t generation); + +struct EditDelta { + uint32_t index = 0; + int32_t length = 0; +}; + +class TextEditEngine { +public: + void Reset(Type type, uint32_t option, uint32_t max_length, std::u16string text); + + bool Insert(std::u16string_view text, EditDelta* edit = nullptr); + bool Backspace(EditDelta* edit = nullptr); + bool MoveCursor(int delta); + + void ReplaceText(std::u16string text, uint32_t cursor); + void SetCursor(uint32_t cursor); + + [[nodiscard]] Type GetType() const { return m_type; } + [[nodiscard]] uint32_t GetOption() const { return m_option; } + [[nodiscard]] uint32_t GetMaxLength() const { return m_max_length; } + [[nodiscard]] uint32_t GetCursor() const { return m_cursor; } + [[nodiscard]] const std::u16string& GetText() const { return m_text; } + +private: + Type m_type = Type::Default; + uint32_t m_option = 0; + uint32_t m_max_length = 0; + uint32_t m_cursor = 0; + std::u16string m_text; +}; + +[[nodiscard]] bool IsValidUtf16(std::u16string_view text); +[[nodiscard]] bool IsValidInputText(std::u16string_view text, bool multiline); +[[nodiscard]] bool IsAllowedInput(char16_t value, Type type, uint32_t option); +[[nodiscard]] uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor); +void ClampText(std::u16string* text, uint32_t limit); +[[nodiscard]] EditDelta ComputeEditDelta(std::u16string_view before, std::u16string_view after); +[[nodiscard]] bool RunTextFilter(TextFilter filter, const std::u16string& source, + uint32_t output_capacity, std::u16string* output); +[[nodiscard]] bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, + uint32_t status, bool multiline); + +} // namespace Libs::ImeCommon + +#endif // EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_ diff --git a/src/libs/imeDialog.cpp b/src/libs/imeDialog.cpp index 568f78e..fb777f5 100644 --- a/src/libs/imeDialog.cpp +++ b/src/libs/imeDialog.cpp @@ -53,9 +53,8 @@ struct State { ExtendedParam extended {}; bool input_changed = false; bool commit_pending = false; - uint32_t cursor = 0; std::u16string original_text; - std::u16string current_text; + ImeCommon::TextEditEngine editor; std::u16string title; std::u16string placeholder; std::vector external_inputs; @@ -71,8 +70,6 @@ bool AllZero(const int8_t* data, size_t size) { return std::all_of(data, data + size, [](int8_t value) { return value == 0; }); } -bool IsValidUtf16(std::u16string_view text); - bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) { out->clear(); if (text == nullptr) { @@ -82,7 +79,7 @@ bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) { for (uint32_t i = 0; i <= limit; ++i) { const char16_t value = text[i]; if (value == u'\0') { - return IsValidUtf16(*out); + return ImeCommon::IsValidUtf16(*out); } if (i == limit) { return false; @@ -92,128 +89,6 @@ bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) { return false; } -bool IsValidUtf16(std::u16string_view text) { - for (size_t i = 0; i < text.size(); i++) { - const char16_t current = text[i]; - if (current >= 0xd800 && current <= 0xdbff) { - if (++i >= text.size() || text[i] < 0xdc00 || text[i] > 0xdfff) { - return false; - } - } else if (current >= 0xdc00 && current <= 0xdfff) { - return false; - } - } - return true; -} - -bool IsAllowedInput(char16_t value, Type type, uint32_t option) { - if (value == u'\r') { - return false; - } - if (value == u'\n') { - return (option & OPTION_MULTILINE) != 0; - } - if (value == u'\0') { - return false; - } - if (type == Type::Number) { - return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.'; - } - if (type == Type::BasicLatin) { - return value >= u' ' && value <= u'~'; - } - return true; -} - -char16_t HidCharacter(uint16_t keycode, uint32_t status) { - const bool shift = (status & 0x00002200) != 0; - const bool caps = (status & 0x00020000) != 0; - if (keycode >= 4 && keycode <= 29) { - const bool upper = shift != caps; - return static_cast((upper ? u'A' : u'a') + keycode - 4); - } - if (keycode >= 30 && keycode <= 39) { - static constexpr char16_t plain[] = u"1234567890"; - static constexpr char16_t shifted[] = u"!@#$%^&*()"; - return (shift ? shifted : plain)[keycode - 30]; - } - if (keycode == 44) { - return u' '; - } - if (keycode >= 45 && keycode <= 56) { - static constexpr char16_t plain[] = u"-=[]\\#;'`,./"; - static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?"; - return (shift ? shifted : plain)[keycode - 45]; - } - if (keycode >= 89 && keycode <= 98) { - static constexpr char16_t keypad[] = u"1234567890"; - return keypad[keycode - 89]; - } - if (keycode == 99) { - return u'.'; - } - return u'\0'; -} - -bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status, - bool multiline) { - constexpr uint32_t KEYCODE_VALID = 0x00000001; - constexpr uint32_t CHARACTER_VALID = 0x00000002; - if (keycode == input->key.keycode && status == input->key.status) { - return true; - } - if ((status & KEYCODE_VALID) == 0 || keycode == 0) { - return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0; - } - input->key.keycode = keycode; - input->key.status = status; - input->text.clear(); - switch (keycode) { - case 40: - case 88: - case 158: - input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept; - break; - case 41: input->action = ExternalAction::Cancel; break; - case 42: - case 187: input->action = ExternalAction::Backspace; break; - case 43: - case 186: input->action = ExternalAction::None; break; - case 79: input->action = ExternalAction::MoveRight; break; - case 80: input->action = ExternalAction::MoveLeft; break; - default: { - const char16_t character = HidCharacter(keycode, status); - if (character == u'\0') { - return false; - } - input->action = ExternalAction::Text; - input->key.character = character; - input->text.push_back(character); - break; - } - } - return true; -} - -void ClampText(std::u16string* text, uint32_t limit) { - if (text->size() <= limit) { - return; - } - text->resize(limit); - if (!text->empty() && text->back() >= 0xd800 && text->back() <= 0xdbff) { - text->pop_back(); - } -} - -uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) { - cursor = std::min(cursor, static_cast(text.size())); - if (cursor > 0 && cursor < text.size() && text[cursor - 1] >= 0xd800 && - text[cursor - 1] <= 0xdbff && text[cursor] >= 0xdc00 && text[cursor] <= 0xdfff) { - cursor++; - } - return cursor; -} - void WriteGuestText(const State& state, const std::u16string& text) { if (state.param.input_text_buffer == nullptr) { return; @@ -355,7 +230,7 @@ void ApplyFilterAndCommit() { return; } filter = g_state.input_changed ? g_state.param.filter : nullptr; - source = g_state.current_text; + source = g_state.editor.GetText(); max_length = g_state.param.max_text_length; generation = g_state.generation; revision = g_state.revision; @@ -363,14 +238,10 @@ void ApplyFilterAndCommit() { } if (filter != nullptr) { - std::vector output(IME_DIALOG_MAX_TEXT_LENGTH + 1, u'\0'); - uint32_t output_length = IME_DIALOG_MAX_TEXT_LENGTH; - if (filter(output.data(), &output_length, source.c_str(), - static_cast(source.size())) == 0 && - output_length <= IME_DIALOG_MAX_TEXT_LENGTH && - IsValidUtf16(std::u16string_view(output.data(), output_length))) { - source.assign(output.data(), output.data() + output_length); - ClampText(&source, max_length); + std::u16string filtered; + if (ImeCommon::RunTextFilter(filter, source, IME_DIALOG_MAX_TEXT_LENGTH, &filtered)) { + source = std::move(filtered); + ImeCommon::ClampText(&source, max_length); } } @@ -386,13 +257,12 @@ void ApplyFilterAndCommit() { return; } if (!g_state.input_changed) { - g_state.current_text = std::move(source); - g_state.cursor = NormalizeCursor(g_state.current_text, g_state.cursor); + g_state.editor.ReplaceText(std::move(source), g_state.editor.GetCursor()); } const auto& committed = g_state.status == Status::Finished && g_state.end_status != EndStatus::Ok ? g_state.original_text - : g_state.current_text; + : g_state.editor.GetText(); WriteGuestText(g_state, committed); g_state.commit_pending = false; } @@ -451,8 +321,8 @@ void ApplyExternalInputs() { uint16_t output_keycode = input.key.keycode; uint32_t output_status = input.key.status; if (filter(&input.key, &output_keycode, &output_status, nullptr) == 0) { - accepted = - ApplyKeyboardFilterOutput(&input, output_keycode, output_status, multiline); + accepted = ImeCommon::ApplyKeyboardFilterOutput(&input, output_keycode, + output_status, multiline); } } if (!accepted) { @@ -528,9 +398,9 @@ int KYTY_SYSV_ABI ImeDialogInit(const Param* param, const ExtendedParam* extende if (extended != nullptr) { g_state.extended = *extended; } - g_state.original_text = initial; - g_state.current_text = std::move(initial); - g_state.cursor = static_cast(g_state.current_text.size()); + g_state.original_text = initial; + g_state.editor.Reset(param->type, param->option, param->max_text_length, + std::move(initial)); g_state.title = std::move(title); g_state.placeholder = std::move(placeholder); g_state.commit_pending = true; @@ -664,7 +534,7 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { snapshot->enter_label = g_state.param.enter_label; snapshot->option = g_state.param.option; snapshot->max_text_length = g_state.param.max_text_length; - snapshot->cursor = g_state.cursor; + snapshot->cursor = g_state.editor.GetCursor(); snapshot->disable_device = g_state.extended.disable_device; snapshot->key_panel_visible = (g_state.param.option & OPTION_EXT_KEYBOARD) == 0 || (g_state.extended.option & 0x00000400) == 0; @@ -674,7 +544,7 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { snapshot->vertical_alignment = g_state.param.vertical_alignment; ComputePanelSize(g_state.param, &g_state.extended, &snapshot->panel_width, &snapshot->panel_height); - snapshot->text = g_state.current_text; + snapshot->text = g_state.editor.GetText(); snapshot->title = g_state.title; snapshot->placeholder = g_state.placeholder; return true; @@ -682,38 +552,9 @@ bool GetHostSnapshot(HostSnapshot* snapshot) { bool HostInsertText(uint64_t generation, std::u16string_view text) { std::scoped_lock lock(g_mutex); - if (!MatchRunningGeneration(generation) || text.empty()) { + if (!MatchRunningGeneration(generation) || !g_state.editor.Insert(text)) { return false; } - if (!IsValidUtf16(text)) { - return false; - } - std::u16string allowed; - allowed.reserve(text.size()); - for (const char16_t value: text) { - if (IsAllowedInput(value, g_state.param.type, g_state.param.option)) { - allowed.push_back(value); - } - } - if (allowed.empty()) { - return false; - } - const size_t available = g_state.param.max_text_length - g_state.current_text.size(); - if (available == 0) { - return false; - } - std::u16string insertion(std::u16string_view(allowed).substr(0, available)); - if (insertion.size() < allowed.size() && !insertion.empty() && insertion.back() >= 0xd800 && - insertion.back() <= 0xdbff) { - insertion.pop_back(); - } - if (insertion.empty()) { - return false; - } - std::u16string candidate = g_state.current_text; - candidate.insert(g_state.cursor, insertion); - g_state.current_text = std::move(candidate); - g_state.cursor += static_cast(insertion.size()); g_state.input_changed = true; g_state.commit_pending = true; return true; @@ -721,17 +562,9 @@ bool HostInsertText(uint64_t generation, std::u16string_view text) { bool HostBackspace(uint64_t generation) { std::scoped_lock lock(g_mutex); - if (!MatchRunningGeneration(generation) || g_state.cursor == 0) { + if (!MatchRunningGeneration(generation) || !g_state.editor.Backspace()) { return false; } - uint32_t first = g_state.cursor - 1; - if (first > 0 && g_state.current_text[first] >= 0xdc00 && - g_state.current_text[first] <= 0xdfff && g_state.current_text[first - 1] >= 0xd800 && - g_state.current_text[first - 1] <= 0xdbff) { - first--; - } - g_state.current_text.erase(first, g_state.cursor - first); - g_state.cursor = first; g_state.input_changed = true; g_state.commit_pending = true; return true; @@ -739,26 +572,7 @@ bool HostBackspace(uint64_t generation) { bool HostMoveCursor(uint64_t generation, int delta) { std::scoped_lock lock(g_mutex); - if (!MatchRunningGeneration(generation) || delta == 0) { - return false; - } - int next = std::clamp(static_cast(g_state.cursor) + delta, 0, - static_cast(g_state.current_text.size())); - if (delta < 0 && next > 0 && next < static_cast(g_state.current_text.size()) && - g_state.current_text[next] >= 0xdc00 && g_state.current_text[next] <= 0xdfff && - g_state.current_text[next - 1] >= 0xd800 && g_state.current_text[next - 1] <= 0xdbff) { - next--; - } else if (delta > 0 && next > 0 && next < static_cast(g_state.current_text.size()) && - g_state.current_text[next - 1] >= 0xd800 && - g_state.current_text[next - 1] <= 0xdbff && g_state.current_text[next] >= 0xdc00 && - g_state.current_text[next] <= 0xdfff) { - next++; - } - if (next == static_cast(g_state.cursor)) { - return false; - } - g_state.cursor = static_cast(next); - return true; + return MatchRunningGeneration(generation) && g_state.editor.MoveCursor(delta); } bool HostAccept(uint64_t generation) { diff --git a/src/libs/imeDialog.h b/src/libs/imeDialog.h index c6c5282..69a4c08 100644 --- a/src/libs/imeDialog.h +++ b/src/libs/imeDialog.h @@ -1,7 +1,7 @@ #ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_ #define EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_ -#include "common/abi.h" +#include "libs/imeCommon.h" #include #include @@ -10,53 +10,43 @@ namespace Libs::Dialog::ImeDialog { -constexpr uint32_t IME_DIALOG_MAX_TEXT_LENGTH = 2048; +constexpr uint32_t IME_DIALOG_MAX_TEXT_LENGTH = ImeCommon::MAX_TEXT_LENGTH; constexpr uint32_t IME_DIALOG_MAX_TITLE_LENGTH = 128; constexpr uint32_t IME_DIALOG_MAX_PLACEHOLDER_LENGTH = 64; enum class Status : uint32_t { None = 0, Running = 1, Finished = 2 }; enum class EndStatus : uint32_t { Ok = 0, UserCanceled = 1, Aborted = 2 }; -enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 }; -enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 }; -enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 }; +using Type = ImeCommon::Type; +using EnterLabel = ImeCommon::EnterLabel; +using Alignment = ImeCommon::Alignment; enum Option : uint32_t { - OPTION_MULTILINE = 0x00000001, - OPTION_NO_AUTO_CAPITALIZE = 0x00000002, - OPTION_PASSWORD = 0x00000004, + OPTION_MULTILINE = ImeCommon::OPTION_MULTILINE, + OPTION_NO_AUTO_CAPITALIZE = ImeCommon::OPTION_NO_AUTO_CAPITALIZE, + OPTION_PASSWORD = ImeCommon::OPTION_PASSWORD, OPTION_LANGUAGES_FORCED = 0x00000008, - OPTION_EXT_KEYBOARD = 0x00000010, + OPTION_EXT_KEYBOARD = ImeCommon::OPTION_EXT_KEYBOARD, OPTION_NO_LEARNING = 0x00000020, - OPTION_FIXED_POSITION = 0x00000040, + OPTION_FIXED_POSITION = ImeCommon::OPTION_FIXED_POSITION, OPTION_DISABLE_COPY_PASTE = 0x00000080, OPTION_DISABLE_RESUME = 0x00000100, OPTION_DISABLE_AUTO_SPACE = 0x00000200, - OPTION_DISABLE_POSITION_ADJ = 0x00000800, - OPTION_EXPANDED_PREEDIT = 0x00001000, + OPTION_DISABLE_POSITION_ADJ = ImeCommon::OPTION_DISABLE_POSITION_ADJUST, + OPTION_EXPANDED_PREEDIT = ImeCommon::OPTION_EXPANDED_PREEDIT, OPTION_JAPANESE_CAPS_LOCK = 0x00002000, - OPTION_USE_OVER_2K = 0x00004000, + OPTION_USE_OVER_2K = ImeCommon::OPTION_USE_OVER_2K, }; enum DisableDevice : uint32_t { - DISABLE_DEVICE_CONTROLLER = 0x00000001, - DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002, + DISABLE_DEVICE_CONTROLLER = ImeCommon::DISABLE_DEVICE_CONTROLLER, + DISABLE_DEVICE_EXT_KEYBOARD = ImeCommon::DISABLE_DEVICE_EXT_KEYBOARD, DISABLE_DEVICE_REMOTE_OSK = 0x00000004, }; -struct Color { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -}; - -struct Keycode; - -using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length, - const char16_t* source_text, - uint32_t source_text_length); -using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode, - uint32_t* out_status, void* reserved); +using Color = ImeCommon::Color; +using Keycode = ImeCommon::Keycode; +using TextFilter = ImeCommon::TextFilter; +using ExtKeyboardFilter = ImeCommon::ExtKeyboardFilter; struct Param { int32_t user_id; @@ -82,24 +72,7 @@ struct Result { int8_t reserved[12]; }; -struct ExtendedParam { - uint32_t option; - Color color_base; - Color color_line; - Color color_text_field; - Color color_preedit; - Color color_button_default; - Color color_button_function; - Color color_button_symbol; - Color color_text; - Color color_special; - uint32_t priority; - const char* additional_dictionary_path; - ExtKeyboardFilter ext_keyboard_filter; - uint32_t disable_device; - uint32_t ext_keyboard_mode; - int8_t reserved[60]; -}; +using ExtendedParam = ImeCommon::ExtendedParam; struct PositionAndForm { uint32_t type; @@ -111,32 +84,8 @@ struct PositionAndForm { uint32_t height; }; -struct Keycode { - uint16_t keycode; - char16_t character; - uint32_t status; - uint32_t type; - int32_t user_id; - uint32_t resource_id; - uint64_t timestamp; -}; - -enum class ExternalAction : uint8_t { - None, - Text, - Backspace, - MoveLeft, - MoveRight, - Cancel, - Accept, - Newline, -}; - -struct ExternalInput { - Keycode key; - ExternalAction action; - std::u16string text; -}; +using ExternalAction = ImeCommon::ExternalAction; +using ExternalInput = ImeCommon::ExternalInput; static_assert(sizeof(Param) == 0x60); static_assert(offsetof(Param, input_text_buffer) == 0x28); @@ -147,32 +96,9 @@ static_assert(offsetof(ExtendedParam, additional_dictionary_path) == 0x30); static_assert(sizeof(PositionAndForm) == 0x1c); static_assert(sizeof(Keycode) == 0x20); -struct VisualState { - bool active; - uint64_t revision; -}; - -struct HostSnapshot { - uint64_t generation; - Type type; - EnterLabel enter_label; - uint32_t option; - uint32_t max_text_length; - uint32_t cursor; - uint32_t disable_device; - bool key_panel_visible; - float posx; - float posy; - Alignment horizontal_alignment; - Alignment vertical_alignment; - uint32_t panel_width; - uint32_t panel_height; - std::u16string text; - std::u16string title; - std::u16string placeholder; -}; - -using VisibilityCallback = void (*)(bool visible, uint64_t generation); +using VisualState = ImeCommon::VisualState; +using HostSnapshot = ImeCommon::HostSnapshot; +using VisibilityCallback = ImeCommon::VisibilityCallback; int KYTY_SYSV_ABI ImeDialogGetPanelSize(const Param* param, uint32_t* width, uint32_t* height); int KYTY_SYSV_ABI ImeDialogGetPanelSizeExtended(const Param* param, const ExtendedParam* extended, diff --git a/src/libs/network.cpp b/src/libs/network.cpp index 4a7f29e..55967ba 100644 --- a/src/libs/network.cpp +++ b/src/libs/network.cpp @@ -237,15 +237,16 @@ private: static Network* g_net = nullptr; -KYTY_SUBSYSTEM_INIT(Network) { +void Initialize() { EXIT_IF(g_net != nullptr); g_net = new Network; } -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Network) {} - -KYTY_SUBSYSTEM_DESTROY(Network) {} +void Shutdown() { + delete g_net; + g_net = nullptr; +} int Network::PoolCreate(const char* name, int size) { Common::LockGuard lock(m_mutex); diff --git a/src/libs/network.h b/src/libs/network.h index a4e74ed..afd8c29 100644 --- a/src/libs/network.h +++ b/src/libs/network.h @@ -3,11 +3,16 @@ #include "common/abi.h" #include "common/common.h" -#include "common/subsystems.h" - namespace Libs::Network { -KYTY_SUBSYSTEM_DEFINE(Network); +void Initialize(); +void Shutdown(); + +struct Lifecycle { + static constexpr const char* name = "Network"; + static constexpr auto initialize = Libs::Network::Initialize; + static constexpr auto shutdown = Libs::Network::Shutdown; +}; namespace Net { diff --git a/src/loader/timer.cpp b/src/loader/timer.cpp index 994e865..0d21f74 100644 --- a/src/loader/timer.cpp +++ b/src/loader/timer.cpp @@ -2,21 +2,12 @@ #include "common/abi.h" #include "common/dateTime.h" -#include "common/subsystems.h" #include "loader/timer.h" namespace Loader::Timer { static Common::Timer g_timer; -KYTY_SUBSYSTEM_INIT(Timer) { - Start(); -} - -KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Timer) {} - -KYTY_SUBSYSTEM_DESTROY(Timer) {} - void Start() { g_timer.Start(); } diff --git a/src/loader/timer.h b/src/loader/timer.h index 3097c3e..f424261 100644 --- a/src/loader/timer.h +++ b/src/loader/timer.h @@ -4,18 +4,19 @@ #include "common/abi.h" #include "common/common.h" #include "common/dateTime.h" -#include "common/subsystems.h" - namespace Loader::Timer { -KYTY_SUBSYSTEM_DEFINE(Timer); - void Start(); double GetTimeMs(); Common::Time GetTime(); uint64_t GetCounter(); uint64_t GetFrequency(); +struct Lifecycle { + static constexpr const char* name = "Timer"; + static constexpr auto initialize = Loader::Timer::Start; +}; + } // namespace Loader::Timer #endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_ */ diff --git a/src/main.cpp b/src/main.cpp index 8463d46..b731620 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ #include "common/common.h" -#include "common/commonSubsystem.h" #include "common/dateTime.h" #include "common/debug.h" #include "common/file.h" @@ -7,6 +6,7 @@ #include "common/platform/sysDbg.h" #include "common/stringUtils.h" #include "common/threads.h" +#include "common/virtualMemory.h" #include "emulator.h" #include "kytyGitVersion.h" @@ -275,46 +275,28 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he } int main(int argc, char* argv[]) { - auto& slist = *SubsystemsList::Instance(); - - slist.SetArgs(argc, argv); - - auto* core = CommonSubsystem::Instance(); - auto* threads = ThreadsSubsystem::Instance(); - - slist.Add(core, {}); - slist.Add(threads, {core}); - - if (!slist.InitAll(false)) { - ::printf("Failed to initialize '%s' subsystem: %s\n", slist.GetFailName(), - slist.GetFailMsg()); - return 1; - } + VirtualMemory::Init(); + InitializeThreads(); RunOptions options; bool show_help = false; if (argc < 2) { PrintUsage(); - slist.DestroyAll(false); return 0; } if (!ParseArgs(argc, argv, options, show_help)) { PrintUsage(); - slist.DestroyAll(false); return 1; } if (show_help) { PrintUsage(); - slist.DestroyAll(false); return 0; } Run(options); - slist.DestroyAll(false); - return 0; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 3e3a6b9..e8aa661 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -1,6 +1,7 @@ #include "common/assert.h" #include "common/emulatorConfig.h" #include "common/logging/log.h" +#include "common/subsystems.h" #include "common/threads.h" #include "gpu_test_shaders/gpu_test_ms_depth_spv.h" #include "graphics/guest_gpu/command_processor/commandProcessor.h" @@ -706,13 +707,14 @@ void Require(const char* shader_name, const char* stage, bool value, const std:: void EnsureConfigInitialized() { static bool config_initialized = false; if (!config_initialized) { - Common::ThreadsSubsystem::Instance()->Init(nullptr); - Config::ConfigSubsystem::Instance()->Init(nullptr); + static Common::Subsystems subsystems; + Common::InitializeThreads(); + subsystems.Initialize(); Config::ConfigOptions options; options.printf_direction = Config::OutputDirection::Silent; Config::Load(options); - Log::LogSubsystem::Instance()->Init(nullptr); - Libs::LibKernel::Memory::MemorySubsystem::Instance()->Init(nullptr); + subsystems.Initialize(); + subsystems.Initialize(); config_initialized = true; } } diff --git a/tests/VirtualMemoryAllocationTests.cpp b/tests/VirtualMemoryAllocationTests.cpp index f78e3ce..259d762 100644 --- a/tests/VirtualMemoryAllocationTests.cpp +++ b/tests/VirtualMemoryAllocationTests.cpp @@ -1,4 +1,3 @@ -#include "common/commonSubsystem.h" #include "common/emulatorConfig.h" #include "common/file.h" #include "common/logging/log.h" @@ -97,28 +96,16 @@ void InitSubsystems() { return; } - static char arg0[] = "virtual_memory_allocation_tests"; - static char* argv[] = {arg0}; - - auto* slist = Common::SubsystemsList::Instance(); - auto* core = Common::CommonSubsystem::Instance(); - auto* config = Config::ConfigSubsystem::Instance(); - auto* log = Log::LogSubsystem::Instance(); - auto* memory = Libs::LibKernel::Memory::MemorySubsystem::Instance(); - auto* thread = Common::ThreadsSubsystem::Instance(); - - slist->SetArgs(1, argv); - slist->Add(thread, {}); - slist->Add(core, {}); - slist->Add(config, {core}); - Check("InitSubsystems", slist->InitAll(false), "failed to initialize base subsystems"); + static Common::Subsystems subsystems; + Common::VirtualMemory::Init(); + Common::InitializeThreads(); + subsystems.Initialize(); Config::ConfigOptions options; options.printf_direction = Config::OutputDirection::Silent; Config::Load(options); - slist->Add(log, {core, config}); - Check("InitSubsystems", slist->InitAll(false), "failed to initialize logging subsystem"); + subsystems.Initialize(); const auto param_json = std::filesystem::temp_directory_path() / ("kyty_virtual_memory_" + @@ -140,8 +127,7 @@ void InitSubsystems() { "failed to read flexible memory size from param.json"); Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size); - slist->Add(memory, {core, log, thread}); - Check("InitSubsystems", slist->InitAll(false), "failed to initialize memory subsystem"); + subsystems.Initialize(); initialized = true; } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c968fa4..194c542 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1,6 +1,7 @@ #include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/stringUtils.h" +#include "common/subsystems.h" #include "common/threads.h" #include "graphics/guest_gpu/hardwareContext.h" #include "graphics/guest_gpu/pm4.h" @@ -344,12 +345,13 @@ void SetIdentityInterpolatorSettings(ShaderPixelInputInfo* input_info) { void EnsureConfigInitialized() { static bool config_initialized = false; if (!config_initialized) { - Common::ThreadsSubsystem::Instance()->Init(nullptr); - Config::ConfigSubsystem::Instance()->Init(nullptr); + static Common::Subsystems subsystems; + Common::InitializeThreads(); + subsystems.Initialize(); Config::ConfigOptions options; options.printf_direction = Config::OutputDirection::Silent; Config::Load(options); - Log::LogSubsystem::Instance()->Init(nullptr); + subsystems.Initialize(); ShaderInit(); config_initialized = true; }