[Android] Implement support for real Wiimotes with the DolphinBar

This is the only way to get Wiimotes working under Android now.
This, just like the Wii U Gamecube Controller Adapter, completely goes around Android's limitations and talks with the device directly through USBManager.

Couple notes.
Continuous scanning must be enabled otherwise the Wiimotes won't be seen.
The UI doesn't expose support for this yet. One must change the Wiimote source and continuous scanning settings manually.

Testing up to two wiimotes in Taiko No Tatsujin, no reason to believe all four won't work.
This commit is contained in:
Ryan Houdek committed 2016-02-07 05:31:48 -06:00
1 parent 2282651fdb
commit fe53461611
9 files changed
+351

No files matched your search

+2
View File
@@ -256,6 +256,8 @@ elseif(UNIX)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND BLUEZ_FOUND)
set(SRCS ${SRCS} HW/WiimoteReal/IONix.cpp)
set(LIBS ${LIBS} bluetooth)
elseif(ANDROID)
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
else()
set(SRCS ${SRCS} HW/WiimoteReal/IODummy.cpp)
endif()
@@ -0,0 +1,165 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <jni.h>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Flag.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Common/Timer.h"
#include "Common/Logging/Log.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
// Global java_vm class
extern JavaVM* g_java_vm;
namespace WiimoteReal
{
// Java classes
static jclass s_adapter_class;
class WiimoteAndroid final : public Wiimote
{
public:
WiimoteAndroid(int index);
~WiimoteAndroid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() {}
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
int m_mayflash_index;
bool is_connected = true;
JNIEnv* m_env;
jmethodID m_input_func;
jmethodID m_output_func;
jbyteArray m_java_wiimote_payload;
};
WiimoteScanner::WiimoteScanner()
{}
WiimoteScanner::~WiimoteScanner()
{}
void WiimoteScanner::Update()
{}
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
{
found_wiimotes.clear();
found_board = nullptr;
NOTICE_LOG(WIIMOTE, "Finding Wiimotes");
JNIEnv* env;
int get_env_status = g_java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (get_env_status == JNI_EDETACHED)
g_java_vm->AttachCurrentThread(&env, nullptr);
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))
{
for (int i = 0; i < MAX_WIIMOTES; ++i)
found_wiimotes.emplace_back(new WiimoteAndroid(i));
}
if (get_env_status == JNI_EDETACHED)
g_java_vm->DetachCurrentThread();
}
bool WiimoteScanner::IsReady() const
{
return true;
}
WiimoteAndroid::WiimoteAndroid(int index)
: Wiimote(), m_mayflash_index(index)
{
}
WiimoteAndroid::~WiimoteAndroid()
{
Shutdown();
}
// Connect to a Wiimote with a known address.
bool WiimoteAndroid::ConnectInternal()
{
g_java_vm->AttachCurrentThread(&m_env, nullptr);
jfieldID payload_field = m_env->GetStaticFieldID(s_adapter_class, "wiimote_payload", "[[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");
is_connected = true;
return true;
}
void WiimoteAndroid::DisconnectInternal()
{
g_java_vm->DetachCurrentThread();
}
bool WiimoteAndroid::IsConnected() const
{
return is_connected;
}
// positive = read packet
// negative = didn't read packet
// zero = error
int WiimoteAndroid::IORead(u8* buf)
{
int read_size = m_env->CallStaticIntMethod(s_adapter_class, m_input_func, m_mayflash_index);
jbyte* java_data = m_env->GetByteArrayElements(m_java_wiimote_payload, nullptr);
memcpy(buf + 1, java_data, std::min(MAX_PAYLOAD - 1, read_size));
buf[0] = 0xA1;
m_env->ReleaseByteArrayElements(m_java_wiimote_payload, java_data, 0);
return read_size <= 0 ? read_size : read_size + 1;
}
int WiimoteAndroid::IOWrite(u8 const* buf, size_t len)
{
jbyteArray output_array = m_env->NewByteArray(len);
jbyte* output = m_env->GetByteArrayElements(output_array, nullptr);
memcpy(output, buf, len);
m_env->ReleaseByteArrayElements(output_array, output, 0);
int written = m_env->CallStaticIntMethod(s_adapter_class, m_output_func, m_mayflash_index, output_array, len);
m_env->DeleteLocalRef(output_array);
return written;
}
void InitAdapterClass()
{
JNIEnv* env;
g_java_vm->AttachCurrentThread(&env, nullptr);
jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter");
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
}
}
@@ -161,4 +161,8 @@ void ChangeWiimoteSource(unsigned int index, int source);
bool IsValidBluetoothName(const std::string& name);
bool IsBalanceBoardName(const std::string& name);
#ifdef ANDROID
void InitAdapterClass();
#endif
} // WiimoteReal