summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2025-09-28 14:03:50 -0400
committerGitHub <noreply@github.com>2025-09-28 14:03:50 -0400
commite1c7734ee44e1f49089c8f4e3c2496e573e7cae6 (patch)
tree7d4b541822867ff58287a21155208ed9f4289956
parentcbdb7ac38ef9336c20c0ddccf106d42151c84c15 (diff)
parent8524e725a8e883a0c763b6d791095aba5ff5e5ab (diff)
Merge pull request #13260 from JosJuice/android-gcadapter-hotplug-callback
Android: Detect GCAdapter hotplug using BroadcastReceiver
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java8
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java259
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java158
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java (renamed from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java)40
-rw-r--r--Source/Core/Core/HW/WiimoteReal/IOAndroid.cpp12
-rw-r--r--Source/Core/InputCommon/GCAdapter.cpp98
6 files changed, 342 insertions, 233 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java
index 8ff78ebb0e..91200852a1 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java
@@ -9,8 +9,8 @@ import android.hardware.usb.UsbManager;
import org.dolphinemu.dolphinemu.utils.ActivityTracker;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
-import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
-import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
+import org.dolphinemu.dolphinemu.utils.GCAdapter;
+import org.dolphinemu.dolphinemu.utils.WiimoteAdapter;
import org.dolphinemu.dolphinemu.utils.VolleyUtil;
public class DolphinApplication extends Application
@@ -28,8 +28,8 @@ public class DolphinApplication extends Application
VolleyUtil.init(getApplicationContext());
System.loadLibrary("main");
- Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
- Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE);
if (DirectoryInitialization.shouldStart(getApplicationContext()))
DirectoryInitialization.start(getApplicationContext());
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java
new file mode 100644
index 0000000000..b9e67e654f
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.utils;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbConfiguration;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Build;
+import android.widget.Toast;
+
+import androidx.annotation.Keep;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import org.dolphinemu.dolphinemu.BuildConfig;
+import org.dolphinemu.dolphinemu.DolphinApplication;
+import org.dolphinemu.dolphinemu.R;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GCAdapter
+{
+ public static UsbManager manager;
+
+ @Keep
+ static byte[] controllerPayload = new byte[37];
+
+ static UsbDeviceConnection usbConnection;
+ static UsbInterface usbInterface;
+ static UsbEndpoint usbIn;
+ static UsbEndpoint usbOut;
+
+ private static final String ACTION_GC_ADAPTER_PERMISSION_GRANTED =
+ BuildConfig.APPLICATION_ID + ".GC_ADAPTER_PERMISSION_GRANTED";
+
+ private static final Object hotplugCallbackLock = new Object();
+ private static boolean hotplugCallbackEnabled = false;
+ private static UsbDevice adapterDevice = null;
+ private static BroadcastReceiver hotplugBroadcastReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ onUsbDevicesChanged();
+ }
+ };
+
+ private static void requestPermission()
+ {
+ HashMap<String, UsbDevice> devices = manager.getDeviceList();
+ for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
+ {
+ UsbDevice dev = pair.getValue();
+ if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
+ {
+ if (!manager.hasPermission(dev))
+ {
+ Context context = DolphinApplication.getAppContext();
+
+ int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
+ PendingIntent.FLAG_IMMUTABLE : 0;
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
+ new Intent(ACTION_GC_ADAPTER_PERMISSION_GRANTED), flags);
+
+ manager.requestPermission(dev, pendingIntent);
+ }
+ }
+ }
+ }
+
+ public static void shutdown()
+ {
+ usbConnection.close();
+ }
+
+ @Keep
+ public static int getFd()
+ {
+ return usbConnection.getFileDescriptor();
+ }
+
+ @Keep
+ public static boolean isUsbDeviceAvailable()
+ {
+ synchronized (hotplugCallbackLock)
+ {
+ return adapterDevice != null;
+ }
+ }
+
+ @Nullable
+ private static UsbDevice queryAdapter()
+ {
+ HashMap<String, UsbDevice> devices = manager.getDeviceList();
+ for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
+ {
+ UsbDevice dev = pair.getValue();
+ if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
+ {
+ if (manager.hasPermission(dev))
+ return dev;
+ else
+ requestPermission();
+ }
+ }
+ return null;
+ }
+
+ public static void initAdapter()
+ {
+ byte[] init = {0x13};
+ usbConnection.bulkTransfer(usbOut, init, init.length, 0);
+ }
+
+ @Keep
+ public static int input()
+ {
+ return usbConnection.bulkTransfer(usbIn, controllerPayload, controllerPayload.length, 16);
+ }
+
+ @Keep
+ public static int output(byte[] rumble)
+ {
+ return usbConnection.bulkTransfer(usbOut, rumble, 5, 16);
+ }
+
+ @Keep
+ public static boolean openAdapter()
+ {
+ UsbDevice dev;
+ synchronized (hotplugCallbackLock)
+ {
+ dev = adapterDevice;
+ }
+ if (dev == null)
+ {
+ return false;
+ }
+
+ usbConnection = manager.openDevice(dev);
+ if (usbConnection == null)
+ {
+ return false;
+ }
+
+ Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount());
+ Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount());
+
+ if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0)
+ {
+ UsbConfiguration conf = dev.getConfiguration(0);
+ usbInterface = conf.getInterface(0);
+ usbConnection.claimInterface(usbInterface, true);
+
+ Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount());
+
+ if (usbInterface.getEndpointCount() == 2)
+ {
+ for (int i = 0; i < usbInterface.getEndpointCount(); ++i)
+ if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
+ usbIn = usbInterface.getEndpoint(i);
+ else
+ usbOut = usbInterface.getEndpoint(i);
+
+ initAdapter();
+ return true;
+ }
+ else
+ {
+ usbConnection.releaseInterface(usbInterface);
+ }
+ }
+
+ Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter,
+ Toast.LENGTH_LONG).show();
+ usbConnection.close();
+ return false;
+ }
+
+ @Keep
+ public static void enableHotplugCallback()
+ {
+ synchronized (hotplugCallbackLock)
+ {
+ if (hotplugCallbackEnabled)
+ {
+ throw new IllegalStateException("enableHotplugCallback was called when already enabled");
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ filter.addAction(ACTION_GC_ADAPTER_PERMISSION_GRANTED);
+
+ ContextCompat.registerReceiver(DolphinApplication.getAppContext(), hotplugBroadcastReceiver,
+ filter, ContextCompat.RECEIVER_EXPORTED);
+
+ hotplugCallbackEnabled = true;
+
+ onUsbDevicesChanged();
+ }
+ }
+
+ @Keep
+ public static void disableHotplugCallback()
+ {
+ synchronized (hotplugCallbackLock)
+ {
+ if (hotplugCallbackEnabled)
+ {
+ DolphinApplication.getAppContext().unregisterReceiver(hotplugBroadcastReceiver);
+ hotplugCallbackEnabled = false;
+ adapterDevice = null;
+ }
+ }
+ }
+
+ public static void onUsbDevicesChanged()
+ {
+ synchronized (hotplugCallbackLock)
+ {
+ if (adapterDevice != null)
+ {
+ boolean adapterStillConnected = manager.getDeviceList().entrySet().stream()
+ .anyMatch(pair -> pair.getValue().getDeviceId() == adapterDevice.getDeviceId());
+
+ if (!adapterStillConnected)
+ {
+ adapterDevice = null;
+ onAdapterDisconnected();
+ }
+ }
+
+ if (adapterDevice == null)
+ {
+ UsbDevice newAdapter = queryAdapter();
+ if (newAdapter != null)
+ {
+ adapterDevice = newAdapter;
+ onAdapterConnected();
+ }
+ }
+ }
+ }
+
+ private static native void onAdapterConnected();
+
+ private static native void onAdapterDisconnected();
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java
deleted file mode 100644
index 3fcd58afac..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.utils;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbConstants;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbDeviceConnection;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-import android.hardware.usb.UsbManager;
-import android.os.Build;
-import android.widget.Toast;
-
-import androidx.annotation.Keep;
-
-import org.dolphinemu.dolphinemu.DolphinApplication;
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.services.USBPermService;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class Java_GCAdapter
-{
- public static UsbManager manager;
-
- @Keep
- static byte[] controller_payload = new byte[37];
-
- static UsbDeviceConnection usb_con;
- static UsbInterface usb_intf;
- static UsbEndpoint usb_in;
- static UsbEndpoint usb_out;
-
- private static void RequestPermission()
- {
- HashMap<String, UsbDevice> devices = manager.getDeviceList();
- for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
- {
- UsbDevice dev = pair.getValue();
- if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
- {
- if (!manager.hasPermission(dev))
- {
- Context context = DolphinApplication.getAppContext();
- Intent intent = new Intent(context, USBPermService.class);
-
- int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
- PendingIntent.FLAG_IMMUTABLE : 0;
- PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags);
-
- manager.requestPermission(dev, pendingIntent);
- }
- }
- }
- }
-
- public static void Shutdown()
- {
- usb_con.close();
- }
-
- @Keep
- public static int GetFD()
- {
- return usb_con.getFileDescriptor();
- }
-
- @Keep
- public static boolean QueryAdapter()
- {
- HashMap<String, UsbDevice> devices = manager.getDeviceList();
- for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
- {
- UsbDevice dev = pair.getValue();
- if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
- {
- if (manager.hasPermission(dev))
- return true;
- else
- RequestPermission();
- }
- }
- return false;
- }
-
- public static void InitAdapter()
- {
- byte[] init = {0x13};
- usb_con.bulkTransfer(usb_out, init, init.length, 0);
- }
-
- @Keep
- public static int Input()
- {
- return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16);
- }
-
- @Keep
- public static int Output(byte[] rumble)
- {
- return usb_con.bulkTransfer(usb_out, rumble, 5, 16);
- }
-
- @Keep
- public static boolean OpenAdapter()
- {
- HashMap<String, UsbDevice> devices = manager.getDeviceList();
- for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
- {
- UsbDevice dev = pair.getValue();
- if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
- {
- if (manager.hasPermission(dev))
- {
- usb_con = manager.openDevice(dev);
-
- Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount());
- Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount());
-
- if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0)
- {
- UsbConfiguration conf = dev.getConfiguration(0);
- usb_intf = conf.getInterface(0);
- usb_con.claimInterface(usb_intf, true);
-
- Log.info("GCAdapter: Number of endpoints: " + usb_intf.getEndpointCount());
-
- if (usb_intf.getEndpointCount() == 2)
- {
- for (int i = 0; i < usb_intf.getEndpointCount(); ++i)
- if (usb_intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
- usb_in = usb_intf.getEndpoint(i);
- else
- usb_out = usb_intf.getEndpoint(i);
-
- InitAdapter();
- return true;
- }
- else
- {
- usb_con.releaseInterface(usb_intf);
- }
- }
-
- Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter,
- Toast.LENGTH_LONG).show();
- usb_con.close();
- }
- }
- }
- return false;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java
index 469375614f..98a7d51900 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java
@@ -22,7 +22,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-public class Java_WiimoteAdapter
+public class WiimoteAdapter
{
final static int MAX_PAYLOAD = 23;
final static int MAX_WIIMOTES = 4;
@@ -31,14 +31,14 @@ public class Java_WiimoteAdapter
final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306;
public static UsbManager manager;
- static UsbDeviceConnection usb_con;
- static UsbInterface[] usb_intf = new UsbInterface[MAX_WIIMOTES];
- static UsbEndpoint[] usb_in = new UsbEndpoint[MAX_WIIMOTES];
+ static UsbDeviceConnection usbConnection;
+ static UsbInterface[] usbInterface = new UsbInterface[MAX_WIIMOTES];
+ static UsbEndpoint[] usbIn = new UsbEndpoint[MAX_WIIMOTES];
@Keep
- public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD];
+ public static byte[][] wiimotePayload = new byte[MAX_WIIMOTES][MAX_PAYLOAD];
- private static void RequestPermission()
+ private static void requestPermission()
{
HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
@@ -65,7 +65,7 @@ public class Java_WiimoteAdapter
}
@Keep
- public static boolean QueryAdapter()
+ public static boolean queryAdapter()
{
HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
@@ -77,20 +77,20 @@ public class Java_WiimoteAdapter
if (manager.hasPermission(dev))
return true;
else
- RequestPermission();
+ requestPermission();
}
}
return false;
}
@Keep
- public static int Input(int index)
+ public static int input(int index)
{
- return usb_con.bulkTransfer(usb_in[index], wiimote_payload[index], MAX_PAYLOAD, TIMEOUT);
+ return usbConnection.bulkTransfer(usbIn[index], wiimotePayload[index], MAX_PAYLOAD, TIMEOUT);
}
@Keep
- public static int Output(int index, byte[] buf, int size)
+ public static int output(int index, byte[] buf, int size)
{
byte report_number = buf[0];
@@ -105,7 +105,7 @@ public class Java_WiimoteAdapter
final int HID_SET_REPORT = 0x9;
final int HID_OUTPUT = (2 << 8);
- int write = usb_con.controlTransfer(
+ int write = usbConnection.controlTransfer(
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
HID_SET_REPORT,
HID_OUTPUT | report_number,
@@ -120,10 +120,10 @@ public class Java_WiimoteAdapter
}
@Keep
- public static boolean OpenAdapter()
+ public static boolean openAdapter()
{
// If the adapter is already open. Don't attempt to do it again
- if (usb_con != null && usb_con.getFileDescriptor() != -1)
+ if (usbConnection != null && usbConnection.getFileDescriptor() != -1)
return true;
HashMap<String, UsbDevice> devices = manager.getDeviceList();
@@ -135,7 +135,7 @@ public class Java_WiimoteAdapter
{
if (manager.hasPermission(dev))
{
- usb_con = manager.openDevice(dev);
+ usbConnection = manager.openDevice(dev);
UsbConfiguration conf = dev.getConfiguration(0);
Log.info("Number of configurations: " + dev.getConfigurationCount());
@@ -149,20 +149,20 @@ public class Java_WiimoteAdapter
for (int i = 0; i < MAX_WIIMOTES; ++i)
{
// One interface per Wii Remote
- usb_intf[i] = dev.getInterface(i);
- usb_con.claimInterface(usb_intf[i], true);
+ usbInterface[i] = dev.getInterface(i);
+ usbConnection.claimInterface(usbInterface[i], true);
// One endpoint per Wii Remote. Input only
// Output reports go through the control channel.
- usb_in[i] = usb_intf[i].getEndpoint(0);
- Log.info("Interface " + i + " endpoint count:" + usb_intf[i].getEndpointCount());
+ usbIn[i] = usbInterface[i].getEndpoint(0);
+ Log.info("Interface " + i + " endpoint count:" + usbInterface[i].getEndpointCount());
}
return true;
}
else
{
// XXX: Message that the device was found, but it needs to be unplugged and plugged back in?
- usb_con.close();
+ usbConnection.close();
}
}
}
diff --git a/Source/Core/Core/HW/WiimoteReal/IOAndroid.cpp b/Source/Core/Core/HW/WiimoteReal/IOAndroid.cpp
index 10234ceef7..507cacf006 100644
--- a/Source/Core/Core/HW/WiimoteReal/IOAndroid.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/IOAndroid.cpp
@@ -30,8 +30,8 @@ void WiimoteScannerAndroid::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
JNIEnv* env = IDCache::GetEnvForThread();
- jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()Z");
- jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
+ jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "openAdapter", "()Z");
+ jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "queryAdapter", "()Z");
if (env->CallStaticBooleanMethod(s_adapter_class, queryadapter_func) &&
env->CallStaticBooleanMethod(s_adapter_class, openadapter_func))
@@ -55,15 +55,15 @@ bool WiimoteAndroid::ConnectInternal()
{
m_env = IDCache::GetEnvForThread();
- jfieldID payload_field = m_env->GetStaticFieldID(s_adapter_class, "wiimote_payload", "[[B");
+ jfieldID payload_field = m_env->GetStaticFieldID(s_adapter_class, "wiimotePayload", "[[B");
jobjectArray payload_object =
reinterpret_cast<jobjectArray>(m_env->GetStaticObjectField(s_adapter_class, payload_field));
m_java_wiimote_payload =
(jbyteArray)m_env->GetObjectArrayElement(payload_object, m_mayflash_index);
// Get function pointers
- m_input_func = m_env->GetStaticMethodID(s_adapter_class, "Input", "(I)I");
- m_output_func = m_env->GetStaticMethodID(s_adapter_class, "Output", "(I[BI)I");
+ m_input_func = m_env->GetStaticMethodID(s_adapter_class, "input", "(I)I");
+ m_output_func = m_env->GetStaticMethodID(s_adapter_class, "output", "(I[BI)I");
is_connected = true;
@@ -110,7 +110,7 @@ int WiimoteAndroid::IOWrite(u8 const* buf, size_t len)
void InitAdapterClass()
{
JNIEnv* env = IDCache::GetEnvForThread();
- jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter");
+ jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/WiimoteAdapter");
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
}
} // namespace WiimoteReal
diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp
index 5cfe513935..df212e8f4e 100644
--- a/Source/Core/InputCommon/GCAdapter.cpp
+++ b/Source/Core/InputCommon/GCAdapter.cpp
@@ -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
{