Functional implementation

This commit is contained in:
Lander Gallastegi 2024-10-06 01:34:38 +02:00
commit c10a06422f
4 changed files with 362 additions and 89 deletions

View file

@ -1,28 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <magic_enum.hpp>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "ime_dialog.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 { namespace Libraries::ImeDialog {
static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; 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() { static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); if (False(~option & (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) {
return ORBIS_OK; 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() { // static bool IsMemZero(const void* ptr, size_t size) {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); // return std::all_of(static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size, [](u8 c) { return c == 0; });
return ORBIS_OK; // }
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() { Error PS4_SYSV_ABI sceImeDialogForceClose() {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
return ORBIS_OK; 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() { int PS4_SYSV_ABI sceImeDialogGetCurrentStarState() {
@ -45,26 +93,134 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) {
result->endstatus = OrbisImeDialogEndStatus::OK; if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); LOG_INFO(Lib_ImeDialog, "IME dialog is not running");
return ORBIS_OK; 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() { OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() {
if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) {
return OrbisImeDialogStatus::FINISHED; g_ime_dlg_state.CallTextFilter();
} }
return g_ime_dlg_status; return g_ime_dlg_status;
} }
int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) {
const std::wstring_view text = L"shadPS4"; LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
param->maxTextLength = text.size(); return Error::BUSY;
std::memcpy(param->inputTextBuffer, text.data(), text.size() * sizeof(wchar_t)); }
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; 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() { int PS4_SYSV_ABI sceImeDialogInitInternal() {
@ -87,10 +243,22 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeDialogTerm() { Error PS4_SYSV_ABI sceImeDialogTerm() {
LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); 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; 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) { void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) {

View file

@ -68,13 +68,13 @@ enum class OrbisImeDialogEndStatus : u32 {
ABORTED = 2 ABORTED = 2
}; };
enum class OrbisImeType : u32 { enum class OrbisImeType : u32 {
DEFAULT = 0, DEFAULT = 0,
BASIC_LATIN = 1, BASIC_LATIN = 1,
URL = 2, URL = 2,
MAIL = 3, MAIL = 3,
NUMBER = 4 NUMBER = 4
}; };
enum class OrbisImeEnterLabel : u32 { enum class OrbisImeEnterLabel : u32 {
DEFAULT = 0, DEFAULT = 0,
@ -87,7 +87,9 @@ enum class OrbisImeDialogOption : u32 {
DEFAULT = 0, DEFAULT = 0,
MULTILINE = 1, MULTILINE = 1,
NO_AUTO_CORRECTION = 2, NO_AUTO_CORRECTION = 2,
NO_AUTO_COMPLETION = 4 NO_AUTO_COMPLETION = 4,
// TODO: Document missing options
LARGE_RESOLUTION = 1024
}; };
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
@ -222,21 +224,21 @@ struct OrbisImeParamExtended {
int8_t reserved[60]; int8_t reserved[60];
}; };
int PS4_SYSV_ABI sceImeDialogAbort(); Error PS4_SYSV_ABI sceImeDialogAbort();
int PS4_SYSV_ABI sceImeDialogForceClose(); Error PS4_SYSV_ABI sceImeDialogForceClose();
int PS4_SYSV_ABI sceImeDialogForTestFunction(); Error PS4_SYSV_ABI sceImeDialogForTestFunction();
int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState();
int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm();
int PS4_SYSV_ABI sceImeDialogGetPanelSize(); int PS4_SYSV_ABI sceImeDialogGetPanelSize();
int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended();
int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result);
OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); 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 sceImeDialogInitInternal();
int PS4_SYSV_ABI sceImeDialogInitInternal2(); int PS4_SYSV_ABI sceImeDialogInitInternal2();
int PS4_SYSV_ABI sceImeDialogInitInternal3(); int PS4_SYSV_ABI sceImeDialogInitInternal3();
int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); int PS4_SYSV_ABI sceImeDialogSetPanelPosition();
int PS4_SYSV_ABI sceImeDialogTerm(); Error PS4_SYSV_ABI sceImeDialogTerm();
void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::ImeDialog } // namespace Libraries::ImeDialog

View file

@ -32,6 +32,7 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, const OrbisImeP
userId = param->userId; userId = param->userId;
is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE);
is_numeric = param->type == OrbisImeType::NUMBER;
type = param->type; type = param->type;
enter_label = param->enterLabel; enter_label = param->enterLabel;
text_filter = param->filter; 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"); ASSERT_MSG(utf8_to_orbis != (iconv_t)-1, "Failed to open iconv utf8_to_orbis");
#endif #endif
if (param->title) {
std::size_t title_len = std::char_traits<char16_t>::length(param->title); std::size_t title_len = std::char_traits<char16_t>::length(param->title);
title = new char[title_len * 4 + 1]; title = new char[title_len * 4 + 1];
if (!ConvertOrbisToUTF8(param->title, title_len, title, title_len)) { if (!ConvertOrbisToUTF8(param->title, title_len, title, title_len)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding");
return; }
}
if (!param->placeholder) {
return;
} }
if (param->placeholder) {
std::size_t placeholder_len = std::char_traits<char16_t>::length(param->placeholder); std::size_t placeholder_len = std::char_traits<char16_t>::length(param->placeholder);
placeholder = new char[placeholder_len * 4 + 1]; placeholder = new char[placeholder_len * 4 + 1];
if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, placeholder, placeholder_len)) { if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, placeholder, placeholder_len)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding");
} }
}
std::size_t text_len = std::char_traits<char16_t>::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() { 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 #ifndef _WIN32
if (orbis_to_utf8 != (iconv_t)-1) { orbis_to_utf8 = other.orbis_to_utf8;
iconv_close(orbis_to_utf8); utf8_to_orbis = other.utf8_to_orbis;
}
if (utf8_to_orbis != (iconv_t)-1) { other.orbis_to_utf8 = (iconv_t)-1;
iconv_close(utf8_to_orbis); other.utf8_to_orbis = (iconv_t)-1;
}
#endif #endif
if (title) { other.text_buffer = nullptr;
delete[] title; 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) { return *this;
delete[] placeholder; }
bool ImeDialogState::CopyTextToOrbisBuffer() {
if (!text_buffer) {
return false;
} }
std::size_t text_len = std::char_traits<char>::length(current_text);
return ConvertUTF8ToOrbis(current_text, text_len, text_buffer, max_text_length);
} }
bool ImeDialogState::CallTextFilter() { bool ImeDialogState::CallTextFilter() {
std::scoped_lock lock(mutex);
if (!text_filter || !input_changed) { if (!text_filter || !input_changed) {
return true; return true;
} }
@ -120,6 +181,25 @@ bool ImeDialogState::CallTextFilter() {
return true; 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) { bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status) {
if (!keyboard_filter) { if (!keyboard_filter) {
return true; return true;
@ -259,14 +339,15 @@ ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, Or
} }
ImeDialogUi::~ImeDialogUi() { ImeDialogUi::~ImeDialogUi() {
RemoveLayer(this); std::scoped_lock lock(draw_mutex);
Free();
} }
ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
: state(other.state), status(other.status), result(other.result) { : state(other.state), status(other.status), result(other.result) {
if (state) std::scoped_lock lock(state->mutex); std::scoped_lock lock(draw_mutex, other.draw_mutex);
if (other.state) std::scoped_lock lock2(other.state->mutex);
other.state = nullptr; other.state = nullptr;
other.status = nullptr; other.status = nullptr;
other.result = nullptr; other.result = nullptr;
@ -276,27 +357,35 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
} }
} }
ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi other) { ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) {
if (state) std::scoped_lock lock(state->mutex); std::scoped_lock lock(draw_mutex, other.draw_mutex);
if (other.state) std::scoped_lock lock2(other.state->mutex); Free();
std::swap(state, other.state);
std::swap(status, other.status);
std::swap(result, other.result);
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); AddLayer(this);
} }
return *this; return *this;
} }
void ImeDialogUi::Free() {
RemoveLayer(this);
}
void ImeDialogUi::Draw() { void ImeDialogUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) { if (!state) {
return; return;
} }
std::unique_lock lock{state->mutex};
if (!status || *status != OrbisImeDialogStatus::RUNNING) { if (!status || *status != OrbisImeDialogStatus::RUNNING) {
return; return;
} }
@ -322,7 +411,7 @@ void ImeDialogUi::Draw() {
first_render = false; first_render = false;
if (Begin("IME Dialog#ImeDialog", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { if (Begin("IME Dialog##ImeDialog", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground(); DrawPrettyBackground();
Separator(); Separator();
@ -345,17 +434,17 @@ void ImeDialogUi::Draw() {
switch (state->enter_label) { switch (state->enter_label) {
case OrbisImeEnterLabel::GO: case OrbisImeEnterLabel::GO:
button_text = "Go#ImeDialogOK"; button_text = "Go##ImeDialogOK";
break; break;
case OrbisImeEnterLabel::SEARCH: case OrbisImeEnterLabel::SEARCH:
button_text = "Search#ImeDialogOK"; button_text = "Search##ImeDialogOK";
break; break;
case OrbisImeEnterLabel::SEND: case OrbisImeEnterLabel::SEND:
button_text = "Send#ImeDialogOK"; button_text = "Send##ImeDialogOK";
break; break;
case OrbisImeEnterLabel::DEFAULT: case OrbisImeEnterLabel::DEFAULT:
default: default:
button_text = "OK#ImeDialogOK"; button_text = "OK##ImeDialogOK";
break; break;
} }
@ -364,7 +453,7 @@ void ImeDialogUi::Draw() {
result->endstatus = OrbisImeDialogEndStatus::OK; result->endstatus = OrbisImeDialogEndStatus::OK;
} }
if (Button("Cancel#ImeDialogCancel", BUTTON_SIZE)) { if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) {
*status = OrbisImeDialogStatus::FINISHED; *status = OrbisImeDialogStatus::FINISHED;
result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED;
@ -374,7 +463,7 @@ void ImeDialogUi::Draw() {
} }
void ImeDialogUi::DrawInputText() { 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; state->input_changed = true;
} }
} }
@ -382,7 +471,7 @@ void ImeDialogUi::DrawInputText() {
void ImeDialogUi::DrawMultiLineInputText() { void ImeDialogUi::DrawMultiLineInputText() {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion" #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; state->input_changed = true;
} }
#pragma clang diagnostic pop #pragma clang diagnostic pop
@ -393,8 +482,17 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ASSERT(ui); 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 // ImGui encodes ImWchar32 as multi-byte UTF-8 characters
char* event_char = reinterpret_cast<char*>(data->EventChar); char* event_char = reinterpret_cast<char*>(&data->EventChar);
// Call the keyboard filter // Call the keyboard filter
OrbisImeKeycode src_keycode = { OrbisImeKeycode src_keycode = {

View file

@ -7,7 +7,7 @@
#ifndef _WIN32 #ifndef _WIN32
#include <iconv.h> #include <iconv.h>
#endif #endif
#include <imgui.h>
#include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/dialogs/ime_dialog.h"
#include "common/types.h" #include "common/types.h"
#include "imgui/imgui_layer.h" #include "imgui/imgui_layer.h"
@ -23,6 +23,7 @@ class ImeDialogState final {
s32 userId{}; s32 userId{};
bool is_multiLine{}; bool is_multiLine{};
bool is_numeric{};
OrbisImeType type{}; OrbisImeType type{};
OrbisImeEnterLabel enter_label{}; OrbisImeEnterLabel enter_label{};
OrbisImeTextFilter text_filter{}; OrbisImeTextFilter text_filter{};
@ -34,20 +35,22 @@ class ImeDialogState final {
// A character can hold up to 4 bytes in UTF-8 // A character can hold up to 4 bytes in UTF-8
char current_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4] = {0}; char current_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4] = {0};
std::mutex mutex;
#ifndef _WIN32 #ifndef _WIN32
iconv_t orbis_to_utf8 = (iconv_t)-1; iconv_t orbis_to_utf8 = (iconv_t)-1;
iconv_t utf8_to_orbis = (iconv_t)-1; iconv_t utf8_to_orbis = (iconv_t)-1;
#endif #endif
public: public:
ImeDialogState(const OrbisImeDialogParam* param = nullptr, const OrbisImeParamExtended* extended = nullptr); ImeDialogState(const OrbisImeDialogParam* param = nullptr, const OrbisImeParamExtended* extended = nullptr);
ImeDialogState(const ImeDialogState& other) = delete;
ImeDialogState(ImeDialogState&& other) noexcept;
ImeDialogState& operator=(ImeDialogState&& other);
~ImeDialogState(); ~ImeDialogState();
ImeDialogState() = default; bool CopyTextToOrbisBuffer();
bool CallTextFilter(); bool CallTextFilter();
private: private:
void Free();
bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); 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); 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{}; OrbisImeDialogResult* result{};
bool first_render = true; bool first_render = true;
std::mutex draw_mutex;
public: public:
explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, OrbisImeDialogResult* result = nullptr); explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, OrbisImeDialogResult* result = nullptr);
~ImeDialogUi() override; ~ImeDialogUi() override;
ImeDialogUi(const ImeDialogUi& other) = delete; ImeDialogUi(const ImeDialogUi& other) = delete;
ImeDialogUi(ImeDialogUi&& other) noexcept; ImeDialogUi(ImeDialogUi&& other) noexcept;
ImeDialogUi& operator=(ImeDialogUi other); ImeDialogUi& operator=(ImeDialogUi&& other);
void Draw() override; void Draw() override;
private: private:
void Free();
void DrawInputText(); void DrawInputText();
void DrawMultiLineInputText(); void DrawMultiLineInputText();