IME fixes (#3207)
Some checks are pending
Build and Release / reuse (push) Waiting to run
Build and Release / clang-format (push) Waiting to run
Build and Release / get-info (push) Waiting to run
Build and Release / windows-sdl (push) Blocked by required conditions
Build and Release / windows-qt (push) Blocked by required conditions
Build and Release / macos-sdl (push) Blocked by required conditions
Build and Release / macos-qt (push) Blocked by required conditions
Build and Release / linux-sdl (push) Blocked by required conditions
Build and Release / linux-qt (push) Blocked by required conditions
Build and Release / linux-sdl-gcc (push) Blocked by required conditions
Build and Release / linux-qt-gcc (push) Blocked by required conditions
Build and Release / pre-release (push) Blocked by required conditions

- Moved enums, flags, and structs to ime_common.h to simplify usage with Ime and ImeDialog
- Updated Ime to use an enum as the return type, consistent with ImeDialog
- Removed duplicate definition of OrbisImeKeycode
- Added OrbisImeLanguage as a flags enum
- Added missing options to OrbisImeOption
- Removed OrbisImeDialogOption; OrbisImeOption should be used instead
- Added OrbisImeTextAreaMode
- Updated OrbisImeTextAreaMode
- Fixed OrbisImeEventParam by adding the missing member OrbisImePanelType panel_type
- Updated the sceImeOpen declaration to use extended parameters (not yet implemented)
-Fixed Diablo III (CUSA00434) assertion failure on ImeDialog initialization

Co-authored-by: w1naenator <valdis.bogdans@hotmail.com>
This commit is contained in:
Valdis Bogdāns 2025-07-08 01:04:16 +03:00 committed by GitHub
parent 80f7ec2681
commit ddede4a52d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 325 additions and 258 deletions

View file

@ -43,8 +43,8 @@ public:
openEvent.param.rect.x = m_param.ime.posx; openEvent.param.rect.x = m_param.ime.posx;
openEvent.param.rect.y = m_param.ime.posy; openEvent.param.rect.y = m_param.ime.posy;
} else { } else {
openEvent.param.resource_id_array.userId = 1; openEvent.param.resource_id_array.user_id = 1;
openEvent.param.resource_id_array.resourceId[0] = 1; openEvent.param.resource_id_array.resource_id[0] = 1;
} }
// Are we supposed to call the event handler on init with // Are we supposed to call the event handler on init with
@ -59,10 +59,10 @@ public:
} }
} }
s32 Update(OrbisImeEventHandler handler) { Error Update(OrbisImeEventHandler handler) {
if (!m_ime_mode) { if (!m_ime_mode) {
/* We don't handle any events for ImeKeyboard */ /* We don't handle any events for ImeKeyboard */
return ORBIS_OK; return Error::OK;
} }
std::unique_lock lock{g_ime_state.queue_mutex}; std::unique_lock lock{g_ime_state.queue_mutex};
@ -73,7 +73,7 @@ public:
Execute(handler, &event, false); Execute(handler, &event, false);
} }
return ORBIS_OK; return Error::OK;
} }
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) { void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
@ -94,14 +94,14 @@ public:
} }
} }
s32 SetText(const char16_t* text, u32 length) { Error SetText(const char16_t* text, u32 length) {
g_ime_state.SetText(text, length); g_ime_state.SetText(text, length);
return ORBIS_OK; return Error::OK;
} }
s32 SetCaret(const OrbisImeCaret* caret) { Error SetCaret(const OrbisImeCaret* caret) {
g_ime_state.SetCaret(caret->index); g_ime_state.SetCaret(caret->index);
return ORBIS_OK; return Error::OK;
} }
bool IsIme() { bool IsIme() {
@ -222,11 +222,11 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) { Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
LOG_INFO(Lib_Ime, "called"); LOG_INFO(Lib_Ime, "called");
if (!width || !height) { if (!width || !height) {
return ORBIS_IME_ERROR_INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
switch (param->type) { switch (param->type) {
@ -244,18 +244,18 @@ s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32*
break; break;
} }
return ORBIS_OK; return Error::OK;
} }
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) { Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
LOG_INFO(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_keyboard_handler) { if (!g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED; return Error::NOT_OPENED;
} }
g_keyboard_handler.release(); g_keyboard_handler.release();
return ORBIS_OK; return Error::OK;
} }
int PS4_SYSV_ABI sceImeKeyboardGetInfo() { int PS4_SYSV_ABI sceImeKeyboardGetInfo() {
@ -268,25 +268,25 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) { Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
LOG_INFO(Lib_Ime, "called"); LOG_INFO(Lib_Ime, "called");
if (!param) { if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
if (!param->arg) { if (!param->arg) {
return ORBIS_IME_ERROR_INVALID_ARG; return Error::INVALID_ARG;
} }
if (!param->handler) { if (!param->handler) {
return ORBIS_IME_ERROR_INVALID_HANDLER; return Error::INVALID_HANDLER;
} }
if (g_keyboard_handler) { if (g_keyboard_handler) {
return ORBIS_IME_ERROR_BUSY; return Error::BUSY;
} }
g_keyboard_handler = std::make_unique<ImeHandler>(param); g_keyboard_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK; return Error::OK;
} }
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
@ -304,18 +304,18 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) { Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended) {
LOG_INFO(Lib_Ime, "called"); LOG_INFO(Lib_Ime, "called");
if (!param) { if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
if (g_ime_handler) { if (g_ime_handler) {
return ORBIS_IME_ERROR_BUSY; return Error::BUSY;
} }
g_ime_handler = std::make_unique<ImeHandler>(param); g_ime_handler = std::make_unique<ImeHandler>(param);
return ORBIS_OK; return Error::OK;
} }
int PS4_SYSV_ABI sceImeOpenInternal() { int PS4_SYSV_ABI sceImeOpenInternal() {
@ -339,27 +339,27 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) { Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
LOG_TRACE(Lib_Ime, "called"); LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) { if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED; return Error::NOT_OPENED;
} }
if (!caret) { if (!caret) {
return ORBIS_IME_ERROR_INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
return g_ime_handler->SetCaret(caret); return g_ime_handler->SetCaret(caret);
} }
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) { Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length) {
LOG_TRACE(Lib_Ime, "called"); LOG_TRACE(Lib_Ime, "called");
if (!g_ime_handler) { if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED; return Error::NOT_OPENED;
} }
if (!text) { if (!text) {
return ORBIS_IME_ERROR_INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
return g_ime_handler->SetText(text, length); return g_ime_handler->SetText(text, length);
@ -370,7 +370,7 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) { Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
if (g_ime_handler) { if (g_ime_handler) {
g_ime_handler->Update(handler); g_ime_handler->Update(handler);
} }
@ -380,10 +380,10 @@ s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
} }
if (!g_ime_handler || !g_keyboard_handler) { if (!g_ime_handler || !g_keyboard_handler) {
return ORBIS_IME_ERROR_NOT_OPENED; return Error::NOT_OPENED;
} }
return ORBIS_OK; return Error::OK;
} }
int PS4_SYSV_ABI sceImeVshClearPreedit() { int PS4_SYSV_ABI sceImeVshClearPreedit() {

View file

@ -13,72 +13,6 @@ class SymbolsResolver;
namespace Libraries::Ime { namespace Libraries::Ime {
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
enum class OrbisImeKeyboardOption : u32 {
Default = 0,
Repeat = 1,
RepeatEachKey = 2,
AddOsk = 4,
EffectiveWithIme = 8,
DisableResume = 16,
DisableCapslockWithoutShift = 32,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
enum class OrbisImeOption : u32 {
DEFAULT = 0,
MULTILINE = 1,
NO_AUTO_CAPITALIZATION = 2,
PASSWORD = 4,
LANGUAGES_FORCED = 8,
EXT_KEYBOARD = 16,
NO_LEARNING = 32,
FIXED_POSITION = 64,
DISABLE_RESUME = 256,
DISABLE_AUTO_SPACE = 512,
DISABLE_POSITION_ADJUSTMENT = 2048,
EXPANDED_PREEDIT_BUFFER = 4096,
USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
USE_2K_COORDINATES = 16384,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
void* arg;
OrbisImeEventHandler handler;
s8 reserved2[8];
};
struct OrbisImeParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages;
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 maxTextLength;
char16_t* inputTextBuffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
void* work;
void* arg;
OrbisImeEventHandler handler;
s8 reserved[8];
};
struct OrbisImeCaret {
f32 x;
f32 y;
u32 height;
u32 index;
};
int PS4_SYSV_ABI FinalizeImeModule(); int PS4_SYSV_ABI FinalizeImeModule();
int PS4_SYSV_ABI InitializeImeModule(); int PS4_SYSV_ABI InitializeImeModule();
int PS4_SYSV_ABI sceImeCheckFilterText(); int PS4_SYSV_ABI sceImeCheckFilterText();
@ -98,22 +32,22 @@ int PS4_SYSV_ABI sceImeDisableController();
int PS4_SYSV_ABI sceImeFilterText(); int PS4_SYSV_ABI sceImeFilterText();
int PS4_SYSV_ABI sceImeForTestFunction(); int PS4_SYSV_ABI sceImeForTestFunction();
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height); Error PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId); Error PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
int PS4_SYSV_ABI sceImeKeyboardGetInfo(); int PS4_SYSV_ABI sceImeKeyboardGetInfo();
int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param); Error PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
int PS4_SYSV_ABI sceImeKeyboardSetMode(); int PS4_SYSV_ABI sceImeKeyboardSetMode();
int PS4_SYSV_ABI sceImeKeyboardUpdate(); int PS4_SYSV_ABI sceImeKeyboardUpdate();
s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended); Error PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const OrbisImeParamExtended* extended);
int PS4_SYSV_ABI sceImeOpenInternal(); int PS4_SYSV_ABI sceImeOpenInternal();
void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param); void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
int PS4_SYSV_ABI sceImeSetCandidateIndex(); int PS4_SYSV_ABI sceImeSetCandidateIndex();
s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret); Error PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
s32 PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length); Error PS4_SYSV_ABI sceImeSetText(const char16_t* text, u32 length);
int PS4_SYSV_ABI sceImeSetTextGeometry(); int PS4_SYSV_ABI sceImeSetTextGeometry();
s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler); Error PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
int PS4_SYSV_ABI sceImeVshClearPreedit(); int PS4_SYSV_ABI sceImeVshClearPreedit();
int PS4_SYSV_ABI sceImeVshClose(); int PS4_SYSV_ABI sceImeVshClose();
int PS4_SYSV_ABI sceImeVshConfirmPreedit(); int PS4_SYSV_ABI sceImeVshConfirmPreedit();

