mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-23 07:44:18 +00:00
Compare commits
2 commits
860acb4faf
...
417ed904f0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
417ed904f0 | ||
|
|
838cc926f6 |
|
|
@ -1,3 +1,7 @@
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|
@ -33,8 +37,10 @@ object SoftwareKeyboard {
|
||||||
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||||
|
|
||||||
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
|
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
|
||||||
|
overlayView.requestFocus()
|
||||||
val im =
|
val im =
|
||||||
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
im.restartInput(overlayView)
|
||||||
im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)
|
im.showSoftInput(overlayView, InputMethodManager.SHOW_FORCED)
|
||||||
|
|
||||||
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
|
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,23 @@ import android.graphics.drawable.VectorDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.InputType
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.OnTouchListener
|
import android.view.View.OnTouchListener
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
|
import android.view.inputmethod.BaseInputConnection
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputConnection
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.window.layout.WindowMetricsCalculator
|
import androidx.window.layout.WindowMetricsCalculator
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.features.input.NativeInput
|
import org.yuzu.yuzu_emu.features.input.NativeInput
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
|
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
|
||||||
|
|
@ -49,6 +56,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||||
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
|
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
|
||||||
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
|
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
|
||||||
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
|
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
|
||||||
|
private val imeEditable = Editable.Factory.getInstance().newEditable("")
|
||||||
|
|
||||||
private var inEditMode = false
|
private var inEditMode = false
|
||||||
private var gamelessMode = false
|
private var gamelessMode = false
|
||||||
|
|
@ -75,6 +83,63 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||||
// External listener for EmulationFragment joypad overlay auto-hide
|
// External listener for EmulationFragment joypad overlay auto-hide
|
||||||
var touchEventListener: ((MotionEvent) -> Unit)? = null
|
var touchEventListener: ((MotionEvent) -> Unit)? = null
|
||||||
|
|
||||||
|
override fun onCheckIsTextEditor(): Boolean = true
|
||||||
|
|
||||||
|
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
|
||||||
|
imeEditable.clear()
|
||||||
|
outAttrs.inputType =
|
||||||
|
InputType.TYPE_CLASS_TEXT or
|
||||||
|
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or
|
||||||
|
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||||
|
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI or EditorInfo.IME_ACTION_DONE
|
||||||
|
outAttrs.initialSelStart = 0
|
||||||
|
outAttrs.initialSelEnd = 0
|
||||||
|
|
||||||
|
return object : BaseInputConnection(this, true) {
|
||||||
|
override fun getEditable(): Editable = imeEditable
|
||||||
|
|
||||||
|
override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
|
||||||
|
if (!text.isNullOrEmpty()) {
|
||||||
|
forwardCommittedText(text)
|
||||||
|
}
|
||||||
|
return super.commitText(text, newCursorPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean {
|
||||||
|
repeat(beforeLength.coerceAtLeast(0)) {
|
||||||
|
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_DEL)
|
||||||
|
}
|
||||||
|
return super.deleteSurroundingText(beforeLength, afterLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
if (event.action != KeyEvent.ACTION_DOWN) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
when (event.keyCode) {
|
||||||
|
KeyEvent.KEYCODE_BACK,
|
||||||
|
KeyEvent.KEYCODE_DEL,
|
||||||
|
KeyEvent.KEYCODE_ENTER -> {
|
||||||
|
NativeLibrary.submitInlineKeyboardInput(event.keyCode)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val textChar = event.unicodeChar
|
||||||
|
if (textChar != 0) {
|
||||||
|
NativeLibrary.submitInlineKeyboardText(textChar.toChar().toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun performEditorAction(actionCode: Int): Boolean {
|
||||||
|
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
super.onLayout(changed, left, top, right, bottom)
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
|
|
||||||
|
|
@ -119,6 +184,25 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun forwardCommittedText(text: CharSequence) {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
text.forEach { character ->
|
||||||
|
when (character) {
|
||||||
|
'\n' -> {
|
||||||
|
if (builder.isNotEmpty()) {
|
||||||
|
NativeLibrary.submitInlineKeyboardText(builder.toString())
|
||||||
|
builder.clear()
|
||||||
|
}
|
||||||
|
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
|
||||||
|
}
|
||||||
|
else -> builder.append(character)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (builder.isNotEmpty()) {
|
||||||
|
NativeLibrary.submitInlineKeyboardText(builder.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun drawGrid(canvas: Canvas) {
|
private fun drawGrid(canvas: Canvas) {
|
||||||
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
|
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
|
||||||
val width = canvas.width
|
val width = canvas.width
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,9 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
|
||||||
static_cast<s32>(m_current_text.size()));
|
static_cast<s32>(m_current_text.size()));
|
||||||
break;
|
break;
|
||||||
case KEYCODE_DEL:
|
case KEYCODE_DEL:
|
||||||
|
if (m_current_text.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_current_text.pop_back();
|
m_current_text.pop_back();
|
||||||
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
|
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
|
||||||
static_cast<int>(m_current_text.size()));
|
static_cast<int>(m_current_text.size()));
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,14 @@ static const auto default_cg_mode = nullptr; //Allow RWE
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
|
||||||
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
|
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
|
||||||
// TODO: this is because we lack regalloc - so better to be safe :(
|
|
||||||
if (waitpkg) {
|
|
||||||
code.push(Xbyak::util::eax);
|
|
||||||
code.push(Xbyak::util::ebx);
|
|
||||||
code.push(Xbyak::util::edx);
|
|
||||||
}
|
|
||||||
Xbyak::Label start, loop;
|
Xbyak::Label start, loop;
|
||||||
code.jmp(start, code.T_NEAR);
|
code.jmp(start, code.T_NEAR);
|
||||||
code.L(loop);
|
code.L(loop);
|
||||||
if (waitpkg) {
|
if (waitpkg) {
|
||||||
|
// TODO: this is because we lack regalloc - so better to be safe :(
|
||||||
|
code.push(Xbyak::util::rax);
|
||||||
|
code.push(Xbyak::util::rbx);
|
||||||
|
code.push(Xbyak::util::rdx);
|
||||||
// TODO: This clobbers EAX and EDX did we tell the regalloc?
|
// TODO: This clobbers EAX and EDX did we tell the regalloc?
|
||||||
// ARM ptr for address-monitoring
|
// ARM ptr for address-monitoring
|
||||||
code.umonitor(ptr);
|
code.umonitor(ptr);
|
||||||
|
|
@ -49,6 +47,9 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||||
code.umwait(Xbyak::util::ebx);
|
code.umwait(Xbyak::util::ebx);
|
||||||
// CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write
|
// CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write
|
||||||
// CF == 0 if we exited the wait for any other reason
|
// CF == 0 if we exited the wait for any other reason
|
||||||
|
code.pop(Xbyak::util::rdx);
|
||||||
|
code.pop(Xbyak::util::rbx);
|
||||||
|
code.pop(Xbyak::util::rax);
|
||||||
} else {
|
} else {
|
||||||
code.pause();
|
code.pause();
|
||||||
}
|
}
|
||||||
|
|
@ -57,11 +58,6 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||||
/*code.lock();*/ code.xchg(code.dword[ptr], tmp);
|
/*code.lock();*/ code.xchg(code.dword[ptr], tmp);
|
||||||
code.test(tmp, tmp);
|
code.test(tmp, tmp);
|
||||||
code.jnz(loop, code.T_NEAR);
|
code.jnz(loop, code.T_NEAR);
|
||||||
if (waitpkg) {
|
|
||||||
code.pop(Xbyak::util::edx);
|
|
||||||
code.pop(Xbyak::util::ebx);
|
|
||||||
code.pop(Xbyak::util::eax);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) {
|
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue