mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #13260 from JosJuice/android-gcadapter-hotplug-callback
Android: Detect GCAdapter hotplug using BroadcastReceiver
This commit is contained in:
@@ -66,13 +66,7 @@ static void AddGCAdapter(libusb_device* device);
|
||||
static void ResetRumbleLockNeeded();
|
||||
#endif
|
||||
|
||||
enum class CalledFromReadThread
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
static void Reset(CalledFromReadThread called_from_read_thread);
|
||||
static void Reset();
|
||||
static void Setup();
|
||||
static void ProcessInputPayload(const u8* data, std::size_t size);
|
||||
static void ReadThreadFunc();
|
||||
@@ -129,24 +123,22 @@ static std::atomic<int> s_controller_write_payload_size{0};
|
||||
|
||||
static std::thread s_read_adapter_thread;
|
||||
static Common::Flag s_read_adapter_thread_running;
|
||||
static Common::Flag s_read_adapter_thread_needs_joining;
|
||||
static std::thread s_write_adapter_thread;
|
||||
static Common::Flag s_write_adapter_thread_running;
|
||||
static Common::Event s_write_happened;
|
||||
|
||||
static std::mutex s_read_mutex;
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
static std::mutex s_init_mutex;
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
static std::mutex s_read_mutex;
|
||||
#if GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
static std::mutex s_write_mutex;
|
||||
#endif
|
||||
|
||||
static std::thread s_adapter_detect_thread;
|
||||
static Common::Flag s_adapter_detect_thread_running;
|
||||
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
static Common::Event s_hotplug_event;
|
||||
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
static std::function<void(void)> s_detect_callback;
|
||||
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 11
|
||||
@@ -180,14 +172,14 @@ static void ReadThreadFunc()
|
||||
bool first_read = true;
|
||||
JNIEnv* const env = IDCache::GetEnvForThread();
|
||||
|
||||
const jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controller_payload", "[B");
|
||||
const jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controllerPayload", "[B");
|
||||
jobject payload_object = env->GetStaticObjectField(s_adapter_class, payload_field);
|
||||
auto* const java_controller_payload = reinterpret_cast<jbyteArray*>(&payload_object);
|
||||
|
||||
// Get function pointers
|
||||
const jmethodID getfd_func = env->GetStaticMethodID(s_adapter_class, "GetFD", "()I");
|
||||
const jmethodID input_func = env->GetStaticMethodID(s_adapter_class, "Input", "()I");
|
||||
const jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()Z");
|
||||
const jmethodID getfd_func = env->GetStaticMethodID(s_adapter_class, "getFd", "()I");
|
||||
const jmethodID input_func = env->GetStaticMethodID(s_adapter_class, "input", "()I");
|
||||
const jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "openAdapter", "()Z");
|
||||
|
||||
const bool connected = env->CallStaticBooleanMethod(s_adapter_class, openadapter_func);
|
||||
|
||||
@@ -279,7 +271,7 @@ static void WriteThreadFunc()
|
||||
int size = 0;
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
JNIEnv* const env = IDCache::GetEnvForThread();
|
||||
const jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "Output", "([B)I");
|
||||
const jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "output", "([B)I");
|
||||
#endif
|
||||
|
||||
while (s_write_adapter_thread_running.IsSet())
|
||||
@@ -331,7 +323,7 @@ static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotpl
|
||||
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
{
|
||||
if (s_handle != nullptr && libusb_get_device(s_handle) == dev)
|
||||
Reset(CalledFromReadThread::No);
|
||||
Reset();
|
||||
|
||||
// Reset a potential error status now that the adapter is unplugged
|
||||
if (s_status == AdapterStatus::Error)
|
||||
@@ -344,6 +336,25 @@ static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotpl
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_utils_GCAdapter_onAdapterConnected(JNIEnv* env, jclass)
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "GC adapter connected");
|
||||
if (!s_detected)
|
||||
s_hotplug_event.Set();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_utils_GCAdapter_onAdapterDisconnected(JNIEnv* env, jclass)
|
||||
{
|
||||
INFO_LOG_FMT(CONTROLLERINTERFACE, "GC adapter disconnected");
|
||||
if (s_detected)
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ScanThreadFunc()
|
||||
@@ -393,15 +404,23 @@ static void ScanThreadFunc()
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
JNIEnv* const env = IDCache::GetEnvForThread();
|
||||
|
||||
const jmethodID queryadapter_func =
|
||||
env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
|
||||
const jmethodID enable_hotplug_callback_func =
|
||||
env->GetStaticMethodID(s_adapter_class, "enableHotplugCallback", "()V");
|
||||
env->CallStaticVoidMethod(s_adapter_class, enable_hotplug_callback_func);
|
||||
|
||||
const jmethodID is_usb_device_available_func =
|
||||
env->GetStaticMethodID(s_adapter_class, "isUsbDeviceAvailable", "()Z");
|
||||
|
||||
while (s_adapter_detect_thread_running.IsSet())
|
||||
{
|
||||
if (!s_detected && UseAdapter() &&
|
||||
env->CallStaticBooleanMethod(s_adapter_class, queryadapter_func))
|
||||
env->CallStaticBooleanMethod(s_adapter_class, is_usb_device_available_func))
|
||||
{
|
||||
std::lock_guard lk(s_init_mutex);
|
||||
Setup();
|
||||
Common::SleepCurrentThread(1000);
|
||||
}
|
||||
|
||||
s_hotplug_event.Wait();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -456,7 +475,7 @@ void Init()
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
JNIEnv* const env = IDCache::GetEnvForThread();
|
||||
|
||||
const jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_GCAdapter");
|
||||
const jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/GCAdapter");
|
||||
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
|
||||
#endif
|
||||
|
||||
@@ -484,9 +503,7 @@ void StopScanThread()
|
||||
{
|
||||
if (s_adapter_detect_thread_running.TestAndClear())
|
||||
{
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
s_hotplug_event.Set();
|
||||
#endif
|
||||
s_adapter_detect_thread.join();
|
||||
}
|
||||
}
|
||||
@@ -523,11 +540,8 @@ static void Setup()
|
||||
s_detected = true;
|
||||
|
||||
// Make sure the thread isn't in the middle of shutting down while starting a new one
|
||||
if (s_read_adapter_thread_needs_joining.TestAndClear() ||
|
||||
s_read_adapter_thread_running.TestAndClear())
|
||||
{
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
s_read_adapter_thread.join();
|
||||
}
|
||||
|
||||
s_read_adapter_thread_running.Set(true);
|
||||
s_read_adapter_thread = std::thread(ReadThreadFunc);
|
||||
@@ -691,8 +705,13 @@ void Shutdown()
|
||||
if (s_libusb_context && s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
|
||||
libusb_hotplug_deregister_callback(*s_libusb_context, s_hotplug_handle);
|
||||
#endif
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
JNIEnv* const env = IDCache::GetEnvForThread();
|
||||
const jmethodID disable_hotplug_callback_func =
|
||||
env->GetStaticMethodID(s_adapter_class, "disableHotplugCallback", "()V");
|
||||
env->CallStaticVoidMethod(s_adapter_class, disable_hotplug_callback_func);
|
||||
#endif
|
||||
Reset(CalledFromReadThread::No);
|
||||
Reset();
|
||||
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
s_libusb_context.reset();
|
||||
@@ -706,12 +725,12 @@ void Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
static void Reset(CalledFromReadThread called_from_read_thread)
|
||||
static void Reset()
|
||||
{
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
std::unique_lock lock(s_init_mutex, std::defer_lock);
|
||||
if (!lock.try_lock())
|
||||
return;
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
if (s_status != AdapterStatus::Detected)
|
||||
return;
|
||||
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
@@ -719,16 +738,8 @@ static void Reset(CalledFromReadThread called_from_read_thread)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (called_from_read_thread == CalledFromReadThread::No)
|
||||
{
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
s_read_adapter_thread.join();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_read_adapter_thread_needs_joining.Set();
|
||||
s_read_adapter_thread_running.Clear();
|
||||
}
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
s_read_adapter_thread.join();
|
||||
// The read thread will close the write thread
|
||||
|
||||
s_port_states.fill({});
|
||||
@@ -807,9 +818,6 @@ void ProcessInputPayload(const u8* data, std::size_t size)
|
||||
// This can occur for a few frames on initialization.
|
||||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "error reading payload (size: {}, type: {:02x})", size,
|
||||
data[0]);
|
||||
#if GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
Reset(CalledFromReadThread::Yes);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user