View file

@ -3,9 +3,108 @@
#pragma once #pragma once
#include "common/enum.h"
#include "common/types.h" #include "common/types.h"
#include "core/libraries/rtc/rtc.h" #include "core/libraries/rtc/rtc.h"
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
enum class Error : u32 {
OK = 0x0,
BUSY = 0x80bc0001,
NOT_OPENED = 0x80bc0002,
NO_MEMORY = 0x80bc0003,
CONNECTION_FAILED = 0x80bc0004,
TOO_MANY_REQUESTS = 0x80bc0005,
INVALID_TEXT = 0x80bc0006,
EVENT_OVERFLOW = 0x80bc0007,
NOT_ACTIVE = 0x80bc0008,
IME_SUSPENDING = 0x80bc0009,
DEVICE_IN_USE = 0x80bc000a,
INVALID_USER_ID = 0x80bc0010,
INVALID_TYPE = 0x80bc0011,
INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
INVALID_ENTER_LABEL = 0x80bc0013,
INVALID_INPUT_METHOD = 0x80bc0014,
INVALID_OPTION = 0x80bc0015,
INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
INVALID_POSX = 0x80bc0018,
INVALID_POSY = 0x80bc0019,
INVALID_HORIZONTALIGNMENT = 0x80bc001a,
INVALID_VERTICALALIGNMENT = 0x80bc001b,
INVALID_EXTENDED = 0x80bc001c,
INVALID_KEYBOARD_TYPE = 0x80bc001d,
INVALID_WORK = 0x80bc0020,
INVALID_ARG = 0x80bc0021,
INVALID_HANDLER = 0x80bc0022,
NO_RESOURCE_ID = 0x80bc0023,
INVALID_MODE = 0x80bc0024,
INVALID_PARAM = 0x80bc0030,
INVALID_ADDRESS = 0x80bc0031,
INVALID_RESERVED = 0x80bc0032,
INVALID_TIMING = 0x80bc0033,
INTERNAL = 0x80bc00ff,
DIALOG_INVALID_TITLE = 0x80bc0101,
DIALOG_NOT_RUNNING = 0x80bc0105,
DIALOG_NOT_FINISHED = 0x80bc0106,
DIALOG_NOT_IN_USE = 0x80bc0107
};
enum class OrbisImeOption : u32 {
DEFAULT = 0,
MULTILINE = 1,
NO_AUTO_CAPITALIZATION = 2,
PASSWORD = 4,
LANGUAGES_FORCED = 8,
EXT_KEYBOARD = 16,
NO_LEARNING = 32,
FIXED_POSITION = 64,
DISABLE_COPY_PASTE = 128,
DISABLE_RESUME = 256,
DISABLE_AUTO_SPACE = 512,
DISABLE_POSITION_ADJUSTMENT = 2048,
EXPANDED_PREEDIT_BUFFER = 4096,
USE_JAPANESE_EISUU_KEY_AS_CAPSLOCK = 8192,
USE_2K_COORDINATES = 16384,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeOption);
enum class OrbisImeLanguage : u64 {
DANISH = 0x0000000000000001,
GERMAN = 0x0000000000000002,
ENGLISH_US = 0x0000000000000004,
SPANISH = 0x0000000000000008,
FRENCH = 0x0000000000000010,
ITALIAN = 0x0000000000000020,
DUTCH = 0x0000000000000040,
NORWEGIAN = 0x0000000000000080,
POLISH = 0x0000000000000100,
PORTUGUESE_PT = 0x0000000000000200,
RUSSIAN = 0x0000000000000400,
FINNISH = 0x0000000000000800,
SWEDISH = 0x0000000000001000,
JAPANESE = 0x0000000000002000,
KOREAN = 0x0000000000004000,
SIMPLIFIED_CHINESE = 0x0000000000008000,
TRADITIONAL_CHINESE = 0x0000000000010000,
PORTUGUESE_BR = 0x0000000000020000,
ENGLISH_GB = 0x0000000000040000,
TURKISH = 0x0000000000080000,
SPANISH_LA = 0x0000000000100000,
ARABIC = 0x0000000001000000,
FRENCH_CA = 0x0000000002000000,
THAI = 0x0000000004000000,
CZECH = 0x0000000008000000,
GREEK = 0x0000000010000000,
INDONESIAN = 0x0000000020000000,
VIETNAMESE = 0x0000000040000000,
ROMANIAN = 0x0000000080000000,
HUNGARIAN = 0x0000000100000000,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeLanguage);
enum class OrbisImeType : u32 { enum class OrbisImeType : u32 {
Default = 0, Default = 0,
BasicLatin = 1, BasicLatin = 1,
@ -41,6 +140,7 @@ enum class OrbisImeEventId : u32 {
Open = 0, Open = 0,
UpdateText = 1, UpdateText = 1,
UpdateCaret = 2, UpdateCaret = 2,
ChangeSize = 3,
PressClose = 4, PressClose = 4,
PressEnter = 5, PressEnter = 5,
Abort = 6, Abort = 6,
@ -51,6 +151,10 @@ enum class OrbisImeEventId : u32 {
CandidateDone = 11, CandidateDone = 11,
CandidateCancel = 12, CandidateCancel = 12,
ChangeDevice = 14, ChangeDevice = 14,
JumpToNextObject = 15,
JumpToBeforeObject = 16,
ChangeWindowType = 17,
ChangeInputMethodState = 18, ChangeInputMethodState = 18,
KeyboardOpen = 256, KeyboardOpen = 256,
@ -110,6 +214,13 @@ enum class OrbisImeDeviceType : u32 {
RemoteOsk = 3, RemoteOsk = 3,
}; };
enum class OrbisImePanelPriority : u32 {
Default = 0,
Alphabet = 1,
Symbol = 2,
Accent = 3,
};
struct OrbisImeRect { struct OrbisImeRect {
f32 x; f32 x;
f32 y; f32 y;
@ -117,8 +228,22 @@ struct OrbisImeRect {
u32 height; u32 height;
}; };
struct OrbisImeColor {
u8 r;
u8 g;
u8 b;
u8 a;
};
enum class OrbisImeTextAreaMode : u32 {
Disable = 0,
Edit = 1,
Preedit = 2,
Select = 3,
};
struct OrbisImeTextAreaProperty { struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode OrbisImeTextAreaMode mode;
u32 index; u32 index;
s32 length; s32 length;
}; };
@ -135,14 +260,14 @@ struct OrbisImeKeycode {
char16_t character; char16_t character;
u32 status; u32 status;
OrbisImeKeyboardType type; OrbisImeKeyboardType type;
s32 user_id; s32 user_id; // Todo: switch to OrbisUserServiceUserId
u32 resource_id; u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp; Libraries::Rtc::OrbisRtcTick timestamp;
}; };
struct OrbisImeKeyboardResourceIdArray { struct OrbisImeKeyboardResourceIdArray {
s32 userId; s32 user_id; // Todo: switch to OrbisUserServiceUserId
u32 resourceId[5]; u32 resource_id[5];
}; };
enum class OrbisImeCaretMovementDirection : u32 { enum class OrbisImeCaretMovementDirection : u32 {
@ -159,6 +284,16 @@ enum class OrbisImeCaretMovementDirection : u32 {
Bottom = 10, Bottom = 10,
}; };
enum class OrbisImePanelType : u32 {
Hide = 0,
Osk = 1,
Dialog = 2,
Candidate = 3,
Edit = 4,
EditAndCandidate = 5,
Accessibility = 6,
};
union OrbisImeEventParam { union OrbisImeEventParam {
OrbisImeRect rect; OrbisImeRect rect;
OrbisImeEditText text; OrbisImeEditText text;
@ -168,6 +303,7 @@ union OrbisImeEventParam {
char16_t* candidate_word; char16_t* candidate_word;
s32 candidate_index; s32 candidate_index;
OrbisImeDeviceType device_type; OrbisImeDeviceType device_type;
OrbisImePanelType panel_type;
u32 input_method_state; u32 input_method_state;
s8 reserved[64]; s8 reserved[64];
}; };
@ -177,7 +313,95 @@ struct OrbisImeEvent {
OrbisImeEventParam param; OrbisImeEventParam param;
}; };
using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
u16* outKeycode, u32* outStatus,
void* reserved);
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength, using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength); const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e); using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);
enum class OrbisImeKeyboardOption : u32 {
Default = 0,
Repeat = 1,
RepeatEachKey = 2,
AddOsk = 4,
EffectiveWithIme = 8,
DisableResume = 16,
DisableCapslockWithoutShift = 32,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
void* arg;
OrbisImeEventHandler handler;
s8 reserved2[8];
};
struct OrbisImeParam {
s32 user_id; // Todo: switch to OrbisUserServiceUserId
OrbisImeType type;
u64 supported_languages; // OrbisImeLanguage flags
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 maxTextLength;
char16_t* inputTextBuffer;
f32 posx;
f32 posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
void* work;
void* arg;
OrbisImeEventHandler handler;
s8 reserved[8];
};
struct OrbisImeCaret {
f32 x;
f32 y;
u32 height;
u32 index;
};
struct OrbisImeDialogParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages; // OrbisImeLanguage flags
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeOption option;
u32 max_text_length;
char16_t* input_text_buffer;
f32 posx;
f32 posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
const char16_t* placeholder;
const char16_t* title;
s8 reserved[16];
};
struct OrbisImeParamExtended {
u32 option; // OrbisImeExtOption flags
OrbisImeColor color_base;
OrbisImeColor color_line;
OrbisImeColor color_text_field;
OrbisImeColor color_preedit;
OrbisImeColor color_button_default;
OrbisImeColor color_button_function;
OrbisImeColor color_button_symbol;
OrbisImeColor color_text;
OrbisImeColor color_special;
OrbisImePanelPriority priority;
char* additional_dictionary_path;
OrbisImeExtKeyboardFilter ext_keyboard_filter;
u32 disable_device;
u32 ext_keyboard_mode;
s8 reserved[60];
};

