From 1585e0aa67da219986691c266cad0952c8e315d2 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Tue, 5 May 2026 18:51:10 +0300 Subject: [PATCH] Preparing to create a new InputConnection for text input on android --- CMakeLists.txt | 1 + android-project/app/proguard-rules.pro | 4 + .../erroratline0/infinipaint/InfiniPaint.java | 37 ++ .../InfiniPaintTextBoxInputConnection.java | 330 ++++++++++++++++++ .../infinipaint/InfiniPaintTextBoxView.java | 36 ++ .../main/java/org/libsdl/app/SDLActivity.java | 2 +- src/AndroidJNICalls.cpp | 148 ++++++++ src/AndroidJNICalls.hpp | 14 + src/InputManager.cpp | 29 +- src/InputManager.hpp | 1 + 10 files changed, 585 insertions(+), 17 deletions(-) create mode 100644 android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java create mode 100644 android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java create mode 100644 src/AndroidJNICalls.cpp create mode 100644 src/AndroidJNICalls.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f863c9e..954fc8a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ option(MACOS_MAKE_BUNDLE "Package the application as a bundle" ON) set(sources "src/main.cpp" "src/SwitchCWD.cpp" "src/MainProgram.cpp" + "src/AndroidJNICalls.cpp" "src/DrawData.cpp" "src/FontData.cpp" "src/ClientData.cpp" diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index 0fb7ae0..edae1f8 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -16,6 +16,10 @@ # public *; #} +-keep,includedescriptorclasses,allowoptimization class com.erroratline0.infinipaint.InfiniPaint { + void startTextInput(); +} + -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity { java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it java.lang.String clipboardGetText(); diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java index 7fa5036..53fb881 100644 --- a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaint.java @@ -3,11 +3,17 @@ package com.erroratline0.infinipaint; import android.content.Context; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.util.Log; +import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.RelativeLayout; import org.libsdl.app.SDLActivity; +import org.libsdl.app.SDLDummyEdit; import org.libsdl.app.SDLSurface; /** @@ -15,6 +21,7 @@ import org.libsdl.app.SDLSurface; */ public class InfiniPaint extends SDLActivity { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Window window = this.getWindow(); @@ -31,4 +38,34 @@ public class InfiniPaint extends SDLActivity { protected SDLSurface createSDLSurface(Context context) { return new InfiniPaintSurface(context); } + + static class StartInfiniPaintTextInputTask implements Runnable { + public StartInfiniPaintTextInputTask() {} + + @Override + public void run() { + RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(1, 1); + + if (mTextEdit == null) { + mTextEdit = new InfiniPaintTextBoxView(getContext()); + mLayout.addView(mTextEdit, params); + } + else + mTextEdit.setLayoutParams(params); + + mTextEdit.setVisibility(View.VISIBLE); + mTextEdit.requestFocus(); + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mTextEdit, 0); + + if (imm.isAcceptingText()) + onNativeScreenKeyboardShown(); + } + } + + static public void startTextInput() { + mSingleton.commandHandler.post(new StartInfiniPaintTextInputTask()); + } + + static InfiniPaintTextBoxView mTextEdit; } diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java new file mode 100644 index 0000000..756e8a9 --- /dev/null +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxInputConnection.java @@ -0,0 +1,330 @@ +package com.erroratline0.infinipaint; + +import static android.text.TextUtils.CAP_MODE_SENTENCES; + +import android.content.*; +import android.graphics.RectF; +import android.os.Build; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.text.Editable; +import android.view.*; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.HandwritingGesture; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; +import android.view.inputmethod.PreviewableHandwritingGesture; +import android.view.inputmethod.SurroundingText; +import android.view.inputmethod.TextAttribute; +import android.view.inputmethod.TextBoundsInfoResult; +import android.view.inputmethod.TextSnapshot; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.IntConsumer; + +import android.content.*; +import android.os.Build; +import android.text.Editable; +import android.view.*; +import android.view.inputmethod.BaseInputConnection; +import android.widget.EditText; + +import org.libsdl.app.SDL; +import org.libsdl.app.SDLActivity; + +class InfiniPaintTextBoxInputConnection extends BaseInputConnection +{ + protected EditText mEditText; + protected String mCommittedText = ""; + + InfiniPaintTextBoxInputConnection(View targetView, boolean fullEditor) { + super(targetView, fullEditor); + mEditText = new EditText(SDL.getContext()); + } + + @Override + public Editable getEditable() { + return mEditText.getEditableText(); + } + + @Override + public boolean sendKeyEvent(KeyEvent event) { + /* + * This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard) + * However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses + * and so we need to generate them ourselves in commitText. To avoid duplicates on the handful of keys + * that still do, we empty this out. + */ + + /* + * Return DOES still generate a key event, however. So rather than using it as the 'click a button' key + * as we do with physical keyboards, let's just use it to hide the keyboard. + */ + + if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { + if (SDLActivity.onNativeSoftReturnKey()) { + return true; + } + } + + return super.sendKeyEvent(event); + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) { + if (!super.commitText(text, newCursorPosition)) { + return false; + } + updateText(); + return true; + } + + @Override + public boolean setComposingText(CharSequence text, int newCursorPosition) { + if (!super.setComposingText(text, newCursorPosition)) { + return false; + } + updateText(); + return true; + } + + @Override + public boolean deleteSurroundingText(int beforeLength, int afterLength) { + // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection + // and https://bugzilla.libsdl.org/show_bug.cgi?id=2265 + if (beforeLength > 0 && afterLength == 0) { + // backspace(s) + while (beforeLength-- > 0) { + } + return true; + } + + if (!super.deleteSurroundingText(beforeLength, afterLength)) { + return false; + } + updateText(); + return true; + } + + protected void updateText() { + final Editable content = getEditable(); + if (content == null) { + return; + } + + String text = content.toString(); + int compareLength = Math.min(text.length(), mCommittedText.length()); + int matchLength, offset; + + /* Backspace over characters that are no longer in the string */ + for (matchLength = 0; matchLength < compareLength; ) { + int codePoint = mCommittedText.codePointAt(matchLength); + if (codePoint != text.codePointAt(matchLength)) { + break; + } + matchLength += Character.charCount(codePoint); + } + /* FIXME: This doesn't handle graphemes, like '🌬️' */ + for (offset = matchLength; offset < mCommittedText.length(); ) { + int codePoint = mCommittedText.codePointAt(offset); + offset += Character.charCount(codePoint); + } + + if (matchLength < text.length()) { + String pendingText = text.subSequence(matchLength, text.length()).toString(); + if (!SDLActivity.dispatchingKeyEvent()) { + for (offset = 0; offset < pendingText.length(); ) { + int codePoint = pendingText.codePointAt(offset); + if (codePoint == '\n') { + if (SDLActivity.onNativeSoftReturnKey()) { + return; + } + } + /* Higher code points don't generate simulated scancodes */ + if (codePoint > 0 && codePoint < 128) { + } + offset += Character.charCount(codePoint); + } + } + } + mCommittedText = text; + } +} + +//class InfiniPaintTextBoxInputConnection extends BaseInputConnection { +// InfiniPaintTextBoxInputConnection(View targetView, boolean fullEditor) { +// super(targetView, fullEditor); +// } +// +// public native void nativeBeginBatchEdit(); +// public native void nativeClearMetaKeyStates(int states); +// public native void nativeSetComposingText(CharSequence text, int newCursorPosition); +// public native void nativeSetComposingRegion(int start, int end); +// public native void nativeSetSelection(int start, int end); +// public native void nativeCommitCorrection(int offset, CharSequence oldText, CharSequence newText); +// public native void nativeCommitText(CharSequence text, int newCursorPosition); +// public native void nativeDeleteSurroundingText(int beforeLength, int afterLength); +// public native void nativeDeleteSurroundingTextInCodePoints(int beforeLength, int afterLength); +// public native void nativeEndBatchEdit(); +// public native void nativeFinishComposingText(); +// public native CharSequence nativeGetSelectedText(); +// public native CharSequence nativeGetTextAfterCursor(int n); +// public native CharSequence nativeGetTextBeforeCursor(int n); +// public native void nativePerformContextMenuAction(int action); +// public native void nativePerformEditorAction(int action); +// +// @Override +// public boolean beginBatchEdit() { +// nativeBeginBatchEdit(); +// return true; +// } +// +// @Override +// public boolean clearMetaKeyStates(int states) { +// nativeClearMetaKeyStates(states); +// return true; +// } +// +// @Override +// public boolean performSpellCheck() { +// return true; +// } +// +// @Override +// public TextSnapshot takeSnapshot() { +// return null; +// } +// +// @Override +// public CharSequence getTextBeforeCursor(int n, int flags) { +// return nativeGetTextBeforeCursor(n); +// } +// +// @Override +// public CharSequence getTextAfterCursor(int n, int flags) { +// return nativeGetTextAfterCursor(n); +// } +// +// @Override +// public CharSequence getSelectedText(int i) { +// return nativeGetSelectedText(); +// } +// +// @Override +// public int getCursorCapsMode(int i) { +// return CAP_MODE_SENTENCES; +// } +// +// @Override +// public ExtractedText getExtractedText(ExtractedTextRequest extractedTextRequest, int i) { +// return null; +// } +// +// @Override +// public boolean deleteSurroundingText(int i, int i1) { +// nativeDeleteSurroundingText(i, i1); +// return true; +// } +// +// @Override +// public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { +// nativeDeleteSurroundingTextInCodePoints(beforeLength, afterLength); +// return true; +// } +// +// @Override +// public boolean setComposingRegion(int i, int i1) { +// nativeSetComposingRegion(i, i1); +// return true; +// } +// +// @Override +// public boolean finishComposingText() { +// nativeFinishComposingText(); +// return true; +// } +// +// @Override +// public boolean commitCompletion(CompletionInfo completionInfo) { +// return true; +// } +// +// @Override +// public boolean commitCorrection(CorrectionInfo correctionInfo) { +// nativeCommitCorrection(correctionInfo.getOffset(), correctionInfo.getOldText(), correctionInfo.getNewText()); +// return true; +// } +// +// @Override +// public boolean setSelection(int i, int i1) { +// nativeSetSelection(i, i1); +// return true; +// } +// +// @Override +// public boolean performEditorAction(int i) { +// nativePerformEditorAction(i); +// return true; +// } +// +// @Override +// public boolean performContextMenuAction(int i) { +// nativePerformContextMenuAction(i); +// return true; +// } +// +// @Override +// public boolean endBatchEdit() { +// nativeEndBatchEdit(); +// return true; +// } +// +// @Override +// public boolean sendKeyEvent(KeyEvent keyEvent) { +// return true; +// } +// +// @Override +// public boolean reportFullscreenMode(boolean b) { +// return true; +// } +// +// @Override +// public boolean performPrivateCommand(String s, Bundle bundle) { +// return false; +// } +// +// @Override +// public boolean requestCursorUpdates(int i) { +// return true; +// } +// +// @Override +// public Handler getHandler() { +// return null; +// } +// +// @Override +// public boolean commitContent(InputContentInfo inputContentInfo, int i, Bundle bundle) { +// return true; +// } +// +// @Override +// public boolean commitText(CharSequence text, int newCursorPosition) { +// nativeCommitText(text, newCursorPosition); +// return true; +// } +// +// @Override +// public boolean setComposingText(CharSequence text, int newCursorPosition) { +// nativeSetComposingText(text, newCursorPosition); +// return true; +// } +//} + diff --git a/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java new file mode 100644 index 0000000..45a3251 --- /dev/null +++ b/android-project/app/src/main/java/com/erroratline0/infinipaint/InfiniPaintTextBoxView.java @@ -0,0 +1,36 @@ +package com.erroratline0.infinipaint; + +import android.content.*; +import android.text.InputType; +import android.view.*; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +public class InfiniPaintTextBoxView extends View +{ + InputConnection ic; + + InfiniPaintTextBoxView(Context context) { + super(context); + setFocusableInTouchMode(true); + setFocusable(true); + } + + @Override + public boolean onCheckIsTextEditor() { + return true; + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + ic = new InfiniPaintTextBoxInputConnection(this, true); + + outAttrs.inputType = InputType.TYPE_CLASS_TEXT; + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | + EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */ | + EditorInfo.IME_ACTION_DONE; + + return ic; + } +} + diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 8935622..d0edb48 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -980,7 +980,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh } // Handler for the messages - Handler commandHandler = new SDLCommandHandler(); + public Handler commandHandler = new SDLCommandHandler(); // Send a message from the SDLMain thread protected boolean sendCommand(int command, Object data) { diff --git a/src/AndroidJNICalls.cpp b/src/AndroidJNICalls.cpp new file mode 100644 index 0000000..6b9be73 --- /dev/null +++ b/src/AndroidJNICalls.cpp @@ -0,0 +1,148 @@ +#ifdef __ANDROID__ +#include "AndroidJNICalls.hpp" +#include +#include "InputManager.hpp" +#include "MainProgram.hpp" +#include +#include +#include + +namespace AndroidJNICalls { + InputManager* globalInputManager; + std::mutex globalCallMutex; + + void startTextInput() { + JNIEnv* env = static_cast(SDL_GetAndroidJNIEnv()); + Logger::get().log("INFO", std::to_string((uint64_t)env) + " jni env value"); + jobject activity = (jobject)SDL_GetAndroidActivity(); + Logger::get().log("INFO", std::to_string((uint64_t)activity) + " activity value"); + jclass clazz = env->GetObjectClass(activity); + jmethodID method_id = env->GetStaticMethodID(clazz, "startTextInput", "()V"); + Logger::get().log("INFO", std::to_string((uint64_t)method_id) + " method value"); + env->CallStaticVoidMethod(clazz, method_id); + env->DeleteLocalRef(activity); + env->DeleteLocalRef(clazz); + } + + void stopTextInput() { + + } +} + +jobject activity; +jmethodID startTextInputMethod; +JNIEnv* jniEnv; + +using namespace AndroidJNICalls; + +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintSurface_nativeInfiniPaintUpdateIMESafeArea(JNIEnv *env, jclass clazz, jint top, jint bottom, jint left, jint right) { + if(globalInputManager) { + std::scoped_lock a{globalInputManager->safeAreaWithoutIMEMutex}; + globalInputManager->safeAreaWithoutIME = {{left, top}, {globalInputManager->main.window.size.x() - right, globalInputManager->main.window.size.y() - bottom}}; + } +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeBeginBatchEdit( + JNIEnv *env, jobject jobject1) { + // TODO: implement nativeBeginBatchEdit() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeClearMetaKeyStates( + JNIEnv *env, jobject jobject1, jint states) { + // TODO: implement nativeClearMetaKeyStates() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetComposingText( + JNIEnv *env, jobject jobject1, jobject text, jint new_cursor_position) { + // TODO: implement nativeSetComposingText() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetComposingRegion( + JNIEnv *env, jobject jobject1, jint start, jint end) { + // TODO: implement nativeSetComposingRegion() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeSetSelection(JNIEnv *env, + jobject jobject1, + jint start, + jint end) { + // TODO: implement nativeSetSelection() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeCommitCorrection( + JNIEnv *env, jobject jobject1, jint offset, jobject old_text, jobject new_text) { + // TODO: implement nativeCommitCorrection() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeCommitText(JNIEnv *env, + jobject jobject1, + jobject text, + jint new_cursor_position) { + // TODO: implement nativeCommitText() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeDeleteSurroundingText( + JNIEnv *env, jobject jobject1, jint before_length, jint after_length) { + // TODO: implement nativeDeleteSurroundingText() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeDeleteSurroundingTextInCodePoints( + JNIEnv *env, jobject jobject1, jint before_length, jint after_length) { + // TODO: implement nativeDeleteSurroundingTextInCodePoints() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeEndBatchEdit(JNIEnv *env, + jobject jobject1) { + // TODO: implement nativeEndBatchEdit() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeFinishComposingText( + JNIEnv *env, jobject jobject1) { + // TODO: implement nativeFinishComposingText() +} +extern "C" +JNIEXPORT jobject JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetSelectedText( + JNIEnv *env, jobject jobject1) { + // TODO: implement nativeGetSelectedText() +} +extern "C" +JNIEXPORT jobject JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetTextAfterCursor( + JNIEnv *env, jobject jobject1, jint n) { + // TODO: implement nativeGetTextAfterCursor() +} +extern "C" +JNIEXPORT jobject JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativeGetTextBeforeCursor( + JNIEnv *env, jobject jobject1, jint n) { + // TODO: implement nativeGetTextBeforeCursor() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativePerformContextMenuAction( + JNIEnv *env, jobject jobject1, jint action) { + // TODO: implement nativePerformContextMenuAction() +} +extern "C" +JNIEXPORT void JNICALL +Java_com_erroratline0_infinipaint_InfiniPaintTextBoxInputConnection_nativePerformEditorAction( + JNIEnv *env, jobject jobject1, jint action) { + // TODO: implement nativePerformEditorAction() +} + +#endif diff --git a/src/AndroidJNICalls.hpp b/src/AndroidJNICalls.hpp new file mode 100644 index 0000000..f0cd54a --- /dev/null +++ b/src/AndroidJNICalls.hpp @@ -0,0 +1,14 @@ +#pragma once +#ifdef __ANDROID__ +#include + +struct InputManager; + +namespace AndroidJNICalls { + extern InputManager* globalInputManager; + extern std::mutex globalCallMutex; + + void startTextInput(); +} + +#endif diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 56ccd39..fdca52d 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -12,8 +12,7 @@ #include #include "MainProgram.hpp" - -InputManager* globalInputManager = nullptr; +#include "AndroidJNICalls.hpp" #ifdef _WIN32 #include @@ -32,21 +31,11 @@ extern "C" { } #endif -#ifdef __ANDROID__ -#include - -extern "C" -JNIEXPORT void JNICALL -Java_com_erroratline0_infinipaint_InfiniPaintSurface_nativeInfiniPaintUpdateIMESafeArea(JNIEnv *env, jclass clazz, jint top, jint bottom, jint left, jint right) { - if(globalInputManager) - globalInputManager->safeAreaWithoutIME = {{left, top}, {globalInputManager->main.window.size.x() - right, globalInputManager->main.window.size.y() - bottom}}; -} - -#endif - InputManager::InputManager(MainProgram& initMain): main(initMain) { - globalInputManager = this; +#ifdef __ANDROID__ + AndroidJNICalls::globalInputManager = this; +#endif frame_reset({0, 0}); @@ -167,7 +156,13 @@ void InputManager::update_accepting_input(SDL_Window* window) { excludeIMEFromSafeArea = false; else excludeIMEFromSafeArea = true; - SDL_StartTextInputWithProperties(window, propIDVal); + + #ifdef __ANDROID__ + AndroidJNICalls::startTextInput(); + #else + SDL_StartTextInputWithProperties(window, propIDVal); + #endif + #ifdef __EMSCRIPTEN__ isAcceptingInputEmscripten = 1; #endif @@ -875,6 +870,7 @@ void InputManager::update_safe_area() { else main.window.safeArea = SCollision::AABB({0, 0}, {main.window.size.x(), main.window.size.y()}); + std::scoped_lock a{safeAreaWithoutIMEMutex}; if(excludeIMEFromSafeArea && safeAreaWithoutIME.has_value()) main.window.safeArea = safeAreaWithoutIME.value(); } @@ -1148,6 +1144,7 @@ void InputManager::update() { constexpr float PADDING_BETWEEN_TEXTBOX_AND_IME = 15.0f; if(!text.textBoxes.empty()) { auto& r = text.textBoxes.front().rect; + std::scoped_lock a{safeAreaWithoutIMEMutex}; if(r.has_value() && safeAreaWithoutIME.has_value()) { float bottomY = r.value().max.y(); float yLimit = safeAreaWithoutIME.value().max.y() - PADDING_BETWEEN_TEXTBOX_AND_IME; diff --git a/src/InputManager.hpp b/src/InputManager.hpp index 8faa4e3..8d2f656 100644 --- a/src/InputManager.hpp +++ b/src/InputManager.hpp @@ -414,6 +414,7 @@ struct InputManager { float scale; }; + std::mutex safeAreaWithoutIMEMutex; std::optional> safeAreaWithoutIME; bool excludeIMEFromSafeArea = false; Vector2f screenOffset = {0.0f, 0.0f};