Input Method Editor (#3634)
Some checks are pending
Nightly Build / Check Recent Commit (push) Successful in 31s
Nightly Build / Build Nightly (push) Waiting to run
Nightly Build / Recreate Release (push) Blocked by required conditions

This commit is contained in:
Mr-X-GTA 2024-09-01 23:58:01 +02:00 committed by GitHub
parent fd66ab60e3
commit 3232515a61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 202 additions and 1 deletions

View File

@ -3,7 +3,7 @@ include(FetchContent)
FetchContent_Declare(
gtav_classes
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
GIT_TAG 21cbc2076b8d0ac9cbd98d05ebabadcced546f30
GIT_TAG b98cf8d4dafbde003bfbde27707574da77c01134
GIT_PROGRESS TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""

View File

@ -22,6 +22,7 @@ class CGameScriptHandlerMgr;
class CPedFactory;
class GtaThread;
class GameDataHash;
class InputMethodEditor;
namespace rage
{
@ -411,6 +412,9 @@ namespace big
functions::is_ped_enemies_with m_is_ped_enemies_with;
functions::can_do_damage_to_ped m_can_do_damage_to_ped;
bool* m_allow_keyboard_layout_change;
InputMethodEditor* m_ime;
};
#pragma pack(pop)
static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned");

View File

@ -1,6 +1,7 @@
#include "fiber_pool.hpp"
#include "gui/components/components.hpp"
#include "misc/cpp/imgui_stdlib.h"
#include "util/input_method_editor.hpp"
namespace big
{
@ -15,8 +16,19 @@ namespace big
}
if (ImGui::IsItemActive())
{
g.self.hud.typing = TYPING_TICKS;
draw_input_method_editor();
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
}
if (ImGui::IsItemDeactivated())
{
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
}
return retval;
}
@ -31,8 +43,19 @@ namespace big
}
if (ImGui::IsItemActive())
{
g.self.hud.typing = TYPING_TICKS;
draw_input_method_editor();
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
}
if (ImGui::IsItemDeactivated())
{
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
}
return retval;
}
}

View File

@ -1,6 +1,7 @@
#include "fiber_pool.hpp"
#include "gui/components/components.hpp"
#include "misc/cpp/imgui_stdlib.h"
#include "util/input_method_editor.hpp"
namespace big
{
@ -11,7 +12,19 @@ namespace big
g_fiber_pool->queue_job(std::move(cb));
if (ImGui::IsItemActive())
{
g.self.hud.typing = TYPING_TICKS;
draw_input_method_editor();
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
}
if (ImGui::IsItemDeactivated())
{
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
}
return returned;
}
@ -22,7 +35,19 @@ namespace big
g_fiber_pool->queue_job(std::move(cb));
if (ImGui::IsItemActive())
{
g.self.hud.typing = TYPING_TICKS;
draw_input_method_editor();
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
}
if (ImGui::IsItemDeactivated())
{
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
}
return returned;
}
}

View File

@ -1,5 +1,6 @@
#include "hooking/hooking.hpp"
#include "renderer/renderer.hpp"
#include "util/input_method_editor.hpp"
namespace big
{
@ -8,6 +9,11 @@ namespace big
if (g_running) [[likely]]
{
g_renderer.wndproc(hwnd, msg, wparam, lparam);
if (msg == WM_IME_COMPOSITION && lparam & GCS_RESULTSTR) [[unlikely]]
{
handle_ime_result();
}
}
return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam);

View File

@ -1958,6 +1958,16 @@ namespace big
{
g_pointers->m_gta.m_can_do_damage_to_ped = ptr.add(1).rip().as<functions::can_do_damage_to_ped>();
}
},
// Input Method Editor
{
"IME",
"75 25 89 44 24 28",
[](memory::handle ptr)
{
g_pointers->m_gta.m_allow_keyboard_layout_change = ptr.sub(4).rip().as<bool*>();
g_pointers->m_gta.m_ime = ptr.add(44).rip().sub(0x278 + 0x8).as<InputMethodEditor*>();
}
}
>(); // don't leave a trailing comma at the end

View File

