mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-03-27 05:08:34 -06:00
* 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>
200 lines
7.2 KiB
C++
200 lines
7.2 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <cmath>
|
|
#include <imgui.h>
|
|
|
|
#include "imgui_internal.h"
|
|
|
|
#define IM_COL32_GRAY(x) IM_COL32(x, x, x, 0xFF)
|
|
|
|
#define IMGUI_FONT_TEXT 0
|
|
#define IMGUI_FONT_MONO 1
|
|
|
|
namespace ImGui {
|
|
|
|
namespace Easing {
|
|
|
|
inline float FastInFastOutCubic(float x) {
|
|
constexpr float c4 = 1.587401f; // 4^(1/3)
|
|
constexpr float c05 = 0.7937f; // 0.5^(1/3)
|
|
return std::pow(c4 * x - c05, 3.0f) + 0.5f;
|
|
}
|
|
|
|
} // namespace Easing
|
|
|
|
inline void CentralizeNextWindow() {
|
|
const auto display_size = GetIO().DisplaySize;
|
|
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
|
|
}
|
|
|
|
inline void CentralizeWindow() {
|
|
const auto display_size = GetIO().DisplaySize - GetCurrentWindowRead()->SizeFull;
|
|
SetWindowPos(display_size / 2.0f);
|
|
}
|
|
|
|
inline void KeepWindowInside(ImVec2 display_size = GetIO().DisplaySize) {
|
|
const auto cur_pos = GetWindowPos();
|
|
if (cur_pos.x < 0.0f || cur_pos.y < 0.0f) {
|
|
SetWindowPos(ImMax(cur_pos, ImVec2(0.0f, 0.0f)));
|
|
return;
|
|
}
|
|
const auto cur_size = GetCurrentWindowRead()->SizeFull;
|
|
const auto bottom_right = cur_pos + cur_size;
|
|
if (bottom_right.x > display_size.x || bottom_right.y > display_size.y) {
|
|
const auto max_pos = display_size - cur_size;
|
|
SetWindowPos(ImMin(cur_pos, max_pos));
|
|
}
|
|
}
|
|
|
|
inline void KeepNavHighlight() {
|
|
GetCurrentContext()->NavCursorVisible = true;
|
|
}
|
|
|
|
inline void SetItemCurrentNavFocus(const ImGuiID id = -1) {
|
|
const auto ctx = GetCurrentContext();
|
|
SetFocusID(id == -1 ? ctx->LastItemData.ID : id, ctx->CurrentWindow);
|
|
ctx->NavInitResult.Clear();
|
|
ctx->NavCursorVisible = true;
|
|
}
|
|
|
|
inline void DrawPrettyBackground() {
|
|
const double time = GetTime() / 1.5f;
|
|
const float x = ((float)std::cos(time) + 1.0f) / 2.0f;
|
|
const float d = Easing::FastInFastOutCubic(x);
|
|
u8 top_left = ImLerp(0x13, 0x05, d);
|
|
u8 top_right = ImLerp(0x00, 0x07, d);
|
|
u8 bottom_right = ImLerp(0x03, 0x27, d);
|
|
u8 bottom_left = ImLerp(0x05, 0x00, d);
|
|
|
|
auto& window = *GetCurrentWindowRead();
|
|
auto inner_pos = window.DC.CursorPos - window.WindowPadding;
|
|
auto inner_size = GetContentRegionAvail() + window.WindowPadding * 2.0f;
|
|
GetWindowDrawList()->AddRectFilledMultiColor(
|
|
inner_pos, inner_pos + inner_size, IM_COL32_GRAY(top_left), IM_COL32_GRAY(top_right),
|
|
IM_COL32_GRAY(bottom_right), IM_COL32_GRAY(bottom_left));
|
|
}
|
|
|
|
static void DrawCenteredText(const char* text, const char* text_end = nullptr,
|
|
ImVec2 content = GetContentRegionAvail()) {
|
|
auto pos = GetCursorPos();
|
|
const auto text_size = CalcTextSize(text, text_end, false, content.x - 40.0f);
|
|
PushTextWrapPos(content.x);
|
|
SetCursorPos(pos + (content - text_size) / 2.0f);
|
|
TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
|
|
PopTextWrapPos();
|
|
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
|