Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hidapi wiimote #934

Merged
merged 12 commits into from
Aug 15, 2023
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ if (WIN32)
option(ENABLE_XINPUT "Enables the usage of XInput" ON)
option(ENABLE_DIRECTINPUT "Enables the usage of DirectInput" ON)
add_compile_definitions(HAS_DIRECTINPUT)
set(ENABLE_WIIMOTE ON)
elseif (UNIX)
option(ENABLE_HIDAPI "Build with HIDAPI" ON)
endif()
option(ENABLE_SDL "Enables the SDLController backend" ON)

Expand Down Expand Up @@ -155,6 +158,12 @@ if (ENABLE_DISCORD_RPC)
target_include_directories(discord-rpc INTERFACE ./dependencies/discord-rpc/include)
endif()

if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED)
set(ENABLE_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please move this to src/input/CMakeLists.txt and use target_compile_definitions(PRIVATE) instead? It'd be a bit cleaner, in my opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about moving this, considering that every other find_package() usage in the project is in the base CMakeLists.txt. Also add_compile_definitions() is used everywhere else in the project, rather than target_compile_definitions()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case consistency is better than supposed code clarity, it's fine to leave this as is :)

endif ()

if(UNIX AND NOT APPLE)
if(ENABLE_FERAL_GAMEMODE)
add_compile_definitions(ENABLE_FERAL_GAMEMODE)
Expand Down
38 changes: 26 additions & 12 deletions src/input/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,6 @@ add_library(CemuInput
set_property(TARGET CemuInput PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

if(WIN32)
# Native wiimote (Win32 only for now)
target_sources(CemuInput PRIVATE
api/Wiimote/WiimoteControllerProvider.h
api/Wiimote/windows/WinWiimoteDevice.cpp
api/Wiimote/windows/WinWiimoteDevice.h
api/Wiimote/WiimoteControllerProvider.cpp
api/Wiimote/WiimoteMessages.h
api/Wiimote/NativeWiimoteController.h
api/Wiimote/NativeWiimoteController.cpp
api/Wiimote/WiimoteDevice.h
)

# XInput
target_sources(CemuInput PRIVATE
api/XInput/XInputControllerProvider.cpp
Expand All @@ -73,6 +61,29 @@ if(WIN32)
)
endif()

if (ENABLE_WIIMOTE)
target_sources(CemuInput PRIVATE
api/Wiimote/WiimoteControllerProvider.h
api/Wiimote/WiimoteControllerProvider.cpp
api/Wiimote/WiimoteMessages.h
api/Wiimote/NativeWiimoteController.h
api/Wiimote/NativeWiimoteController.cpp
api/Wiimote/WiimoteDevice.h
)
if (ENABLE_HIDAPI)
target_sources(CemuInput PRIVATE
api/Wiimote/hidapi/HidapiWiimote.cpp
api/Wiimote/hidapi/HidapiWiimote.h
)
elseif (WIN32)
target_sources(CemuInput PRIVATE
api/Wiimote/windows/WinWiimoteDevice.cpp
api/Wiimote/windows/WinWiimoteDevice.h
)
endif()
endif ()


target_include_directories(CemuInput PUBLIC "../")

target_link_libraries(CemuInput PRIVATE
Expand All @@ -87,6 +98,9 @@ target_link_libraries(CemuInput PRIVATE
pugixml::pugixml
SDL2::SDL2
)
if (ENABLE_HIDAPI)
target_link_libraries(CemuInput PRIVATE hidapi::hidapi)
endif()

if (ENABLE_WXWIDGETS)
target_link_libraries(CemuInput PRIVATE wx::base wx::core)
Expand Down
3 changes: 3 additions & 0 deletions src/input/InputManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#if BOOST_OS_WINDOWS
#include "input/api/DirectInput/DirectInputControllerProvider.h"
#include "input/api/XInput/XInputControllerProvider.h"
#endif

#if defined(HAS_HIDAPI) || BOOST_OS_WINDOWS
#include "input/api/Wiimote/WiimoteControllerProvider.h"
#endif

Expand Down
128 changes: 52 additions & 76 deletions src/input/api/Wiimote/WiimoteControllerProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#include "input/api/Wiimote/NativeWiimoteController.h"
#include "input/api/Wiimote/WiimoteMessages.h"

#if BOOST_OS_WINDOWS
#ifdef HAS_HIDAPI
#include "input/api/Wiimote/hidapi/HidapiWiimote.h"
#elif BOOST_OS_WINDOWS
#include "input/api/Wiimote/windows/WinWiimoteDevice.h"
#endif

Expand Down Expand Up @@ -36,7 +38,7 @@ std::vector<std::shared_ptr<ControllerBase>> WiimoteControllerProvider::get_cont
{
// only add unknown, connected devices to our list
const bool is_new_device = std::none_of(m_wiimotes.cbegin(), m_wiimotes.cend(),
[&device](const auto& it) { return *it.device == *device; });
[device](const auto& it) { return *it.device == *device; });
if (is_new_device)
{
m_wiimotes.push_back(std::make_unique<Wiimote>(device));
Expand Down Expand Up @@ -163,9 +165,7 @@ void WiimoteControllerProvider::reader_thread()
{
case kStatus:
{
#ifdef WIIMOTE_DEBUG
printf("WiimoteControllerProvider::read_thread: kStatus\n");
#endif
cemuLog_logDebug(LogType::Force,"WiimoteControllerProvider::read_thread: kStatus");
new_state.buttons = (*(uint16*)data) & (~0x60E0);
data += 2;
new_state.flags = *data;
Expand All @@ -183,9 +183,7 @@ void WiimoteControllerProvider::reader_thread()

if (HAS_FLAG(new_state.flags, kExtensionConnected))
{
#ifdef WIIMOTE_DEBUG
printf("\tExtension flag is set\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension flag is set");
if(new_state.m_extension.index() == 0)
request_extension(index);
}
Expand All @@ -199,30 +197,25 @@ void WiimoteControllerProvider::reader_thread()
break;
case kRead:
{
#ifdef WIIMOTE_DEBUG
printf("WiimoteControllerProvider::read_thread: kRead\n");
#endif
cemuLog_logDebug(LogType::Force,"WiimoteControllerProvider::read_thread: kRead");
new_state.buttons = (*(uint16*)data) & (~0x60E0);
data += 2;
const uint8 error_flag = *data & 0xF, size = (*data >> 4) + 1;
++data;

if (error_flag)
{

// 7 means that wiimote is already enabled or not available
#ifdef WIIMOTE_DEBUG
printf("Received error on data read 0x%x\n", error_flag);
#endif
cemuLog_logDebug(LogType::Force,"Received error on data read {:#x}", error_flag);
continue;
}

auto address = *(betype<uint16>*)data;
data += 2;
if (address == (kRegisterCalibration & 0xFFFF))
{
#ifdef WIIMOTE_DEBUG
printf("Calibration received\n");
#endif
cemuLog_logDebug(LogType::Force,"Calibration received");

cemu_assert(size == 8);

Expand Down Expand Up @@ -255,17 +248,10 @@ void WiimoteControllerProvider::reader_thread()
{
if (size == 0xf)
{
#ifdef WIIMOTE_DEBUG
printf("Extension type received but no extension connected\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension type received but no extension connected");
continue;
}


#ifdef WIIMOTE_DEBUG
printf("Extension type received\n");
#endif

cemu_assert(size == 6);
auto be_type = *(betype<uint64>*)data;
data += 6; // 48
Expand All @@ -274,42 +260,38 @@ void WiimoteControllerProvider::reader_thread()
switch (be_type.value())
{
case kExtensionNunchuck:
#ifdef WIIMOTE_DEBUG
printf("\tNunchuck\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension Type Received: Nunchuck");
new_state.m_extension = NunchuckData{};
break;
case kExtensionClassic:
#ifdef WIIMOTE_DEBUG
printf("\tClassic\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension Type Received: Classic");
new_state.m_extension = ClassicData{};
break;
case kExtensionClassicPro:
break;
cemuLog_logDebug(LogType::Force,"Extension Type Received: Classic Pro");
break;
case kExtensionGuitar:
break;
cemuLog_logDebug(LogType::Force,"Extension Type Received: Guitar");
break;
case kExtensionDrums:
break;
cemuLog_logDebug(LogType::Force,"Extension Type Received: Drums");
break;
case kExtensionBalanceBoard:
break;
cemuLog_logDebug(LogType::Force,"Extension Type Received: Balance Board");
break;
case kExtensionMotionPlus:
//m_motion_plus = true;
#ifdef WIIMOTE_DEBUG
printf("\tMotion plus detected\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension Type Received: MotionPlus");
set_motion_plus(index, true);
new_state.m_motion_plus = MotionPlusData{};
break;
case kExtensionPartialyInserted:
#ifdef WIIMOTE_DEBUG
printf("\tExtension only partially inserted!\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension only partially inserted");
new_state.m_extension = {};
request_status(index);
break;
default:
new_state.m_extension = {};
cemuLog_logDebug(LogType::Force,"Unknown extension: {:#x}", be_type.value());
new_state.m_extension = {};
break;
}

Expand All @@ -319,9 +301,7 @@ void WiimoteControllerProvider::reader_thread()
else if (address == (kRegisterExtensionCalibration & 0xFFFF))
{
cemu_assert(size == 0x10);
#ifdef WIIMOTE_DEBUG
printf("Extension calibration received\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension calibration received");
std::visit(
overloaded
{
Expand All @@ -337,9 +317,7 @@ void WiimoteControllerProvider::reader_thread()
std::array<uint8, 14> zero{};
if (memcmp(zero.data(), data, zero.size()) == 0)
{
#ifdef WIIMOTE_DEBUG
printf("\tExtension calibration data is zero!\n");
#endif
cemuLog_logDebug(LogType::Force,"Extension calibration data is zero");
return;
}

Expand Down Expand Up @@ -372,15 +350,23 @@ void WiimoteControllerProvider::reader_thread()
}
else
{
#ifdef WIIMOTE_DEBUG
printf("Unhandled read data received\n");
#endif
continue;
cemuLog_logDebug(LogType::Force,"Unhandled read data received");
continue;
}

update_report = true;
}
break;
case kAcknowledge:
{
new_state.buttons = *(uint16*)data & (~0x60E0);
data += 2;
const auto report_id = *data++;
const auto error = *data++;
if (error)
cemuLog_logDebug(LogType::Force, "Error {:#x} from output report {:#x}", error, report_id);
break;
}
case kDataCore:
{
// 30 BB BB
Expand Down Expand Up @@ -476,10 +462,7 @@ void WiimoteControllerProvider::reader_thread()
orientation /= tmp;*/

mp.orientation = orientation;
#ifdef WIIMOTE_DEBUG
printf("\tmp: %.2lf %.2lf %.2lf\n", mp.orientation.x, mp.orientation.y,
mp.orientation.z);
#endif
cemuLog_logDebug(LogType::Force,"MotionPlus: {:.2f}, {:.2f} {:.2f}", mp.orientation.x, mp.orientation.y, mp.orientation.z);
},
[data](NunchuckData& nunchuck) mutable
{
Expand Down Expand Up @@ -553,12 +536,11 @@ void WiimoteControllerProvider::reader_thread()
zero3,
zero4
);
#ifdef WIIMOTE_DEBUG
printf("\tn: %d,%d | %lf - %lf | %.2lf %.2lf %.2lf\n", nunchuck.z, nunchuck.c,
nunchuck.axis.x, nunchuck.axis.y,
RadToDeg(nunchuck.acceleration.x), RadToDeg(nunchuck.acceleration.y),
RadToDeg(nunchuck.acceleration.z));
#endif
cemuLog_logDebug(LogType::Force,"Nunchuck: Z={}, C={} | {}, {} | {:.2f}, {:.2f}, {:.2f}",
nunchuck.z, nunchuck.c,
nunchuck.axis.x, nunchuck.axis.y,
RadToDeg(nunchuck.acceleration.x), RadToDeg(nunchuck.acceleration.y),
RadToDeg(nunchuck.acceleration.z));
},
[data](ClassicData& classic) mutable
{
Expand Down Expand Up @@ -592,11 +574,11 @@ void WiimoteControllerProvider::reader_thread()

classic.trigger = classic.raw_trigger;
classic.trigger /= 31.0f;
#ifdef WIIMOTE_DEBUG
printf("\tc: %d | %lf - %lf | %lf - %lf | %lf - %lf\n", classic.buttons,
classic.left_axis.x, classic.left_axis.y, classic.right_axis.x,
classic.right_axis.y, classic.trigger.x, classic.trigger.y);
#endif
cemuLog_logDebug(LogType::Force,"Classic Controller: Buttons={:b} | {}, {} | {}, {} | {}, {}",
classic.buttons, classic.left_axis.x, classic.left_axis.y,
classic.right_axis.x, classic.right_axis.y, classic.trigger.x,
classic.trigger.y);

}
}, new_state.m_extension);

Expand All @@ -609,9 +591,7 @@ void WiimoteControllerProvider::reader_thread()
break;
}
default:
#ifdef WIIMOTE_DEBUG
printf("unhandled input packet id %d for wiimote\n", data[0]);
#endif
cemuLog_logDebug(LogType::Force,"unhandled input packet id {} for wiimote {}", id, index);
}

// update motion data
Expand Down Expand Up @@ -694,7 +674,6 @@ void WiimoteControllerProvider::parse_acceleration(WiimoteState& wiimote_state,
tmp -= calib.zero;
acceleration = (wiimote_state.m_acceleration / tmp);

//printf("%d, %d, %d\n", (int)m_acceleration.x, (int)m_acceleration.y, (int)m_acceleration.z);
const float pi_2 = (float)std::numbers::pi / 2.0f;
wiimote_state.m_roll = std::atan2(acceleration.z, acceleration.x) - pi_2;
}
Expand All @@ -713,7 +692,6 @@ void WiimoteControllerProvider::rotate_ir(WiimoteState& wiimote_state)
i++;
if (!dot.visible)
continue;
//printf("%d:\t%.02lf | %.02lf\n", i, dot.pos.x, dot.pos.y);
// move to center, rotate and move back
dot.pos -= 0.5f;
dot.pos.x = (dot.pos.x * cos) + (dot.pos.y * (-sin));
Expand Down Expand Up @@ -973,9 +951,7 @@ void WiimoteControllerProvider::update_report_type(size_t index)
else
report_type = kDataCore;

#ifdef WIIMOTE_DEBUG
printf("Setting report type to %d\n", report_type);
#endif
cemuLog_logDebug(LogType::Force,"Setting report type to {}", report_type);
send_packet(index, {kType, 0x04, report_type});

state.ir_camera.mode = set_ir_camera(index, true);
Expand Down
2 changes: 1 addition & 1 deletion src/input/api/Wiimote/WiimoteMessages.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ enum InputReportId : uint8

kStatus = 0x20,
kRead = 0x21,
kWrite = 0x22,
kAcknowledge = 0x22,

kDataCore = 0x30,
kDataCoreAcc = 0x31,
Expand Down
Loading