Initial Kotlin Input Implementation

This commit contains the Kotlin side of the initial Input implementation, this is based on the work done in the `hid` branch in `bylaws/skyline`.
Co-authored-by: ◱ PixelyIon <pixelyion@protonmail.com>
This commit is contained in:
Billy Laws 2020-04-26 20:02:24 +05:30 committed by ◱ PixelyIon
parent 0219eda2db
commit b167abcdb7
7 changed files with 217 additions and 26 deletions

View File

@ -89,19 +89,22 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *
JniMtx.unlock(); JniMtx.unlock();
} }
extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject ) { extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject) {
return fps; return fps;
} }
extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject ) { extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject) {
return static_cast<float>(frametime) / 100; return static_cast<float>(frametime) / 100;
} }
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jlong id, jint state) { extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jlong id, jint state) {
skyline::input::npad::NpadButton button{.raw = static_cast<skyline::u64>(id)}; if (input) {
input->npad[0]->SetButtonState(button, static_cast<skyline::input::npad::NpadButtonState>(state)); skyline::input::npad::NpadButton button{.raw = static_cast<skyline::u64>(id)};
input->npad[0]->SetButtonState(button, static_cast<skyline::input::npad::NpadButtonState>(state));
}
} }
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint id, jint value) { extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint id, jint value) {
input->npad[0]->SetAxisValue(static_cast<skyline::input::npad::NpadAxisId>(id), value); if (input)
input->npad[0]->SetAxisValue(static_cast<skyline::input::npad::NpadAxisId>(id), value);
} }

View File

@ -42,7 +42,6 @@ namespace skyline {
bool leftSr : 1; //!< Left Joy-Con SR button bool leftSr : 1; //!< Left Joy-Con SR button
bool rightSl : 1; //!< Right Joy-Con SL button bool rightSl : 1; //!< Right Joy-Con SL button
bool rightSr : 1; //!< Right Joy-Con SR button bool rightSr : 1; //!< Right Joy-Con SR button
bool touch : 1; //!< The touch button
}; };
u64 raw; u64 raw;
}; };

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <input.h>
#include "IAppletResource.h" #include "IAppletResource.h"
namespace skyline::service::hid { namespace skyline::service::hid {
@ -9,8 +10,7 @@ namespace skyline::service::hid {
}) {} }) {}
void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { void IAppletResource::GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
hidSharedMemory = std::make_shared<kernel::type::KSharedMemory>(state, NULL, constant::HidSharedMemSize, memory::Permission{true, false, false}); auto handle = state.process->InsertItem<type::KSharedMemory>(state.input->hidKMem);
auto handle = state.process->InsertItem<type::KSharedMemory>(hidSharedMemory);
state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle); state.logger->Debug("HID Shared Memory Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);

View File

@ -7,25 +7,17 @@
#include <services/base_service.h> #include <services/base_service.h>
#include <services/serviceman.h> #include <services/serviceman.h>
namespace skyline { namespace skyline::service::hid {
namespace constant { /**
constexpr auto HidSharedMemSize = 0x40000; //!< The size of HID Shared Memory (https://switchbrew.org/wiki/HID_Shared_Memory) * @brief IAppletResource is used to get a handle to the HID shared memory (https://switchbrew.org/wiki/HID_services#IAppletResource)
} */
class IAppletResource : public BaseService {
public:
IAppletResource(const DeviceState &state, ServiceManager &manager);
namespace service::hid {
/** /**
* @brief IAppletResource is used to get a handle to the HID shared memory (https://switchbrew.org/wiki/HID_services#IAppletResource) * @brief This opens a handle to HID shared memory (https://switchbrew.org/wiki/HID_services#GetSharedMemoryHandle)
*/ */
class IAppletResource : public BaseService { void GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
public: };
std::shared_ptr<type::KSharedMemory> hidSharedMemory; //!< A pointer to HID shared memory
IAppletResource(const DeviceState &state, ServiceManager &manager);
/**
* @brief This opens a handle to HID shared memory (https://switchbrew.org/wiki/HID_services#GetSharedMemoryHandle)
*/
void GetSharedMemoryHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}
} }

View File

@ -15,9 +15,13 @@ import android.util.Log
import android.view.* import android.view.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import emu.skyline.input.ButtonState
import emu.skyline.input.NpadAxis
import emu.skyline.input.NpadButton
import emu.skyline.loader.getRomFormat import emu.skyline.loader.getRomFormat
import kotlinx.android.synthetic.main.emu_activity.* import kotlinx.android.synthetic.main.emu_activity.*
import java.io.File import java.io.File
import kotlin.math.abs
class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback { class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
init { init {
@ -84,8 +88,14 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
*/ */
private external fun getFrametime() : Float private external fun getFrametime() : Float
/**
* This sets the state of a specific button
*/
private external fun setButtonState(id : Long, state : Int) private external fun setButtonState(id : Long, state : Int)
/**
* This sets the value of a specific axis
*/
private external fun setAxisValue(id : Int, value : Int) private external fun setAxisValue(id : Int, value : Int)
/** /**
@ -210,4 +220,113 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
surface = null surface = null
setSurface(surface) setSurface(surface)
} }
/**
* This handles passing on any key events to libskyline
*/
override fun dispatchKeyEvent(event : KeyEvent) : Boolean {
val action : ButtonState = when (event.action) {
KeyEvent.ACTION_DOWN -> ButtonState.Pressed
KeyEvent.ACTION_UP -> ButtonState.Released
else -> return false
}
val buttonMap : Map<Int, NpadButton> = mapOf(
KeyEvent.KEYCODE_BUTTON_A to NpadButton.A,
KeyEvent.KEYCODE_BUTTON_B to NpadButton.B,
KeyEvent.KEYCODE_BUTTON_X to NpadButton.X,
KeyEvent.KEYCODE_BUTTON_Y to NpadButton.Y,
KeyEvent.KEYCODE_BUTTON_THUMBL to NpadButton.L3,
KeyEvent.KEYCODE_BUTTON_THUMBR to NpadButton.R3,
KeyEvent.KEYCODE_BUTTON_L1 to NpadButton.L,
KeyEvent.KEYCODE_BUTTON_R1 to NpadButton.R,
KeyEvent.KEYCODE_BUTTON_L2 to NpadButton.ZL,
KeyEvent.KEYCODE_BUTTON_R2 to NpadButton.ZR,
KeyEvent.KEYCODE_BUTTON_START to NpadButton.Plus,
KeyEvent.KEYCODE_BUTTON_SELECT to NpadButton.Minus,
KeyEvent.KEYCODE_DPAD_DOWN to NpadButton.DpadDown,
KeyEvent.KEYCODE_DPAD_UP to NpadButton.DpadUp,
KeyEvent.KEYCODE_DPAD_LEFT to NpadButton.DpadLeft,
KeyEvent.KEYCODE_DPAD_RIGHT to NpadButton.DpadRight)
return try {
setButtonState(buttonMap.getValue(event.keyCode).value(), action.ordinal);
true
} catch (ignored : NoSuchElementException) {
super.dispatchKeyEvent(event)
}
}
/**
* This is the controller HAT X value
*/
private var controllerHatX : Float = 0.0f
/**
* This is the controller HAT Y value
*/
private var controllerHatY : Float = 0.0f
/**
* This handles passing on any motion events to libskyline
*/
override fun dispatchGenericMotionEvent(event : MotionEvent) : Boolean {
if ((event.source and InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD ||
(event.source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK) {
val hatXMap : Map<Float, NpadButton> = mapOf(
-1.0f to NpadButton.DpadLeft,
+1.0f to NpadButton.DpadRight)
val hatYMap : Map<Float, NpadButton> = mapOf(
-1.0f to NpadButton.DpadUp,
+1.0f to NpadButton.DpadDown)
if (controllerHatX != event.getAxisValue(MotionEvent.AXIS_HAT_X)) {
if (event.getAxisValue(MotionEvent.AXIS_HAT_X) == 0.0f)
setButtonState(hatXMap.getValue(controllerHatX).value(), ButtonState.Released.ordinal)
else
setButtonState(hatXMap.getValue(event.getAxisValue(MotionEvent.AXIS_HAT_X)).value(), ButtonState.Pressed.ordinal)
controllerHatX = event.getAxisValue(MotionEvent.AXIS_HAT_X)
return true
}
if (controllerHatY != event.getAxisValue(MotionEvent.AXIS_HAT_Y)) {
if (event.getAxisValue(MotionEvent.AXIS_HAT_Y) == 0.0f)
setButtonState(hatYMap.getValue(controllerHatY).value(), ButtonState.Released.ordinal)
else
setButtonState(hatYMap.getValue(event.getAxisValue(MotionEvent.AXIS_HAT_Y)).value(), ButtonState.Pressed.ordinal)
controllerHatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
return true
}
}
if ((event.source and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.action == MotionEvent.ACTION_MOVE) {
val axisMap : Map<Int, NpadAxis> = mapOf(
MotionEvent.AXIS_X to NpadAxis.LX,
MotionEvent.AXIS_Y to NpadAxis.LY,
MotionEvent.AXIS_Z to NpadAxis.RX,
MotionEvent.AXIS_RZ to NpadAxis.RY)
//TODO: Digital inputs based off of analog sticks
event.device.motionRanges.forEach {
if (axisMap.containsKey(it.axis)) {
var axisValue : Float = event.getAxisValue(it.axis)
if (abs(axisValue) <= it.flat)
axisValue = 0.0f
val ratio : Float = axisValue / (it.max - it.min)
val rangedAxisValue : Int = (ratio * (Short.MAX_VALUE - Short.MIN_VALUE)).toInt()
setAxisValue(axisMap.getValue(it.axis).ordinal, rangedAxisValue)
}
}
return true
}
return super.dispatchGenericMotionEvent(event)
}
} }

View File

@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.input
/**
* This is a generic interface for all Button classes to implement
*/
interface ButtonId {
/**
* This should return the value of the Button according to what libskyline expects
*/
fun value() : Long
}
enum class ButtonState(val state : Boolean) {
Released(false),
Pressed(true),
}

View File

@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.input
/**
* This enumerates all buttons on an NPad controller
*/
enum class NpadButton : ButtonId {
A,
B,
X,
Y,
L3,
R3,
L,
R,
ZL,
ZR,
Plus,
Minus,
DpadLeft,
DpadUp,
DpadRight,
DpadDown,
LeftStickLeft,
LeftStickUp,
LeftStickRight,
LeftStickDown,
RightStickLeft,
RightStickUp,
RightStickRight,
RightStickDown,
LeftSL,
LeftSR,
RightSL,
RightSR;
/**
* This just returns the value as setting the [ordinal]-th bit in a [Long]
*/
override fun value() : Long {
return (1.toLong()) shl ordinal
}
}
/**
* This enumerates all the axis on an NPad controller
*/
enum class NpadAxis {
RX,
RY,
LX,
LY,
}