Revert the added keyboard work: it duplicated an existing feature and shifted hotkey ordinals

Reverts 422d831ef and 00ce69a31.

ARMSX3 already had all of this. Settings.usbKeyboard writes USB1/Type = hidkbd and
NativeApp.usbSetKeyboardEnabled, the TOGGLE_KEYBOARD hotkey raises the Android IME through
SoftKeyboard.toggle, and dispatchKeyEvent already forwards keys via forwardKeyToUsbKeyboard.
The toast users see -- "Turn on Emulate USB Keyboard (Network settings) first" -- is that
feature correctly reporting that its setting is off, not a missing capability. What I added
was a second, parallel path through cellKb with its own setting and its own hotkey.

The revert is not only for redundancy. SysHotkey is persisted BY ORDINAL, as the comments
around TOGGLE_KEYBOARD and GYRO_RECENTER say in as many words, and both are appended last
for exactly that reason. KEYBOARD_TOGGLE was inserted mid-enum, ahead of GYRO_TOGGLE, which
re-points every binding after it for every existing user.
This commit is contained in:
jpolo1224
2026-08-18 13:21:57 -04:00
parent 00ce69a315
commit fda4cc3b50
12 changed files with 2 additions and 455 deletions
@@ -24,7 +24,6 @@ struct RPCSXApi {
bool (*overlayPadData)(int port, int digital1, int digital2, int leftStickX,
int leftStickY, int rightStickX, int rightStickY);
bool (*overlayPadPressure)(int port, const int *values, int count);
bool (*keyboardKey)(int keyCode, bool pressed, int unicode);
bool (*initialize)(std::string_view rootDir, std::string_view user);
void (*setSocInfo)(std::string_view socInfo);
bool (*processCompilationQueue)(JNIEnv *env);
@@ -122,7 +121,6 @@ struct RPCSXLibrary : RPCSXApi {
// clang-format off
result.overlayPadData = reinterpret_cast<decltype(overlayPadData)>(dlsym(handle, "_rpcsx_overlayPadData"));
result.keyboardKey = reinterpret_cast<decltype(keyboardKey)>(dlsym(handle, "_rpcsx_keyboardKey"));
result.overlayPadPressure = reinterpret_cast<decltype(overlayPadPressure)>(dlsym(handle, "_rpcsx_overlayPadPressure"));
result.initialize = reinterpret_cast<decltype(initialize)>(dlsym(handle, "_rpcsx_initialize"));
result.setSocInfo = reinterpret_cast<decltype(setSocInfo)>(dlsym(handle, "_rpcsx_setSocInfo"));
@@ -236,17 +234,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_net_rpcsx_RPCSX_overlayPadData(
leftStickY, rightStickX, rightStickY);
}
extern "C" JNIEXPORT jboolean JNICALL Java_net_rpcsx_RPCSX_keyboardKey(
JNIEnv *, jobject, jint keyCode, jboolean pressed, jint unicode) {
// Absent on a core older than this export, and null before the core is dlopen()ed. Report
// failure rather than faulting, so the on-screen keyboard can show itself as inert.
if (rpcsxLib.keyboardKey == nullptr) {
return false;
}
return rpcsxLib.keyboardKey(keyCode, pressed == JNI_TRUE, unicode);
}
extern "C" JNIEXPORT jboolean JNICALL Java_net_rpcsx_RPCSX_overlayPadPressure(
JNIEnv *env, jobject, jint port, jintArray values) {
// Absent on a core older than this export: the pad still works, every button
@@ -193,9 +193,6 @@ data class Ps3Settings(
/** Stops the core writing any log after startup. A real performance lever on games that
* log heavily, but it also destroys the only artifact a bug report can carry, so it is
* off by default and the UI says so plainly. */
// Emulated keyboard. Off by default, matching the core's keyboard_handler::null: a game that
// sees a keyboard attached can behave differently, so this is opt-in rather than assumed.
val emulatedKeyboard: Boolean = false,
val silenceAllLogs: Boolean = false,
val netEnabled: Boolean = false,
val psnStatus: Boolean = false,
@@ -1068,7 +1065,6 @@ data class Settings(
// surface layout. Keeping them in sync stops "Stretch" looking inert.
put("PS3/Video", "Stretch To Display Area", "bool", (displayFitMode == 1).toString())
put("PS3/Video", "Display Aspect Override", "int", ps3.displayAspect.coerceIn(0, 4000).toString())
put("PS3/Input", "Keyboard", "bool", ps3.emulatedKeyboard.toString())
put("PS3/Misc", "Silence All Logs", "bool", ps3.silenceAllLogs.toString())
put("PS3/Overlay", "Enabled", "bool", ps3.overlayEnabled.toString())
put("PS3/Overlay", "Detail level", "enum", ps3.overlayDetail.toString())
@@ -2064,7 +2060,6 @@ data class Settings(
put("ps3FrameGenFlowScale", ps3.frameGenFlowScale)
put("ps3WriteColorBuffers", ps3.writeColorBuffers)
put("ps3GpuTurbo", ps3.gpuTurbo)
put("ps3EmulatedKeyboard", ps3.emulatedKeyboard)
put("ps3SilenceAllLogs", ps3.silenceAllLogs)
put("ps3WriteDepthBuffer", ps3.writeDepthBuffer)
put("ps3ReadColorBuffers", ps3.readColorBuffers)
@@ -2412,7 +2407,6 @@ data class Settings(
frameGenFlowScale = json.optInt("ps3FrameGenFlowScale", def.ps3.frameGenFlowScale),
writeColorBuffers = json.optBoolean("ps3WriteColorBuffers", def.ps3.writeColorBuffers),
gpuTurbo = json.optBoolean("ps3GpuTurbo", def.ps3.gpuTurbo),
emulatedKeyboard = json.optBoolean("ps3EmulatedKeyboard", def.ps3.emulatedKeyboard),
silenceAllLogs = json.optBoolean("ps3SilenceAllLogs", def.ps3.silenceAllLogs),
writeDepthBuffer = json.optBoolean("ps3WriteDepthBuffer", def.ps3.writeDepthBuffer),
readColorBuffers = json.optBoolean("ps3ReadColorBuffers", def.ps3.readColorBuffers),
@@ -2740,7 +2734,6 @@ data class Settings(
if (current.ps3.frameGenFlowScale != base.ps3.frameGenFlowScale) j.put("ps3FrameGenFlowScale", current.ps3.frameGenFlowScale)
if (current.ps3.writeColorBuffers != base.ps3.writeColorBuffers) j.put("ps3WriteColorBuffers", current.ps3.writeColorBuffers)
if (current.ps3.gpuTurbo != base.ps3.gpuTurbo) j.put("ps3GpuTurbo", current.ps3.gpuTurbo)
if (current.ps3.emulatedKeyboard != base.ps3.emulatedKeyboard) j.put("ps3EmulatedKeyboard", current.ps3.emulatedKeyboard)
if (current.ps3.silenceAllLogs != base.ps3.silenceAllLogs) j.put("ps3SilenceAllLogs", current.ps3.silenceAllLogs)
if (current.ps3.writeDepthBuffer != base.ps3.writeDepthBuffer) j.put("ps3WriteDepthBuffer", current.ps3.writeDepthBuffer)
if (current.ps3.readColorBuffers != base.ps3.readColorBuffers) j.put("ps3ReadColorBuffers", current.ps3.readColorBuffers)
@@ -3049,7 +3042,6 @@ data class Settings(
frameGenFlowScale = if (overrides.has("ps3FrameGenFlowScale")) overrides.getInt("ps3FrameGenFlowScale") else base.ps3.frameGenFlowScale,
writeColorBuffers = if (overrides.has("ps3WriteColorBuffers")) overrides.getBoolean("ps3WriteColorBuffers") else base.ps3.writeColorBuffers,
gpuTurbo = if (overrides.has("ps3GpuTurbo")) overrides.getBoolean("ps3GpuTurbo") else base.ps3.gpuTurbo,
emulatedKeyboard = if (overrides.has("ps3EmulatedKeyboard")) overrides.getBoolean("ps3EmulatedKeyboard") else base.ps3.emulatedKeyboard,
silenceAllLogs = if (overrides.has("ps3SilenceAllLogs")) overrides.getBoolean("ps3SilenceAllLogs") else base.ps3.silenceAllLogs,
writeDepthBuffer = if (overrides.has("ps3WriteDepthBuffer")) overrides.getBoolean("ps3WriteDepthBuffer") else base.ps3.writeDepthBuffer,
readColorBuffers = if (overrides.has("ps3ReadColorBuffers")) overrides.getBoolean("ps3ReadColorBuffers") else base.ps3.readColorBuffers,
@@ -1080,9 +1080,6 @@ val EN: Map<String, String> = mapOf(
"perf.framegen.x3" to "x3",
"perf.framegen.x4" to "x4",
"perf.framegen.description" to "EXPERIMENTAL. Insert generated frames between the ones the game actually draws. Costs GPU time and adds latency, so it helps when the CPU is the limit and hurts when the GPU already is.\n\nIt works best from a steady framerate. Interpolating a game that is already struggling tends to look worse rather than better \u2014 generated frames land at the wrong moment when the real interval keeps changing, which reads as judder. A locked 25 usually looks better than a wandering 28.\n\nOn-screen text shimmers or flickers while this is on \u2014 the overlay and the game\u0027s own menus get interpolated along with everything else, and fine text is what that looks worst on. That is how frame generation behaves, not a fault. Turning it off restores steady text. Switching it on or off during a game also pauses for a few seconds while the shaders are prepared.\n\nThis does nothing until you import Lossless.dll below. It is part of Lossless Scaling on Steam \u2014 you need your own copy, and nothing is bundled or downloaded. On Windows the file sits in steamapps\\common\\Lossless Scaling\\Lossless.dll; copy it to your device and pick it with the button below. Only the shaders are kept, and your copy of the file is deleted afterwards.",
"pad.section.keyboard" to "Keyboard",
"pad.emulatedKeyboard.label" to "Emulated Keyboard",
"pad.emulatedKeyboard.description" to "Tell games a keyboard is attached. Needed by titles with keyboard support \u2014 online text chat, Counter-Strike, and the debug menus in some beta builds. With this on, a USB or Bluetooth keyboard works directly, and the \"Toggle Keyboard\" hotkey raises the Android keyboard over the game. Off by default because a game that sees a keyboard can behave differently.",
"perf.ppuDecoder.label" to "PPU Decoder",
"perf.ppuDecoder.description" to "How the PS3's main CPU (PPU) is executed. LLVM recompiles PowerPC to native ARM64 and is enormously faster \u2014 keep it unless you are debugging. Interpreter is only for diagnosing a game LLVM gets wrong.",
"perf.spuDecoder.label" to "SPU Decoder",
@@ -887,12 +887,6 @@ object ControllerMappings {
// "only while aiming" binding (gyro live only while the button is held, so the
// phone can sit still the rest of the time). Both drive
// MainActivityRuntime.gyroActive and are session-only, never persisted.
// Raises and dismisses the ANDROID system keyboard over the running game, for titles
// that want real keyboard input -- online text chat, Counter-Strike, NFS Most Wanted's
// beta debug menu. The system IME is used rather than a drawn key grid so layouts,
// languages, prediction and emoji all come for free. Keys reach the guest through
// android_keyboard_handler; see MainActivityRuntime.toggleGuestKeyboard.
KEYBOARD_TOGGLE("pad.keyboardtoggle.keycode", "Toggle Keyboard"),
GYRO_TOGGLE("pad.gyrotoggle.keycode", "Gyro On/Off (toggle)"),
GYRO_HOLD("pad.gyrohold.keycode", "Gyro (hold to aim)"),
// Raises/drops the Android IME over the running game and routes what it types to the
@@ -2716,48 +2716,6 @@ open class MainActivityRuntime : ComponentActivity() {
KeyEvent.ACTION_UP -> heldKeys.remove(kc)
}
}
// Route a real keyboard to the guest's emulated keyboard.
//
// cellKb reported no keyboard at all before this: the only handler upstream ships derives
// from QObject and filters QKeyEvent, and the Android build excludes the whole Qt input
// layer, so games needing one were unreachable -- NFS Most Wanted's beta debug menu, and
// native keyboard support in games like Counter-Strike.
//
// KEYBOARD_TYPE_ALPHABETIC is the part that matters. Gamepads also report SOURCE_KEYBOARD
// for their buttons, so testing the source alone would send every controller press to the
// guest keyboard as well as the pad. Only a device that actually has alphabetic keys
// qualifies.
//
// Consumed only when the native side reports the key landed, which it does not when the
// Keyboard setting is Null, no game is running, or the core predates the export. That
// keeps a physical keyboard usable for UI navigation everywhere else.
if (event.keyCode != KeyEvent.KEYCODE_UNKNOWN &&
(event.action == KeyEvent.ACTION_DOWN || event.action == KeyEvent.ACTION_UP) &&
eState.value == EmuState.RUNNING &&
WindowImpl.inGameScreen.value == null &&
!WindowImpl.overlayVisible.value
) {
val dev = runCatching { InputDevice.getDevice(event.deviceId) }.getOrNull()
if (dev != null &&
dev.keyboardType == InputDevice.KEYBOARD_TYPE_ALPHABETIC &&
!event.isFromSource(InputDevice.SOURCE_GAMEPAD) &&
!event.isFromSource(InputDevice.SOURCE_JOYSTICK)
) {
val landed = runCatching {
net.rpcsx.RPCSX.instance.keyboardKey(
event.keyCode,
event.action == KeyEvent.ACTION_DOWN,
event.unicodeChar,
)
}.getOrDefault(false)
if (landed) {
return true
}
}
}
// Track the active gamepad so PS2 rumble routes to its vibrator.
if (event.isFromSource(InputDevice.SOURCE_GAMEPAD) ||
event.isFromSource(InputDevice.SOURCE_JOYSTICK)) {
@@ -3235,10 +3193,6 @@ open class MainActivityRuntime : ComponentActivity() {
if (down && event.repeatCount == 0) hotkeyToast(InGameOverlay.cycleOsd())
return true
}
ControllerMappings.SysHotkey.KEYBOARD_TOGGLE -> {
if (down && event.repeatCount == 0) toggleGuestKeyboard()
return true
}
ControllerMappings.SysHotkey.GYRO_TOGGLE -> {
if (down && event.repeatCount == 0) toggleGyro()
return true
@@ -3407,134 +3361,6 @@ open class MainActivityRuntime : ComponentActivity() {
* hotkey and the on-screen fast-forward touch button (FastForwardWidget). Restores
* the user's base limiter mode when turning off so it stays in sync with the
* frame-limit toggle. */
// ---- Guest keyboard (Android IME over the running game) --------------------------------
//
// The system IME is deliberately used instead of a drawn key grid: layouts, languages,
// prediction and emoji all come for free, and it is the keyboard users already know. Keys
// reach the guest through android_keyboard_handler, which registers Android keycodes directly.
//
// The IME will only open for a focused view that accepts input, so a zero-size EditText is
// parked in the content view and focused on demand. Its InputConnection is where the work
// happens, because an IME reports typing in two different ways and only one of them is a key
// event:
// * sendKeyEvent -- backspace, enter, arrows: forward the keycode as-is
// * commitText -- ordinary typed characters, no key event at all, so one has to be
// synthesised per character
// KeyCharacterMap.getEvents does that synthesis properly, including the shift presses needed
// for capitals and symbols, rather than guessing a keycode per char.
private var guestKeyboardView: android.widget.EditText? = null
private fun guestKeyboardTarget(): android.widget.EditText {
guestKeyboardView?.let { return it }
val view = object : android.widget.EditText(this) {
override fun onCreateInputConnection(outAttrs: android.view.inputmethod.EditorInfo): android.view.inputmethod.InputConnection {
outAttrs.inputType = android.text.InputType.TYPE_CLASS_TEXT
outAttrs.imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_FULLSCREEN or
android.view.inputmethod.EditorInfo.IME_FLAG_NO_EXTRACT_UI
return object : android.view.inputmethod.BaseInputConnection(this, false) {
override fun sendKeyEvent(event: KeyEvent): Boolean {
sendToGuest(event.keyCode, event.action == KeyEvent.ACTION_DOWN, event.unicodeChar)
return true
}
override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
val chars = text?.toString() ?: return true
val map = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
val events = map.getEvents(chars.toCharArray())
if (events == null) {
// No keycode produces this character on the virtual layout (emoji, CJK
// from an IME candidate list). Still deliver the text: the guest reads
// the unicode field, and CELL_KEYC_NO_EVENT is the right keycode for
// "a character arrived with no key behind it".
for (ch in chars) {
sendToGuest(KeyEvent.KEYCODE_UNKNOWN, true, ch.code)
sendToGuest(KeyEvent.KEYCODE_UNKNOWN, false, ch.code)
}
return true
}
for (ev in events) {
sendToGuest(ev.keyCode, ev.action == KeyEvent.ACTION_DOWN, ev.unicodeChar)
}
return true
}
override fun deleteSurroundingText(before: Int, after: Int): Boolean {
// Some IMEs delete by range rather than by sending backspace.
repeat(before.coerceAtLeast(0)) {
sendToGuest(KeyEvent.KEYCODE_DEL, true, 0)
sendToGuest(KeyEvent.KEYCODE_DEL, false, 0)
}
return true
}
}
}
}
view.isFocusable = true
view.isFocusableInTouchMode = true
// Zero-size and transparent: it exists to own IME focus, never to be seen or to steal a
// touch from the game or the on-screen pad.
view.layoutParams = android.view.ViewGroup.LayoutParams(0, 0)
view.setBackgroundColor(0)
view.alpha = 0f
runCatching {
(findViewById<android.view.ViewGroup>(android.R.id.content)).addView(view)
}
guestKeyboardView = view
return view
}
private fun sendToGuest(keyCode: Int, pressed: Boolean, unicode: Int) {
runCatching { net.rpcsx.RPCSX.instance.keyboardKey(keyCode, pressed, unicode) }
}
/**
* Raise or dismiss the Android keyboard over the running game (KEYBOARD_TOGGLE hotkey).
*
* Reports through a toast when the guest has no keyboard to receive the keys, which is the
* common case: the core's Keyboard setting defaults to Null, and silently showing an IME that
* goes nowhere is worse than saying so.
*/
fun toggleGuestKeyboard() {
val imm = getSystemService(android.content.Context.INPUT_METHOD_SERVICE)
as? android.view.inputmethod.InputMethodManager ?: return
if (guestKeyboardShown) {
guestKeyboardShown = false
runCatching { imm.hideSoftInputFromWindow(guestKeyboardTarget().windowToken, 0) }
guestKeyboardView?.clearFocus()
hotkeyToast("Keyboard hidden")
return
}
// Probe before showing: keyboardKey returns false when no keyboard is active, so a
// harmless no-op key tells us whether anything would receive input at all.
val reachable = runCatching {
net.rpcsx.RPCSX.instance.keyboardKey(KeyEvent.KEYCODE_UNKNOWN, false, 0)
}.getOrDefault(false)
val view = guestKeyboardTarget()
view.requestFocus()
guestKeyboardShown = true
runCatching { imm.showSoftInput(view, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT) }
hotkeyToast(
if (reachable) "Keyboard shown"
else "Keyboard shown (set Keyboard to Basic for the game to see it)",
)
}
private var guestKeyboardShown = false
/** Flip the runtime gyro enable (issue #337). Shared by the GYRO_TOGGLE hotkey and the
* edge-triggered (stick/combo) path. Only silences the sensor for this session the
* user's Gyro Mode setting is untouched, so re-enabling restores their configured mode. */
@@ -4714,7 +4540,6 @@ open class MainActivityRuntime : ComponentActivity() {
runCatching { NativeApp.speedhackLimitermode(if (on) ffLimiterMode() else baseLimiterMode()) }
hotkeyToast(if (on) "Fast Forward ON" else "Fast Forward OFF")
}
ControllerMappings.SysHotkey.KEYBOARD_TOGGLE -> toggleGuestKeyboard()
ControllerMappings.SysHotkey.GYRO_TOGGLE -> toggleGyro()
// GYRO_HOLD needs key up/down edges, which this edge-triggered path (stick
// directions / combos) doesn't provide — behave as a toggle here rather than
@@ -466,25 +466,6 @@ fun PadTab(state: MutableState<Settings>) {
)
}
CollapsibleSection(str("pad.section.keyboard"), initiallyExpanded = false) {
// Emulated keyboard (keyboard_handler). Off by default, matching the core: a game that
// sees a keyboard attached can behave differently, so this is opt-in.
//
// With it on, a physical USB/BT keyboard reaches the guest directly, and the
// "Toggle Keyboard" hotkey raises the Android keyboard over the running game.
SegmentedRow(
label = str("pad.emulatedKeyboard.label"),
options = listOf(str("common.off"), str("common.on")),
selectedIndex = if (state.value.ps3.emulatedKeyboard) 1 else 0,
description = str("pad.emulatedKeyboard.description"),
onChange = { idx ->
com.armsx2.ui.InGameOverlay.saveSettings(
state.value.copy(ps3 = state.value.ps3.copy(emulatedKeyboard = idx == 1)),
)
},
)
}
CollapsibleSection(str("pad.section.buttonMapping"), initiallyExpanded = false) {
ControllerMappings.actions.forEach { action ->
val physical = ControllerMappings.physicalForScope(action, editPlayer.intValue, editSerial)
@@ -42,7 +42,6 @@ internal val SETTINGS_SEARCH_INDEX: List<SettingsSearchEntry> = listOf(
SettingsSearchEntry("perf.sustainedPerformance.label", true, SettingsCategory.Performance),
SettingsSearchEntry("perf.adpf.label", true, SettingsCategory.Performance),
SettingsSearchEntry("perf.scheduler.label", true, SettingsCategory.Performance),
SettingsSearchEntry("pad.emulatedKeyboard.label", true, SettingsCategory.Controls),
SettingsSearchEntry("perf.ppuDecoder.label", true, SettingsCategory.Performance),
SettingsSearchEntry("perf.spuDecoder.label", true, SettingsCategory.Performance),
SettingsSearchEntry("perf.spuBlockSize.label", true, SettingsCategory.Performance),
@@ -612,11 +612,6 @@ object Rpcs3Bridge {
//
// Values cross as indices and Rpcs3Settings turns them into the core's enum NAMES;
// see the tables there for why an index cannot be mapped arithmetically.
"PS3/Input" -> when (key) {
"Keyboard" -> Rpcs3Settings.setEmulatedKeyboard(asBool(value))
else -> return false
}
"PS3/System" -> when (key) {
"Language" -> Rpcs3Settings.setConsoleLanguage(asInt(value))
"License Area" -> Rpcs3Settings.setConsoleRegion(asInt(value))
@@ -108,15 +108,6 @@ object Rpcs3Settings {
fun setKeyboardType(index: Int) = setIndexedEnum("$SYSTEM@@Keyboard Type", KEYBOARD_TYPES, index, 0)
fun setDateFormat(index: Int) = setIndexedEnum("$SYSTEM@@Date Format", DATE_FORMATS, index, 1)
fun setTimeFormat(index: Int) = setIndexedEnum("$SYSTEM@@Time Format", TIME_FORMATS, index, 1)
/**
* keyboard_handler: exactly "Null" | "Basic".
*
* Basic makes cellKb report a keyboard attached, which is what lets games with keyboard
* support see input at all -- online text chat, Counter-Strike, NFS Most Wanted's debug menu.
* Off by default because a game that sees a keyboard can behave differently.
*/
fun setEmulatedKeyboard(on: Boolean) = setEnum("$IO@@Keyboard", if (on) "Basic" else "Null")
fun setEnterButtonAssign(index: Int) =
setIndexedEnum("$SYSTEM@@Enter button assignment", ENTER_BUTTONS, index, 1)
@@ -107,18 +107,6 @@ class RPCSX {
external fun processCompilationQueue(): Boolean
external fun startMainThreadProcessor(): Boolean
external fun overlayPadData(port: Int, digital1: Int, digital2: Int, leftStickX: Int, leftStickY: Int, rightStickX: Int, rightStickY: Int): Boolean
/**
* Deliver one key to the guest keyboard.
*
* keyCode is an Android KeyEvent keycode -- the native handler registers those directly, so
* nothing needs translating on the way through. unicode is the character the key produces, or
* 0 for keys that produce none (modifiers, arrows, function keys).
*
* Returns false when there is no active keyboard: the Keyboard setting is Null, no game is
* running, or the core predates this export.
*/
external fun keyboardKey(keyCode: Int, pressed: Boolean, unicode: Int): Boolean
/** Analog pressure per pressure-capable button, in CELL_PAD press-offset order
* (RIGHT, LEFT, UP, DOWN, TRIANGLE, CIRCLE, CROSS, SQUARE, L1, R1, L2, R2),
* each 1..255, or 0 to leave that button digital. */
-159
View File
@@ -1,159 +0,0 @@
#pragma once
#include "util/types.hpp"
#include "Emu/Io/KeyboardHandler.h"
#include "Emu/system_config.h"
#include "Emu/Io/interception.h"
// A keyboard the guest believes is physically attached, driven from the Android UI.
//
// RPCS3's only real handler is basic_keyboard_handler, which derives from QObject and filters
// QKeyEvent off a QWindow -- android/CMakeLists.txt excludes it along with the rest of the Qt
// input layer, so cellKb previously had nothing but NullKeyboardHandler and reported no keyboard
// at all. Games that require one are then unreachable: NFS Most Wanted's beta debug menu, and
// native keyboard support in the likes of Counter-Strike.
//
// Almost none of basic_keyboard_handler is actually Qt-bound. KeyboardHandlerBase::HandleKey
// already takes plain u32 codes and keyboard_consumer::ConsumeKey resolves them with
// m_keys.find(code), so the code space only has to agree between whatever registers the buttons
// and whatever injects them. This handler therefore registers ANDROID KeyEvent keycodes directly
// rather than pretending to be Qt, and the UI passes the keycodes it already has.
//
// The PS3 side uses USB HID usage IDs (A = 0x04 .. Z = 0x1d, 1 = 0x1e .. 0 = 0x27), and Android's
// letter and digit keycodes are contiguous too, so those map arithmetically; only the rest needs a
// table.
class android_keyboard_handler final : public KeyboardHandlerBase
{
using KeyboardHandlerBase::KeyboardHandlerBase;
// Android KeyEvent keycodes. Named here rather than pulled from a header because the native
// side has no Android SDK constants, and these are ABI-stable platform values.
enum : u32
{
AKEY_0 = 7, AKEY_9 = 16,
AKEY_DPAD_UP = 19, AKEY_DPAD_DOWN = 20, AKEY_DPAD_LEFT = 21, AKEY_DPAD_RIGHT = 22,
AKEY_A = 29, AKEY_Z = 54,
AKEY_COMMA = 55, AKEY_PERIOD = 56,
AKEY_ALT_LEFT = 57, AKEY_ALT_RIGHT = 58,
AKEY_SHIFT_LEFT = 59, AKEY_SHIFT_RIGHT = 60,
AKEY_TAB = 61, AKEY_SPACE = 62,
AKEY_ENTER = 66, AKEY_DEL = 67, AKEY_GRAVE = 68, AKEY_MINUS = 69, AKEY_EQUALS = 70,
AKEY_LEFT_BRACKET = 71, AKEY_RIGHT_BRACKET = 72, AKEY_BACKSLASH = 73,
AKEY_SEMICOLON = 74, AKEY_APOSTROPHE = 75, AKEY_SLASH = 76,
AKEY_PAGE_UP = 92, AKEY_PAGE_DOWN = 93,
AKEY_ESCAPE = 111, AKEY_FORWARD_DEL = 112,
AKEY_CTRL_LEFT = 113, AKEY_CTRL_RIGHT = 114,
AKEY_CAPS_LOCK = 115,
AKEY_META_LEFT = 117, AKEY_META_RIGHT = 118,
AKEY_MOVE_HOME = 122, AKEY_MOVE_END = 123, AKEY_INSERT = 124,
AKEY_F1 = 131, AKEY_F12 = 142,
};
public:
void Init(keyboard_consumer& consumer, const u32 max_connect) override
{
KbInfo& info = consumer.GetInfo();
std::vector<Keyboard>& keyboards = consumer.GetKeyboards();
info = {};
keyboards.clear();
for (u32 i = 0; i < max_connect; i++)
{
Keyboard kb{};
kb.m_config.arrange = g_cfg.sys.keyboard_type;
if (consumer.id() == keyboard_consumer::identifier::overlays)
{
// Enable key repeat, matching basic_keyboard_handler: the OSK and the other
// overlays rely on repeat for held arrows and backspace.
kb.m_key_repeat = true;
}
LoadSettings(kb);
keyboards.emplace_back(kb);
}
info.max_connect = max_connect;
info.now_connect = std::min(::size32(keyboards), max_connect);
// Ownership of keyboard data: 0 = application, 1 = system.
info.info = input::g_keyboards_intercepted ? CELL_KB_INFO_INTERCEPTED : 0;
info.status[0] = CELL_KB_STATUS_CONNECTED;
}
private:
static void LoadSettings(Keyboard& keyboard)
{
std::vector<KbButton> buttons;
const auto add = [&buttons](u32 android_code, u32 cell_code)
{
buttons.emplace_back(android_code, cell_code);
};
// Modifiers. Unlike Qt, Android does tell left from right, so all eight are real here.
add(AKEY_CTRL_LEFT, CELL_KB_MKEY_L_CTRL);
add(AKEY_CTRL_RIGHT, CELL_KB_MKEY_R_CTRL);
add(AKEY_SHIFT_LEFT, CELL_KB_MKEY_L_SHIFT);
add(AKEY_SHIFT_RIGHT, CELL_KB_MKEY_R_SHIFT);
add(AKEY_ALT_LEFT, CELL_KB_MKEY_L_ALT);
add(AKEY_ALT_RIGHT, CELL_KB_MKEY_R_ALT);
add(AKEY_META_LEFT, CELL_KB_MKEY_L_WIN);
add(AKEY_META_RIGHT, CELL_KB_MKEY_R_WIN);
// Letters and digits are contiguous on both sides. PS3 digits run 1..9 then 0, which is
// why zero is handled apart from the rest.
for (u32 i = 0; i <= (AKEY_Z - AKEY_A); i++)
{
add(AKEY_A + i, CELL_KEYC_A + i);
}
for (u32 i = 1; i <= 9; i++)
{
add(AKEY_0 + i, CELL_KEYC_1 + (i - 1));
}
add(AKEY_0, CELL_KEYC_0);
// Function keys, also contiguous.
for (u32 i = 0; i <= (AKEY_F12 - AKEY_F1); i++)
{
add(AKEY_F1 + i, CELL_KEYC_F1 + i);
}
add(AKEY_ENTER, CELL_KEYC_ENTER);
add(AKEY_ESCAPE, CELL_KEYC_ESCAPE);
add(AKEY_DEL, CELL_KEYC_BS);
add(AKEY_TAB, CELL_KEYC_TAB);
add(AKEY_SPACE, CELL_KEYC_SPACE);
add(AKEY_MINUS, CELL_KEYC_MINUS);
add(AKEY_EQUALS, CELL_KEYC_EQUAL_101);
add(AKEY_LEFT_BRACKET, CELL_KEYC_LEFT_BRACKET_101);
add(AKEY_RIGHT_BRACKET, CELL_KEYC_RIGHT_BRACKET_101);
add(AKEY_BACKSLASH, CELL_KEYC_BACKSLASH_101);
add(AKEY_SEMICOLON, CELL_KEYC_SEMICOLON);
add(AKEY_APOSTROPHE, CELL_KEYC_QUOTATION_101);
add(AKEY_COMMA, CELL_KEYC_COMMA);
add(AKEY_PERIOD, CELL_KEYC_PERIOD);
add(AKEY_SLASH, CELL_KEYC_SLASH);
add(AKEY_CAPS_LOCK, CELL_KEYC_CAPS_LOCK);
add(AKEY_INSERT, CELL_KEYC_INSERT);
add(AKEY_FORWARD_DEL, CELL_KEYC_DELETE);
add(AKEY_MOVE_HOME, CELL_KEYC_HOME);
add(AKEY_MOVE_END, CELL_KEYC_END);
add(AKEY_PAGE_UP, CELL_KEYC_PAGE_UP);
add(AKEY_PAGE_DOWN, CELL_KEYC_PAGE_DOWN);
add(AKEY_DPAD_LEFT, CELL_KEYC_LEFT_ARROW);
add(AKEY_DPAD_RIGHT, CELL_KEYC_RIGHT_ARROW);
add(AKEY_DPAD_UP, CELL_KEYC_UP_ARROW);
add(AKEY_DPAD_DOWN, CELL_KEYC_DOWN_ARROW);
keyboard.m_keys.clear();
for (const KbButton& button : buttons)
{
keyboard.m_keys[button.m_keyCode] = button;
}
}
};
+2 -45
View File
@@ -11,7 +11,6 @@
#include "Emu/IdManager.h"
#include "Emu/Io/KeyboardHandler.h"
#include "Emu/Io/Null/NullKeyboardHandler.h"
#include "android_keyboard_handler.h"
#include "Emu/Io/Null/NullMouseHandler.h"
#include "Emu/Io/Null/NullPadHandler.h"
#include "Emu/Io/Null/null_camera_handler.h"
@@ -2277,18 +2276,8 @@ static void setupCallbacks() {
.handle_taskbar_progress = [](auto...) {},
.init_kb_handler =
[](auto...) {
// Honour the Keyboard setting instead of always reporting none.
//
// This was hardcoded to NullKeyboardHandler, so cellKb told every game there was no
// keyboard attached. Games that need one were simply unreachable: NFS Most Wanted's
// beta debug menu, and native keyboard support in games like Counter-Strike.
if (g_cfg.io.keyboard == keyboard_handler::basic) {
ensure(g_fxo->init<KeyboardHandlerBase, android_keyboard_handler>(
Emu.DeserialManager()));
} else {
ensure(g_fxo->init<KeyboardHandlerBase, NullKeyboardHandler>(
Emu.DeserialManager()));
}
ensure(g_fxo->init<KeyboardHandlerBase, NullKeyboardHandler>(
Emu.DeserialManager()));
},
.init_mouse_handler =
[](auto...) {
@@ -2543,38 +2532,6 @@ static bool initVirtualPad(const std::shared_ptr<Pad> &pad) {
return true;
}
// Deliver one key from the Android UI to the guest keyboard.
//
// keyCode is an Android KeyEvent keycode, which is what android_keyboard_handler registers, so no
// translation happens here. unicode is the character the key produces (0 when it produces none);
// the guest needs it for text fields, and cellKb reports it alongside the keycode.
//
// Returns false when no keyboard is active -- either the Keyboard setting is Null, or no game is
// running -- so the caller can leave the on-screen keyboard visibly inert rather than pretending
// the key landed.
extern "C" bool _rpcsx_keyboardKey(int keyCode, bool pressed, int unicode) {
if (keyCode < 0) {
return false;
}
auto *handler = g_fxo->try_get<KeyboardHandlerBase>();
if (handler == nullptr) {
return false;
}
std::u32string text;
if (unicode > 0) {
text.push_back(static_cast<char32_t>(unicode));
}
// native_code is the platform scancode on desktop; the guest only reads it for raw-mode
// keyboards, and Android gives us the keycode rather than a scancode, so pass it through.
return handler->HandleKey(static_cast<u32>(keyCode), static_cast<u32>(keyCode), pressed,
/*is_auto_repeat=*/false, text);
}
extern "C" bool _rpcsx_overlayPadData(int port, int digital1, int digital2,
int leftStickX, int leftStickY,
int rightStickX, int rightStickY) {