summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
new file mode 100644
index 000000000..43d664d21
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableDpad.kt
@@ -0,0 +1,274 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.overlay
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
+import android.view.MotionEvent
+import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
+
+/**
+ * Custom [BitmapDrawable] that is capable
+ * of storing it's own ID.
+ *
+ * @param res [Resources] instance.
+ * @param defaultStateBitmap [Bitmap] of the default state.
+ * @param pressedOneDirectionStateBitmap [Bitmap] of the pressed state in one direction.
+ * @param pressedTwoDirectionsStateBitmap [Bitmap] of the pressed state in two direction.
+ * @param buttonUp Identifier for the up button.
+ * @param buttonDown Identifier for the down button.
+ * @param buttonLeft Identifier for the left button.
+ * @param buttonRight Identifier for the right button.
+ */
+class InputOverlayDrawableDpad(
+ res: Resources,
+ defaultStateBitmap: Bitmap,
+ pressedOneDirectionStateBitmap: Bitmap,
+ pressedTwoDirectionsStateBitmap: Bitmap,
+ buttonUp: Int,
+ buttonDown: Int,
+ buttonLeft: Int,
+ buttonRight: Int
+) {
+ /**
+ * Gets one of the InputOverlayDrawableDpad's button IDs.
+ *
+ * @return the requested InputOverlayDrawableDpad's button ID.
+ */
+ // The ID identifying what type of button this Drawable represents.
+ val upId: Int
+ val downId: Int
+ val leftId: Int
+ val rightId: Int
+ var trackId: Int
+
+ val width: Int
+ val height: Int
+
+ private val defaultStateBitmap: BitmapDrawable
+ private val pressedOneDirectionStateBitmap: BitmapDrawable
+ private val pressedTwoDirectionsStateBitmap: BitmapDrawable
+
+ private var previousTouchX = 0
+ private var previousTouchY = 0
+ private var controlPositionX = 0
+ private var controlPositionY = 0
+
+ private var upButtonState = false
+ private var downButtonState = false
+ private var leftButtonState = false
+ private var rightButtonState = false
+
+ init {
+ this.defaultStateBitmap = BitmapDrawable(res, defaultStateBitmap)
+ this.pressedOneDirectionStateBitmap = BitmapDrawable(res, pressedOneDirectionStateBitmap)
+ this.pressedTwoDirectionsStateBitmap = BitmapDrawable(res, pressedTwoDirectionsStateBitmap)
+ width = this.defaultStateBitmap.intrinsicWidth
+ height = this.defaultStateBitmap.intrinsicHeight
+ upId = buttonUp
+ downId = buttonDown
+ leftId = buttonLeft
+ rightId = buttonRight
+ trackId = -1
+ }
+
+ fun updateStatus(event: MotionEvent, dpad_slide: Boolean): Boolean {
+ val pointerIndex = event.actionIndex
+ val xPosition = event.getX(pointerIndex).toInt()
+ val yPosition = event.getY(pointerIndex).toInt()
+ val pointerId = event.getPointerId(pointerIndex)
+ val motionEvent = event.action and MotionEvent.ACTION_MASK
+ val isActionDown =
+ motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
+ val isActionUp =
+ motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
+ if (isActionDown) {
+ if (!bounds.contains(xPosition, yPosition)) {
+ return false
+ }
+ trackId = pointerId
+ }
+ if (isActionUp) {
+ if (trackId != pointerId) {
+ return false
+ }
+ trackId = -1
+ upButtonState = false
+ downButtonState = false
+ leftButtonState = false
+ rightButtonState = false
+ return true
+ }
+ if (trackId == -1) {
+ return false
+ }
+ if (!dpad_slide && !isActionDown) {
+ return false
+ }
+ for (i in 0 until event.pointerCount) {
+ if (trackId != event.getPointerId(i)) {
+ continue
+ }
+
+ var touchX = event.getX(i)
+ var touchY = event.getY(i)
+ var maxY = bounds.bottom.toFloat()
+ var maxX = bounds.right.toFloat()
+ touchX -= bounds.centerX().toFloat()
+ maxX -= bounds.centerX().toFloat()
+ touchY -= bounds.centerY().toFloat()
+ maxY -= bounds.centerY().toFloat()
+ val axisX = touchX / maxX
+ val axisY = touchY / maxY
+ val oldUpState = upButtonState
+ val oldDownState = downButtonState
+ val oldLeftState = leftButtonState
+ val oldRightState = rightButtonState
+
+ upButtonState = axisY < -VIRT_AXIS_DEADZONE
+ downButtonState = axisY > VIRT_AXIS_DEADZONE
+ leftButtonState = axisX < -VIRT_AXIS_DEADZONE
+ rightButtonState = axisX > VIRT_AXIS_DEADZONE
+ return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState
+ }
+ return false
+ }
+
+ fun draw(canvas: Canvas) {
+ val px = controlPositionX + width / 2
+ val py = controlPositionY + height / 2
+
+ // Pressed up
+ if (upButtonState && !leftButtonState && !rightButtonState) {
+ pressedOneDirectionStateBitmap.draw(canvas)
+ return
+ }
+
+ // Pressed down
+ if (downButtonState && !leftButtonState && !rightButtonState) {
+ canvas.save()
+ canvas.rotate(180f, px.toFloat(), py.toFloat())
+ pressedOneDirectionStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Pressed left
+ if (leftButtonState && !upButtonState && !downButtonState) {
+ canvas.save()
+ canvas.rotate(270f, px.toFloat(), py.toFloat())
+ pressedOneDirectionStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Pressed right
+ if (rightButtonState && !upButtonState && !downButtonState) {
+ canvas.save()
+ canvas.rotate(90f, px.toFloat(), py.toFloat())
+ pressedOneDirectionStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Pressed up left
+ if (upButtonState && leftButtonState && !rightButtonState) {
+ pressedTwoDirectionsStateBitmap.draw(canvas)
+ return
+ }
+
+ // Pressed up right
+ if (upButtonState && !leftButtonState && rightButtonState) {
+ canvas.save()
+ canvas.rotate(90f, px.toFloat(), py.toFloat())
+ pressedTwoDirectionsStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Pressed down right
+ if (downButtonState && !leftButtonState && rightButtonState) {
+ canvas.save()
+ canvas.rotate(180f, px.toFloat(), py.toFloat())
+ pressedTwoDirectionsStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Pressed down left
+ if (downButtonState && leftButtonState && !rightButtonState) {
+ canvas.save()
+ canvas.rotate(270f, px.toFloat(), py.toFloat())
+ pressedTwoDirectionsStateBitmap.draw(canvas)
+ canvas.restore()
+ return
+ }
+
+ // Not pressed
+ defaultStateBitmap.draw(canvas)
+ }
+
+ val upStatus: Int
+ get() = if (upButtonState) ButtonState.PRESSED else ButtonState.RELEASED
+ val downStatus: Int
+ get() = if (downButtonState) ButtonState.PRESSED else ButtonState.RELEASED
+ val leftStatus: Int
+ get() = if (leftButtonState) ButtonState.PRESSED else ButtonState.RELEASED
+ val rightStatus: Int
+ get() = if (rightButtonState) ButtonState.PRESSED else ButtonState.RELEASED
+
+ fun onConfigureTouch(event: MotionEvent): Boolean {
+ val pointerIndex = event.actionIndex
+ val fingerPositionX = event.getX(pointerIndex).toInt()
+ val fingerPositionY = event.getY(pointerIndex).toInt()
+
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ previousTouchX = fingerPositionX
+ previousTouchY = fingerPositionY
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ controlPositionX += fingerPositionX - previousTouchX
+ controlPositionY += fingerPositionY - previousTouchY
+ setBounds(
+ controlPositionX,
+ controlPositionY,
+ width + controlPositionX,
+ height + controlPositionY
+ )
+ previousTouchX = fingerPositionX
+ previousTouchY = fingerPositionY
+ }
+ }
+ return true
+ }
+
+ fun setPosition(x: Int, y: Int) {
+ controlPositionX = x
+ controlPositionY = y
+ }
+
+ fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
+ defaultStateBitmap.setBounds(left, top, right, bottom)
+ pressedOneDirectionStateBitmap.setBounds(left, top, right, bottom)
+ pressedTwoDirectionsStateBitmap.setBounds(left, top, right, bottom)
+ }
+
+ fun setOpacity(value: Int) {
+ defaultStateBitmap.alpha = value
+ pressedOneDirectionStateBitmap.alpha = value
+ pressedTwoDirectionsStateBitmap.alpha = value
+ }
+
+ val bounds: Rect
+ get() = defaultStateBitmap.bounds
+
+ companion object {
+ const val VIRT_AXIS_DEADZONE = 0.5f
+ }
+}