mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
Android: Hotkey Enable Button (#1464)
This commit is contained in:
parent
5ac0ef8fde
commit
6b2ac400eb
7 changed files with 177 additions and 64 deletions
|
|
@ -267,36 +267,28 @@ class EmulationActivity : AppCompatActivity() {
|
||||||
return super.dispatchKeyEvent(event)
|
return super.dispatchKeyEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
val button =
|
when (event.action) {
|
||||||
preferences.getInt(InputBindingSetting.getInputButtonKey(event.keyCode), event.keyCode)
|
|
||||||
val action: Int = when (event.action) {
|
|
||||||
KeyEvent.ACTION_DOWN -> {
|
KeyEvent.ACTION_DOWN -> {
|
||||||
hotkeyUtility.handleHotkey(button)
|
|
||||||
|
|
||||||
// On some devices, the back gesture / button press is not intercepted by androidx
|
// On some devices, the back gesture / button press is not intercepted by androidx
|
||||||
// and fails to open the emulation menu. So we're stuck running deprecated code to
|
// and fails to open the emulation menu. So we're stuck running deprecated code to
|
||||||
// cover for either a fault on androidx's side or in OEM skins (MIUI at least)
|
// cover for either a fault on androidx's side or in OEM skins (MIUI at least)
|
||||||
|
|
||||||
if (event.keyCode == KeyEvent.KEYCODE_BACK) {
|
if (event.keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
// If the hotkey is pressed, we don't want to open the drawer
|
// If the hotkey is pressed, we don't want to open the drawer
|
||||||
if (!hotkeyUtility.HotkeyIsPressed) {
|
if (!hotkeyUtility.hotkeyIsPressed) {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hotkeyUtility.handleKeyPress(event)
|
||||||
// Normal key events.
|
|
||||||
NativeLibrary.ButtonState.PRESSED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.ACTION_UP -> {
|
KeyEvent.ACTION_UP -> {
|
||||||
hotkeyUtility.HotkeyIsPressed = false
|
return hotkeyUtility.handleKeyRelease(event)
|
||||||
NativeLibrary.ButtonState.RELEASED
|
}
|
||||||
|
else -> {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else -> return false
|
|
||||||
}
|
}
|
||||||
val input = event.device
|
|
||||||
?: // Controller was disconnected
|
|
||||||
return false
|
|
||||||
return NativeLibrary.onGamePadEvent(input.descriptor, button, action)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAmiiboSelected(selectedFile: String) {
|
private fun onAmiiboSelected(selectedFile: String) {
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@ enum class Hotkey(val button: Int) {
|
||||||
PAUSE_OR_RESUME(10004),
|
PAUSE_OR_RESUME(10004),
|
||||||
QUICKSAVE(10005),
|
QUICKSAVE(10005),
|
||||||
QUICKLOAD(10006),
|
QUICKLOAD(10006),
|
||||||
TURBO_LIMIT(10007);
|
TURBO_LIMIT(10007),
|
||||||
|
ENABLE(10008);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,50 +5,140 @@
|
||||||
package org.citra.citra_emu.features.hotkeys
|
package org.citra.citra_emu.features.hotkeys
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.citra.citra_emu.CitraApplication
|
||||||
import org.citra.citra_emu.NativeLibrary
|
import org.citra.citra_emu.NativeLibrary
|
||||||
import org.citra.citra_emu.R
|
import org.citra.citra_emu.R
|
||||||
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
import org.citra.citra_emu.utils.EmulationLifecycleUtil
|
||||||
import org.citra.citra_emu.utils.TurboHelper
|
import org.citra.citra_emu.utils.TurboHelper
|
||||||
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
import org.citra.citra_emu.display.ScreenAdjustmentUtil
|
||||||
|
import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
|
||||||
|
import org.citra.citra_emu.features.settings.model.Settings
|
||||||
|
|
||||||
class HotkeyUtility(
|
class HotkeyUtility(
|
||||||
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
|
private val screenAdjustmentUtil: ScreenAdjustmentUtil,
|
||||||
private val context: Context) {
|
private val context: Context
|
||||||
|
) {
|
||||||
|
|
||||||
private val hotkeyButtons = Hotkey.entries.map { it.button }
|
private val hotkeyButtons = Hotkey.entries.map { it.button }
|
||||||
var HotkeyIsPressed = false
|
private var hotkeyIsEnabled = false
|
||||||
|
var hotkeyIsPressed = false
|
||||||
|
private val currentlyPressedButtons = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
fun handleKeyPress(keyEvent: KeyEvent): Boolean {
|
||||||
|
var handled = false
|
||||||
|
val buttonSet = InputBindingSetting.getButtonSet(keyEvent)
|
||||||
|
val enableButton =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||||
|
.getString(Settings.HOTKEY_ENABLE, "")
|
||||||
|
val thisKeyIsEnableButton = buttonSet.contains(Hotkey.ENABLE.button)
|
||||||
|
val thisKeyIsHotkey =
|
||||||
|
!thisKeyIsEnableButton && Hotkey.entries.any { buttonSet.contains(it.button) }
|
||||||
|
hotkeyIsEnabled = hotkeyIsEnabled || enableButton == "" || thisKeyIsEnableButton
|
||||||
|
|
||||||
|
// Now process all internal buttons associated with this keypress
|
||||||
|
for (button in buttonSet) {
|
||||||
|
currentlyPressedButtons.add(button)
|
||||||
|
//option 1 - this is the enable command, which was already handled
|
||||||
|
if (button == Hotkey.ENABLE.button) {
|
||||||
|
handled = true
|
||||||
|
}
|
||||||
|
// option 2 - this is a different hotkey command
|
||||||
|
else if (hotkeyButtons.contains(button)) {
|
||||||
|
if (hotkeyIsEnabled) {
|
||||||
|
handled = handleHotkey(button) || handled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// option 3 - this is a normal key
|
||||||
|
else {
|
||||||
|
// if this key press is ALSO associated with a hotkey that will process, skip
|
||||||
|
// the normal key event.
|
||||||
|
if (!thisKeyIsHotkey || !hotkeyIsEnabled) {
|
||||||
|
handled = NativeLibrary.onGamePadEvent(
|
||||||
|
keyEvent.device.descriptor,
|
||||||
|
button,
|
||||||
|
NativeLibrary.ButtonState.PRESSED
|
||||||
|
) || handled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleKeyRelease(keyEvent: KeyEvent): Boolean {
|
||||||
|
var handled = false
|
||||||
|
val buttonSet = InputBindingSetting.getButtonSet(keyEvent)
|
||||||
|
val thisKeyIsEnableButton = buttonSet.contains(Hotkey.ENABLE.button)
|
||||||
|
val thisKeyIsHotkey =
|
||||||
|
!thisKeyIsEnableButton && Hotkey.entries.any { buttonSet.contains(it.button) }
|
||||||
|
if (thisKeyIsEnableButton) {
|
||||||
|
handled = true; hotkeyIsEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (button in buttonSet) {
|
||||||
|
// this is a hotkey button
|
||||||
|
if (hotkeyButtons.contains(button)) {
|
||||||
|
currentlyPressedButtons.remove(button)
|
||||||
|
if (!currentlyPressedButtons.any { hotkeyButtons.contains(it) }) {
|
||||||
|
// all hotkeys are no longer pressed
|
||||||
|
hotkeyIsPressed = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if this key ALSO sends a hotkey command that we already/will handle,
|
||||||
|
// or if we did not register the press of this button, e.g. if this key
|
||||||
|
// was also a hotkey pressed after enable, but released after enable button release, then
|
||||||
|
// skip the normal key event
|
||||||
|
if ((!thisKeyIsHotkey || !hotkeyIsEnabled) && currentlyPressedButtons.contains(
|
||||||
|
button
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
handled = NativeLibrary.onGamePadEvent(
|
||||||
|
keyEvent.device.descriptor,
|
||||||
|
button,
|
||||||
|
NativeLibrary.ButtonState.RELEASED
|
||||||
|
) || handled
|
||||||
|
currentlyPressedButtons.remove(button)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled
|
||||||
|
}
|
||||||
|
|
||||||
fun handleHotkey(bindedButton: Int): Boolean {
|
fun handleHotkey(bindedButton: Int): Boolean {
|
||||||
if(hotkeyButtons.contains(bindedButton)) {
|
when (bindedButton) {
|
||||||
when (bindedButton) {
|
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()
|
||||||
Hotkey.SWAP_SCREEN.button -> screenAdjustmentUtil.swapScreen()
|
Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts()
|
||||||
Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts()
|
Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame()
|
||||||
Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame()
|
Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume()
|
||||||
Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume()
|
Hotkey.TURBO_LIMIT.button -> TurboHelper.toggleTurbo(true)
|
||||||
Hotkey.TURBO_LIMIT.button -> TurboHelper.toggleTurbo(true)
|
Hotkey.QUICKSAVE.button -> {
|
||||||
Hotkey.QUICKSAVE.button -> {
|
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
||||||
NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT)
|
Toast.makeText(
|
||||||
Toast.makeText(context,
|
context,
|
||||||
context.getString(R.string.saving),
|
context.getString(R.string.saving),
|
||||||
Toast.LENGTH_SHORT).show()
|
Toast.LENGTH_SHORT
|
||||||
}
|
).show()
|
||||||
Hotkey.QUICKLOAD.button -> {
|
|
||||||
val wasLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT)
|
|
||||||
val stringRes = if(wasLoaded) {
|
|
||||||
R.string.loading
|
|
||||||
} else {
|
|
||||||
R.string.quickload_not_found
|
|
||||||
}
|
|
||||||
Toast.makeText(context,
|
|
||||||
context.getString(stringRes),
|
|
||||||
Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
HotkeyIsPressed = true
|
|
||||||
return true
|
Hotkey.QUICKLOAD.button -> {
|
||||||
|
val wasLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT)
|
||||||
|
val stringRes = if (wasLoaded) {
|
||||||
|
R.string.loading
|
||||||
|
} else {
|
||||||
|
R.string.quickload_not_found
|
||||||
|
}
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(stringRes),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
return false
|
hotkeyIsPressed = true
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ class Settings {
|
||||||
const val KEY_CSTICK_AXIS_HORIZONTAL = "cstick_axis_horizontal"
|
const val KEY_CSTICK_AXIS_HORIZONTAL = "cstick_axis_horizontal"
|
||||||
const val KEY_DPAD_AXIS_VERTICAL = "dpad_axis_vertical"
|
const val KEY_DPAD_AXIS_VERTICAL = "dpad_axis_vertical"
|
||||||
const val KEY_DPAD_AXIS_HORIZONTAL = "dpad_axis_horizontal"
|
const val KEY_DPAD_AXIS_HORIZONTAL = "dpad_axis_horizontal"
|
||||||
|
const val HOTKEY_ENABLE = "hotkey_enable"
|
||||||
const val HOTKEY_SCREEN_SWAP = "hotkey_screen_swap"
|
const val HOTKEY_SCREEN_SWAP = "hotkey_screen_swap"
|
||||||
const val HOTKEY_CYCLE_LAYOUT = "hotkey_toggle_layout"
|
const val HOTKEY_CYCLE_LAYOUT = "hotkey_toggle_layout"
|
||||||
const val HOTKEY_CLOSE_GAME = "hotkey_close_game"
|
const val HOTKEY_CLOSE_GAME = "hotkey_close_game"
|
||||||
|
|
@ -202,6 +203,7 @@ class Settings {
|
||||||
R.string.button_zr
|
R.string.button_zr
|
||||||
)
|
)
|
||||||
val hotKeys = listOf(
|
val hotKeys = listOf(
|
||||||
|
HOTKEY_ENABLE,
|
||||||
HOTKEY_SCREEN_SWAP,
|
HOTKEY_SCREEN_SWAP,
|
||||||
HOTKEY_CYCLE_LAYOUT,
|
HOTKEY_CYCLE_LAYOUT,
|
||||||
HOTKEY_CLOSE_GAME,
|
HOTKEY_CLOSE_GAME,
|
||||||
|
|
@ -211,6 +213,7 @@ class Settings {
|
||||||
HOTKEY_TURBO_LIMIT
|
HOTKEY_TURBO_LIMIT
|
||||||
)
|
)
|
||||||
val hotkeyTitles = listOf(
|
val hotkeyTitles = listOf(
|
||||||
|
R.string.controller_hotkey_enable_button,
|
||||||
R.string.emulation_swap_screens,
|
R.string.emulation_swap_screens,
|
||||||
R.string.emulation_cycle_landscape_layouts,
|
R.string.emulation_cycle_landscape_layouts,
|
||||||
R.string.emulation_close_game,
|
R.string.emulation_close_game,
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ class InputBindingSetting(
|
||||||
Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
|
Settings.KEY_BUTTON_DOWN -> NativeLibrary.ButtonType.DPAD_DOWN
|
||||||
Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
|
Settings.KEY_BUTTON_LEFT -> NativeLibrary.ButtonType.DPAD_LEFT
|
||||||
Settings.KEY_BUTTON_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
|
Settings.KEY_BUTTON_RIGHT -> NativeLibrary.ButtonType.DPAD_RIGHT
|
||||||
|
Settings.HOTKEY_ENABLE -> Hotkey.ENABLE.button
|
||||||
Settings.HOTKEY_SCREEN_SWAP -> Hotkey.SWAP_SCREEN.button
|
Settings.HOTKEY_SCREEN_SWAP -> Hotkey.SWAP_SCREEN.button
|
||||||
Settings.HOTKEY_CYCLE_LAYOUT -> Hotkey.CYCLE_LAYOUT.button
|
Settings.HOTKEY_CYCLE_LAYOUT -> Hotkey.CYCLE_LAYOUT.button
|
||||||
Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button
|
Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button
|
||||||
|
|
@ -162,36 +163,40 @@ class InputBindingSetting(
|
||||||
fun removeOldMapping() {
|
fun removeOldMapping() {
|
||||||
// Try remove all possible keys we wrote for this setting
|
// Try remove all possible keys we wrote for this setting
|
||||||
val oldKey = preferences.getString(reverseKey, "")
|
val oldKey = preferences.getString(reverseKey, "")
|
||||||
(setting as AbstractStringSetting).string = ""
|
|
||||||
if (oldKey != "") {
|
if (oldKey != "") {
|
||||||
|
(setting as AbstractStringSetting).string = ""
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
.remove(abstractSetting.key) // Used for ui text
|
.remove(abstractSetting.key) // Used for ui text
|
||||||
.remove(oldKey) // Used for button mapping
|
|
||||||
.remove(oldKey + "_GuestOrientation") // Used for axis orientation
|
.remove(oldKey + "_GuestOrientation") // Used for axis orientation
|
||||||
.remove(oldKey + "_GuestButton") // Used for axis button
|
.remove(oldKey + "_GuestButton") // Used for axis button
|
||||||
.remove(oldKey + "_Inverted") // used for axis inversion
|
.remove(oldKey + "_Inverted") // used for axis inversion
|
||||||
.apply()
|
.remove(reverseKey)
|
||||||
|
val buttonCodes = try {
|
||||||
|
preferences.getStringSet(oldKey, mutableSetOf<String>())!!.toMutableSet()
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
// if this is an int pref, either old button or an axis, so just remove it
|
||||||
|
preferences.edit().remove(oldKey).apply()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buttonCodes.remove(buttonCode.toString());
|
||||||
|
preferences.edit().putStringSet(oldKey,buttonCodes).apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to write a gamepad button mapping for the setting.
|
* Helper function to write a gamepad button mapping for the setting.
|
||||||
*/
|
*/
|
||||||
private fun writeButtonMapping(key: String) {
|
private fun writeButtonMapping(keyEvent: KeyEvent) {
|
||||||
val editor = preferences.edit()
|
val editor = preferences.edit()
|
||||||
|
val key = getInputButtonKey(keyEvent)
|
||||||
// Remove mapping for another setting using this input
|
// Pull in all codes associated with this key
|
||||||
val oldButtonCode = preferences.getInt(key, -1)
|
// Migrate from the old int preference if need be
|
||||||
if (oldButtonCode != -1) {
|
val buttonCodes = InputBindingSetting.getButtonSet(keyEvent)
|
||||||
val oldKey = getButtonKey(oldButtonCode)
|
buttonCodes.add(buttonCode)
|
||||||
editor.remove(oldKey) // Only need to remove UI text setting, others will be overwritten
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup old mapping for this setting
|
// Cleanup old mapping for this setting
|
||||||
removeOldMapping()
|
removeOldMapping()
|
||||||
|
|
||||||
// Write new mapping
|
editor.putStringSet(key, buttonCodes.mapTo(mutableSetOf()) {it.toString()})
|
||||||
editor.putInt(key, buttonCode)
|
|
||||||
|
|
||||||
// Write next reverse mapping for future cleanup
|
// Write next reverse mapping for future cleanup
|
||||||
editor.putString(reverseKey, key)
|
editor.putString(reverseKey, key)
|
||||||
|
|
@ -229,7 +234,7 @@ class InputBindingSetting(
|
||||||
}
|
}
|
||||||
|
|
||||||
val code = translateEventToKeyId(keyEvent)
|
val code = translateEventToKeyId(keyEvent)
|
||||||
writeButtonMapping(getInputButtonKey(code))
|
writeButtonMapping(keyEvent)
|
||||||
val uiString = "${keyEvent.device.name}: Button $code"
|
val uiString = "${keyEvent.device.name}: Button $code"
|
||||||
value = uiString
|
value = uiString
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +294,26 @@ class InputBindingSetting(
|
||||||
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
NativeLibrary.ButtonType.DPAD_RIGHT -> Settings.KEY_BUTTON_RIGHT
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get the mutable set of int button values this key should map to given an event
|
||||||
|
*/
|
||||||
|
fun getButtonSet(keyCode: KeyEvent):MutableSet<Int> {
|
||||||
|
val key = getInputButtonKey(keyCode)
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||||
|
var buttonCodes = try {
|
||||||
|
preferences.getStringSet(key, mutableSetOf<String>())
|
||||||
|
} catch (e: ClassCastException) {
|
||||||
|
val prefInt = preferences.getInt(key, -1);
|
||||||
|
val migratedSet = if (prefInt != -1) {
|
||||||
|
mutableSetOf(prefInt.toString())
|
||||||
|
} else {
|
||||||
|
mutableSetOf<String>()
|
||||||
|
}
|
||||||
|
migratedSet
|
||||||
|
}
|
||||||
|
if (buttonCodes == null) buttonCodes = mutableSetOf<String>()
|
||||||
|
return buttonCodes.mapNotNull { it.toIntOrNull() }.toMutableSet()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to get the settings key for an gamepad button.
|
* Helper function to get the settings key for an gamepad button.
|
||||||
|
|
|
||||||
|
|
@ -811,7 +811,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||||
add(InputBindingSetting(button, Settings.triggerTitles[i]))
|
add(InputBindingSetting(button, Settings.triggerTitles[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
add(HeaderSetting(R.string.controller_hotkeys))
|
add(HeaderSetting(R.string.controller_hotkeys,R.string.controller_hotkeys_description))
|
||||||
Settings.hotKeys.forEachIndexed { i: Int, key: String ->
|
Settings.hotKeys.forEachIndexed { i: Int, key: String ->
|
||||||
val button = getInputObject(key)
|
val button = getInputObject(key)
|
||||||
add(InputBindingSetting(button, Settings.hotkeyTitles[i]))
|
add(InputBindingSetting(button, Settings.hotkeyTitles[i]))
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@
|
||||||
<string name="controller_circlepad">Circle Pad</string>
|
<string name="controller_circlepad">Circle Pad</string>
|
||||||
<string name="controller_c">C-Stick</string>
|
<string name="controller_c">C-Stick</string>
|
||||||
<string name="controller_hotkeys">Hotkeys</string>
|
<string name="controller_hotkeys">Hotkeys</string>
|
||||||
|
<string name="controller_hotkeys_description">If the "Hotkey Enable" key is mapped, that key must be pressed in addition to the mapped hotkey</string>
|
||||||
|
<string name="controller_hotkey_enable_button">Hotkey Enable</string>
|
||||||
<string name="controller_triggers">Triggers</string>
|
<string name="controller_triggers">Triggers</string>
|
||||||
<string name="controller_trigger">Trigger</string>
|
<string name="controller_trigger">Trigger</string>
|
||||||
<string name="controller_dpad">D-Pad</string>
|
<string name="controller_dpad">D-Pad</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue