* Changes
-Added support for OrbisImeParamExtended (extended IME parameters) in ImeHandler, ImeState, and ImeUi
-Updated all relevant constructors and logic to propagate and store the extended parameter
- Now fully supports passing extended options from sceImeOpen to the IME UI and backend

* Potential CUSA00434 [Debug] <Critical> assert.cpp:30 assert_fail_debug_msg: Assertion Failed!
buf_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?" at C:/VS/shadPS4-ime-fixes/externals/dear_imgui/imgui_widgets.cpp:4601 fix

* Attempting to resolve an assertion failure in Diablo III:
- Adjusted buffer sizes
- Updated the calculation of text‑length values

* ime-lib another hotfix

Fixed incorrect param->title validation, which caused the IME dialog to fail to appear in Stardew Valley. Need to be checked.

* Clang fix

* FF9 ImeDialog Hotfix

* Removed the validation that disallowed null text and null placeholder, since using null values is valid in `ImeDialog`.
* Added additional debug logs to aid troubleshooting.

* IME Fixes
- Add missing flags to `OrbisImeExtOption`
- Improve debug logging
- Resolve nonstop `sceImeKeyboardOpen` calls in Stardew Valley (MonoGame engine) for `userId = 254`

* IME: guard null params for CUSA04909

- Add null checks in IME constructors to prevent crashes seen in CUSA04909.
- Leave a clear note about deferring keyboard event dispatch until guest-space translation is ready.

* Some improvements
- Added debug logs so every IME event and host callback (text/caret updates) shows what the guest sent back.
- Updated ImeState to respect the guest’s text-length limit, keep buffers in sync, and record caret/text changes without duplicates.
- Fixed shutdown by actually destroying the handler on close and letting sceImeUpdate exit quietly once the IME is gone.

* CLang

* IME: simplify handlers, add param checks, fix caret index

- Unify ImeHandler init; support optional OrbisImeParamExtended; drop userId from keyboard handler.
- Add basic null checks for work and inputTextBuffer; early error logging.
- Fixed incorrect caret position. Make caret and text area indices 1-based in ImeUi::InputTextCallback.
- Set default user_id to ORBIS_USER_SERVICE_USER_ID_INVALID in sceImeParamInit.
- Reduce noisy debug logs; promote key calls to LOG_INFO.
- Remove unused extended fields from ImeState; minor cleanups.

* IME: text/caret sync fixes; add Enter payload

- Sync UI input and work buffers on UpdateText
- Sync caret position on mouse click by emiting multiple UpdateCaret events for jumps (loop over delta)
- Add text payload to PressEnter (and Close); fixes IME in God Eater 2
- Queue initial Open event after open
- Fix UTF-8 → UTF-16 conversion bounds
- Add debug logs for all queued events

* CLang

* fixed accidental copy / paste replacement in text update event that broke text deletion.

* IME: Add code-point limited InputText and use in IME UI

- Add InputTextExLimited helper to cap Unicode code points and forward callbacks
- Switch IME input to InputTextExLimited with ime_param->maxTextLength and CallbackAlways

---------

Co-authored-by: w1naenator <valdis.bogdans@hotmail.com>
This commit is contained in:
Valdis Bogdāns
2025-10-13 13:38:44 +03:00
committed by GitHub
parent a1973438db
commit 0cd6248eee
4 changed files with 276 additions and 240 deletions

View File

