diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/dialogs/ime_dialog.cpp index 2096d0914..0dc0a656f 100644 --- a/src/core/libraries/dialogs/ime_dialog.cpp +++ b/src/core/libraries/dialogs/ime_dialog.cpp @@ -1,28 +1,76 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "ime_dialog.h" +#include "ime_dialog_ui.h" + +static constexpr float MAX_X_POSITIONS[] = {3840.0f, 1920.0f}; +static constexpr float MAX_Y_POSITIONS[] = {2160.0f, 1080.0f}; namespace Libraries::ImeDialog { static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; +static OrbisImeDialogResult g_ime_dlg_result{}; +static ImeDialogState g_ime_dlg_state{}; +static ImeDialogUi g_ime_dlg_ui; -int PS4_SYSV_ABI sceImeDialogAbort() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { + if (False(~option & (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) { + return false; + } + + if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT && type != OrbisImeType::BASIC_LATIN) { + return false; + } + + if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER && type != OrbisImeType::BASIC_LATIN ) { + return false; + } + + return true; } -int PS4_SYSV_ABI sceImeDialogForceClose() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +// static bool IsMemZero(const void* ptr, size_t size) { +// return std::all_of(static_cast(ptr), static_cast(ptr) + size, [](u8 c) { return c == 0; }); +// } + +Error PS4_SYSV_ABI sceImeDialogAbort() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog not running"); + return Error::DIALOG_NOT_RUNNING; + } + + g_ime_dlg_status = OrbisImeDialogStatus::FINISHED; + g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED; + + return Error::OK; } -int PS4_SYSV_ABI sceImeDialogForTestFunction() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogForceClose() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; +} + +Error PS4_SYSV_ABI sceImeDialogForTestFunction() { + return Error::INTERNAL; } int PS4_SYSV_ABI sceImeDialogGetCurrentStarState() { @@ -45,26 +93,134 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { - result->endstatus = OrbisImeDialogEndStatus::OK; - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is not running"); + return Error::DIALOG_NOT_IN_USE; + } + + if (result == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with result (NULL)"); + return Error::INVALID_ADDRESS; + } + + // if (!IsMemZero(result->reserved, 12)) { + // LOG_INFO(Lib_ImeDialog, "Invalid result->reserved"); + // return Error::INVALID_RESERVED; + // } + + result->endstatus = g_ime_dlg_result.endstatus; + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + return Error::DIALOG_NOT_FINISHED; + } + + g_ime_dlg_state.CopyTextToOrbisBuffer(); + return Error::OK; } OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { - return OrbisImeDialogStatus::FINISHED; + g_ime_dlg_state.CallTextFilter(); } + return g_ime_dlg_status; } -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - const std::wstring_view text = L"shadPS4"; - param->maxTextLength = text.size(); - std::memcpy(param->inputTextBuffer, text.data(), text.size() * sizeof(wchar_t)); +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { + if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); + return Error::BUSY; + } + + if (param == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with param (NULL)"); + return Error::INVALID_ADDRESS; + } + + if (!magic_enum::enum_contains(param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->type"); + return Error::INVALID_ADDRESS; + } + + //TODO: do correct param->option validation + //TODO: do correct param->supportedLanguages validation + + if (param->posx < 0.0f || param->posx >= MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posx"); + return Error::INVALID_POSX; + } + + if (param->posy < 0.0f || param->posy >= MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posy"); + return Error::INVALID_POSY; + } + + if (!magic_enum::enum_contains(param->horizontalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment"); + return Error::INVALID_HORIZONTALIGNMENT; + } + + if (!magic_enum::enum_contains(param->verticalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment"); + return Error::INVALID_VERTICALALIGNMENT; + } + + if (!IsValidOption(param->option, param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->option"); + return Error::INVALID_PARAM; + } + + if (param->userId != 1) { //We only support user 1 for now + LOG_INFO(Lib_ImeDialog, "Invalid param->userId"); + return Error::INVALID_USER_ID; + } + + // if (IsMemZero(param->reserved, 16)) { + // LOG_INFO(Lib_ImeDialog, "Invalid param->reserved"); + // return Error::INVALID_RESERVED; + // } + + if (param->inputTextBuffer == nullptr) { + LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); + return Error::INVALID_INPUT_TEXT_BUFFER; + } + + if (extended) { + if (magic_enum::enum_contains(extended->priority)) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->priority"); + return Error::INVALID_EXTENDED; + } + + //TODO: do correct extended->option validation + + // if (IsMemZero(extended->reserved, 60)) { + // LOG_INFO(Lib_ImeDialog, "Invalid extended->reserved"); + // return Error::INVALID_EXTENDED; + // } + + if ((extended->extKeyboardMode & 0xe3fffffc) != 0) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode"); + return Error::INVALID_EXTENDED; + } + + if (extended->disableDevice > 7) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice"); + return Error::INVALID_EXTENDED; + } + } + + if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); + return Error::INVALID_MAX_TEXT_LENGTH; + } + + g_ime_dlg_result = {}; + g_ime_dlg_state = ImeDialogState(param, extended); g_ime_dlg_status = OrbisImeDialogStatus::RUNNING; - return ORBIS_OK; + g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); + + return Error::OK; } int PS4_SYSV_ABI sceImeDialogInitInternal() { @@ -87,10 +243,22 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogTerm() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); +Error PS4_SYSV_ABI sceImeDialogTerm() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog is still running"); + return Error::DIALOG_NOT_FINISHED; + } + g_ime_dlg_status = OrbisImeDialogStatus::NONE; - return ORBIS_OK; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; } void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/dialogs/ime_dialog.h b/src/core/libraries/dialogs/ime_dialog.h index 24914044a..24ca8b340 100755 --- a/src/core/libraries/dialogs/ime_dialog.h +++ b/src/core/libraries/dialogs/ime_dialog.h @@ -68,13 +68,13 @@ enum class OrbisImeDialogEndStatus : u32 { ABORTED = 2 }; -enum class OrbisImeType : u32 { - DEFAULT = 0, - BASIC_LATIN = 1, - URL = 2, - MAIL = 3, - NUMBER = 4 -}; + enum class OrbisImeType : u32 { + DEFAULT = 0, + BASIC_LATIN = 1, + URL = 2, + MAIL = 3, + NUMBER = 4 + }; enum class OrbisImeEnterLabel : u32 { DEFAULT = 0, @@ -87,7 +87,9 @@ enum class OrbisImeDialogOption : u32 { DEFAULT = 0, MULTILINE = 1, NO_AUTO_CORRECTION = 2, - NO_AUTO_COMPLETION = 4 + NO_AUTO_COMPLETION = 4, + // TODO: Document missing options + LARGE_RESOLUTION = 1024 }; DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) @@ -222,21 +224,21 @@ struct OrbisImeParamExtended { int8_t reserved[60]; }; -int PS4_SYSV_ABI sceImeDialogAbort(); -int PS4_SYSV_ABI sceImeDialogForceClose(); -int PS4_SYSV_ABI sceImeDialogForTestFunction(); +Error PS4_SYSV_ABI sceImeDialogAbort(); +Error PS4_SYSV_ABI sceImeDialogForceClose(); +Error PS4_SYSV_ABI sceImeDialogForTestFunction(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeDialogGetPanelSize(); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); int PS4_SYSV_ABI sceImeDialogInitInternal(); int PS4_SYSV_ABI sceImeDialogInitInternal2(); int PS4_SYSV_ABI sceImeDialogInitInternal3(); int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); -int PS4_SYSV_ABI sceImeDialogTerm(); +Error PS4_SYSV_ABI sceImeDialogTerm(); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::ImeDialog \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/dialogs/ime_dialog_ui.cpp index 28666e1b4..083907d32 100755 --- a/src/core/libraries/dialogs/ime_dialog_ui.cpp +++ b/src/core/libraries/dialogs/ime_dialog_ui.cpp @@ -32,6 +32,7 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, const OrbisImeP userId = param->userId; is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); + is_numeric = param->type == OrbisImeType::NUMBER; type = param->type; enter_label = param->enterLabel; text_filter = param->filter; @@ -47,48 +48,108 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, const OrbisImeP ASSERT_MSG(utf8_to_orbis != (iconv_t)-1, "Failed to open iconv utf8_to_orbis"); #endif - std::size_t title_len = std::char_traits::length(param->title); - title = new char[title_len * 4 + 1]; + if (param->title) { + std::size_t title_len = std::char_traits::length(param->title); + title = new char[title_len * 4 + 1]; - if (!ConvertOrbisToUTF8(param->title, title_len, title, title_len)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); - return; + if (!ConvertOrbisToUTF8(param->title, title_len, title, title_len)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); + } } - if (!param->placeholder) { - return; + if (param->placeholder) { + std::size_t placeholder_len = std::char_traits::length(param->placeholder); + placeholder = new char[placeholder_len * 4 + 1]; + + if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, placeholder, placeholder_len)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); + } } - std::size_t placeholder_len = std::char_traits::length(param->placeholder); - placeholder = new char[placeholder_len * 4 + 1]; - - if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, placeholder, placeholder_len)) { - LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); + std::size_t text_len = std::char_traits::length(text_buffer); + if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text, max_text_length)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); } } ImeDialogState::~ImeDialogState() { + Free(); +} + +ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept + : userId(other.userId), + is_multiLine(other.is_multiLine), + is_numeric(other.is_numeric), + type(other.type), + enter_label(other.enter_label), + text_filter(other.text_filter), + keyboard_filter(other.keyboard_filter), + max_text_length(other.max_text_length), + text_buffer(other.text_buffer), + title(other.title), + placeholder(other.placeholder), + input_changed(other.input_changed) { + + std::memcpy(current_text, other.current_text, sizeof(current_text)); + #ifndef _WIN32 - if (orbis_to_utf8 != (iconv_t)-1) { - iconv_close(orbis_to_utf8); - } - if (utf8_to_orbis != (iconv_t)-1) { - iconv_close(utf8_to_orbis); - } + orbis_to_utf8 = other.orbis_to_utf8; + utf8_to_orbis = other.utf8_to_orbis; + + other.orbis_to_utf8 = (iconv_t)-1; + other.utf8_to_orbis = (iconv_t)-1; #endif - if (title) { - delete[] title; + other.text_buffer = nullptr; + other.title = nullptr; + other.placeholder = nullptr; +} + +ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) { + if (this != &other) { + Free(); + + userId = other.userId; + is_multiLine = other.is_multiLine; + is_numeric = other.is_numeric; + type = other.type; + enter_label = other.enter_label; + text_filter = other.text_filter; + keyboard_filter = other.keyboard_filter; + max_text_length = other.max_text_length; + text_buffer = other.text_buffer; + title = other.title; + placeholder = other.placeholder; + input_changed = other.input_changed; + + std::memcpy(current_text, other.current_text, sizeof(current_text)); + +#ifndef _WIN32 + orbis_to_utf8 = other.orbis_to_utf8; + utf8_to_orbis = other.utf8_to_orbis; + + other.orbis_to_utf8 = (iconv_t)-1; + other.utf8_to_orbis = (iconv_t)-1; +#endif + + other.text_buffer = nullptr; + other.title = nullptr; + other.placeholder = nullptr; } - if (placeholder) { - delete[] placeholder; + return *this; +} + +bool ImeDialogState::CopyTextToOrbisBuffer() { + if (!text_buffer) { + return false; } + + std::size_t text_len = std::char_traits::length(current_text); + return ConvertUTF8ToOrbis(current_text, text_len, text_buffer, max_text_length); } bool ImeDialogState::CallTextFilter() { - std::scoped_lock lock(mutex); - if (!text_filter || !input_changed) { return true; } @@ -120,6 +181,25 @@ bool ImeDialogState::CallTextFilter() { return true; } +void ImeDialogState::Free() { +#ifndef _WIN32 + if (orbis_to_utf8 != (iconv_t)-1) { + iconv_close(orbis_to_utf8); + } + if (utf8_to_orbis != (iconv_t)-1) { + iconv_close(utf8_to_orbis); + } +#endif + + if (title) { + delete[] title; + } + + if (placeholder) { + delete[] placeholder; + } +} + bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status) { if (!keyboard_filter) { return true; @@ -259,14 +339,15 @@ ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, Or } ImeDialogUi::~ImeDialogUi() { - RemoveLayer(this); + std::scoped_lock lock(draw_mutex); + + Free(); } ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept : state(other.state), status(other.status), result(other.result) { - if (state) std::scoped_lock lock(state->mutex); - if (other.state) std::scoped_lock lock2(other.state->mutex); + std::scoped_lock lock(draw_mutex, other.draw_mutex); other.state = nullptr; other.status = nullptr; other.result = nullptr; @@ -276,27 +357,35 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept } } -ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi other) { - if (state) std::scoped_lock lock(state->mutex); - if (other.state) std::scoped_lock lock2(other.state->mutex); - std::swap(state, other.state); - std::swap(status, other.status); - std::swap(result, other.result); +ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { + std::scoped_lock lock(draw_mutex, other.draw_mutex); + Free(); - if (state) { + state = other.state; + status = other.status; + result = other.result; + other.state = nullptr; + other.status = nullptr; + other.result = nullptr; + + if (state && *status == OrbisImeDialogStatus::RUNNING) { AddLayer(this); } return *this; } +void ImeDialogUi::Free() { + RemoveLayer(this); +} + void ImeDialogUi::Draw() { + std::unique_lock lock{draw_mutex}; + if (!state) { return; } - std::unique_lock lock{state->mutex}; - if (!status || *status != OrbisImeDialogStatus::RUNNING) { return; } @@ -322,7 +411,7 @@ void ImeDialogUi::Draw() { first_render = false; - if (Begin("IME Dialog#ImeDialog", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { + if (Begin("IME Dialog##ImeDialog", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { DrawPrettyBackground(); Separator(); @@ -345,17 +434,17 @@ void ImeDialogUi::Draw() { switch (state->enter_label) { case OrbisImeEnterLabel::GO: - button_text = "Go#ImeDialogOK"; + button_text = "Go##ImeDialogOK"; break; case OrbisImeEnterLabel::SEARCH: - button_text = "Search#ImeDialogOK"; + button_text = "Search##ImeDialogOK"; break; case OrbisImeEnterLabel::SEND: - button_text = "Send#ImeDialogOK"; + button_text = "Send##ImeDialogOK"; break; case OrbisImeEnterLabel::DEFAULT: default: - button_text = "OK#ImeDialogOK"; + button_text = "OK##ImeDialogOK"; break; } @@ -364,7 +453,7 @@ void ImeDialogUi::Draw() { result->endstatus = OrbisImeDialogEndStatus::OK; } - if (Button("Cancel#ImeDialogCancel", BUTTON_SIZE)) { + if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { *status = OrbisImeDialogStatus::FINISHED; result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; @@ -374,7 +463,7 @@ void ImeDialogUi::Draw() { } void ImeDialogUi::DrawInputText() { - if (InputTextEx("##ImeDialogInput", state->placeholder, state->current_text, ORBIS_IME_DIALOG_MAX_TEXT_LENGTH, ImVec2(0, 0), ImGuiInputTextFlags_CallbackCharFilter, InputTextCallback, this)) { + if (InputTextEx("##ImeDialogInput", state->placeholder, state->current_text, state->max_text_length, ImVec2(0, 0), ImGuiInputTextFlags_CallbackCharFilter, InputTextCallback, this)) { state->input_changed = true; } } @@ -382,7 +471,7 @@ void ImeDialogUi::DrawInputText() { void ImeDialogUi::DrawMultiLineInputText() { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" - if (InputTextEx("##ImeDialogInput", state->placeholder, state->current_text, ORBIS_IME_DIALOG_MAX_TEXT_LENGTH, ImVec2(380.0f, 100.0f), ImGuiInputTextFlags_CallbackCharFilter | ImGuiInputTextFlags_Multiline, InputTextCallback, this)) { + if (InputTextEx("##ImeDialogInput", state->placeholder, state->current_text, state->max_text_length, ImVec2(380.0f, 100.0f), ImGuiInputTextFlags_CallbackCharFilter | ImGuiInputTextFlags_Multiline, InputTextCallback, this)) { state->input_changed = true; } #pragma clang diagnostic pop @@ -393,8 +482,17 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ASSERT(ui); + // Should we filter punctuation? + if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') && data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') { + return 1; + } + + if (!ui->state->keyboard_filter) { + return 0; + } + // ImGui encodes ImWchar32 as multi-byte UTF-8 characters - char* event_char = reinterpret_cast(data->EventChar); + char* event_char = reinterpret_cast(&data->EventChar); // Call the keyboard filter OrbisImeKeycode src_keycode = { diff --git a/src/core/libraries/dialogs/ime_dialog_ui.h b/src/core/libraries/dialogs/ime_dialog_ui.h index fba0f4808..80867c8af 100755 --- a/src/core/libraries/dialogs/ime_dialog_ui.h +++ b/src/core/libraries/dialogs/ime_dialog_ui.h @@ -7,7 +7,7 @@ #ifndef _WIN32 #include #endif - +#include #include "core/libraries/dialogs/ime_dialog.h" #include "common/types.h" #include "imgui/imgui_layer.h" @@ -23,6 +23,7 @@ class ImeDialogState final { s32 userId{}; bool is_multiLine{}; + bool is_numeric{}; OrbisImeType type{}; OrbisImeEnterLabel enter_label{}; OrbisImeTextFilter text_filter{}; @@ -34,20 +35,22 @@ class ImeDialogState final { // A character can hold up to 4 bytes in UTF-8 char current_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4] = {0}; - - std::mutex mutex; #ifndef _WIN32 iconv_t orbis_to_utf8 = (iconv_t)-1; iconv_t utf8_to_orbis = (iconv_t)-1; #endif public: ImeDialogState(const OrbisImeDialogParam* param = nullptr, const OrbisImeParamExtended* extended = nullptr); - ~ImeDialogState(); + ImeDialogState(const ImeDialogState& other) = delete; + ImeDialogState(ImeDialogState&& other) noexcept; + ImeDialogState& operator=(ImeDialogState&& other); - ImeDialogState() = default; + ~ImeDialogState(); + bool CopyTextToOrbisBuffer(); bool CallTextFilter(); private: + void Free(); bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, std::size_t native_text_len); @@ -62,17 +65,19 @@ class ImeDialogUi final : public ImGui::Layer { OrbisImeDialogResult* result{}; bool first_render = true; - + std::mutex draw_mutex; public: explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, OrbisImeDialogResult* result = nullptr); ~ImeDialogUi() override; ImeDialogUi(const ImeDialogUi& other) = delete; ImeDialogUi(ImeDialogUi&& other) noexcept; - ImeDialogUi& operator=(ImeDialogUi other); + ImeDialogUi& operator=(ImeDialogUi&& other); void Draw() override; private: + void Free(); + void DrawInputText(); void DrawMultiLineInputText();