From a799cb63f1b8d6de017ca5fb64cd80a489853330 Mon Sep 17 00:00:00 2001 From: lynxnb Date: Sat, 18 Mar 2023 00:26:32 +0100 Subject: [PATCH] Add separate L3 and R3 buttons to OSC As part of this commit, a `defaultEnabled` property was added to `OnScreenButton` to determine the default visibility of buttons. This is required because L3 and R3 should be hidden by default and only enabled by the user on demand. Additionally, the buttons' mask values were added to `ButtonId` members, as adding entries in the middle of the class conflicted with the `ordinal` enum property, making it unfit to use for our purposes. Finally, the `ControllerType` class was extended with an array of optional buttons. Optional buttons represent buttons that are allowed to be displayed on screen, but shouldn't be included in the controller mapping activity. --- .../java/emu/skyline/EmulationActivity.kt | 2 +- .../main/java/emu/skyline/input/Controller.kt | 53 ++++++++++++-- .../main/java/emu/skyline/input/GuestEvent.kt | 71 +++++++++---------- .../java/emu/skyline/input/InputHandler.kt | 4 +- .../skyline/input/onscreen/OnScreenButton.kt | 7 +- .../input/onscreen/OnScreenConfiguration.kt | 4 +- .../input/onscreen/OnScreenControllerView.kt | 2 +- .../input/onscreen/OnScreenItemDefinitions.kt | 21 ++++-- app/src/main/res/values/strings.xml | 2 + 9 files changed, 108 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index c2d3626e..768df54c 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -557,7 +557,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo return inputHandler.handleTouchEvent(view, event) } - private fun onButtonStateChanged(buttonId : ButtonId, state : ButtonState) = InputHandler.setButtonState(0, buttonId.value(), state.state) + private fun onButtonStateChanged(buttonId : ButtonId, state : ButtonState) = InputHandler.setButtonState(0, buttonId.value, state.state) private fun onStickStateChanged(stickId : StickId, position : PointF) { InputHandler.setAxisValue(0, stickId.xAxis.ordinal, (position.x * Short.MAX_VALUE).toInt()) diff --git a/app/src/main/java/emu/skyline/input/Controller.kt b/app/src/main/java/emu/skyline/input/Controller.kt index 544ccda1..630de870 100644 --- a/app/src/main/java/emu/skyline/input/Controller.kt +++ b/app/src/main/java/emu/skyline/input/Controller.kt @@ -13,13 +13,56 @@ import java.io.Serializable * * @param stringRes The string resource of the controller's name * @param firstController If the type only applies to the first controller + * @param optionalButtons Optional buttons that are allowed to be displayed on screen, but shouldn't be included in the controller mapping activity */ -enum class ControllerType(val stringRes : Int, val firstController : Boolean, val sticks : Array = arrayOf(), val buttons : Array = arrayOf(), val id : Int) { +enum class ControllerType(val stringRes : Int, val firstController : Boolean, val sticks : Array = arrayOf(), val buttons : Array = arrayOf(), val optionalButtons : Array = arrayOf(), val id : Int) { None(R.string.none, false, id = 0b0), - ProController(R.string.procon, false, arrayOf(StickId.Left, StickId.Right), arrayOf(ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, ButtonId.L, ButtonId.R, ButtonId.ZL, ButtonId.ZR, ButtonId.Plus, ButtonId.Minus), 0b1), - HandheldProController(R.string.handheld_procon, true, arrayOf(StickId.Left, StickId.Right), arrayOf(ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, ButtonId.L, ButtonId.R, ButtonId.ZL, ButtonId.ZR, ButtonId.Plus, ButtonId.Minus), 0b10), - JoyConLeft(R.string.ljoycon, false, arrayOf(StickId.Left), arrayOf(ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, ButtonId.L, ButtonId.ZL, ButtonId.Minus, ButtonId.LeftSL, ButtonId.LeftSR), 0b1000), - JoyConRight(R.string.rjoycon, false, arrayOf(StickId.Right), arrayOf(ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, ButtonId.R, ButtonId.ZR, ButtonId.Plus, ButtonId.RightSL, ButtonId.RightSR), 0b10000), + ProController( + R.string.procon, + false, + arrayOf(StickId.Left, StickId.Right), + arrayOf( + ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, + ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, + ButtonId.L, ButtonId.R, ButtonId.ZL, ButtonId.ZR, ButtonId.Plus, ButtonId.Minus + ), + arrayOf(ButtonId.L3, ButtonId.R3), + 0b1 + ), + HandheldProController( + R.string.handheld_procon, + true, + arrayOf(StickId.Left, StickId.Right), + arrayOf( + ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, + ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, + ButtonId.L, ButtonId.R, ButtonId.ZL, ButtonId.ZR, ButtonId.Plus, ButtonId.Minus + ), + arrayOf(ButtonId.L3, ButtonId.R3), + 0b10 + ), + JoyConLeft( + R.string.ljoycon, + false, + arrayOf(StickId.Left), + arrayOf( + ButtonId.DpadUp, ButtonId.DpadDown, ButtonId.DpadLeft, ButtonId.DpadRight, + ButtonId.L, ButtonId.ZL, ButtonId.Minus, ButtonId.LeftSL, ButtonId.LeftSR + ), + arrayOf(ButtonId.L3), + 0b1000 + ), + JoyConRight( + R.string.rjoycon, + false, + arrayOf(StickId.Right), + arrayOf( + ButtonId.A, ButtonId.B, ButtonId.X, ButtonId.Y, + ButtonId.R, ButtonId.ZR, ButtonId.Plus, ButtonId.RightSL, ButtonId.RightSR + ), + arrayOf(ButtonId.R3), + 0b10000 + ), } /** diff --git a/app/src/main/java/emu/skyline/input/GuestEvent.kt b/app/src/main/java/emu/skyline/input/GuestEvent.kt index f91e4c72..2f13557b 100644 --- a/app/src/main/java/emu/skyline/input/GuestEvent.kt +++ b/app/src/main/java/emu/skyline/input/GuestEvent.kt @@ -7,49 +7,44 @@ package emu.skyline.input import emu.skyline.R.string import java.io.Serializable -import java.util.* +import java.util.Objects import kotlin.math.abs /** * This enumerates all of the buttons that the emulator recognizes */ -enum class ButtonId(val short : String? = null, val long : Int? = null) { - A("A", string.a_button), - B("B", string.b_button), - X("X", string.x_button), - Y("Y", string.y_button), - LeftStick("L", string.left_stick), - RightStick("R", string.right_stick), - L("L", string.left_shoulder), - R("R", string.right_shoulder), - ZL("ZL", string.left_trigger), - ZR("ZR", string.right_trigger), - Plus("+", string.plus_button), - Minus("-", string.minus_button), - DpadLeft("◀︎", string.left), - DpadUp("▲︎", string.up), - DpadRight("▶︎", string.right), - DpadDown("▼︎", string.down), - LeftStickLeft, - LeftStickUp, - LeftStickRight, - LeftStickDown, - RightStickLeft, - RightStickUp, - RightStickRight, - RightStickDown, - LeftSL("SL", string.left_shoulder), - LeftSR("SR", string.right_shoulder), - RightSL("SL", string.left_shoulder), - RightSR("SR", string.right_shoulder), - Menu("⌂︎", string.emu_menu_button); - - /** - * This returns the value as setting the [ordinal]-th bit in a [Long] - */ - fun value() : Long { - return (1.toLong()) shl ordinal - } +enum class ButtonId(val value : Long, val short : String? = null, val long : Int? = null) { + A(1 shl 0, "A", string.a_button), + B(1 shl 1, "B", string.b_button), + X(1 shl 2, "X", string.x_button), + Y(1 shl 3, "Y", string.y_button), + LeftStick(1 shl 4, "L", string.left_stick), + RightStick(1 shl 5, "R", string.right_stick), + L3(1 shl 4, "L3", string.left_stick_button), + R3(1 shl 5, "R3", string.right_stick_button), + L(1 shl 6, "L", string.left_shoulder), + R(1 shl 7, "R", string.right_shoulder), + ZL(1 shl 8, "ZL", string.left_trigger), + ZR(1 shl 9, "ZR", string.right_trigger), + Plus(1 shl 10, "+", string.plus_button), + Minus(1 shl 11, "-", string.minus_button), + DpadLeft(1 shl 12, "◀︎", string.left), + DpadUp(1 shl 13, "▲︎", string.up), + DpadRight(1 shl 14, "▶︎", string.right), + DpadDown(1 shl 15, "▼︎", string.down), + LeftStickLeft(1 shl 16), + LeftStickUp(1 shl 17), + LeftStickRight(1 shl 18), + LeftStickDown(1 shl 19), + RightStickLeft(1 shl 20), + RightStickUp(1 shl 21), + RightStickRight(1 shl 22), + RightStickDown(1 shl 23), + LeftSL(1 shl 24, "SL", string.left_shoulder), + LeftSR(1 shl 25, "SR", string.right_shoulder), + RightSL(1 shl 26, "SL", string.left_shoulder), + RightSR(1 shl 27, "SR", string.right_shoulder), + Menu(1 shl 28, "⌂︎", string.emu_menu_button); } /** diff --git a/app/src/main/java/emu/skyline/input/InputHandler.kt b/app/src/main/java/emu/skyline/input/InputHandler.kt index d1512134..2955ca72 100644 --- a/app/src/main/java/emu/skyline/input/InputHandler.kt +++ b/app/src/main/java/emu/skyline/input/InputHandler.kt @@ -221,7 +221,7 @@ class InputHandler(private val inputManager : InputManager, private val emulatio return when (val guestEvent = inputManager.eventMap[KeyHostEvent(event.device.descriptor, event.keyCode)]) { is ButtonGuestEvent -> { if (guestEvent.button != ButtonId.Menu) - setButtonState(guestEvent.id, guestEvent.button.value(), action.state) + setButtonState(guestEvent.id, guestEvent.button.value, action.state) true } @@ -274,7 +274,7 @@ class InputHandler(private val inputManager : InputManager, private val emulatio is ButtonGuestEvent -> { val action = if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.state else ButtonState.Released.state if (guestEvent.button != ButtonId.Menu) - setButtonState(guestEvent.id, guestEvent.button.value(), action) + setButtonState(guestEvent.id, guestEvent.button.value, action) } is AxisGuestEvent -> { diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt index 060bbd04..0b8d14f9 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt @@ -27,7 +27,8 @@ abstract class OnScreenButton( private val defaultRelativeY : Float, private val defaultRelativeWidth : Float, private val defaultRelativeHeight : Float, - drawableId : Int + drawableId : Int, + private val defaultEnabled : Boolean ) { companion object { /** @@ -36,7 +37,7 @@ abstract class OnScreenButton( const val CONFIGURED_ASPECT_RATIO = 2074f / 874f } - val config = OnScreenConfiguration(onScreenControllerView.context, buttonId, defaultRelativeX, defaultRelativeY) + val config = OnScreenConfiguration(onScreenControllerView.context, buttonId, defaultRelativeX, defaultRelativeY, defaultEnabled) protected val drawable = ContextCompat.getDrawable(onScreenControllerView.context, drawableId)!! @@ -242,7 +243,7 @@ abstract class OnScreenButton( open fun resetConfig() { resetRelativeValues() - config.enabled = true + config.enabled = defaultEnabled config.scale = OnScreenConfiguration.DefaultScale } } diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt index 06824f36..96bdfd62 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt @@ -10,7 +10,7 @@ import emu.skyline.input.ButtonId import emu.skyline.utils.SwitchColors import emu.skyline.utils.sharedPreferences -class OnScreenConfiguration(private val context : Context, private val buttonId : ButtonId, defaultRelativeX : Float, defaultRelativeY : Float) { +class OnScreenConfiguration(private val context : Context, private val buttonId : ButtonId, defaultRelativeX : Float, defaultRelativeY : Float, defaultEnabled : Boolean) { companion object { const val DefaultAlpha = 130 const val DefaultGlobalScale = 1.15f @@ -19,7 +19,7 @@ class OnScreenConfiguration(private val context : Context, private val buttonId private inline fun config(default : T, prefix : String = "${buttonId.name}_") = sharedPreferences(context, default, prefix, "controller_config") - var enabled by config(true) + var enabled by config(defaultEnabled) var alpha by config(DefaultAlpha, "") var textColor by config(SwitchColors.BLACK.color) diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt index 6aefe384..52a9ba43 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt @@ -35,7 +35,7 @@ typealias OnStickStateChangedListener = (stickId : StickId, position : PointF) - class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = 0, defStyleRes : Int = 0) : View(context, attrs, defStyleAttr, defStyleRes) { companion object { private val controllerTypeMappings = mapOf(*ControllerType.values().map { - it to (setOf(*it.buttons) to setOf(*it.sticks)) + it to (setOf(*it.buttons) + setOf(*it.optionalButtons) to setOf(*it.sticks)) }.toTypedArray()) private const val SCALE_STEP = 0.05f diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt index 2e981db2..f9c188cf 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt @@ -25,7 +25,8 @@ open class CircularButton( defaultRelativeX : Float, defaultRelativeY : Float, defaultRelativeRadiusToX : Float, - drawableId : Int = R.drawable.ic_button + drawableId : Int = R.drawable.ic_button, + defaultEnabled : Boolean = true ) : OnScreenButton( onScreenControllerView, buttonId, @@ -33,7 +34,8 @@ open class CircularButton( defaultRelativeY, defaultRelativeRadiusToX * 2f, defaultRelativeRadiusToX * CONFIGURED_ASPECT_RATIO * 2f, - drawableId + drawableId, + defaultEnabled ) { val radius get() = itemWidth / 2f @@ -60,7 +62,7 @@ class JoystickButton( private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick) var recenterSticks = false - private lateinit var initialTapPosition : PointF + private var initialTapPosition = PointF() private var fingerDownTime = 0L private var fingerUpTime = 0L var shortDoubleTapped = false @@ -156,7 +158,8 @@ open class RectangularButton( defaultRelativeY : Float, defaultRelativeWidth : Float, defaultRelativeHeight : Float, - drawableId : Int = R.drawable.ic_rectangular_button + drawableId : Int = R.drawable.ic_rectangular_button, + defaultEnabled : Boolean = true ) : OnScreenButton( onScreenControllerView, buttonId, @@ -164,7 +167,8 @@ open class RectangularButton( defaultRelativeY, defaultRelativeWidth, defaultRelativeHeight, - drawableId + drawableId, + defaultEnabled ) { override fun isTouched(x : Float, y : Float) = currentBounds.contains(x.roundToInt(), y.roundToInt()) } @@ -209,13 +213,18 @@ class Controls(onScreenControllerView : OnScreenControllerView) { private val buttonZL = TriggerButton(onScreenControllerView, ZL, 0.1f, 0.1f, 0.09f, 0.1f) private val buttonZR = TriggerButton(onScreenControllerView, ZR, 0.9f, 0.1f, 0.09f, 0.1f) + private val buttonL3 = CircularButton(onScreenControllerView, L3, 0.35f, 0.87f, 0.025f, defaultEnabled = false) + private val buttonR3 = CircularButton(onScreenControllerView, R3, 0.65f, 0.87f, 0.025f, defaultEnabled = false) + private val circularButtonPairs = listOf(setOf(buttonA, buttonB, buttonX, buttonY), setOf(buttonDpadLeft, buttonDpadUp, buttonDpadRight, buttonDpadDown)) private val triggerButtonPairs = listOf(setOf(buttonL, buttonZL), setOf(buttonR, buttonZR)) + private val stickButtons = setOf(buttonL3, buttonR3) + val buttonPairs = circularButtonPairs + triggerButtonPairs - val circularButtons = circularButtonPairs.flatten() + listOf( + val circularButtons = circularButtonPairs.flatten() + stickButtons + listOf( CircularButton(onScreenControllerView, Plus, 0.57f, 0.75f, 0.025f), CircularButton(onScreenControllerView, Minus, 0.43f, 0.75f, 0.025f), CircularButton(onScreenControllerView, Menu, 0.5f, 0.75f, 0.025f) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7271b3fb..05426883 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -220,6 +220,8 @@ D-pad Left Stick Right Stick + Left Stick Button + Right Stick Button Face Buttons Shoulder & Trigger Buttons Shoulder Buttons on Joy-Con Rail