@@ -88,4 +88,112 @@ static void DrawCenteredText(const char* text, const char* text_end = nullptr,
SetCursorPos(pos + content);
}
// Limited-length InputTextEx wrapper (limits UTF-8 code points)
// - max_chars counts Unicode code points, not bytes
// - Works for single-line and multi-line
// - Chains user callbacks if provided
struct InputTextLimitCtx {
int max_chars;
ImGuiInputTextCallback user_cb;
void* user_user_data;
ImGuiInputTextFlags forward_flags; // original flags requested by caller
};
inline int InputTextLimitCallback(ImGuiInputTextCallbackData* data) {
InputTextLimitCtx* ctx = static_cast<InputTextLimitCtx*>(data->UserData);
if (ctx && ctx->user_cb) {
const ImGuiInputTextFlags ev = data->EventFlag;
const ImGuiInputTextFlags ff = ctx->forward_flags;
const bool should_forward =
((ev == ImGuiInputTextFlags_CallbackAlways) &&
(ff & ImGuiInputTextFlags_CallbackAlways)) ||
((ev == ImGuiInputTextFlags_CallbackEdit) && (ff & ImGuiInputTextFlags_CallbackEdit)) ||
((ev == ImGuiInputTextFlags_CallbackCharFilter) &&
(ff & ImGuiInputTextFlags_CallbackCharFilter)) ||
((ev == ImGuiInputTextFlags_CallbackCompletion) &&
(ff & ImGuiInputTextFlags_CallbackCompletion)) ||
((ev == ImGuiInputTextFlags_CallbackHistory) &&
(ff & ImGuiInputTextFlags_CallbackHistory)) ||
((ev == ImGuiInputTextFlags_CallbackResize) &&
(ff & ImGuiInputTextFlags_CallbackResize));
if (should_forward) {
void* orig = data->UserData;
data->UserData = ctx->user_user_data;
int user_ret = ctx->user_cb(data);
data->UserData = orig;
if (user_ret != 0) {
return user_ret;
}
}
}
if (!ctx || ctx->max_chars < 0) {
return 0;
}
// Enforce limit: discard extra characters on filter, trim on edit
if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) {
ImGuiContext* g = data->Ctx;
if (!g) {
return 0;
}
ImGuiInputTextState* st = &g->InputTextState;
if (st == nullptr || st->TextSrc == nullptr) {
return 0;
}
int cur_chars = ImTextCountCharsFromUtf8(st->TextSrc, st->TextSrc + st->TextLen);
int sel_chars = 0;
if (st->HasSelection()) {
const int ib = st->GetSelectionStart();
const int ie = st->GetSelectionEnd();
sel_chars = ImTextCountCharsFromUtf8(st->TextSrc + ib, st->TextSrc + ie);
}
const int remaining = ctx->max_chars - (cur_chars - sel_chars);
if (remaining <= 0) {
data->EventChar = 0;
return 1; // discard
}
return 0;
}
if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit) {
// Trim tail to ensure text length <= max_chars code points
const char* s = data->Buf;
const char* end = s + data->BufTextLen;
const char* p = s;
int codepoints = 0;
while (p < end && codepoints < ctx->max_chars) {
unsigned int c;
int len = ImTextCharFromUtf8(&c, p, end);
if (len <= 0)
break;
p += len;
codepoints++;
}
if (p < end) {
const int keep_bytes = static_cast<int>(p - s);
data->DeleteChars(keep_bytes, data->BufTextLen - keep_bytes);
if (data->CursorPos > data->BufTextLen)
data->CursorPos = data->BufTextLen;
if (data->SelectionStart > data->BufTextLen)
data->SelectionStart = data->BufTextLen;
if (data->SelectionEnd > data->BufTextLen)
data->SelectionEnd = data->BufTextLen;
}
return 0;
}
return 0;
}
inline bool InputTextExLimited(const char* label, const char* hint, char* buf, int buf_size,
const ImVec2& size_arg, ImGuiInputTextFlags flags, int max_chars,
ImGuiInputTextCallback callback = nullptr,
void* user_data = nullptr) {
InputTextLimitCtx ctx{max_chars, callback, user_data, flags};
ImGuiInputTextFlags flags2 =
flags | ImGuiInputTextFlags_CallbackCharFilter | ImGuiInputTextFlags_CallbackEdit;
return InputTextEx(label, hint, buf, buf_size, size_arg, flags2, InputTextLimitCallback, &ctx);
}
} // namespace ImGui