mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-18 22:42:23 +00:00
refactor(subsystems&ime): RAII lifecycle
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -9,15 +9,15 @@ namespace Config {
|
||||
|
||||
static std::unique_ptr<ConfigOptions> g_config;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Config) {
|
||||
void Initialize() {
|
||||
EXIT_IF(g_config != nullptr);
|
||||
|
||||
g_config = std::make_unique<ConfigOptions>();
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Config) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Config) {}
|
||||
void Shutdown() {
|
||||
g_config.reset();
|
||||
}
|
||||
|
||||
void Load(const ConfigOptions& cfg) {
|
||||
EXIT_IF(g_config == nullptr);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_EMULATOR_CONFIG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_LOGGING_LOG_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/printf.h>
|
||||
@@ -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 };
|
||||
|
||||
|
||||
+5
-14
@@ -1,7 +1,6 @@
|
||||
#include "common/profiler.h"
|
||||
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <common/TracyProtocol.hpp>
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define KYTY_COMMON_PROFILER_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
@@ -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
|
||||
|
||||
|
||||
+25
-214
@@ -1,232 +1,43 @@
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
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<Subsystem*> deps) {
|
||||
EXIT_IF(!s);
|
||||
|
||||
const char* name = s->Id();
|
||||
|
||||
EXIT_IF(FindByName(name) != nullptr);
|
||||
|
||||
auto nl = std::make_unique<SubsListStruct>();
|
||||
|
||||
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<DepsListStruct>();
|
||||
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<DepsListStruct> next;
|
||||
};
|
||||
|
||||
struct SubsListStruct {
|
||||
Subsystem* s;
|
||||
const char* name;
|
||||
std::unique_ptr<DepsListStruct> deps;
|
||||
std::unique_ptr<SubsListStruct> 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<SubsListStruct> 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<SubsystemsListPrivate>(*this)) {}
|
||||
|
||||
SubsystemsList::~SubsystemsList() = default;
|
||||
|
||||
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> 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<SubsystemPrivate>()) {}
|
||||
|
||||
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<size_t>(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
|
||||
|
||||
+41
-70
@@ -2,90 +2,61 @@
|
||||
#define KYTY_COMMON_SUBSYSTEMS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
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<Subsystem*> deps);
|
||||
explicit Subsystems(bool print = false);
|
||||
~Subsystems();
|
||||
|
||||
bool InitAll(bool print_msg = false);
|
||||
void DestroyAll(bool print_msg = false);
|
||||
template <typename Lifecycle>
|
||||
void Initialize() {
|
||||
Lifecycle::initialize();
|
||||
m_active.push_back({ShutdownCallback<Lifecycle>(), EmergencyCallback<Lifecycle>()});
|
||||
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<SubsystemsList>::Instance(); }
|
||||
|
||||
KYTY_CLASS_NO_COPY(SubsystemsList);
|
||||
KYTY_CLASS_NO_COPY(Subsystems);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SubsystemsListPrivate> m_p;
|
||||
template <typename Lifecycle>
|
||||
static consteval Callback ShutdownCallback() {
|
||||
if constexpr (requires { Lifecycle::shutdown; }) {
|
||||
return Lifecycle::shutdown;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Lifecycle>
|
||||
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<Entry> m_active;
|
||||
bool m_print;
|
||||
};
|
||||
|
||||
using SubsystemsListSingleton = Common::Singleton<SubsystemsList>;
|
||||
|
||||
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<SubsystemPrivate> m_p;
|
||||
};
|
||||
|
||||
#define KYTY_SUBSYSTEM_DEFINE(s) \
|
||||
class s##Subsystem: public Common::Subsystem { \
|
||||
public: \
|
||||
static Subsystem* Instance() { return Common::Singleton<s##Subsystem>::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##Subsystem> 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_ */
|
||||
|
||||
@@ -252,15 +252,11 @@ static thread_id_t g_main_thread;
|
||||
static int g_main_thread_int;
|
||||
static std::atomic<int> 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<ThreadPrivate>(func, arg)) {
|
||||
while (!m_thread->started) {
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
#define KYTY_COMMON_THREADS_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Threads);
|
||||
void InitializeThreads();
|
||||
|
||||
using thread_func_t = void (*)(void*);
|
||||
using wait_poll_func_t = void (*)();
|
||||
|
||||
+18
-36
@@ -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::Lifecycle>();
|
||||
Config::Load(cfg);
|
||||
slist->Add(log, {core, config});
|
||||
slist->InitAll(true);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
|
||||
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<Loader::Timer::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::PthreadLifecycle>();
|
||||
subsystems.Initialize<Profiler::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Network::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::FileSystem::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Controller::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Audio::Lifecycle>();
|
||||
subsystems.Initialize<Libs::Graphics::Lifecycle>();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -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<Type>(core.type);
|
||||
snapshot->enter_label = static_cast<EnterLabel>(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<Alignment>(core.horizontal_alignment);
|
||||
snapshot->vertical_alignment = static_cast<Alignment>(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<Type>(dialog.type);
|
||||
snapshot->enter_label = static_cast<EnterLabel>(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<Alignment>(dialog.horizontal_alignment);
|
||||
snapshot->vertical_alignment = static_cast<Alignment>(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<DialogIme::ExternalAction>(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<CoreIme::ExternalAction>(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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+10
-2
@@ -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 <filesystem>
|
||||
@@ -33,7 +32,16 @@ struct FileStat {
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(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);
|
||||
|
||||
@@ -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<GuestAddressSpace>(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();
|
||||
|
||||
+8
-2
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
// IWYU pragma: no_include <pthread.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-4
@@ -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();
|
||||
|
||||
+8
-2
@@ -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);
|
||||
|
||||
|
||||
+11
-13
@@ -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<Ngs2SubmixerVoiceState*>(state);
|
||||
*submixer = {};
|
||||
auto* submixer = reinterpret_cast<Ngs2SubmixerVoiceState*>(state);
|
||||
*submixer = {};
|
||||
submixer->voice_state.state_flags = Ngs2GetStateFlags(voice);
|
||||
LOGF("\t state_flags = %u\n", submixer->voice_state.state_flags);
|
||||
break;
|
||||
|
||||
+8
-3
@@ -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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+80
-231
@@ -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<QueuedEvent> events;
|
||||
std::vector<ExternalInput> 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<char16_t>(((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<uint32_t>(cursor, static_cast<uint32_t>(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<char16_t*>(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<char16_t*>(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<char16_t> output(max_length + 1, u'\0');
|
||||
uint32_t output_length = max_length;
|
||||
if (filter(output.data(), &output_length, candidate.c_str(),
|
||||
static_cast<uint32_t>(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<int32_t>(new_tail - prefix) - static_cast<int32_t>(old_tail - prefix);
|
||||
g_state.text = std::move(candidate);
|
||||
g_state.cursor = NormalizeCursor(g_state.text, cursor);
|
||||
QueueEventLocked(MakeTextEventLocked(1, static_cast<uint32_t>(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<uint32_t>(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<char16_t*>(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<uint32_t>(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<uint32_t>(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<int>(old_cursor) + delta, 0, static_cast<int>(g_state.text.size()));
|
||||
if (delta < 0 && next > 0 && next < static_cast<int>(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<int>(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<uint32_t>(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<uint32_t>(
|
||||
std::abs(static_cast<int>(g_state.cursor) - static_cast<int>(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<uint32_t>(
|
||||
std::abs(static_cast<int>(new_cursor) - static_cast<int>(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
|
||||
|
||||
+15
-86
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
#include "libs/imeCommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
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<char16_t>(((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<uint32_t>(cursor, static_cast<uint32_t>(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<uint32_t>(prefix),
|
||||
static_cast<int32_t>(after_tail - prefix) - static_cast<int32_t>(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<char16_t> buffer(static_cast<size_t>(output_capacity) + 1, u'\0');
|
||||
uint32_t output_length = output_capacity;
|
||||
if (filter(buffer.data(), &output_length, source.c_str(),
|
||||
static_cast<uint32_t>(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<uint32_t>(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<int32_t>(allowed.size())};
|
||||
}
|
||||
m_text.insert(m_cursor, allowed);
|
||||
m_cursor += static_cast<uint32_t>(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<int32_t>(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<int>(m_cursor) + delta, 0, static_cast<int>(m_text.size()));
|
||||
if (delta < 0 && next > 0 && next < static_cast<int>(m_text.size()) &&
|
||||
IsLowSurrogate(m_text[next]) && IsHighSurrogate(m_text[next - 1])) {
|
||||
next--;
|
||||
} else if (delta > 0 && next > 0 && next < static_cast<int>(m_text.size()) &&
|
||||
IsHighSurrogate(m_text[next - 1]) && IsLowSurrogate(m_text[next])) {
|
||||
next++;
|
||||
}
|
||||
if (next == static_cast<int>(m_cursor)) {
|
||||
return false;
|
||||
}
|
||||
m_cursor = static_cast<uint32_t>(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
|
||||
@@ -0,0 +1,159 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IMECOMMON_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
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_
|
||||
+19
-205
@@ -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<ExternalInput> 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<char16_t>((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<uint32_t>(cursor, static_cast<uint32_t>(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<char16_t> 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<uint32_t>(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<uint32_t>(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<uint32_t>(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<int>(g_state.cursor) + delta, 0,
|
||||
static_cast<int>(g_state.current_text.size()));
|
||||
if (delta < 0 && next > 0 && next < static_cast<int>(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<int>(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<int>(g_state.cursor)) {
|
||||
return false;
|
||||
}
|
||||
g_state.cursor = static_cast<uint32_t>(next);
|
||||
return true;
|
||||
return MatchRunningGeneration(generation) && g_state.editor.MoveCursor(delta);
|
||||
}
|
||||
|
||||
bool HostAccept(uint64_t generation) {
|
||||
|
||||
+25
-99
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
+8
-3
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+5
-4
@@ -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_ */
|
||||
|
||||
+3
-21
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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::Lifecycle>();
|
||||
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<Log::Lifecycle>();
|
||||
subsystems.Initialize<Libs::LibKernel::Memory::Lifecycle>();
|
||||
config_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::Lifecycle>();
|
||||
|
||||
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<Log::Lifecycle>();
|
||||
|
||||
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<Libs::LibKernel::Memory::Lifecycle>();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@@ -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::Lifecycle>();
|
||||
Config::ConfigOptions options;
|
||||
options.printf_direction = Config::OutputDirection::Silent;
|
||||
Config::Load(options);
|
||||
Log::LogSubsystem::Instance()->Init(nullptr);
|
||||
subsystems.Initialize<Log::Lifecycle>();
|
||||
ShaderInit();
|
||||
config_initialized = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user