mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 10:01:50 +01:00
Move to Callback for Input Initialization + ConditionalVariable for Surface
This commit is contained in:
parent
07c2f2d891
commit
7290a80c3e
@ -53,6 +53,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
|
||||
try {
|
||||
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
|
||||
inputWeak = os.state.input;
|
||||
jvmManager->InitializeControllers();
|
||||
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
|
||||
|
||||
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
|
||||
@ -100,27 +101,22 @@ extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIE
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
|
||||
while (inputWeak.expired()); // If this isn't called then the guest won't know that the following host controller exists
|
||||
auto input = inputWeak.lock();
|
||||
std::lock_guard guard(input->npad.mutex);
|
||||
input->npad.controllers[index] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
|
||||
while (inputWeak.expired()); // If this isn't called then the mappings will not update unless the guest initiates an update itself
|
||||
auto input = inputWeak.lock();
|
||||
std::unique_lock lock(input->npad.mutex);
|
||||
input->npad.Update(lock, true);
|
||||
inputWeak.lock()->npad.Update();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jint state) {
|
||||
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
|
||||
try {
|
||||
auto input = inputWeak.lock();
|
||||
auto device = input->npad.controllers[index].device;
|
||||
skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(mask)};
|
||||
if (device)
|
||||
device->SetButtonState(button, state);
|
||||
} catch (const std::bad_weak_ptr) {
|
||||
device->SetButtonState(skyline::input::NpadButton{.raw = static_cast<skyline::u64>(mask)}, pressed);
|
||||
} catch (const std::bad_weak_ptr&) {
|
||||
// We don't mind if we miss button updates while input hasn't been initialized
|
||||
}
|
||||
}
|
||||
@ -131,7 +127,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValu
|
||||
auto device = input->npad.controllers[index].device;
|
||||
if (device)
|
||||
device->SetAxisValue(static_cast<skyline::input::NpadAxisId>(axis), value);
|
||||
} catch (const std::bad_weak_ptr) {
|
||||
} catch (const std::bad_weak_ptr&) {
|
||||
// We don't mind if we miss axis updates while input hasn't been initialized
|
||||
}
|
||||
}
|
||||
|
@ -13,15 +13,8 @@ namespace skyline::input {
|
||||
{*this, hid->npad[8], NpadId::Unknown}, {*this, hid->npad[9], NpadId::Handheld},
|
||||
} {}
|
||||
|
||||
void NpadManager::Update(std::unique_lock<std::mutex> &lock, bool host) {
|
||||
if (host) {
|
||||
updated = true;
|
||||
} else if (!updated) {
|
||||
lock.unlock();
|
||||
while (!updated);
|
||||
lock.lock();
|
||||
return;
|
||||
}
|
||||
void NpadManager::Update() {
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
if (!activated)
|
||||
return;
|
||||
@ -85,17 +78,17 @@ namespace skyline::input {
|
||||
}
|
||||
|
||||
void NpadManager::Activate() {
|
||||
std::unique_lock lock(mutex);
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
supportedIds = {NpadId::Handheld, NpadId::Player1, NpadId::Player2, NpadId::Player3, NpadId::Player4, NpadId::Player5, NpadId::Player6, NpadId::Player7, NpadId::Player8};
|
||||
styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
|
||||
activated = true;
|
||||
|
||||
Update(lock);
|
||||
Update();
|
||||
}
|
||||
|
||||
void NpadManager::Deactivate() {
|
||||
std::unique_lock lock(mutex);
|
||||
std::lock_guard guard(mutex);
|
||||
|
||||
supportedIds = {};
|
||||
styles = {};
|
||||
|
@ -22,7 +22,6 @@ namespace skyline::input {
|
||||
private:
|
||||
const DeviceState &state;
|
||||
bool activated{false}; //!< If this NpadManager is activated or not
|
||||
std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest
|
||||
|
||||
friend NpadDevice;
|
||||
|
||||
@ -43,7 +42,7 @@ namespace skyline::input {
|
||||
}
|
||||
|
||||
public:
|
||||
std::mutex mutex; //!< This mutex must be locked before any modifications to class members
|
||||
std::recursive_mutex mutex; //!< This mutex must be locked before any modifications to class members
|
||||
std::array<NpadDevice, constant::NpadCount> npads;
|
||||
std::array<GuestController, constant::ControllerCount> controllers;
|
||||
std::vector<NpadId> supportedIds; //!< The NpadId(s) that are supported by the application
|
||||
@ -71,10 +70,9 @@ namespace skyline::input {
|
||||
|
||||
/**
|
||||
* @brief This deduces all the mappings from guest controllers -> players based on the configuration supplied by HID services and available controllers
|
||||
* @param lock A unique_lock which locks the mutex in the class, it should be locked before modifications to any members and must not be passed in an unlocked state
|
||||
* @param host If the update is host-initiated rather than the guest
|
||||
* @note If any class members were edited, the mutex shouldn't be released till this is called
|
||||
*/
|
||||
void Update(std::unique_lock<std::mutex> &lock, bool host = false);
|
||||
void Update();
|
||||
|
||||
/**
|
||||
* @brief This activates the mapping between guest controllers -> players, a call to this is required for function
|
||||
|
@ -6,7 +6,7 @@
|
||||
thread_local JNIEnv *env;
|
||||
|
||||
namespace skyline {
|
||||
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))) {
|
||||
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")) {
|
||||
env = environ;
|
||||
if (env->GetJavaVM(&vm) < 0)
|
||||
throw exception("Cannot get JavaVM from environment");
|
||||
@ -37,4 +37,8 @@ namespace skyline {
|
||||
bool JvmManager::CheckNull(jobject &object) {
|
||||
return env->IsSameObject(object, nullptr);
|
||||
}
|
||||
|
||||
void JvmManager::InitializeControllers() {
|
||||
env->CallVoidMethod(instance, initializeControllersId);
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace skyline {
|
||||
/**
|
||||
* @brief Returns a pointer to the JNI environment for the current thread
|
||||
*/
|
||||
JNIEnv *GetEnv();
|
||||
static JNIEnv *GetEnv();
|
||||
|
||||
/**
|
||||
* @brief Retrieves a specific field of the given type from the activity
|
||||
@ -85,6 +85,14 @@ namespace skyline {
|
||||
* @param object The jobject to check
|
||||
* @return If the object is null or not
|
||||
*/
|
||||
bool CheckNull(jobject &object);
|
||||
static bool CheckNull(jobject &object);
|
||||
|
||||
/**
|
||||
* @brief A call to EmulationActivity.initializeControllers in Kotlin
|
||||
*/
|
||||
void InitializeControllers();
|
||||
|
||||
private:
|
||||
jmethodID initializeControllersId;
|
||||
};
|
||||
}
|
||||
|
@ -29,9 +29,9 @@ namespace skyline::service::hid {
|
||||
|
||||
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto styleSet = request.Pop<NpadStyleSet>();
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
state.input->npad.styles = styleSet;
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
|
||||
state.logger->Debug("Controller Support:\nPro-Controller: {}\nJoy-Con: Handheld: {}, Dual: {}, L: {}, R: {}\nGameCube: {}\nPokeBall: {}\nNES: {}, NES Handheld: {}, SNES: {}", static_cast<bool>(styleSet.proController), static_cast<bool>(styleSet.joyconHandheld), static_cast<bool>(styleSet.joyconDual), static_cast<bool>(styleSet.joyconLeft), static_cast<bool>
|
||||
(styleSet.joyconRight), static_cast<bool>(styleSet.gamecube), static_cast<bool>(styleSet.palma), static_cast<bool>(styleSet.nes), static_cast<bool>(styleSet.nesHandheld), static_cast<bool>(styleSet.snes));
|
||||
@ -45,16 +45,16 @@ namespace skyline::service::hid {
|
||||
const auto &buffer = request.inputBuf.at(0);
|
||||
u64 address = buffer.address;
|
||||
size_t size = buffer.size / sizeof(NpadId);
|
||||
std::vector<NpadId> supportedIds;
|
||||
|
||||
std::vector<NpadId> supportedIds(size);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
supportedIds.push_back(state.process->GetObject<NpadId>(address));
|
||||
supportedIds[i] = state.process->GetObject<NpadId>(address);
|
||||
address += sizeof(NpadId);
|
||||
}
|
||||
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
state.input->npad.supportedIds = supportedIds;
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
}
|
||||
|
||||
void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
@ -75,10 +75,10 @@ namespace skyline::service::hid {
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
request.Skip<u64>();
|
||||
state.input->npad.orientation = request.Pop<NpadJoyOrientation>();
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
}
|
||||
|
||||
void IHidServer::GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
@ -87,22 +87,22 @@ namespace skyline::service::hid {
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<NpadId>();
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<NpadId>();
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
}
|
||||
|
||||
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto id = request.Pop<NpadId>();
|
||||
std::unique_lock lock(state.input->npad.mutex);
|
||||
std::lock_guard lock(state.input->npad.mutex);
|
||||
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual);
|
||||
state.input->npad.Update(lock);
|
||||
state.input->npad.Update();
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace skyline::service::settings {
|
||||
{0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {}
|
||||
|
||||
void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
SysVerTitle title{.major=5, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
|
||||
SysVerTitle title{.major=9, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
|
||||
state.process->WriteMemory(title, request.outputBuf.at(0).address);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.ConditionVariable
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
@ -52,6 +53,11 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
@Volatile
|
||||
private var surface : Surface? = null
|
||||
|
||||
/**
|
||||
* A condition variable keeping track of if the surface is ready or not
|
||||
*/
|
||||
private var surfaceReady = ConditionVariable()
|
||||
|
||||
/**
|
||||
* A boolean flag denoting if the emulation thread should call finish() or not
|
||||
*/
|
||||
@ -120,9 +126,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
*
|
||||
* @param index The index of the controller this is directed to
|
||||
* @param mask The mask of the button that are being set
|
||||
* @param state The state to set the button to
|
||||
* @param pressed If the buttons are being pressed or released
|
||||
*/
|
||||
private external fun setButtonState(index : Int, mask : Long, state : Int)
|
||||
private external fun setButtonState(index : Int, mask : Long, pressed : Boolean)
|
||||
|
||||
/**
|
||||
* This sets the value of a specific axis on a specific controller
|
||||
@ -141,13 +147,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
val controller = entry.value
|
||||
|
||||
if (controller.type != ControllerType.None) {
|
||||
val type : Int = when (controller.type) {
|
||||
val type = when (controller.type) {
|
||||
ControllerType.None -> throw IllegalArgumentException()
|
||||
ControllerType.HandheldProController -> if (operationMode) ControllerType.ProController.id else ControllerType.HandheldProController.id
|
||||
ControllerType.ProController, ControllerType.JoyConLeft, ControllerType.JoyConRight -> controller.type.id
|
||||
}
|
||||
|
||||
val partnerIndex : Int? = when (controller) {
|
||||
val partnerIndex = when (controller) {
|
||||
is JoyConLeftController -> controller.partnerId
|
||||
is JoyConRightController -> controller.partnerId
|
||||
else -> null
|
||||
@ -170,10 +176,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
romFd = contentResolver.openFileDescriptor(rom, "r")!!
|
||||
|
||||
emulationThread = Thread {
|
||||
while (surface == null)
|
||||
Thread.yield()
|
||||
|
||||
runOnUiThread { initializeControllers() }
|
||||
surfaceReady.block()
|
||||
|
||||
executeApplication(rom.toString(), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
|
||||
|
||||
@ -216,14 +219,12 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
if (sharedPreferences.getBoolean("perf_stats", false)) {
|
||||
val perfRunnable = object : Runnable {
|
||||
perf_stats.postDelayed(object : Runnable {
|
||||
override fun run() {
|
||||
perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms"
|
||||
perf_stats.postDelayed(this, 250)
|
||||
}
|
||||
}
|
||||
|
||||
perf_stats.postDelayed(perfRunnable, 250)
|
||||
}, 250)
|
||||
}
|
||||
|
||||
operationMode = sharedPreferences.getBoolean("operation_mode", operationMode)
|
||||
@ -274,6 +275,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
Log.d("surfaceCreated", "Holder: $holder")
|
||||
surface = holder.surface
|
||||
setSurface(surface)
|
||||
surfaceReady.open()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,6 +290,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
*/
|
||||
override fun surfaceDestroyed(holder : SurfaceHolder) {
|
||||
Log.d("surfaceDestroyed", "Holder: $holder")
|
||||
surfaceReady.close()
|
||||
surface = null
|
||||
setSurface(surface)
|
||||
}
|
||||
@ -299,7 +302,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
if (event.repeatCount != 0)
|
||||
return super.dispatchKeyEvent(event)
|
||||
|
||||
val action : ButtonState = when (event.action) {
|
||||
val action = when (event.action) {
|
||||
KeyEvent.ACTION_DOWN -> ButtonState.Pressed
|
||||
KeyEvent.ACTION_UP -> ButtonState.Released
|
||||
else -> return super.dispatchKeyEvent(event)
|
||||
@ -308,7 +311,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
return when (val guestEvent = input.eventMap[KeyHostEvent(event.device.descriptor, event.keyCode)]) {
|
||||
is ButtonGuestEvent -> {
|
||||
if (guestEvent.button != ButtonId.Menu)
|
||||
setButtonState(guestEvent.id, guestEvent.button.value(), action.ordinal)
|
||||
setButtonState(guestEvent.id, guestEvent.button.value(), action.state)
|
||||
true
|
||||
}
|
||||
|
||||
@ -356,7 +359,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
|
||||
when (guestEvent) {
|
||||
is ButtonGuestEvent -> {
|
||||
if (guestEvent.button != ButtonId.Menu)
|
||||
setButtonState(guestEvent.id, guestEvent.button.value(), if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.ordinal else ButtonState.Released.ordinal)
|
||||
setButtonState(guestEvent.id, guestEvent.button.value(), if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.state else ButtonState.Released.state)
|
||||
}
|
||||
|
||||
is AxisGuestEvent -> {
|
||||
|
@ -179,7 +179,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
|
||||
setupAppList()
|
||||
|
||||
app_list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
var y : Int = 0
|
||||
var y = 0
|
||||
|
||||
override fun onScrolled(recyclerView : RecyclerView, dx : Int, dy : Int) {
|
||||
y += dy
|
||||
|
@ -65,12 +65,14 @@
|
||||
android:key="category_input"
|
||||
android:title="@string/input"
|
||||
app:initialExpandedChildrenCount="4">
|
||||
<!--
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:summaryOff="@string/osc_not_shown"
|
||||
android:summaryOn="@string/osc_shown"
|
||||
app:key="show_osc"
|
||||
app:title="@string/show_osc" />
|
||||
-->
|
||||
<emu.skyline.preference.ControllerPreference index="0" />
|
||||
<emu.skyline.preference.ControllerPreference index="1" />
|
||||
<emu.skyline.preference.ControllerPreference index="2" />
|
||||
|
Loading…
Reference in New Issue
Block a user