diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt deleted file mode 100644 index 295b28bf..00000000 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt +++ /dev/null @@ -1,41 +0,0 @@ -package info.cemu.cemu.inputoverlay - -import android.content.res.Resources -import android.graphics.Rect -import android.graphics.drawable.Drawable -import androidx.annotation.DrawableRes -import androidx.core.content.res.ResourcesCompat -import info.cemu.cemu.utils.getInvertedDrawable - -class InputDrawable( - resources: Resources, - @DrawableRes inputDrawableId: Int, -) { - private val iconInactive: Drawable = ResourcesCompat.getDrawable( - resources, - inputDrawableId, - null - )!! - private val iconActive: Drawable = iconInactive.getInvertedDrawable(resources) - private var _icon = iconInactive - val icon: Drawable - get() = _icon - - fun setActiveState(active: Boolean) { - _icon = if (active) iconActive else iconInactive - } - - fun setAlpha(alpha: Int) { - iconActive.alpha = alpha - iconInactive.alpha = alpha - } - - fun setBounds(bounds: Rect) { - iconActive.bounds = bounds - iconInactive.bounds = bounds - } - - fun setBounds(left: Int, top: Int, right: Int, bottom: Int) { - setBounds(Rect(left, top, right, bottom)) - } -} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt index f7a95cdc..88fbf181 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt @@ -13,8 +13,16 @@ import android.view.MotionEvent import android.view.SurfaceView import android.view.View import android.view.View.OnTouchListener -import androidx.annotation.DrawableRes import info.cemu.cemu.R +import info.cemu.cemu.inputoverlay.inputs.DPadInput +import info.cemu.cemu.inputoverlay.inputs.Input +import info.cemu.cemu.inputoverlay.inputs.Joystick +import info.cemu.cemu.inputoverlay.inputs.RectangleButton +import info.cemu.cemu.inputoverlay.inputs.RoundButton +import info.cemu.cemu.inputoverlay.inputs.innerdrawing.ButtonInnerDrawing +import info.cemu.cemu.inputoverlay.inputs.innerdrawing.HomeButtonInnerDrawing +import info.cemu.cemu.inputoverlay.inputs.innerdrawing.StickClickInnerDrawing +import info.cemu.cemu.inputoverlay.inputs.innerdrawing.TextButtonInnerDrawing import info.cemu.cemu.nativeinterface.NativeInput import info.cemu.cemu.nativeinterface.NativeInput.getControllerType import info.cemu.cemu.nativeinterface.NativeInput.isControllerDisabled @@ -35,8 +43,10 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : private var currentConfiguredInput: Input? = null private var nativeControllerType = -1 private var visible = false - var controllerIndex: Int = 0 - + private var controllerIndex: Int = 0 + private var onJoystickChange: (OverlayInput, Float, Float, Float, Float) -> Unit = + { _, _, _, _, _ -> } + private var overlyButtonToNativeButton: (OverlayInput) -> Int = { _ -> -1 } private var inputs: MutableList>? = null private val settingsProvider: InputOverlaySettingsManager private val vibrator: Vibrator? @@ -85,7 +95,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : return } for ((overlayInput, input) in inputs!!) { - input.reset() settingsProvider.saveRectangle(overlayInput, input.getBoundingRectangle()) } } @@ -188,13 +197,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } private fun onButtonStateChange(button: OverlayInput, state: Boolean) { - val nativeButtonId = when (nativeControllerType) { - NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> overlayButtonToVPADButton(button) - NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> overlayButtonToClassicButton(button) - NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> overlayButtonToProButton(button) - NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> overlayButtonToWiimoteButton(button) - else -> -1 - } + val nativeButtonId = overlyButtonToNativeButton(button) if (nativeButtonId == -1) { return } @@ -284,13 +287,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } private fun onJoystickStateChange(joystick: OverlayInput, x: Float, y: Float) { - val onJoystickChange = when (nativeControllerType) { - NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::onVPADJoystickStateChange - NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::onProJoystickStateChange - NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::onClassicJoystickStateChange - NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::onWiimoteJoystickStateChange - else -> return - } val (up, down) = if (y < 0) Pair(-y, 0f) else Pair(0f, y) val (left, right) = if (x < 0) Pair(-x, 0f) else Pair(0f, x) onJoystickChange(joystick, up, down, left, right) @@ -300,15 +296,13 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : return settingsProvider.getInputOverlayRectangle(input, width, height, pixelDensity) } - private fun MutableList>.addRoundButton( button: OverlayButton, - @DrawableRes buttonDrawable: Int, + innerDrawing: ButtonInnerDrawing, ) { add( button to RoundButton( - resources, - buttonDrawable, + innerDrawing, { onButtonStateChange(button, it) }, currentAlpha, getBoundingRectangleForInput(button), @@ -316,16 +310,14 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : ) } - private fun MutableList>.addJoystick( - joystick: OverlayJoystick, - @DrawableRes joystickInnerCircleDrawable: Int = R.drawable.stick_inner, - @DrawableRes joystickBackgroundDrawable: Int = R.drawable.stick_background, - ) { + private fun MutableList>.addRoundButton( + button: OverlayButton, + buttonText: String = button.name, + ) = addRoundButton(button, TextButtonInnerDrawing(buttonText)) + + private fun MutableList>.addJoystick(joystick: OverlayJoystick) { add( joystick to Joystick( - resources, - joystickBackgroundDrawable, - joystickInnerCircleDrawable, { x, y -> onJoystickStateChange(joystick, x, y) }, currentAlpha, getBoundingRectangleForInput(joystick) @@ -336,9 +328,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : private fun MutableList>.addDpad() { add( OverlayDpad.DPAD_UP to DPadInput( - resources, - R.drawable.dpad_background, - R.drawable.dpad_button, ::onButtonStateChange, currentAlpha, getBoundingRectangleForInput(OverlayDpad.DPAD_UP) @@ -348,12 +337,11 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : private fun MutableList>.addRectangleButton( button: OverlayButton, - @DrawableRes buttonDrawable: Int, + buttonText: String = button.name, ) { add( button to RectangleButton( - resources, - buttonDrawable, + TextButtonInnerDrawing(buttonText), { onButtonStateChange(button, it) }, currentAlpha, getBoundingRectangleForInput(button) @@ -372,85 +360,48 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } nativeControllerType = getControllerType(controllerIndex) + overlyButtonToNativeButton = when (nativeControllerType) { + NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::overlayButtonToVPADButton + NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::overlayButtonToClassicButton + NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::overlayButtonToProButton + NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::overlayButtonToWiimoteButton + else -> { _ -> -1 } + } + onJoystickChange = when (nativeControllerType) { + NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::onVPADJoystickStateChange + NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::onProJoystickStateChange + NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> ::onClassicJoystickStateChange + NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::onWiimoteJoystickStateChange + else -> { _, _, _, _, _ -> } + } inputs = mutableListOf>().apply { - addRoundButton( - OverlayButton.MINUS, - R.drawable.button_minus, - ) - addRoundButton( - OverlayButton.PLUS, - R.drawable.button_plus, - ) + addRoundButton(OverlayButton.MINUS, "-") + addRoundButton(OverlayButton.PLUS, "+") addDpad() - addRoundButton( - OverlayButton.A, - R.drawable.button_a, - ) - addRoundButton( - OverlayButton.B, - R.drawable.button_b, - ) + addRoundButton(OverlayButton.A) + addRoundButton(OverlayButton.B) addJoystick(OverlayJoystick.RIGHT) if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) { - addRoundButton( - OverlayButton.X, - R.drawable.button_x, - ) - addRoundButton( - OverlayButton.Y, - R.drawable.button_y, - ) - addRectangleButton( - OverlayButton.ZL, - R.drawable.button_zl, - ) - addRectangleButton( - OverlayButton.ZR, - R.drawable.button_zr, - ) - addRectangleButton( - OverlayButton.L, - R.drawable.button_l, - ) - addRectangleButton( - OverlayButton.R, - R.drawable.button_r, - ) + addRoundButton(OverlayButton.X) + addRoundButton(OverlayButton.Y) + addRectangleButton(OverlayButton.ZL) + addRectangleButton(OverlayButton.ZR) + addRectangleButton(OverlayButton.L) + addRectangleButton(OverlayButton.R) addJoystick(OverlayJoystick.LEFT) } if (nativeControllerType == NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) { - addRoundButton( - OverlayButton.ONE, - R.drawable.button_one, - ) - addRoundButton( - OverlayButton.TWO, - R.drawable.button_two, - ) - addRoundButton( - OverlayButton.C, - R.drawable.button_c, - ) - addRectangleButton( - OverlayButton.Z, - R.drawable.button_z, - ) - addRoundButton( - OverlayButton.HOME, - R.drawable.button_home, - ) + addRoundButton(OverlayButton.ONE, "1") + addRoundButton(OverlayButton.TWO, "2") + addRoundButton(OverlayButton.C) + addRectangleButton(OverlayButton.Z) + addRoundButton(OverlayButton.HOME, HomeButtonInnerDrawing()) } if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC && nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE ) { - addRoundButton( - OverlayButton.L_STICK_CLICK, - R.drawable.button_stick, - ) - addRoundButton( - OverlayButton.R_STICK_CLICK, - R.drawable.button_stick, - ) + addRoundButton(OverlayButton.L_STICK_CLICK, StickClickInnerDrawing()) + addRoundButton(OverlayButton.R_STICK_CLICK, StickClickInnerDrawing()) } } } @@ -472,90 +423,88 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : private fun onEditPosition(event: MotionEvent): Boolean { - when (event.actionMasked) { - MotionEvent.ACTION_DOWN -> { - val x = event.x.toInt() - val y = event.y.toInt() - for ((_, input) in inputs!!) { - if (input.isInside(x, y)) { - currentConfiguredInput = input - input.enableDrawingBoundingRect( - resources.getColor( - R.color.purple, - context.theme - ) - ) - return true - } - } + val configuredInput = currentConfiguredInput + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + if (configuredInput != null) { + return false } - - MotionEvent.ACTION_UP -> { - if (currentConfiguredInput != null) { - currentConfiguredInput!!.disableDrawingBoundingRect() - currentConfiguredInput = null - return true - } - } - - MotionEvent.ACTION_MOVE -> { - if (currentConfiguredInput != null) { - val x = event.x.toInt() - val y = event.y.toInt() - currentConfiguredInput!!.moveInput(x, y, width, height) + val x = event.x + val y = event.y + for ((_, input) in inputs!!) { + if (input.isInside(x, y)) { + currentConfiguredInput = input + input.enableDrawingBoundingRect( + resources.getColor(R.color.purple, context.theme) + ) return true } } } + + if (configuredInput == null) { + return false + } + + if (event.actionMasked == MotionEvent.ACTION_UP) { + configuredInput.disableDrawingBoundingRect() + currentConfiguredInput = null + return true + } + + if (event.actionMasked == MotionEvent.ACTION_MOVE) { + val x = event.x.toInt() + val y = event.y.toInt() + configuredInput.moveInput(x, y, width, height) + return true + } + return false } private fun onEditSize(event: MotionEvent): Boolean { - when (event.actionMasked) { - MotionEvent.ACTION_DOWN -> { - val x = event.x.toInt() - val y = event.y.toInt() - for ((_, input) in inputs!!) { - if (input.isInside(x, y)) { - currentConfiguredInput = input - input.enableDrawingBoundingRect( - resources.getColor( - R.color.red, - context.theme - ) - ) - return true - } - } - } + val configuredInput = currentConfiguredInput - MotionEvent.ACTION_UP -> { - if (currentConfiguredInput != null) { - currentConfiguredInput!!.disableDrawingBoundingRect() - currentConfiguredInput = null + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + + val x = event.x + val y = event.y + for ((_, input) in inputs!!) { + if (input.isInside(x, y)) { + currentConfiguredInput = input + input.enableDrawingBoundingRect( + resources.getColor(R.color.red, context.theme) + ) return true } } + } - MotionEvent.ACTION_MOVE -> { - if (currentConfiguredInput != null) { - val histSize = event.historySize - if (event.historySize >= 2) { - val x1 = event.getHistoricalX(0) - val y1 = event.getHistoricalY(0) - val x2 = event.getHistoricalX(histSize - 1) - val y2 = event.getHistoricalY(histSize - 1) - currentConfiguredInput!!.resize( - (x2 - x1).toInt(), - (y2 - y1).toInt(), - width, - height, - inputsMinWidthHeight - ) - } - return true - } + if (configuredInput == null) { + return false + } + + if (event.actionMasked == MotionEvent.ACTION_UP) { + configuredInput.disableDrawingBoundingRect() + currentConfiguredInput = null + return true + } + + if (event.actionMasked == MotionEvent.ACTION_MOVE) { + val histSize = event.historySize + if (event.historySize >= 2) { + val x1 = event.getHistoricalX(0) + val y1 = event.getHistoricalY(0) + val x2 = event.getHistoricalX(histSize - 1) + val y2 = event.getHistoricalY(histSize - 1) + configuredInput.resize( + (x2 - x1).toInt(), + (y2 - y1).toInt(), + width, + height, + inputsMinWidthHeight + ) } + return true } return false } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt deleted file mode 100644 index 8d673943..00000000 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt +++ /dev/null @@ -1,144 +0,0 @@ -package info.cemu.cemu.inputoverlay - -import android.content.res.Resources -import android.graphics.Canvas -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.view.MotionEvent -import androidx.annotation.DrawableRes -import androidx.core.content.res.ResourcesCompat -import kotlin.math.min -import kotlin.math.sqrt - -class Joystick( - resources: Resources, - @DrawableRes joystickBackgroundId: Int, - @DrawableRes innerStickId: Int, - private val onStickStateChange: (x: Float, y: Float) -> Unit, - private val alpha: Int, - boundingRectangle: Rect, -) : Input(boundingRectangle) { - private val joystickBackground: Drawable = - ResourcesCompat.getDrawable(resources, joystickBackgroundId, null)!! - private val joystickDrawable = InputDrawable(resources, innerStickId) - private var currentPointerId = -1 - private var centerX = 0 - private var centerY = 0 - private var originalCenterX = 0 - private var originalCenterY = 0 - private var radius = 0 - private var innerRadius = 0 - - init { - configure() - } - - private fun updateState(pressed: Boolean, x: Float, y: Float) { - joystickDrawable.setActiveState(pressed) - onStickStateChange(x, y) - val newCentreX = (centerX + radius * x).toInt() - val newCentreY = (centerY + radius * y).toInt() - joystickDrawable.setBounds( - newCentreX - innerRadius, - newCentreY - innerRadius, - newCentreX + innerRadius, - newCentreY + innerRadius - ) - } - - override fun configure() { - centerX = rect.centerX() - originalCenterX = centerX - centerY = rect.centerY() - originalCenterY = centerY - radius = min(rect.width(), rect.height()) / 2 - innerRadius = (radius * 0.65f).toInt() - joystickBackground.setBounds( - centerX - radius, - centerY - radius, - centerX + radius, - centerY + radius - ) - joystickBackground.alpha = alpha - joystickDrawable.setAlpha(alpha) - joystickDrawable.setBounds( - centerX - innerRadius, - centerY - innerRadius, - centerX + innerRadius, - centerY + innerRadius - ) - } - - override fun onTouch(event: MotionEvent): Boolean { - when (event.actionMasked) { - MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { - val pointerIndex = event.actionIndex - val x = event.getX(pointerIndex).toInt() - val y = event.getY(pointerIndex).toInt() - if (isInside(x, y)) { - centerX = x - centerY = y - joystickBackground.setBounds( - centerX - radius, - centerY - radius, - centerX + radius, - centerY + radius - ) - currentPointerId = event.getPointerId(pointerIndex) - updateState(true, 0.0f, 0.0f) - return true - } - } - - MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> { - if (currentPointerId == event.getPointerId(event.actionIndex)) { - centerX = originalCenterX - centerY = originalCenterY - joystickBackground.setBounds( - centerX - radius, - centerY - radius, - centerX + radius, - centerY + radius - ) - currentPointerId = -1 - updateState(false, 0.0f, 0.0f) - return true - } - } - - MotionEvent.ACTION_MOVE -> { - if (currentPointerId == -1) { - return false - } - for (i in 0 until event.pointerCount) { - if (currentPointerId != event.getPointerId(i)) { - continue - } - var x: Float = (event.getX(i) - centerX) / radius - var y: Float = (event.getY(i) - centerY) / radius - val norm = sqrt(x * x + y * y) - if (norm > 1.0f) { - x /= norm - y /= norm - } - updateState(true, x, y) - return true - } - } - } - return false - } - - override fun resetInput() { - updateState(false, 0f, 0f) - } - - override fun drawInput(canvas: Canvas) { - joystickBackground.draw(canvas) - joystickDrawable.icon.draw(canvas) - } - - override fun isInside(x: Int, y: Int): Boolean { - return ((x - originalCenterX) * (x - originalCenterX) + (y - originalCenterY) * (y - originalCenterY)) <= radius * radius - } -} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt deleted file mode 100644 index 39de3c3b..00000000 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt +++ /dev/null @@ -1,35 +0,0 @@ -package info.cemu.cemu.inputoverlay - -import android.content.res.Resources -import android.graphics.Rect -import androidx.annotation.DrawableRes - -class RectangleButton( - resources: Resources, - @DrawableRes buttonId: Int, - onButtonStateChange: (state: Boolean) -> Unit, - private val alpha: Int, - rect: Rect, -) : Button(resources, buttonId, onButtonStateChange, rect) { - var left: Int = 0 - var top: Int = 0 - var right: Int = 0 - var bottom: Int = 0 - - init { - configure() - } - - override fun configure() { - this.left = rect.left - this.top = rect.top - this.right = rect.right - this.bottom = rect.bottom - inputDrawable.setAlpha(alpha) - inputDrawable.setBounds(left, top, right, bottom) - } - - override fun isInside(x: Int, y: Int): Boolean { - return x in left.. Unit, - private val alpha: Int, - rect: Rect, -) : Button(resources, buttonId, onButtonStateChange, rect) { - private var centreX: Int = 0 - private var centreY: Int = 0 - private var radius2: Int = 0 - - init { - configure() - } - - override fun configure() { - centreX = rect.centerX() - centreY = rect.centerY() - val radius = min(rect.width(), rect.height()) / 2 - inputDrawable.setBounds( - centreX - radius, - centreY - radius, - centreX + radius, - centreY + radius - ) - inputDrawable.setAlpha(alpha) - radius2 = radius * radius - } - - override fun isInside(x: Int, y: Int): Boolean { - return ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) <= radius2 - } -} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Button.kt similarity index 68% rename from src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt rename to src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Button.kt index 578d6bb4..5e166fbb 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Button.kt @@ -1,29 +1,21 @@ -package info.cemu.cemu.inputoverlay +package info.cemu.cemu.inputoverlay.inputs -import android.content.res.Resources -import android.graphics.Canvas import android.graphics.Rect import android.view.MotionEvent -import androidx.annotation.DrawableRes abstract class Button( - resources: Resources, - @DrawableRes buttonId: Int, private val onButtonStateChange: (state: Boolean) -> Unit, boundingRect: Rect, ) : Input(boundingRect) { - - - protected val inputDrawable = InputDrawable(resources, buttonId) - + protected var state = false private var currentPointerId: Int = -1 override fun onTouch(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { val pointerIndex = event.actionIndex - val x = event.getX(pointerIndex).toInt() - val y = event.getY(pointerIndex).toInt() + val x = event.getX(pointerIndex) + val y = event.getY(pointerIndex) if (isInside(x, y)) { currentPointerId = event.getPointerId(pointerIndex) updateState(true) @@ -42,16 +34,12 @@ abstract class Button( return false } - override fun drawInput(canvas: Canvas) { - inputDrawable.icon.draw(canvas) - } - override fun resetInput() { updateState(false) } private fun updateState(state: Boolean) { - inputDrawable.setActiveState(state) + this.state = state onButtonStateChange(state) } } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/DPadInput.kt similarity index 58% rename from src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt rename to src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/DPadInput.kt index 0703ffe2..28d28254 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/DPadInput.kt @@ -1,74 +1,72 @@ -package info.cemu.cemu.inputoverlay +package info.cemu.cemu.inputoverlay.inputs -import android.content.res.Resources import android.graphics.Canvas +import android.graphics.Color import android.graphics.Rect +import android.graphics.RectF import android.view.MotionEvent -import androidx.annotation.DrawableRes -import androidx.core.content.res.ResourcesCompat +import info.cemu.cemu.inputoverlay.OverlayDpad +import info.cemu.cemu.utils.fillCircleWithStroke +import info.cemu.cemu.utils.fillRoundRectangleWithStroke import kotlin.math.atan2 import kotlin.math.min class DPadInput( - resources: Resources, - @DrawableRes backgroundId: Int, - @DrawableRes buttonId: Int, private val onButtonStateChange: (button: OverlayDpad, state: Boolean) -> Unit, private val alpha: Int, rect: Rect, ) : Input(rect) { - private val dpadUpDrawable = InputDrawable(resources, buttonId) - private val dpadDownDrawable = InputDrawable(resources, buttonId) - private val dpadLeftDrawable = InputDrawable(resources, buttonId) - private val dpadRightDrawable = InputDrawable(resources, buttonId) - private val background = ResourcesCompat.getDrawable(resources, backgroundId, null)!! + private var backgroundFillColor: Int = 0 + private var backgroundStrokeColor: Int = 0 + + private val dpadUpRect = RectF() + private val dpadDownRect = RectF() + private val dpadLeftRect = RectF() + private val dpadRightRect = RectF() private var dpadState: Int = NONE - private var centerX: Int = 0 - private var centerY: Int = 0 - private var radius2: Int = 0 + private var centerX: Float = 0f + private var centerY: Float = 0f + private var radius2: Float = 0f + private var radius: Float = 0f private var currentPointerId: Int = -1 override fun configure() { - this.centerX = rect.centerX() - this.centerY = rect.centerY() - val radius = min(rect.width(), rect.height()) / 2 - radius2 = radius * radius - background.alpha = alpha - background.setBounds( - centerX - radius, - centerY - radius, - centerX + radius, - centerY + radius - ) + backgroundFillColor = Color.argb(alpha, 128, 128, 128) + backgroundStrokeColor = Color.argb(alpha, 200, 200, 200) + configureColors(alpha) + centerX = rect.exactCenterX() + centerY = rect.exactCenterY() + radius = min(rect.width(), rect.height()) * 0.5f + radius2 = radius * radius val buttonSize = radius / 2 - val configureButton = { circleXPos: Int, circleYPos: Int, inputDrawable: InputDrawable -> - inputDrawable.setAlpha(alpha) - val left = circleXPos - buttonSize / 2 - val top = circleYPos - buttonSize / 2 - inputDrawable.setBounds(left, top, left + buttonSize, top + buttonSize) + val configureButtonRect = { circleXPos: Float, circleYPos: Float, rect: RectF -> + val left = circleXPos - buttonSize * 0.5f + val top = circleYPos - buttonSize * 0.5f + rect.set(left, top, left + buttonSize, top + buttonSize) } - configureButton( + val buttonCenterXYTranslate = 2f * radius / 3f + configureButtonRect( centerX, - centerY - 2 * radius / 3, - dpadUpDrawable + centerY - buttonCenterXYTranslate, + dpadUpRect ) - configureButton( + configureButtonRect( centerX, - centerY + 2 * radius / 3, - dpadDownDrawable + centerY + buttonCenterXYTranslate, + dpadDownRect ) - configureButton( - centerX - 2 * radius / 3, + configureButtonRect( + centerX - buttonCenterXYTranslate, centerY, - dpadLeftDrawable + dpadLeftRect ) - configureButton( - centerX + 2 * radius / 3, + configureButtonRect( + centerX + buttonCenterXYTranslate, centerY, - dpadRightDrawable + dpadRightRect ) } @@ -87,23 +85,19 @@ class DPadInput( if (dpadState == NONE) return if ((dpadState and UP) != 0) { onButtonStateChange(OverlayDpad.DPAD_UP, pressed) - dpadUpDrawable.setActiveState(pressed) } if ((dpadState and DOWN) != 0) { onButtonStateChange(OverlayDpad.DPAD_DOWN, pressed) - dpadDownDrawable.setActiveState(pressed) } if ((dpadState and LEFT) != 0) { onButtonStateChange(OverlayDpad.DPAD_LEFT, pressed) - dpadLeftDrawable.setActiveState(pressed) } if ((dpadState and RIGHT) != 0) { onButtonStateChange(OverlayDpad.DPAD_RIGHT, pressed) - dpadRightDrawable.setActiveState(pressed) } } - private fun getStateByPosition(x: Int, y: Int): Int { + private fun getStateByPosition(x: Float, y: Float): Int { val norm2 = (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) if (norm2 <= radius2 * 0.1f) { return NONE @@ -128,8 +122,8 @@ class DPadInput( return false } val pointerIndex = event.actionIndex - val x = event.getX(pointerIndex).toInt() - val y = event.getY(pointerIndex).toInt() + val x = event.getX(pointerIndex) + val y = event.getY(pointerIndex) val pointerId = event.getPointerId(pointerIndex) if (isInside(x, y)) { currentPointerId = pointerId @@ -155,8 +149,8 @@ class DPadInput( if (currentPointerId != event.getPointerId(i)) { continue } - val x = event.getX(i).toInt() - val y = event.getY(i).toInt() + val x = event.getX(i) + val y = event.getY(i) updateState(getStateByPosition(x, y)) return true } @@ -169,19 +163,40 @@ class DPadInput( updateState(NONE) } - override fun isInside(x: Int, y: Int): Boolean { + override fun isInside(x: Float, y: Float): Boolean { return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius2 } override fun drawInput(canvas: Canvas) { - background.draw(canvas) - dpadUpDrawable.icon.draw(canvas) - dpadDownDrawable.icon.draw(canvas) - dpadLeftDrawable.icon.draw(canvas) - dpadRightDrawable.icon.draw(canvas) + canvas.fillCircleWithStroke( + centerX, + centerY, + radius, + paint, + backgroundFillColor, + backgroundStrokeColor + ) + drawButton(canvas, dpadUpRect, dpadState and UP) + drawButton(canvas, dpadDownRect, dpadState and DOWN) + drawButton(canvas, dpadLeftRect, dpadState and LEFT) + drawButton(canvas, dpadRightRect, dpadState and RIGHT) + } + + private fun drawButton(canvas: Canvas, rect: RectF, state: Int) { + val fillColor: Int + val strokeColor: Int + if (state != 0) { + fillColor = activeFillColor + strokeColor = activeStrokeColor + } else { + fillColor = inactiveFillColor + strokeColor = inactiveStrokeColor + } + canvas.fillRoundRectangleWithStroke(rect, BUTTON_RADIUS, paint, fillColor, strokeColor) } companion object { + private const val BUTTON_RADIUS = 5f private const val NONE = 0 private const val UP = 1 shl 0 private const val DOWN = 1 shl 1 diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Input.kt similarity index 77% rename from src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt rename to src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Input.kt index 125b732a..95d2c835 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Input.kt @@ -1,6 +1,7 @@ -package info.cemu.cemu.inputoverlay +package info.cemu.cemu.inputoverlay.inputs import android.graphics.Canvas +import android.graphics.Color import android.graphics.Paint import android.graphics.Rect import android.view.MotionEvent @@ -13,6 +14,13 @@ abstract class Input protected constructor( ) { private var drawBoundingRectangle = false private val boundingRectanglePaint = Paint() + protected var activeFillColor = 0 + protected var activeStrokeColor = 0 + protected var inactiveFillColor = 0 + protected var inactiveStrokeColor = 0 + protected val paint = Paint().apply { + strokeWidth = 3f + } fun getBoundingRectangle() = rect @@ -65,6 +73,13 @@ abstract class Input protected constructor( protected abstract fun configure() + protected fun configureColors(alpha: Int) { + activeFillColor = Color.argb(alpha, 255, 255, 255) + activeStrokeColor = Color.argb(alpha, 0, 0, 0) + inactiveFillColor = activeStrokeColor + inactiveStrokeColor = activeFillColor + } + protected abstract fun drawInput(canvas: Canvas) fun draw(canvas: Canvas) { @@ -83,5 +98,5 @@ abstract class Input protected constructor( drawBoundingRectangle = false } - abstract fun isInside(x: Int, y: Int): Boolean + abstract fun isInside(x: Float, y: Float): Boolean } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Joystick.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Joystick.kt new file mode 100644 index 00000000..fde7b512 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/Joystick.kt @@ -0,0 +1,134 @@ +package info.cemu.cemu.inputoverlay.inputs + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.view.MotionEvent +import info.cemu.cemu.utils.fillCircleWithStroke +import kotlin.math.min +import kotlin.math.sqrt + +class Joystick( + private val onStickStateChange: (x: Float, y: Float) -> Unit, + private val alpha: Int, + boundingRectangle: Rect, +) : Input(boundingRectangle) { + private var backgroundFillColor: Int = 0 + private var currentPointerId = -1 + private var pressed = false + private var centerX: Float = 0f + private var centerY: Float = 0f + private var originalCenterX: Float = 0f + private var originalCenterY: Float = 0f + private var radius: Float = 0f + private var innerRadius: Float = 0f + private var innerCenterX: Float = 0f + private var innerCenterY: Float = 0f + + init { + configure() + } + + private fun updateState(pressed: Boolean, x: Float, y: Float) { + this.pressed = pressed + onStickStateChange(x, y) + innerCenterX = centerX + radius * x + innerCenterY = centerY + radius * y + } + + override fun configure() { + backgroundFillColor = Color.argb(alpha, 128, 128, 128) + configureColors(alpha) + + centerX = rect.exactCenterX() + originalCenterX = centerX + innerCenterX = centerX + centerY = rect.exactCenterY() + originalCenterY = centerY + innerCenterY = centerY + radius = min(rect.width(), rect.height()) * 0.5f + innerRadius = radius * 0.65f + } + + override fun onTouch(event: MotionEvent): Boolean { + when (event.actionMasked) { + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { + val pointerIndex = event.actionIndex + val x = event.getX(pointerIndex) + val y = event.getY(pointerIndex) + if (isInside(x, y)) { + centerX = x + centerY = y + currentPointerId = event.getPointerId(pointerIndex) + updateState(true, 0.0f, 0.0f) + return true + } + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> { + if (currentPointerId == event.getPointerId(event.actionIndex)) { + centerX = originalCenterX + centerY = originalCenterY + currentPointerId = -1 + updateState(false, 0.0f, 0.0f) + return true + } + } + + MotionEvent.ACTION_MOVE -> { + if (currentPointerId == -1) { + return false + } + for (i in 0 until event.pointerCount) { + if (currentPointerId != event.getPointerId(i)) { + continue + } + var x: Float = (event.getX(i) - centerX) / radius + var y: Float = (event.getY(i) - centerY) / radius + val norm = sqrt(x * x + y * y) + if (norm > 1.0f) { + x /= norm + y /= norm + } + updateState(true, x, y) + return true + } + } + } + return false + } + + override fun resetInput() { + updateState(false, 0f, 0f) + } + + override fun drawInput(canvas: Canvas) { + paint.color = backgroundFillColor + paint.style = Paint.Style.FILL + canvas.drawCircle(centerX, centerY, radius, paint) + + val fillColor: Int + val strokeColor: Int + if (pressed) { + fillColor = activeFillColor + strokeColor = activeStrokeColor + } else { + fillColor = inactiveFillColor + strokeColor = inactiveStrokeColor + } + + canvas.fillCircleWithStroke( + innerCenterX, + innerCenterY, + innerRadius, + paint, + fillColor, + strokeColor + ) + } + + override fun isInside(x: Float, y: Float): Boolean { + return (x - originalCenterX) * (x - originalCenterX) + (y - originalCenterY) * (y - originalCenterY) <= radius * radius + } +} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/RectangleButton.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/RectangleButton.kt new file mode 100644 index 00000000..75c7e0c5 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/RectangleButton.kt @@ -0,0 +1,62 @@ +package info.cemu.cemu.inputoverlay.inputs + +import android.graphics.Canvas +import android.graphics.Rect +import info.cemu.cemu.inputoverlay.inputs.innerdrawing.ButtonInnerDrawing +import info.cemu.cemu.utils.fillRoundRectangleWithStroke + +class RectangleButton( + private val innerDrawing: ButtonInnerDrawing, + onButtonStateChange: (state: Boolean) -> Unit, + private val alpha: Int, + rect: Rect, +) : Button(onButtonStateChange, rect) { + var left: Float = 0f + var top: Float = 0f + var right: Float = 0f + var bottom: Float = 0f + + init { + configure() + } + + override fun configure() { + this.left = rect.left.toFloat() + this.top = rect.top.toFloat() + this.right = rect.right.toFloat() + this.bottom = rect.bottom.toFloat() + configureColors(alpha) + innerDrawing.configure(rect, alpha) + } + + override fun drawInput(canvas: Canvas) { + val fillColor: Int + val strokeColor: Int + if (state) { + fillColor = activeFillColor + strokeColor = activeStrokeColor + } else { + fillColor = inactiveFillColor + strokeColor = inactiveStrokeColor + } + canvas.fillRoundRectangleWithStroke( + left, + top, + right, + bottom, + RECTANGLE_RADIUS, + paint, + fillColor, + strokeColor + ) + innerDrawing.draw(canvas, state) + } + + override fun isInside(x: Float, y: Float): Boolean { + return x in left.. Unit, + private val alpha: Int, + rect: Rect, +) : Button(onButtonStateChange, rect) { + private var centerX: Float = 0f + private var centerY: Float = 0f + private var radius2: Float = 0f + private var radius: Float = 0f + + init { + configure() + } + + override fun configure() { + centerX = rect.centerX().toFloat() + centerY = rect.centerY().toFloat() + radius = min(rect.width().toFloat(), rect.height().toFloat()) / 2f + radius2 = radius * radius + innerDrawing.configure(rect, alpha) + configureColors(alpha) + } + + override fun drawInput(canvas: Canvas) { + val fillColor: Int + val strokeColor: Int + if (state) { + fillColor = activeFillColor + strokeColor = activeStrokeColor + } else { + fillColor = inactiveFillColor + strokeColor = inactiveStrokeColor + } + canvas.fillCircleWithStroke(centerX, centerY, radius, paint, fillColor, strokeColor) + innerDrawing.draw(canvas, state) + } + + override fun isInside(x: Float, y: Float): Boolean { + return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius2 + } +} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/ButtonInnerDrawing.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/ButtonInnerDrawing.kt new file mode 100644 index 00000000..815d3d32 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/ButtonInnerDrawing.kt @@ -0,0 +1,9 @@ +package info.cemu.cemu.inputoverlay.inputs.innerdrawing + +import android.graphics.Canvas +import android.graphics.Rect + +interface ButtonInnerDrawing { + fun draw(canvas: Canvas, state: Boolean) + fun configure(boundingRect: Rect, alpha: Int) +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/HomeButtonInnerDrawing.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/HomeButtonInnerDrawing.kt new file mode 100644 index 00000000..d7b63e98 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/inputs/innerdrawing/HomeButtonInnerDrawing.kt @@ -0,0 +1,55 @@ +package info.cemu.cemu.inputoverlay.inputs.innerdrawing + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Matrix +import android.graphics.Paint +import android.graphics.Path +import android.graphics.Rect +import kotlin.math.min + +class HomeButtonInnerDrawing : ButtonInnerDrawing { + private var activeFillColor = 0 + private var inactiveFillColor = 0 + private val paint = Paint() + private var path = Path() + override fun draw(canvas: Canvas, state: Boolean) { + paint.color = if (state) activeFillColor else inactiveFillColor + canvas.drawPath(path, paint) + } + + override fun configure(boundingRect: Rect, alpha: Int) { + activeFillColor = Color.argb(alpha, 0, 0, 0) + inactiveFillColor = Color.argb(alpha, 255, 255, 255) + + path = Path() + val firstPoint = IconPathPoints[0] + path.moveTo(firstPoint.first, firstPoint.second) + for (i in 1.. - - - diff --git a/src/android/app/src/main/res/drawable/button_b.xml b/src/android/app/src/main/res/drawable/button_b.xml deleted file mode 100644 index 26ccd8fa..00000000 --- a/src/android/app/src/main/res/drawable/button_b.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_c.xml b/src/android/app/src/main/res/drawable/button_c.xml deleted file mode 100644 index c83f0bee..00000000 --- a/src/android/app/src/main/res/drawable/button_c.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_home.xml b/src/android/app/src/main/res/drawable/button_home.xml deleted file mode 100644 index daa3a938..00000000 --- a/src/android/app/src/main/res/drawable/button_home.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_l.xml b/src/android/app/src/main/res/drawable/button_l.xml deleted file mode 100644 index afc0ac7c..00000000 --- a/src/android/app/src/main/res/drawable/button_l.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_minus.xml b/src/android/app/src/main/res/drawable/button_minus.xml deleted file mode 100644 index b3f2a8cd..00000000 --- a/src/android/app/src/main/res/drawable/button_minus.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_one.xml b/src/android/app/src/main/res/drawable/button_one.xml deleted file mode 100644 index 80770e97..00000000 --- a/src/android/app/src/main/res/drawable/button_one.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_plus.xml b/src/android/app/src/main/res/drawable/button_plus.xml deleted file mode 100644 index d6c7bdb1..00000000 --- a/src/android/app/src/main/res/drawable/button_plus.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_r.xml b/src/android/app/src/main/res/drawable/button_r.xml deleted file mode 100644 index 7da63d16..00000000 --- a/src/android/app/src/main/res/drawable/button_r.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_stick.xml b/src/android/app/src/main/res/drawable/button_stick.xml deleted file mode 100644 index cc266942..00000000 --- a/src/android/app/src/main/res/drawable/button_stick.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/src/android/app/src/main/res/drawable/button_two.xml b/src/android/app/src/main/res/drawable/button_two.xml deleted file mode 100644 index e60d43a9..00000000 --- a/src/android/app/src/main/res/drawable/button_two.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_x.xml b/src/android/app/src/main/res/drawable/button_x.xml deleted file mode 100644 index 1d53b1b7..00000000 --- a/src/android/app/src/main/res/drawable/button_x.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_y.xml b/src/android/app/src/main/res/drawable/button_y.xml deleted file mode 100644 index d945b9a2..00000000 --- a/src/android/app/src/main/res/drawable/button_y.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_z.xml b/src/android/app/src/main/res/drawable/button_z.xml deleted file mode 100644 index 8367da11..00000000 --- a/src/android/app/src/main/res/drawable/button_z.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_zl.xml b/src/android/app/src/main/res/drawable/button_zl.xml deleted file mode 100644 index a458611f..00000000 --- a/src/android/app/src/main/res/drawable/button_zl.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/button_zr.xml b/src/android/app/src/main/res/drawable/button_zr.xml deleted file mode 100644 index abd70e13..00000000 --- a/src/android/app/src/main/res/drawable/button_zr.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/dpad_background.xml b/src/android/app/src/main/res/drawable/dpad_background.xml deleted file mode 100644 index 58b24979..00000000 --- a/src/android/app/src/main/res/drawable/dpad_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/src/android/app/src/main/res/drawable/dpad_button.xml b/src/android/app/src/main/res/drawable/dpad_button.xml deleted file mode 100644 index de03cc37..00000000 --- a/src/android/app/src/main/res/drawable/dpad_button.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/src/android/app/src/main/res/drawable/stick_background.xml b/src/android/app/src/main/res/drawable/stick_background.xml deleted file mode 100644 index e9729831..00000000 --- a/src/android/app/src/main/res/drawable/stick_background.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/android/app/src/main/res/drawable/stick_inner.xml b/src/android/app/src/main/res/drawable/stick_inner.xml deleted file mode 100644 index a6a1d831..00000000 --- a/src/android/app/src/main/res/drawable/stick_inner.xml +++ /dev/null @@ -1,12 +0,0 @@ - - -