From 3a657c44cc2fa8e3f39fd330013f5b60baa3d141 Mon Sep 17 00:00:00 2001 From: lynxnb Date: Thu, 3 Nov 2022 01:27:23 +0100 Subject: [PATCH] Don't ignore HAT axes input events Capture HAT axes events ourselves instead of relying on the android framework to turn them into KeyCodes. Fixes handling of DPAD button presses on most controllers. --- .../main/java/emu/skyline/input/HostEvent.kt | 2 + .../java/emu/skyline/input/InputHandler.kt | 68 ++++++++----------- .../emu/skyline/input/dialog/ButtonDialog.kt | 18 +---- 3 files changed, 33 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/emu/skyline/input/HostEvent.kt b/app/src/main/java/emu/skyline/input/HostEvent.kt index 66287519..54a2b552 100644 --- a/app/src/main/java/emu/skyline/input/HostEvent.kt +++ b/app/src/main/java/emu/skyline/input/HostEvent.kt @@ -44,6 +44,8 @@ data class MotionHostEvent(override val descriptor : String = "", val axis : Int MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, + MotionEvent.AXIS_HAT_X, + MotionEvent.AXIS_HAT_Y, MotionEvent.AXIS_LTRIGGER, MotionEvent.AXIS_RTRIGGER, MotionEvent.AXIS_THROTTLE, diff --git a/app/src/main/java/emu/skyline/input/InputHandler.kt b/app/src/main/java/emu/skyline/input/InputHandler.kt index a9e050f1..a25462ea 100644 --- a/app/src/main/java/emu/skyline/input/InputHandler.kt +++ b/app/src/main/java/emu/skyline/input/InputHandler.kt @@ -5,7 +5,6 @@ package emu.skyline.input -import android.graphics.PointF import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent @@ -120,58 +119,47 @@ class InputHandler(private val inputManager : InputManager, private val preferen */ private val axesHistory = arrayOfNulls(MotionHostEvent.axes.size) - /** - * The last value of the HAT axes so it can be ignored in [onGenericMotionEvent] so they are handled by [dispatchKeyEvent] instead - */ - private var oldHat = PointF() - /** * Handles translating any [MotionHostEvent]s to a [GuestEvent] that is passed into libskyline */ fun handleMotionEvent(event : MotionEvent) : Boolean { if ((event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK) || event.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) && event.action == MotionEvent.ACTION_MOVE) { - val hat = PointF(event.getAxisValue(MotionEvent.AXIS_HAT_X), event.getAxisValue(MotionEvent.AXIS_HAT_Y)) + for (axisItem in MotionHostEvent.axes.withIndex()) { + val axis = axisItem.value + var value = event.getAxisValue(axis) - if (hat == oldHat) { - for (axisItem in MotionHostEvent.axes.withIndex()) { - val axis = axisItem.value - var value = event.getAxisValue(axis) + if ((event.historySize != 0 && value != event.getHistoricalAxisValue(axis, 0)) || (axesHistory[axisItem.index]?.let { it == value } == false)) { + var polarity = value > 0 || (value == 0f && axesHistory[axisItem.index]?.let { it >= 0 } == true) - if ((event.historySize != 0 && value != event.getHistoricalAxisValue(axis, 0)) || (axesHistory[axisItem.index]?.let { it == value } == false)) { - var polarity = value > 0 || (value == 0f && axesHistory[axisItem.index]?.let { it >= 0 } == true) - - val guestEvent = MotionHostEvent(event.device.descriptor, axis, polarity).let { hostEvent -> - inputManager.eventMap[hostEvent] ?: if (value == 0f) { - polarity = false - inputManager.eventMap[hostEvent.copy(polarity = false)] - } else { - null - } - } - - when (guestEvent) { - is ButtonGuestEvent -> { - if (guestEvent.button != ButtonId.Menu) - setButtonState(guestEvent.id, guestEvent.button.value(), if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.state else ButtonState.Released.state) - } - - is AxisGuestEvent -> { - value = guestEvent.value(value) - value = if (polarity) abs(value) else -abs(value) - value = if (guestEvent.axis == AxisId.LX || guestEvent.axis == AxisId.RX) value else -value - - setAxisValue(guestEvent.id, guestEvent.axis.ordinal, (value * Short.MAX_VALUE).toInt()) - } + val guestEvent = MotionHostEvent(event.device.descriptor, axis, polarity).let { hostEvent -> + inputManager.eventMap[hostEvent] ?: if (value == 0f) { + polarity = false + inputManager.eventMap[hostEvent.copy(polarity = false)] + } else { + null } } - axesHistory[axisItem.index] = value + when (guestEvent) { + 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) + } + + is AxisGuestEvent -> { + value = guestEvent.value(value) + value = if (polarity) abs(value) else -abs(value) + value = if (guestEvent.axis == AxisId.LX || guestEvent.axis == AxisId.RX) value else -value + setAxisValue(guestEvent.id, guestEvent.axis.ordinal, (value * Short.MAX_VALUE).toInt()) + } + } } - return true - } else { - oldHat = hat + axesHistory[axisItem.index] = value } + + return true } return false diff --git a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt index 1b2bea87..bd5ca4d4 100644 --- a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt +++ b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt @@ -101,10 +101,6 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton var axisRunnable : Runnable? = null // The Runnable that is used for counting down till an axis is selected val axisHandler = Handler(Looper.getMainLooper()) // The handler responsible for handling posting [axisRunnable] - // The last values of the HAT axes so that they can be ignored in [View.OnGenericMotionListener] so they are passed onto [DialogInterface.OnKeyListener] as [KeyEvent]s - var oldDpadX = 0.0f - var oldDpadY = 0.0f - dialog?.setOnKeyListener { _, _, event -> // We want all input events from Joysticks and Buttons except for [KeyEvent.KEYCODE_BACK] as that will should be processed elsewhere if (((event.isFromSource(InputDevice.SOURCE_CLASS_BUTTON) && event.keyCode != KeyEvent.KEYCODE_BACK) || event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) && event.repeatCount == 0) { @@ -155,23 +151,18 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton } } - val axes = arrayOf(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y, MotionEvent.AXIS_LTRIGGER, MotionEvent.AXIS_RTRIGGER, MotionEvent.AXIS_THROTTLE, MotionEvent.AXIS_RUDDER, MotionEvent.AXIS_WHEEL, MotionEvent.AXIS_GAS, MotionEvent.AXIS_BRAKE).plus(IntRange(MotionEvent.AXIS_GENERIC_1, MotionEvent.AXIS_GENERIC_16).toList()) - + val axes = MotionHostEvent.axes val axesHistory = arrayOfNulls(axes.size) // The last recorded value of an axis, this is used to eliminate any stagnant axes view.setOnGenericMotionListener { _, event -> - // We retrieve the value of the HAT axes so that we can check for change and ignore any input from them so it'll be passed onto the [KeyEvent] handler - val dpadX = event.getAxisValue(MotionEvent.AXIS_HAT_X) - val dpadY = event.getAxisValue(MotionEvent.AXIS_HAT_Y) - // We want all input events from Joysticks and Buttons that are [MotionEvent.ACTION_MOVE] and not from the D-pad - if ((event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK) || event.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) && event.action == MotionEvent.ACTION_MOVE && dpadX == oldDpadX && dpadY == oldDpadY) { + if ((event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK) || event.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) && event.action == MotionEvent.ACTION_MOVE) { // We iterate over every axis to check if any of them pass the selection threshold and if they do then select them by setting [deviceId], [inputId] and [axisPolarity] for (axisItem in axes.withIndex()) { val axis = axisItem.value val value = event.getAxisValue(axis) - // This checks the history of the axis so it we can ignore any stagnant axis + // This checks the history of the axis so we can ignore any stagnant axis if ((event.historySize == 0 || value == event.getHistoricalAxisValue(axis, 0)) && (axesHistory[axisItem.index]?.let { it == value } != false)) { axesHistory[axisItem.index] = value continue @@ -246,9 +237,6 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton true } else { - oldDpadX = dpadX - oldDpadY = dpadY - false } }