@ -0,0 +1,133 @@
#pragma once
#include "common.hpp"
#include "pointers.hpp"
#include <imgui_internal.h>
#include <misc/InputMethodEditor.hpp>
namespace
{
// https://github.com/ocornut/imgui/blob/864a2bf6b824f9c1329d8493386208d4b0fd311c/imgui_widgets.cpp#L3948
static void insert_into_input_text(ImGuiInputTextState* obj, const ImWchar* new_text, int new_text_len)
{
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int pos = obj->Stb.cursor;
const int text_len = obj->CurLenW;
const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
return;
// Grow internal buffer if needed
if (new_text_len + text_len + 1 > obj->TextW.Size)
{
if (!is_resizable)
return;
obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
}
ImWchar* text = obj->TextW.Data;
if (pos != text_len)
memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
obj->Edited = true;
obj->CurLenW += new_text_len;
obj->CurLenA += new_text_len_utf8;
obj->TextW[obj->CurLenW] = '\0';
obj->Stb.cursor += new_text_len;
obj->Stb.has_preferred_x = 0;
obj->CursorFollow = true;
}
static void char16_to_char(char* out, const char16_t* in)
{
while (*in)
{
char16_t c = *in++;
if (c < 0x80)
{
*out++ = static_cast<char>(c);
}
else if (c < 0x800)
{
*out++ = static_cast<char>(0xC0 | (c >> 6));
*out++ = static_cast<char>(0x80 | (c & 0x3F));
}
else
{
*out++ = static_cast<char>(0xE0 | (c >> 12));
*out++ = static_cast<char>(0x80 | ((c >> 6) & 0x3F));
*out++ = static_cast<char>(0x80 | (c & 0x3F));
}
}
*out = '\0';
}
}
namespace big
{
inline void handle_ime_result()
{
auto state = ImGui::GetInputTextState(ImGui::GetActiveID());
if (!state)
return;
auto context = ImmGetContext(g_pointers->m_hwnd);
ImWchar buf[31]{};
int len = ImmGetCompositionStringW(context, GCS_RESULTSTR, buf, sizeof(buf) - 1) / sizeof(ImWchar);
insert_into_input_text(state, buf, len);
ImmReleaseContext(g_pointers->m_hwnd, context);
}
inline void draw_input_method_editor()
{
if (!g_pointers->m_gta.m_ime->m_active)
return;
constexpr size_t buf_size = ARRAYSIZE(InputMethodEditor::m_composition_string) * 3;
char buf[buf_size];
char16_to_char(buf, g_pointers->m_gta.m_ime->m_composition_string);
std::string text = buf;
for (uint32_t i = 0; i < g_pointers->m_gta.m_ime->m_count; ++i)
{
char16_to_char(buf, g_pointers->m_gta.m_ime->m_candidate_list[i]);
text += '\n';
text += (i == g_pointers->m_gta.m_ime->m_selected_index ? '>' : ' ');
text += std::to_string(i + 1);
text += '\t';
text += buf;
}
constexpr float pd = 7.5f; // padding
constexpr float lt = 1.f; // line thickness
ImVec2 ts = ImGui::CalcTextSize(text.c_str());
ImVec2 bl = ImGui::GetItemRectMin(); // bottom-left
ImVec2 br = {bl.x + ts.x + 2 * pd, bl.y}; // bottom-right
ImVec2 tl = {bl.x, bl.y - ts.y - 2 * pd}; // top-left
ImVec2 tr = {br.x, tl.y}; // top-right
auto dl = ImGui::GetForegroundDrawList();
dl->AddRectFilled(tl, br, g.window.background_color | IM_COL32_A_MASK);
dl->AddText({tl.x + pd, tl.y + pd}, g.window.text_color, text.c_str());
dl->AddLine(tl, tr, IM_COL32_BLACK); // top
dl->AddLine({bl.x, bl.y - lt}, {br.x, br.y - lt}, IM_COL32_BLACK); // bottom
dl->AddLine(tl, bl, IM_COL32_BLACK); // left
dl->AddLine({tr.x - lt, tr.y}, {br.x - lt, br.y}, IM_COL32_BLACK); // right
}
}