From 371e9f4fae0cd60004d7cfd13cc832bc18808de2 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 31 Dec 2015 10:27:51 -0600 Subject: Move SI_GCAdapter over to InputCommon --- Source/Core/InputCommon/CMakeLists.txt | 6 +- Source/Core/InputCommon/GCAdapter.cpp | 461 +++++++++++++++++++++ Source/Core/InputCommon/GCAdapter.h | 27 ++ Source/Core/InputCommon/InputCommon.vcxproj | 10 +- .../Core/InputCommon/InputCommon.vcxproj.filters | 4 +- 5 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 Source/Core/InputCommon/GCAdapter.cpp create mode 100644 Source/Core/InputCommon/GCAdapter.h (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index b200625000..254dffce10 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -3,8 +3,6 @@ set(SRCS ControllerEmu.cpp ControllerInterface/ControllerInterface.cpp ControllerInterface/Device.cpp ControllerInterface/ExpressionParser.cpp) - - set(LIBS common) if(WIN32) @@ -37,6 +35,10 @@ elseif(ANDROID) ControllerInterface/Android/Android.cpp) endif() +if(LIBUSB_FOUND) + set(SRCS ${SRCS} GCAdapter.cpp) +endif(LIBUSB_FOUND) + if(LIBEVDEV_FOUND AND LIBUDEV_FOUND) set(SRCS ${SRCS} ControllerInterface/evdev/evdev.cpp) set(LIBS ${LIBS} ${LIBEVDEV_LIBRARY} ${LIBUDEV_LIBRARY}) diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp new file mode 100644 index 0000000000..b6b12bcfdb --- /dev/null +++ b/Source/Core/InputCommon/GCAdapter.cpp @@ -0,0 +1,461 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Flag.h" +#include "Common/Thread.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/HW/SI.h" +#include "Core/HW/SI_GCAdapter.h" +#include "Core/HW/SystemTimers.h" +#include "InputCommon/GCPadStatus.h" + +namespace SI_GCAdapter +{ +enum ControllerTypes +{ + CONTROLLER_NONE = 0, + CONTROLLER_WIRED = 1, + CONTROLLER_WIRELESS = 2 +}; + +static bool CheckDeviceAccess(libusb_device* device); +static void AddGCAdapter(libusb_device* device); + +static bool s_detected = false; +static libusb_device_handle* s_handle = nullptr; +static u8 s_controller_type[MAX_SI_CHANNELS] = { CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE }; +static u8 s_controller_rumble[4]; + +static std::mutex s_mutex; +static u8 s_controller_payload[37]; +static u8 s_controller_payload_swap[37]; + +static int s_controller_payload_size = 0; + +static std::thread s_adapter_thread; +static Common::Flag s_adapter_thread_running; + +static std::thread s_adapter_detect_thread; +static Common::Flag s_adapter_detect_thread_running; + +static std::function s_detect_callback; + +static bool s_libusb_driver_not_supported = false; +static libusb_context* s_libusb_context = nullptr; +static bool s_libusb_hotplug_enabled = false; +#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102 +static libusb_hotplug_callback_handle s_hotplug_handle; +#endif + +static u8 s_endpoint_in = 0; +static u8 s_endpoint_out = 0; + +static u64 s_last_init = 0; + +static void Read() +{ + while (s_adapter_thread_running.IsSet()) + { + libusb_interrupt_transfer(s_handle, s_endpoint_in, s_controller_payload_swap, sizeof(s_controller_payload_swap), &s_controller_payload_size, 16); + + { + std::lock_guard lk(s_mutex); + std::swap(s_controller_payload_swap, s_controller_payload); + } + + Common::YieldCPU(); + } +} + +#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102 +static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotplug_event event, void* user_data) +{ + if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) + { + if (s_handle == nullptr && CheckDeviceAccess(dev)) + AddGCAdapter(dev); + } + else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + { + if (s_handle != nullptr && libusb_get_device(s_handle) == dev) + Reset(); + } + return 0; +} +#endif + +static void ScanThreadFunc() +{ + Common::SetCurrentThreadName("GC Adapter Scanning Thread"); + NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started"); + +#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102 + s_libusb_hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0; + if (s_libusb_hotplug_enabled) + { + if (libusb_hotplug_register_callback(s_libusb_context, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback, nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS) + s_libusb_hotplug_enabled = false; + if (s_libusb_hotplug_enabled) + NOTICE_LOG(SERIALINTERFACE, "Using libUSB hotplug detection"); + } +#endif + + while (s_adapter_detect_thread_running.IsSet()) + { + if (s_libusb_hotplug_enabled) + { + static timeval tv = {0, 500000}; + libusb_handle_events_timeout(s_libusb_context, &tv); + } + else + { + if (s_handle == nullptr) + { + Setup(); + if (s_detected && s_detect_callback != nullptr) + s_detect_callback(); + } + Common::SleepCurrentThread(500); + } + } + NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped"); +} + +void SetAdapterCallback(std::function func) +{ + s_detect_callback = func; +} + +void Init() +{ + if (s_handle != nullptr) + return; + + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond()) + return; + + s_last_init = CoreTiming::GetTicks(); + } + + s_libusb_driver_not_supported = false; + + int ret = libusb_init(&s_libusb_context); + + if (ret) + { + ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret); + s_libusb_driver_not_supported = true; + Shutdown(); + } + else + { + if (SConfig::GetInstance().m_GameCubeAdapter) + StartScanThread(); + } +} + +void StartScanThread() +{ + s_adapter_detect_thread_running.Set(true); + s_adapter_detect_thread = std::thread(ScanThreadFunc); +} + +void StopScanThread() +{ + if (s_adapter_detect_thread_running.TestAndClear()) + { + s_adapter_detect_thread.join(); + } +} + +void Setup() +{ + libusb_device** list; + ssize_t cnt = libusb_get_device_list(s_libusb_context, &list); + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + { + s_controller_type[i] = CONTROLLER_NONE; + s_controller_rumble[i] = 0; + } + + for (int d = 0; d < cnt; d++) + { + libusb_device* device = list[d]; + if (CheckDeviceAccess(device)) + AddGCAdapter(device); + } + + libusb_free_device_list(list, 1); +} + +static bool CheckDeviceAccess(libusb_device* device) +{ + int ret; + libusb_device_descriptor desc; + int dRet = libusb_get_device_descriptor(device, &desc); + if (dRet) + { + // could not acquire the descriptor, no point in trying to use it. + ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet); + return false; + } + + if (desc.idVendor == 0x057e && desc.idProduct == 0x0337) + { + NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", desc.idVendor, desc.idProduct, 1); + + u8 bus = libusb_get_bus_number(device); + u8 port = libusb_get_device_address(device); + ret = libusb_open(device, &s_handle); + if (ret) + { + if (ret == LIBUSB_ERROR_ACCESS) + { + if (dRet) + { + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID ????:???? (couldn't get id).", + bus, + port + ); + } + else + { + ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", + bus, + port, + desc.idVendor, + desc.idProduct + ); + } + } + else + { + ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) + s_libusb_driver_not_supported = true; + } + return false; + } + else if ((ret = libusb_kernel_driver_active(s_handle, 0)) == 1) + { + if ((ret = libusb_detach_kernel_driver(s_handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) + { + ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret); + } + } + // this split is needed so that we don't avoid claiming the interface when + // detaching the kernel driver is successful + if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) + { + return false; + } + else if ((ret = libusb_claim_interface(s_handle, 0))) + { + ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret); + } + else + { + return true; + } + } + return false; +} + +static void AddGCAdapter(libusb_device* device) +{ + libusb_config_descriptor *config = nullptr; + libusb_get_config_descriptor(device, 0, &config); + for (u8 ic = 0; ic < config->bNumInterfaces; ic++) + { + const libusb_interface *interfaceContainer = &config->interface[ic]; + for (int i = 0; i < interfaceContainer->num_altsetting; i++) + { + const libusb_interface_descriptor *interface = &interfaceContainer->altsetting[i]; + for (u8 e = 0; e < interface->bNumEndpoints; e++) + { + const libusb_endpoint_descriptor *endpoint = &interface->endpoint[e]; + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) + s_endpoint_in = endpoint->bEndpointAddress; + else + s_endpoint_out = endpoint->bEndpointAddress; + } + } + } + + int tmp = 0; + unsigned char payload = 0x13; + libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 16); + + s_adapter_thread_running.Set(true); + s_adapter_thread = std::thread(Read); + + s_detected = true; + if (s_detect_callback != nullptr) + s_detect_callback(); + ResetRumble(); +} + +void Shutdown() +{ + StopScanThread(); +#if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102 + if (s_libusb_hotplug_enabled) + libusb_hotplug_deregister_callback(s_libusb_context, s_hotplug_handle); +#endif + Reset(); + + if (s_libusb_context) + { + libusb_exit(s_libusb_context); + s_libusb_context = nullptr; + } + + s_libusb_driver_not_supported = false; +} + +void Reset() +{ + if (!s_detected) + return; + + if (s_adapter_thread_running.TestAndClear()) + { + s_adapter_thread.join(); + } + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + s_controller_type[i] = CONTROLLER_NONE; + + s_detected = false; + + if (s_handle) + { + libusb_release_interface(s_handle, 0); + libusb_close(s_handle); + s_handle = nullptr; + } + if (s_detect_callback != nullptr) + s_detect_callback(); + NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached"); +} + +void Input(int chan, GCPadStatus* pad) +{ + if (!SConfig::GetInstance().m_GameCubeAdapter) + return; + + if (s_handle == nullptr || !s_detected) + return; + + u8 controller_payload_copy[37]; + + { + std::lock_guard lk(s_mutex); + std::copy(std::begin(s_controller_payload), std::end(s_controller_payload), std::begin(controller_payload_copy)); + } + + if (s_controller_payload_size != sizeof(controller_payload_copy) || controller_payload_copy[0] != LIBUSB_DT_HID) + { + INFO_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]); + Reset(); + } + else + { + u8 type = controller_payload_copy[1 + (9 * chan)] >> 4; + if (type != CONTROLLER_NONE && s_controller_type[chan] == CONTROLLER_NONE) + NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload_copy[1 + (9 * chan)]); + + s_controller_type[chan] = type; + + if (s_controller_type[chan] != CONTROLLER_NONE) + { + memset(pad, 0, sizeof(*pad)); + u8 b1 = controller_payload_copy[1 + (9 * chan) + 1]; + u8 b2 = controller_payload_copy[1 + (9 * chan) + 2]; + + if (b1 & (1 << 0)) pad->button |= PAD_BUTTON_A; + if (b1 & (1 << 1)) pad->button |= PAD_BUTTON_B; + if (b1 & (1 << 2)) pad->button |= PAD_BUTTON_X; + if (b1 & (1 << 3)) pad->button |= PAD_BUTTON_Y; + + if (b1 & (1 << 4)) pad->button |= PAD_BUTTON_LEFT; + if (b1 & (1 << 5)) pad->button |= PAD_BUTTON_RIGHT; + if (b1 & (1 << 6)) pad->button |= PAD_BUTTON_DOWN; + if (b1 & (1 << 7)) pad->button |= PAD_BUTTON_UP; + + if (b2 & (1 << 0)) pad->button |= PAD_BUTTON_START; + if (b2 & (1 << 1)) pad->button |= PAD_TRIGGER_Z; + if (b2 & (1 << 2)) pad->button |= PAD_TRIGGER_R; + if (b2 & (1 << 3)) pad->button |= PAD_TRIGGER_L; + + pad->stickX = controller_payload_copy[1 + (9 * chan) + 3]; + pad->stickY = controller_payload_copy[1 + (9 * chan) + 4]; + pad->substickX = controller_payload_copy[1 + (9 * chan) + 5]; + pad->substickY = controller_payload_copy[1 + (9 * chan) + 6]; + pad->triggerLeft = controller_payload_copy[1 + (9 * chan) + 7]; + pad->triggerRight = controller_payload_copy[1 + (9 * chan) + 8]; + } + } +} + +void ResetRumble() +{ + if (!SConfig::GetInstance().m_GameCubeAdapter) + return; + if (s_handle == nullptr || !s_detected) + return; + + std::fill(std::begin(s_controller_rumble), std::end(s_controller_rumble), 0); + + unsigned char rumble[5] = {0x11, s_controller_rumble[0], s_controller_rumble[1], s_controller_rumble[2], s_controller_rumble[3]}; + + int size = 0; + libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 16); + + DEBUG_LOG(SERIALINTERFACE, "Rumble state reset"); +} + +void Output(int chan, u8 rumble_command) +{ + if (s_handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter || !SConfig::GetInstance().m_AdapterRumble) + return; + + // Skip over rumble commands if it has not changed or the controller is wireless + if (rumble_command != s_controller_rumble[chan] && s_controller_type[chan] != CONTROLLER_WIRELESS) + { + s_controller_rumble[chan] = rumble_command; + + unsigned char rumble[5] = { 0x11, s_controller_rumble[0], s_controller_rumble[1], s_controller_rumble[2], s_controller_rumble[3] }; + int size = 0; + + libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 16); + // Netplay sends invalid data which results in size = 0x00. Ignore it. + if (size != 0x05 && size != 0x00) + { + INFO_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size); + Reset(); + } + } +} + +bool IsDetected() +{ + return s_detected; +} + +bool IsDriverDetected() +{ + return !s_libusb_driver_not_supported; +} + +} // end of namespace SI_GCAdapter diff --git a/Source/Core/InputCommon/GCAdapter.h b/Source/Core/InputCommon/GCAdapter.h new file mode 100644 index 0000000000..13deb8b536 --- /dev/null +++ b/Source/Core/InputCommon/GCAdapter.h @@ -0,0 +1,27 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +struct GCPadStatus; + +namespace SI_GCAdapter +{ + +void Init(); +void Reset(); +void ResetRumble(); +void Setup(); +void Shutdown(); +void SetAdapterCallback(std::function func); +void StartScanThread(); +void StopScanThread(); +void Input(int chan, GCPadStatus* pad); +void Output(int chan, u8 rumble_command); +bool IsDetected(); +bool IsDriverDetected(); + +} // end of namespace SI_GCAdapter diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index 347c06e770..3e63243949 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -52,6 +52,13 @@ + + + 4200;%(DisableSpecificWarnings) + @@ -66,6 +73,7 @@ + @@ -80,4 +88,4 @@ - \ No newline at end of file + diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index 3c16ad5f26..6a63852303 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -16,6 +16,7 @@ + ControllerInterface\DInput @@ -47,6 +48,7 @@ + @@ -83,4 +85,4 @@ - \ No newline at end of file + -- cgit v1.2.3 From 6e932af8312650a7373630742776979440bebb9c Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 31 Dec 2015 10:33:48 -0600 Subject: Add two missing values that can fit in GCPadStatus --- Source/Core/InputCommon/GCPadStatus.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/GCPadStatus.h b/Source/Core/InputCommon/GCPadStatus.h index d6bb972de1..deecb30523 100644 --- a/Source/Core/InputCommon/GCPadStatus.h +++ b/Source/Core/InputCommon/GCPadStatus.h @@ -17,6 +17,8 @@ enum PadError enum { PAD_USE_ORIGIN = 0x0080, + PAD_GET_ORIGIN = 0x2000, + PAD_ERR_STATUS = 0x8000, }; enum PadButton -- cgit v1.2.3 From 49410576e945ba7bf762b83a474005f1babc5b54 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 31 Dec 2015 11:09:47 -0600 Subject: Make the Wii U Gamecube adapter work with less magic. The Wii U Gamecube controller adapter setup has always been a bit weird. It tries to be as automatic as possible to make the user experience as easy as possible. The problem with this approach is that it brings a large disconnect in the user experience because you have the Gamecube controller setup with regular gamepads and then for some reason below that you have a "direct connect" option which will cause the Gamecube Adapter to overwrite the regular inputs if something was connected. While this works and allows the user to only click one checkbox to get the device working, it breaks the user's experience because they don't really know what "direct connect" means and won't look it up to figure out what it is. Just expecting the device to work (At least one occurence of this in the IRC channel in the last week). This way around also had the terrible nature of making the code more filthy than it needed to be. The GCAdapter namespace was parasitic and hooked in to the regular GC Controller SI class to overwrite the data that it was getting from the default configuration. Now instead we have a specific SIDevice class for the Wii U Gamecube adapter. This class is fairly simple and is a child of the regular SI Gamecube Pad device and only reimplements what it needs to. This also gives the ability to configure controllers individually, which allows the user to configure rumble individually per pad input. Overall the code is cleaner, and it fits more in line with how the rest of Dolphin works. --- Source/Core/InputCommon/GCAdapter.cpp | 48 +++++++++++++++++++++++++++++------ Source/Core/InputCommon/GCAdapter.h | 6 +++-- 2 files changed, 44 insertions(+), 10 deletions(-) (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp index b6b12bcfdb..d357952f4f 100644 --- a/Source/Core/InputCommon/GCAdapter.cpp +++ b/Source/Core/InputCommon/GCAdapter.cpp @@ -8,15 +8,17 @@ #include "Common/Flag.h" #include "Common/Thread.h" +#include "Common/Logging/Log.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/HW/SI.h" -#include "Core/HW/SI_GCAdapter.h" #include "Core/HW/SystemTimers.h" + +#include "InputCommon/GCAdapter.h" #include "InputCommon/GCPadStatus.h" -namespace SI_GCAdapter +namespace GCAdapter { enum ControllerTypes { @@ -158,13 +160,16 @@ void Init() } else { - if (SConfig::GetInstance().m_GameCubeAdapter) + if (UseAdapter()) StartScanThread(); } } void StartScanThread() { + if (s_adapter_detect_thread_running.IsSet()) + return; + s_adapter_detect_thread_running.Set(true); s_adapter_detect_thread = std::thread(ScanThreadFunc); } @@ -351,7 +356,11 @@ void Reset() void Input(int chan, GCPadStatus* pad) { - if (!SConfig::GetInstance().m_GameCubeAdapter) + bool use_adapter = SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[1] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[2] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[3] == SIDEVICE_WIIU_ADAPTER; + if (!use_adapter) return; if (s_handle == nullptr || !s_detected) @@ -371,15 +380,19 @@ void Input(int chan, GCPadStatus* pad) } else { + bool get_origin = false; u8 type = controller_payload_copy[1 + (9 * chan)] >> 4; if (type != CONTROLLER_NONE && s_controller_type[chan] == CONTROLLER_NONE) + { NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload_copy[1 + (9 * chan)]); + get_origin = true; + } s_controller_type[chan] = type; + memset(pad, 0, sizeof(*pad)); if (s_controller_type[chan] != CONTROLLER_NONE) { - memset(pad, 0, sizeof(*pad)); u8 b1 = controller_payload_copy[1 + (9 * chan) + 1]; u8 b2 = controller_payload_copy[1 + (9 * chan) + 2]; @@ -398,6 +411,8 @@ void Input(int chan, GCPadStatus* pad) if (b2 & (1 << 2)) pad->button |= PAD_TRIGGER_R; if (b2 & (1 << 3)) pad->button |= PAD_TRIGGER_L; + if (get_origin) pad->button |= PAD_GET_ORIGIN; + pad->stickX = controller_payload_copy[1 + (9 * chan) + 3]; pad->stickY = controller_payload_copy[1 + (9 * chan) + 4]; pad->substickX = controller_payload_copy[1 + (9 * chan) + 5]; @@ -405,12 +420,29 @@ void Input(int chan, GCPadStatus* pad) pad->triggerLeft = controller_payload_copy[1 + (9 * chan) + 7]; pad->triggerRight = controller_payload_copy[1 + (9 * chan) + 8]; } + else + { + pad->button = PAD_ERR_STATUS; + } } } +bool DeviceConnected(int chan) +{ + return s_controller_type[chan] != CONTROLLER_NONE; +} + +bool UseAdapter() +{ + return SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[1] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[2] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[3] == SIDEVICE_WIIU_ADAPTER; +} + void ResetRumble() { - if (!SConfig::GetInstance().m_GameCubeAdapter) + if (!UseAdapter()) return; if (s_handle == nullptr || !s_detected) return; @@ -427,7 +459,7 @@ void ResetRumble() void Output(int chan, u8 rumble_command) { - if (s_handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter || !SConfig::GetInstance().m_AdapterRumble) + if (s_handle == nullptr || !UseAdapter() || !SConfig::GetInstance().m_AdapterRumble) return; // Skip over rumble commands if it has not changed or the controller is wireless @@ -458,4 +490,4 @@ bool IsDriverDetected() return !s_libusb_driver_not_supported; } -} // end of namespace SI_GCAdapter +} // end of namespace GCAdapter diff --git a/Source/Core/InputCommon/GCAdapter.h b/Source/Core/InputCommon/GCAdapter.h index 13deb8b536..c8091291ff 100644 --- a/Source/Core/InputCommon/GCAdapter.h +++ b/Source/Core/InputCommon/GCAdapter.h @@ -8,7 +8,7 @@ struct GCPadStatus; -namespace SI_GCAdapter +namespace GCAdapter { void Init(); @@ -23,5 +23,7 @@ void Input(int chan, GCPadStatus* pad); void Output(int chan, u8 rumble_command); bool IsDetected(); bool IsDriverDetected(); +bool DeviceConnected(int chan); +bool UseAdapter(); -} // end of namespace SI_GCAdapter +} // end of namespace GCAdapter -- cgit v1.2.3 From af6a5ddafe69a7ba36080cc164318f56a2ebc7f6 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 6 Jan 2016 00:35:56 -0600 Subject: Fixup typo in GCAdapter --- Source/Core/InputCommon/GCAdapter.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp index d357952f4f..c3aea3dc4f 100644 --- a/Source/Core/InputCommon/GCAdapter.cpp +++ b/Source/Core/InputCommon/GCAdapter.cpp @@ -356,11 +356,7 @@ void Reset() void Input(int chan, GCPadStatus* pad) { - bool use_adapter = SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_WIIU_ADAPTER || - SConfig::GetInstance().m_SIDevice[1] == SIDEVICE_WIIU_ADAPTER || - SConfig::GetInstance().m_SIDevice[2] == SIDEVICE_WIIU_ADAPTER || - SConfig::GetInstance().m_SIDevice[3] == SIDEVICE_WIIU_ADAPTER; - if (!use_adapter) + if (!UseAdapter()) return; if (s_handle == nullptr || !s_detected) @@ -459,7 +455,7 @@ void ResetRumble() void Output(int chan, u8 rumble_command) { - if (s_handle == nullptr || !UseAdapter() || !SConfig::GetInstance().m_AdapterRumble) + if (s_handle == nullptr || !UseAdapter() || !SConfig::GetInstance().m_AdapterRumble[chan]) return; // Skip over rumble commands if it has not changed or the controller is wireless -- cgit v1.2.3 From 6e503bebc43e17418d88261568a15cfc75757a17 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 6 Jan 2016 00:57:08 -0600 Subject: Add a stubbed GCAdapter namespace. This cleans up a bunch of #ifdef checks in places. --- Source/Core/InputCommon/CMakeLists.txt | 2 ++ Source/Core/InputCommon/GCAdapter.cpp | 21 +++++++-------------- Source/Core/InputCommon/GCAdapter.h | 7 ++++++- Source/Core/InputCommon/GCAdapter_Null.cpp | 23 +++++++++++++++++++++++ 4 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 Source/Core/InputCommon/GCAdapter_Null.cpp (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 254dffce10..57484db14b 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -37,6 +37,8 @@ endif() if(LIBUSB_FOUND) set(SRCS ${SRCS} GCAdapter.cpp) +else() + set(SRCS ${SRCS} GCAdapter_Null.cpp) endif(LIBUSB_FOUND) if(LIBEVDEV_FOUND AND LIBUDEV_FOUND) diff --git a/Source/Core/InputCommon/GCAdapter.cpp b/Source/Core/InputCommon/GCAdapter.cpp index c3aea3dc4f..b5dc395b49 100644 --- a/Source/Core/InputCommon/GCAdapter.cpp +++ b/Source/Core/InputCommon/GCAdapter.cpp @@ -20,19 +20,12 @@ namespace GCAdapter { -enum ControllerTypes -{ - CONTROLLER_NONE = 0, - CONTROLLER_WIRED = 1, - CONTROLLER_WIRELESS = 2 -}; - static bool CheckDeviceAccess(libusb_device* device); static void AddGCAdapter(libusb_device* device); static bool s_detected = false; static libusb_device_handle* s_handle = nullptr; -static u8 s_controller_type[MAX_SI_CHANNELS] = { CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE }; +static u8 s_controller_type[MAX_SI_CHANNELS] = { ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE }; static u8 s_controller_rumble[4]; static std::mutex s_mutex; @@ -189,7 +182,7 @@ void Setup() for (int i = 0; i < MAX_SI_CHANNELS; i++) { - s_controller_type[i] = CONTROLLER_NONE; + s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; s_controller_rumble[i] = 0; } @@ -339,7 +332,7 @@ void Reset() } for (int i = 0; i < MAX_SI_CHANNELS; i++) - s_controller_type[i] = CONTROLLER_NONE; + s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; s_detected = false; @@ -378,7 +371,7 @@ void Input(int chan, GCPadStatus* pad) { bool get_origin = false; u8 type = controller_payload_copy[1 + (9 * chan)] >> 4; - if (type != CONTROLLER_NONE && s_controller_type[chan] == CONTROLLER_NONE) + if (type != ControllerTypes::CONTROLLER_NONE && s_controller_type[chan] == ControllerTypes::CONTROLLER_NONE) { NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload_copy[1 + (9 * chan)]); get_origin = true; @@ -387,7 +380,7 @@ void Input(int chan, GCPadStatus* pad) s_controller_type[chan] = type; memset(pad, 0, sizeof(*pad)); - if (s_controller_type[chan] != CONTROLLER_NONE) + if (s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE) { u8 b1 = controller_payload_copy[1 + (9 * chan) + 1]; u8 b2 = controller_payload_copy[1 + (9 * chan) + 2]; @@ -425,7 +418,7 @@ void Input(int chan, GCPadStatus* pad) bool DeviceConnected(int chan) { - return s_controller_type[chan] != CONTROLLER_NONE; + return s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE; } bool UseAdapter() @@ -459,7 +452,7 @@ void Output(int chan, u8 rumble_command) return; // Skip over rumble commands if it has not changed or the controller is wireless - if (rumble_command != s_controller_rumble[chan] && s_controller_type[chan] != CONTROLLER_WIRELESS) + if (rumble_command != s_controller_rumble[chan] && s_controller_type[chan] != ControllerTypes::CONTROLLER_WIRELESS) { s_controller_rumble[chan] = rumble_command; diff --git a/Source/Core/InputCommon/GCAdapter.h b/Source/Core/InputCommon/GCAdapter.h index c8091291ff..5af8f7b915 100644 --- a/Source/Core/InputCommon/GCAdapter.h +++ b/Source/Core/InputCommon/GCAdapter.h @@ -10,7 +10,12 @@ struct GCPadStatus; namespace GCAdapter { - +enum ControllerTypes +{ + CONTROLLER_NONE = 0, + CONTROLLER_WIRED = 1, + CONTROLLER_WIRELESS = 2 +}; void Init(); void Reset(); void ResetRumble(); diff --git a/Source/Core/InputCommon/GCAdapter_Null.cpp b/Source/Core/InputCommon/GCAdapter_Null.cpp new file mode 100644 index 0000000000..0936eb33f1 --- /dev/null +++ b/Source/Core/InputCommon/GCAdapter_Null.cpp @@ -0,0 +1,23 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "InputCommon/GCAdapter.h" +namespace GCAdapter +{ +void Init() {} +void Reset() {} +void ResetRumble() {} +void Setup() {} +void Shutdown() {} +void SetAdapterCallback(std::function func) {} +void StartScanThread() {} +void StopScanThread() {} +void Input(int chan, GCPadStatus* pad) {} +void Output(int chan, u8 rumble_command) {} +bool IsDetected() { return false; } +bool IsDriverDetected() { return false; } +bool DeviceConnected(int chan) { return false; } +bool UseAdapter() { return false; } + +} // end of namespace GCAdapter -- cgit v1.2.3 From e62503c873833b33c63585c6f0810b824e5c6a6b Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Wed, 6 Jan 2016 01:00:02 -0600 Subject: [Android] Add support for the Wii U Gamecube adapter under Android. No way to properly enable it from an end user perspective yet. Doesn't require root. This same sort of system can be used for the Dolphinbar in the future for real wiimote support. --- Source/Core/InputCommon/CMakeLists.txt | 6 +- Source/Core/InputCommon/GCAdapter_Android.cpp | 353 ++++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 Source/Core/InputCommon/GCAdapter_Android.cpp (limited to 'Source/Core/InputCommon') diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 57484db14b..01eef4532c 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -31,12 +31,14 @@ elseif(X11_FOUND) endif() set(LIBS ${LIBS} ${X11_LIBRARIES} ${XINPUT2_LIBRARIES}) elseif(ANDROID) - set(SRCS ${SRCS} - ControllerInterface/Android/Android.cpp) + set(SRCS ${SRCS} + ControllerInterface/Android/Android.cpp) endif() if(LIBUSB_FOUND) set(SRCS ${SRCS} GCAdapter.cpp) +elseif(ANDROID) + set(SRCS ${SRCS} GCAdapter_Android.cpp) else() set(SRCS ${SRCS} GCAdapter_Null.cpp) endif(LIBUSB_FOUND) diff --git a/Source/Core/InputCommon/GCAdapter_Android.cpp b/Source/Core/InputCommon/GCAdapter_Android.cpp new file mode 100644 index 0000000000..6e638e2a72 --- /dev/null +++ b/Source/Core/InputCommon/GCAdapter_Android.cpp @@ -0,0 +1,353 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Event.h" +#include "Common/Flag.h" +#include "Common/Thread.h" +#include "Common/Logging/Log.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/HW/SI.h" +#include "Core/HW/SystemTimers.h" + +#include "InputCommon/GCAdapter.h" +#include "InputCommon/GCPadStatus.h" + +// Global java_vm class +extern JavaVM* g_java_vm; + +namespace GCAdapter +{ +// Java classes +static jclass s_adapter_class; + +static bool s_detected = false; +static int s_fd = 0; +static u8 s_controller_type[MAX_SI_CHANNELS] = { ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE }; +static u8 s_controller_rumble[4]; + +// Input handling +static std::mutex s_read_mutex; +static u8 s_controller_payload[37]; +static int s_controller_payload_size = 0; + +// Output handling +static std::mutex s_write_mutex; +static u8 s_controller_write_payload[5]; + +// Adapter running thread +static std::thread s_read_adapter_thread; +static Common::Flag s_read_adapter_thread_running; + +static std::thread s_write_adapter_thread; +static Common::Flag s_write_adapter_thread_running; +static Common::Event s_write_happened; + +// Adapter scanning thread +static std::thread s_adapter_detect_thread; +static Common::Flag s_adapter_detect_thread_running; + +static u64 s_last_init = 0; + +static void ScanThreadFunc() +{ + Common::SetCurrentThreadName("GC Adapter Scanning Thread"); + NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started"); + + JNIEnv* env; + g_java_vm->AttachCurrentThread(&env, NULL); + + jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z"); + + while (s_adapter_detect_thread_running.IsSet()) + { + if (!s_detected && UseAdapter() && + env->CallStaticBooleanMethod(s_adapter_class, queryadapter_func)) + Setup(); + Common::SleepCurrentThread(1000); + } + g_java_vm->DetachCurrentThread(); + + NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped"); +} + +static void Read() +{ + Common::SetCurrentThreadName("GC Adapter Read Thread"); + NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread started"); + + bool first_read = true; + JNIEnv* env; + g_java_vm->AttachCurrentThread(&env, NULL); + + jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controller_payload", "[B"); + jobject payload_object = env->GetStaticObjectField(s_adapter_class, payload_field); + jbyteArray* java_controller_payload = reinterpret_cast(&payload_object); + + // Get function pointers + jmethodID getfd_func = env->GetStaticMethodID(s_adapter_class, "GetFD", "()I"); + jmethodID input_func = env->GetStaticMethodID(s_adapter_class, "Input", "()I"); + jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()V"); + + env->CallStaticVoidMethod(s_adapter_class, openadapter_func); + + // Reset rumble once on initial reading + ResetRumble(); + + while (s_read_adapter_thread_running.IsSet()) + { + s_controller_payload_size = env->CallStaticIntMethod(s_adapter_class, input_func); + + jbyte* java_data = env->GetByteArrayElements(*java_controller_payload, nullptr); + { + std::lock_guard lk(s_read_mutex); + memcpy(s_controller_payload, java_data, 0x37); + } + env->ReleaseByteArrayElements(*java_controller_payload, java_data, 0); + + if (first_read) + { + first_read = false; + s_fd = env->CallStaticIntMethod(s_adapter_class, getfd_func); + } + + Common::YieldCPU(); + } + + g_java_vm->DetachCurrentThread(); + + NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread stopped"); +} + +static void Write() +{ + Common::SetCurrentThreadName("GC Adapter Write Thread"); + NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread started"); + + JNIEnv* env; + g_java_vm->AttachCurrentThread(&env, NULL); + jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "Output", "([B)I"); + + while (s_write_adapter_thread_running.IsSet()) + { + jbyteArray jrumble_array = env->NewByteArray(5); + jbyte* jrumble = env->GetByteArrayElements(jrumble_array, NULL); + + s_write_happened.Wait(); + { + std::lock_guard lk(s_write_mutex); + memcpy(jrumble, s_controller_write_payload, 5); + } + + env->ReleaseByteArrayElements(jrumble_array, jrumble, 0); + int size = env->CallStaticIntMethod(s_adapter_class, output_func, jrumble_array); + // Netplay sends invalid data which results in size = 0x00. Ignore it. + if (size != 0x05 && size != 0x00) + { + ERROR_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size); + Reset(); + } + + Common::YieldCPU(); + } + + g_java_vm->DetachCurrentThread(); + + NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread stopped"); +} + +void Init() +{ + if (s_fd) + return; + + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond()) + return; + + s_last_init = CoreTiming::GetTicks(); + } + + JNIEnv* env; + g_java_vm->AttachCurrentThread(&env, NULL); + + jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_GCAdapter"); + s_adapter_class = reinterpret_cast(env->NewGlobalRef(adapter_class)); + + if (UseAdapter()) + StartScanThread(); +} + +void Setup() +{ + s_read_adapter_thread_running.Set(true); + s_read_adapter_thread = std::thread(Read); + + s_write_adapter_thread_running.Set(true); + s_write_adapter_thread = std::thread(Write); + + s_detected = true; +} + +void Reset() +{ + if (!s_detected) + return; + + if (s_read_adapter_thread_running.TestAndClear()) + s_read_adapter_thread.join(); + + if (s_write_adapter_thread_running.TestAndClear()) + { + s_write_happened.Set(); // Kick the waiting event + s_write_adapter_thread.join(); + } + + for (int i = 0; i < MAX_SI_CHANNELS; i++) + s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; + + s_detected = false; + s_fd = 0; + NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached"); +} + +void Shutdown() +{ + StopScanThread(); + Reset(); +} + +void StartScanThread() +{ + if (s_adapter_detect_thread_running.IsSet()) + return; + + s_adapter_detect_thread_running.Set(true); + s_adapter_detect_thread = std::thread(ScanThreadFunc); +} + +void StopScanThread() +{ + if (s_adapter_detect_thread_running.TestAndClear()) + s_adapter_detect_thread.join(); +} + +void Input(int chan, GCPadStatus* pad) +{ + if (!UseAdapter() || !s_detected || !s_fd) + return; + + u8 controller_payload_copy[37]; + + { + std::lock_guard lk(s_read_mutex); + std::copy(std::begin(s_controller_payload), std::end(s_controller_payload), std::begin(controller_payload_copy)); + } + + if (s_controller_payload_size != sizeof(controller_payload_copy)) + { + ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]); + Reset(); + } + else + { + bool get_origin = false; + u8 type = controller_payload_copy[1 + (9 * chan)] >> 4; + if (type != ControllerTypes::CONTROLLER_NONE && s_controller_type[chan] == ControllerTypes::CONTROLLER_NONE) + { + ERROR_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload_copy[1 + (9 * chan)]); + get_origin = true; + } + + s_controller_type[chan] = type; + + memset(pad, 0, sizeof(*pad)); + if (s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE) + { + u8 b1 = controller_payload_copy[1 + (9 * chan) + 1]; + u8 b2 = controller_payload_copy[1 + (9 * chan) + 2]; + + if (b1 & (1 << 0)) pad->button |= PAD_BUTTON_A; + if (b1 & (1 << 1)) pad->button |= PAD_BUTTON_B; + if (b1 & (1 << 2)) pad->button |= PAD_BUTTON_X; + if (b1 & (1 << 3)) pad->button |= PAD_BUTTON_Y; + + if (b1 & (1 << 4)) pad->button |= PAD_BUTTON_LEFT; + if (b1 & (1 << 5)) pad->button |= PAD_BUTTON_RIGHT; + if (b1 & (1 << 6)) pad->button |= PAD_BUTTON_DOWN; + if (b1 & (1 << 7)) pad->button |= PAD_BUTTON_UP; + + if (b2 & (1 << 0)) pad->button |= PAD_BUTTON_START; + if (b2 & (1 << 1)) pad->button |= PAD_TRIGGER_Z; + if (b2 & (1 << 2)) pad->button |= PAD_TRIGGER_R; + if (b2 & (1 << 3)) pad->button |= PAD_TRIGGER_L; + + if (get_origin) pad->button |= PAD_GET_ORIGIN; + + pad->stickX = controller_payload_copy[1 + (9 * chan) + 3]; + pad->stickY = controller_payload_copy[1 + (9 * chan) + 4]; + pad->substickX = controller_payload_copy[1 + (9 * chan) + 5]; + pad->substickY = controller_payload_copy[1 + (9 * chan) + 6]; + pad->triggerLeft = controller_payload_copy[1 + (9 * chan) + 7]; + pad->triggerRight = controller_payload_copy[1 + (9 * chan) + 8]; + } + else + { + pad->button = PAD_ERR_STATUS; + } + } +} + +void Output(int chan, u8 rumble_command) +{ + if (!UseAdapter() || !s_detected || !s_fd) + return; + + // Skip over rumble commands if it has not changed or the controller is wireless + if (rumble_command != s_controller_rumble[chan] && s_controller_type[chan] != ControllerTypes::CONTROLLER_WIRELESS) + { + s_controller_rumble[chan] = rumble_command; + unsigned char rumble[5] = { 0x11, s_controller_rumble[0], s_controller_rumble[1], s_controller_rumble[2], s_controller_rumble[3] }; + { + std::lock_guard lk(s_write_mutex); + memcpy(s_controller_write_payload, rumble, 5); + } + s_write_happened.Set(); + } +} + +bool IsDetected() { return s_detected; } +bool IsDriverDetected() { return true; } +bool DeviceConnected(int chan) +{ + return s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE; +} + +bool UseAdapter() +{ + return SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[1] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[2] == SIDEVICE_WIIU_ADAPTER || + SConfig::GetInstance().m_SIDevice[3] == SIDEVICE_WIIU_ADAPTER; +} + +void ResetRumble() +{ + unsigned char rumble[5] = {0x11, 0, 0, 0, 0}; + { + std::lock_guard lk(s_read_mutex); + memcpy(s_controller_write_payload, rumble, 5); + } + s_write_happened.Set(); +} + +void SetAdapterCallback(std::function func) { } + +} // end of namespace GCAdapter -- cgit v1.2.3