View file

@ -20,19 +20,19 @@ static OrbisImeDialogResult g_ime_dlg_result{};
static ImeDialogState g_ime_dlg_state{}; static ImeDialogState g_ime_dlg_state{};
static ImeDialogUi g_ime_dlg_ui; static ImeDialogUi g_ime_dlg_ui;
static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { static bool IsValidOption(OrbisImeOption option, OrbisImeType type) {
if (False(~option & if (False(~option & (OrbisImeOption::MULTILINE |
(OrbisImeDialogOption::Multiline | OrbisImeDialogOption::NoAutoCompletion))) { OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */))) {
return false; return false;
} }
if (True(option & OrbisImeDialogOption::Multiline) && type != OrbisImeType::Default && if (True(option & OrbisImeOption::MULTILINE) && type != OrbisImeType::Default &&
type != OrbisImeType::BasicLatin) { type != OrbisImeType::BasicLatin) {
return false; return false;
} }
if (True(option & OrbisImeDialogOption::NoAutoCompletion) && type != OrbisImeType::Number && if (True(option & OrbisImeOption::NO_AUTO_CAPITALIZATION /* NoAutoCompletion */) &&
type != OrbisImeType::BasicLatin) { type != OrbisImeType::Number && type != OrbisImeType::BasicLatin) {
return false; return false;
} }
@ -96,7 +96,7 @@ Error PS4_SYSV_ABI sceImeDialogGetPanelSize(const OrbisImeDialogParam* param, u3
case OrbisImeType::Url: case OrbisImeType::Url:
case OrbisImeType::Mail: case OrbisImeType::Mail:
*width = 500; // original: 793 *width = 500; // original: 793
if (True(param->option & OrbisImeDialogOption::Multiline)) { if (True(param->option & OrbisImeOption::MULTILINE)) {
*height = 300; // original: 576 *height = 300; // original: 576
} else { } else {
*height = 150; // original: 476 *height = 150; // original: 476
@ -149,18 +149,20 @@ OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() {
} }
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
LOG_INFO(Lib_ImeDialog, ">> sceImeDialogInit: entering, param={}, extended={}",
static_cast<void*>(param), static_cast<void*>(extended));
if (g_ime_dlg_status != OrbisImeDialogStatus::None) { if (g_ime_dlg_status != OrbisImeDialogStatus::None) {
LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: busy (status=%u)", (u32)g_ime_dlg_status);
return Error::BUSY; return Error::BUSY;
} }
if (param == nullptr) { if (param == nullptr) {
LOG_INFO(Lib_ImeDialog, "called with param (NULL)"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: param is null");
return Error::INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
if (!magic_enum::enum_contains(param->type)) { if (!magic_enum::enum_contains(param->type)) {
LOG_INFO(Lib_ImeDialog, "Invalid param->type"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid param->type=%u", (u32)param->type);
return Error::INVALID_ADDRESS; return Error::INVALID_ADDRESS;
} }
@ -168,16 +170,14 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
// TODO: do correct param->supportedLanguages validation // TODO: do correct param->supportedLanguages validation
if (param->posx < 0.0f || if (param->posx < 0.0f ||
param->posx >= param->posx >= MAX_X_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posx=%f", param->posx);
LOG_INFO(Lib_ImeDialog, "Invalid param->posx");
return Error::INVALID_POSX; return Error::INVALID_POSX;
} }
if (param->posy < 0.0f || if (param->posy < 0.0f ||
param->posy >= param->posy >= MAX_Y_POSITIONS[False(param->option & OrbisImeOption::USE_2K_COORDINATES)]) {
MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LargeResolution)]) { LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid posy=%f", param->posy);
LOG_INFO(Lib_ImeDialog, "Invalid param->posy");
return Error::INVALID_POSY; return Error::INVALID_POSY;
} }
@ -192,12 +192,13 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
} }
if (!IsValidOption(param->option, param->type)) { if (!IsValidOption(param->option, param->type)) {
LOG_INFO(Lib_ImeDialog, "Invalid param->option"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid option=0x%X for type=%u",
static_cast<u32>(param->option), (u32)param->type);
return Error::INVALID_PARAM; return Error::INVALID_PARAM;
} }
if (param->input_text_buffer == nullptr) { if (param->input_text_buffer == nullptr) {
LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: input_text_buffer is null");
return Error::INVALID_INPUT_TEXT_BUFFER; return Error::INVALID_INPUT_TEXT_BUFFER;
} }
@ -220,16 +221,24 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
} }
} }
if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { if (param->max_text_length == 0 || param->max_text_length > ORBIS_IME_MAX_TEXT_LENGTH) {
LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: invalid max_text_length=%u",
param->max_text_length);
return Error::INVALID_MAX_TEXT_LENGTH; return Error::INVALID_MAX_TEXT_LENGTH;
} }
// Title string validation
if (param->title != nullptr && !std::char_traits<char16_t>::length(param->title)) {
LOG_ERROR(Lib_ImeDialog, "sceImeDialogInit: title is empty");
return Error::INVALID_PARAM;
}
g_ime_dlg_result = {}; g_ime_dlg_result = {};
g_ime_dlg_state = ImeDialogState(param, extended); g_ime_dlg_state = ImeDialogState(param, extended);
g_ime_dlg_status = OrbisImeDialogStatus::Running; g_ime_dlg_status = OrbisImeDialogStatus::Running;
g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result);
LOG_INFO(Lib_ImeDialog, "<< sceImeDialogInit: successful, status now=Running");
return Error::OK; return Error::OK;
} }

View file

@ -13,50 +13,6 @@ class SymbolsResolver;
namespace Libraries::ImeDialog { namespace Libraries::ImeDialog {
constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 2048;
enum class Error : u32 {
OK = 0x0,
BUSY = 0x80bc0001,
NOT_OPENED = 0x80bc0002,
NO_MEMORY = 0x80bc0003,
CONNECTION_FAILED = 0x80bc0004,
TOO_MANY_REQUESTS = 0x80bc0005,
INVALID_TEXT = 0x80bc0006,
EVENT_OVERFLOW = 0x80bc0007,
NOT_ACTIVE = 0x80bc0008,
IME_SUSPENDING = 0x80bc0009,
DEVICE_IN_USE = 0x80bc000a,
INVALID_USER_ID = 0x80bc0010,
INVALID_TYPE = 0x80bc0011,
INVALID_SUPPORTED_LANGUAGES = 0x80bc0012,
INVALID_ENTER_LABEL = 0x80bc0013,
INVALID_INPUT_METHOD = 0x80bc0014,
INVALID_OPTION = 0x80bc0015,
INVALID_MAX_TEXT_LENGTH = 0x80bc0016,
INVALID_INPUT_TEXT_BUFFER = 0x80bc0017,
INVALID_POSX = 0x80bc0018,
INVALID_POSY = 0x80bc0019,
INVALID_HORIZONTALIGNMENT = 0x80bc001a,
INVALID_VERTICALALIGNMENT = 0x80bc001b,
INVALID_EXTENDED = 0x80bc001c,
INVALID_KEYBOARD_TYPE = 0x80bc001d,
INVALID_WORK = 0x80bc0020,
INVALID_ARG = 0x80bc0021,
INVALID_HANDLER = 0x80bc0022,
NO_RESOURCE_ID = 0x80bc0023,
INVALID_MODE = 0x80bc0024,
INVALID_PARAM = 0x80bc0030,
INVALID_ADDRESS = 0x80bc0031,
INVALID_RESERVED = 0x80bc0032,
INVALID_TIMING = 0x80bc0033,
INTERNAL = 0x80bc00ff,
DIALOG_INVALID_TITLE = 0x80bc0101,
DIALOG_NOT_RUNNING = 0x80bc0105,
DIALOG_NOT_FINISHED = 0x80bc0106,
DIALOG_NOT_IN_USE = 0x80bc0107,
};
enum class OrbisImeDialogStatus : u32 { enum class OrbisImeDialogStatus : u32 {
None = 0, None = 0,
Running = 1, Running = 1,
@ -69,87 +25,11 @@ enum class OrbisImeDialogEndStatus : u32 {
Aborted = 2, Aborted = 2,
}; };
enum class OrbisImeDialogOption : u32 {
Default = 0,
Multiline = 1,
NoAutoCorrection = 2,
NoAutoCompletion = 4,
// TODO: Document missing options
LargeResolution = 1024,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
enum class OrbisImePanelPriority : u32 {
Default = 0,
Alphabet = 1,
Symbol = 2,
Accent = 3,
};
struct OrbisImeColor {
u8 r;
u8 g;
u8 b;
u8 a;
};
struct OrbisImeDialogResult { struct OrbisImeDialogResult {
OrbisImeDialogEndStatus endstatus; OrbisImeDialogEndStatus endstatus;
s32 reserved[12]; s32 reserved[12];
}; };
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
u64 timestamp;
};
using OrbisImeExtKeyboardFilter = PS4_SYSV_ABI int (*)(const OrbisImeKeycode* srcKeycode,
u16* outKeycode, u32* outStatus,
void* reserved);
struct OrbisImeDialogParam {
s32 user_id;
OrbisImeType type;
u64 supported_languages;
OrbisImeEnterLabel enter_label;
OrbisImeInputMethod input_method;
OrbisImeTextFilter filter;
OrbisImeDialogOption option;
u32 max_text_length;
char16_t* input_text_buffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontal_alignment;
OrbisImeVerticalAlignment vertical_alignment;
const char16_t* placeholder;
const char16_t* title;
s8 reserved[16];
};
struct OrbisImeParamExtended {
u32 option; // OrbisImeDialogOptionExtended
OrbisImeColor color_base;
OrbisImeColor color_line;
OrbisImeColor color_text_field;
OrbisImeColor color_preedit;
OrbisImeColor color_button_default;
OrbisImeColor color_button_function;
OrbisImeColor color_button_symbol;
OrbisImeColor color_text;
OrbisImeColor color_special;
OrbisImePanelPriority priority;
char* additional_dictionary_path;
OrbisImeExtKeyboardFilter ext_keyboard_filter;
uint32_t disable_device;
uint32_t ext_keyboard_mode;
int8_t reserved[60];
};
Error PS4_SYSV_ABI sceImeDialogAbort(); Error PS4_SYSV_ABI sceImeDialogAbort();
Error PS4_SYSV_ABI sceImeDialogForceClose(); Error PS4_SYSV_ABI sceImeDialogForceClose();
Error PS4_SYSV_ABI sceImeDialogForTestFunction(); Error PS4_SYSV_ABI sceImeDialogForTestFunction();

View file

@ -21,12 +21,16 @@ namespace Libraries::ImeDialog {
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
const OrbisImeParamExtended* extended) { const OrbisImeParamExtended* extended) {
LOG_INFO(Lib_ImeDialog, ">> ImeDialogState::Ctor: param={}, text_buffer={}",
static_cast<const void*>(param),
static_cast<void*>(param ? param->input_text_buffer : nullptr));
if (!param) { if (!param) {
LOG_ERROR(Lib_ImeDialog, " param==nullptr, returning without init");
return; return;
} }
user_id = param->user_id; user_id = param->user_id;
is_multi_line = True(param->option & OrbisImeDialogOption::Multiline); is_multi_line = True(param->option & OrbisImeOption::MULTILINE);
is_numeric = param->type == OrbisImeType::Number; is_numeric = param->type == OrbisImeType::Number;
type = param->type; type = param->type;
enter_label = param->enter_label; enter_label = param->enter_label;
@ -220,6 +224,7 @@ void ImeDialogUi::Free() {
void ImeDialogUi::Draw() { void ImeDialogUi::Draw() {
std::unique_lock lock{draw_mutex}; std::unique_lock lock{draw_mutex};
LOG_INFO(Lib_ImeDialog, ">> ImeDialogUi::Draw: first_render=%d", first_render);
if (!state) { if (!state) {
return; return;
@ -259,9 +264,13 @@ void ImeDialogUi::Draw() {
} }
if (state->is_multi_line) { if (state->is_multi_line) {
LOG_INFO(Lib_ImeDialog, " Drawing multi-line widget…");
DrawMultiLineInputText(); DrawMultiLineInputText();
LOG_INFO(Lib_ImeDialog, " Done DrawMultiLineInputText");
} else { } else {
LOG_INFO(Lib_ImeDialog, " Drawing input text widget…");
DrawInputText(); DrawInputText();
LOG_INFO(Lib_ImeDialog, " Done DrawInputText");
} }
SetCursorPosY(GetCursorPosY() + 10.0f); SetCursorPosY(GetCursorPosY() + 10.0f);
@ -306,6 +315,7 @@ void ImeDialogUi::Draw() {
End(); End();
first_render = false; first_render = false;
LOG_INFO(Lib_ImeDialog, "<< ImeDialogUi::Draw complete");
} }
void ImeDialogUi::DrawInputText() { void ImeDialogUi::DrawInputText() {
@ -316,7 +326,7 @@ void ImeDialogUi::DrawInputText() {
} }
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter, state->max_text_length + 1, input_size, ImGuiInputTextFlags_CallbackCharFilter,
InputTextCallback, this)) { InputTextCallback, this)) {
state->input_changed = true; state->input_changed = true;
} }
@ -332,7 +342,7 @@ void ImeDialogUi::DrawMultiLineInputText() {
} }
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
state->max_text_length, input_size, flags, InputTextCallback, this)) { state->max_text_length + 1, input_size, flags, InputTextCallback, this)) {
state->input_changed = true; state->input_changed = true;
} }
} }
@ -341,13 +351,19 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData); ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
ASSERT(ui); ASSERT(ui);
LOG_DEBUG(Lib_ImeDialog, ">> InputTextCallback: EventFlag={}, EventChar={}", data->EventFlag,
data->EventChar);
// Should we filter punctuation? // Should we filter punctuation?
if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') && if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') &&
data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') { data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') {
LOG_INFO(Lib_ImeDialog, "InputTextCallback: rejecting non-digit char '{}'",
static_cast<char>(data->EventChar));
return 1; return 1;
} }
if (!ui->state->keyboard_filter) { if (!ui->state->keyboard_filter) {
LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: no keyboard_filter, accepting char");
return 0; return 0;
} }
@ -367,16 +383,20 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
}; };
if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); LOG_ERROR(Lib_ImeDialog, "InputTextCallback: ConvertUTF8ToOrbis failed");
return 0; return 0;
} }
LOG_DEBUG(Lib_ImeDialog, "InputTextCallback: converted to Orbis char={:#X}",
static_cast<uint16_t>(src_keycode.character));
src_keycode.keycode = src_keycode.character; // TODO set this to the correct value src_keycode.keycode = src_keycode.character; // TODO set this to the correct value
u16 out_keycode; u16 out_keycode;
u32 out_status; u32 out_status;
ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status); bool keep = ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status);
LOG_DEBUG(Lib_ImeDialog,
"InputTextCallback: CallKeyboardFilter returned %s (keycode=0x%X, status=0x%X)",
keep ? "true" : "false", out_keycode, out_status);
// TODO. set the keycode // TODO. set the keycode
return 0; return 0;

View file

@ -199,7 +199,7 @@ int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
eventParam.caret_index = data->CursorPos; eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1; eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode eventParam.text_area[0].mode = OrbisImeTextAreaMode::Edit;
eventParam.text_area[0].index = data->CursorPos; eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen; eventParam.text_area[0].length = data->BufTextLen;