mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-12-23 16:37:02 +00:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
337
src/input/api/DirectInput/DirectInputController.cpp
Normal file
337
src/input/api/DirectInput/DirectInputController.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#include "input/api/DirectInput/DirectInputController.h"
|
||||
#include "gui/guiWrapper.h"
|
||||
|
||||
DirectInputController::DirectInputController(const GUID& guid)
|
||||
: base_type(StringFromGUID(guid), fmt::format("[{}]", StringFromGUID(guid))),
|
||||
m_guid{ guid }
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DirectInputController::DirectInputController(const GUID& guid, std::string_view display_name)
|
||||
: base_type(StringFromGUID(guid), display_name), m_guid(guid)
|
||||
{
|
||||
}
|
||||
|
||||
DirectInputController::~DirectInputController()
|
||||
{
|
||||
if (m_effect)
|
||||
m_effect->Release();
|
||||
|
||||
if (m_device)
|
||||
{
|
||||
m_device->Unacquire();
|
||||
|
||||
// TODO: test if really needed
|
||||
// workaround for gamecube controllers crash on release?
|
||||
bool should_release_device = true;
|
||||
if (m_product_guid == GUID{}) {
|
||||
DIDEVICEINSTANCE info{};
|
||||
info.dwSize = sizeof(DIDEVICEINSTANCE);
|
||||
if (SUCCEEDED(m_device->GetDeviceInfo(&info)))
|
||||
{
|
||||
m_product_guid = info.guidProduct;
|
||||
}
|
||||
}
|
||||
|
||||
// info.guidProduct = {18440079-0000-0000-0000-504944564944}
|
||||
constexpr GUID kGameCubeController = { 0x18440079, 0, 0, {0,0,0x50,0x49,0x44,0x56,0x49,0x44} };
|
||||
if (kGameCubeController == m_product_guid)
|
||||
should_release_device = false;
|
||||
|
||||
if (should_release_device)
|
||||
m_device->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void DirectInputController::save(pugi::xml_node& node)
|
||||
{
|
||||
base_type::save(node);
|
||||
|
||||
node.append_child("product_guid").append_child(pugi::node_pcdata).set_value(
|
||||
fmt::format("{}", StringFromGUID(m_product_guid)).c_str());
|
||||
}
|
||||
|
||||
void DirectInputController::load(const pugi::xml_node& node)
|
||||
{
|
||||
base_type::load(node);
|
||||
|
||||
if (const auto value = node.child("product_guid")) {
|
||||
if (GUIDFromString(value.child_value(), m_product_guid) && m_product_guid != GUID{} && !is_connected())
|
||||
{
|
||||
// test if another controller with the same product guid is connectable and replace
|
||||
for(const auto& c : m_provider->get_controllers())
|
||||
{
|
||||
if(const auto ptr = std::dynamic_pointer_cast<DirectInputController>(c))
|
||||
{
|
||||
if (ptr->is_connected() && ptr->get_product_guid() == m_product_guid)
|
||||
{
|
||||
const auto tmp_guid = m_guid;
|
||||
m_guid = ptr->get_guid();
|
||||
if (connect())
|
||||
break;
|
||||
|
||||
// couldn't connect
|
||||
m_guid = tmp_guid;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectInputController::connect()
|
||||
{
|
||||
if (is_connected())
|
||||
return true;
|
||||
|
||||
m_effect = nullptr;
|
||||
|
||||
std::scoped_lock lock(m_mutex);
|
||||
HRESULT hr = m_provider->get_dinput()->CreateDevice(m_guid, &m_device, nullptr);
|
||||
if (FAILED(hr) || m_device == nullptr)
|
||||
return false;
|
||||
|
||||
DIDEVICEINSTANCE idi{};
|
||||
idi.dwSize = sizeof(DIDEVICEINSTANCE);
|
||||
if (SUCCEEDED(m_device->GetDeviceInfo(&idi)))
|
||||
{
|
||||
// overwrite guid name with "real" display name
|
||||
m_display_name = boost::nowide::narrow(idi.tszProductName);
|
||||
}
|
||||
|
||||
// set data format
|
||||
if (FAILED(m_device->SetDataFormat(m_provider->get_data_format())))
|
||||
{
|
||||
SAFE_RELEASE(m_device);
|
||||
return false;
|
||||
}
|
||||
|
||||
HWND hwndMainWindow = gui_getWindowInfo().window_main.hwnd;
|
||||
|
||||
// set access
|
||||
if (FAILED(m_device->SetCooperativeLevel(hwndMainWindow, DISCL_BACKGROUND | DISCL_EXCLUSIVE)))
|
||||
{
|
||||
if (FAILED(m_device->SetCooperativeLevel(hwndMainWindow, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
|
||||
{
|
||||
SAFE_RELEASE(m_device);
|
||||
return false;
|
||||
}
|
||||
// rumble can only be used with exclusive access
|
||||
}
|
||||
else
|
||||
{
|
||||
GUID guid_effect = GUID_NULL;
|
||||
// check if constant force is supported
|
||||
HRESULT result = m_device->EnumEffects([](LPCDIEFFECTINFOW eff, LPVOID guid) -> BOOL
|
||||
{
|
||||
*(GUID*)guid = eff->guid;
|
||||
return DIENUM_STOP;
|
||||
}, &guid_effect, DIEFT_CONSTANTFORCE);
|
||||
|
||||
if (SUCCEEDED(result) && guid_effect != GUID_NULL)
|
||||
{
|
||||
DWORD dwAxes[2] = { DIJOFS_X, DIJOFS_Y };
|
||||
LONG lDirection[2] = { 1, 0 };
|
||||
|
||||
DICONSTANTFORCE constant_force = { DI_FFNOMINALMAX }; // DI_FFNOMINALMAX -> should be max normally?!
|
||||
|
||||
DIEFFECT effect{};
|
||||
effect.dwSize = sizeof(DIEFFECT);
|
||||
effect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
|
||||
effect.dwDuration = INFINITE; // DI_SECONDS;
|
||||
effect.dwGain = DI_FFNOMINALMAX; // No scaling
|
||||
effect.dwTriggerButton = DIEB_NOTRIGGER; // Not a button response DIEB_NOTRIGGER DIJOFS_BUTTON0
|
||||
effect.cAxes = 2;
|
||||
effect.rgdwAxes = dwAxes;
|
||||
effect.rglDirection = lDirection;
|
||||
effect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
|
||||
effect.lpvTypeSpecificParams = &constant_force;
|
||||
m_device->CreateEffect(guid_effect, &effect, &m_effect, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
DIDEVICEINSTANCE info{};
|
||||
info.dwSize = sizeof(DIDEVICEINSTANCE);
|
||||
if (SUCCEEDED(m_device->GetDeviceInfo(&info)))
|
||||
{
|
||||
m_product_guid = info.guidProduct;
|
||||
}
|
||||
|
||||
std::fill(m_min_axis.begin(), m_min_axis.end(), 0);
|
||||
std::fill(m_max_axis.begin(), m_max_axis.end(), std::numeric_limits<uint16>::max());
|
||||
m_device->EnumObjects(
|
||||
[](LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) -> BOOL
|
||||
{
|
||||
auto* thisptr = (DirectInputController*)pvRef;
|
||||
|
||||
const auto instance = DIDFT_GETINSTANCE(lpddoi->dwType);
|
||||
// some tools may use state.rglSlider properties, so they have 8 instead of 6 axis
|
||||
if(instance >= thisptr->m_min_axis.size())
|
||||
{
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
DIPROPRANGE range{};
|
||||
range.diph.dwSize = sizeof(range);
|
||||
range.diph.dwHeaderSize = sizeof(range.diph);
|
||||
range.diph.dwHow = DIPH_BYID;
|
||||
range.diph.dwObj = lpddoi->dwType;
|
||||
if (thisptr->m_device->GetProperty(DIPROP_RANGE, &range.diph) == DI_OK)
|
||||
{
|
||||
thisptr->m_min_axis[instance] = range.lMin;
|
||||
thisptr->m_max_axis[instance] = range.lMax;
|
||||
}
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}, this, DIDFT_AXIS);
|
||||
|
||||
m_device->Acquire();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DirectInputController::is_connected()
|
||||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
return m_device != nullptr;
|
||||
}
|
||||
|
||||
bool DirectInputController::has_rumble()
|
||||
{
|
||||
return m_effect != nullptr;
|
||||
}
|
||||
|
||||
void DirectInputController::start_rumble()
|
||||
{
|
||||
if (!has_rumble())
|
||||
return;
|
||||
}
|
||||
|
||||
void DirectInputController::stop_rumble()
|
||||
{
|
||||
if (!has_rumble())
|
||||
return;
|
||||
}
|
||||
|
||||
std::string DirectInputController::get_button_name(uint64 button) const
|
||||
{
|
||||
switch(button)
|
||||
{
|
||||
case kAxisXP: return "X+";
|
||||
case kAxisYP: return "Y+";
|
||||
|
||||
case kAxisXN: return "X-";
|
||||
case kAxisYN: return "Y-";
|
||||
|
||||
case kRotationXP: return "RX+";
|
||||
case kRotationYP: return "RY+";
|
||||
|
||||
case kRotationXN: return "RX-";
|
||||
case kRotationYN: return "RY-";
|
||||
|
||||
case kTriggerXP: return "Z+";
|
||||
case kTriggerYP: return "RZ+";
|
||||
|
||||
case kTriggerXN: return "Z-";
|
||||
case kTriggerYN: return "RZ-";
|
||||
}
|
||||
|
||||
return base_type::get_button_name(button);
|
||||
}
|
||||
|
||||
ControllerState DirectInputController::raw_state()
|
||||
{
|
||||
ControllerState result{};
|
||||
if (!is_connected())
|
||||
return result;
|
||||
|
||||
HRESULT hr = m_device->Poll();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
||||
{
|
||||
result.last_state = hr;
|
||||
m_device->Acquire();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DIJOYSTATE state{};
|
||||
hr = m_device->GetDeviceState(sizeof(state), &state);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
|
||||
{
|
||||
result.last_state = hr;
|
||||
m_device->Acquire();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result.last_state = hr;
|
||||
|
||||
// buttons
|
||||
for (size_t i = 0; i < std::size(state.rgbButtons); ++i)
|
||||
{
|
||||
if (HAS_BIT(state.rgbButtons[i], 7))
|
||||
{
|
||||
result.buttons.set(i);
|
||||
}
|
||||
}
|
||||
|
||||
// axis
|
||||
constexpr float kThreshold = 0.001f;
|
||||
float v = (float(state.lX - m_min_axis[0]) / float(m_max_axis[0] - m_min_axis[0])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.axis.x = v;
|
||||
|
||||
v = (float(state.lY - m_min_axis[1]) / float(m_max_axis[1] - m_min_axis[1])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.axis.y = -v;
|
||||
|
||||
// Right Stick
|
||||
v = (float(state.lRx - m_min_axis[3]) / float(m_max_axis[3] - m_min_axis[3])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.rotation.x = v;
|
||||
|
||||
v = (float(state.lRy - m_min_axis[4]) / float(m_max_axis[4] - m_min_axis[4])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.rotation.y = -v;
|
||||
|
||||
// Trigger
|
||||
v = (float(state.lZ - m_min_axis[2]) / float(m_max_axis[2] - m_min_axis[2])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.trigger.x = v;
|
||||
|
||||
v = (float(state.lRz - m_min_axis[5]) / float(m_max_axis[5] - m_min_axis[5])) * 2.0f - 1.0f;
|
||||
if (std::abs(v) >= kThreshold)
|
||||
result.trigger.y = -v;
|
||||
|
||||
// dpad
|
||||
const auto pov = state.rgdwPOV[0];
|
||||
if (pov != static_cast<DWORD>(-1))
|
||||
{
|
||||
switch (pov)
|
||||
{
|
||||
case 0: result.buttons.set(kButtonUp);
|
||||
break;
|
||||
case 4500: result.buttons.set(kButtonUp); // up + right
|
||||
case 9000: result.buttons.set(kButtonRight);
|
||||
break;
|
||||
case 13500: result.buttons.set(kButtonRight); // right + down
|
||||
case 18000: result.buttons.set(kButtonDown);
|
||||
break;
|
||||
case 22500: result.buttons.set(kButtonDown); // down + left
|
||||
case 27000: result.buttons.set(kButtonLeft);
|
||||
break;
|
||||
case 31500: result.buttons.set(kButtonLeft);; // left + up
|
||||
result.buttons.set(kButtonUp); // left + up
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
49
src/input/api/DirectInput/DirectInputController.h
Normal file
49
src/input/api/DirectInput/DirectInputController.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "input/api/DirectInput/DirectInputControllerProvider.h"
|
||||
#include "input/api/Controller.h"
|
||||
|
||||
class DirectInputController : public Controller<DirectInputControllerProvider>
|
||||
{
|
||||
public:
|
||||
DirectInputController(const GUID& guid);
|
||||
DirectInputController(const GUID& guid, std::string_view display_name);
|
||||
~DirectInputController() override;
|
||||
|
||||
std::string_view api_name() const override
|
||||
{
|
||||
static_assert(to_string(InputAPI::DirectInput) == "DirectInput");
|
||||
return to_string(InputAPI::DirectInput);
|
||||
}
|
||||
InputAPI::Type api() const override { return InputAPI::DirectInput; }
|
||||
|
||||
void save(pugi::xml_node& node) override;
|
||||
void load(const pugi::xml_node& node) override;
|
||||
|
||||
bool connect() override;
|
||||
bool is_connected() override;
|
||||
|
||||
bool has_rumble() override;
|
||||
|
||||
void start_rumble() override;
|
||||
void stop_rumble() override;
|
||||
|
||||
std::string get_button_name(uint64 button) const override;
|
||||
|
||||
const GUID& get_guid() const { return m_guid; }
|
||||
const GUID& get_product_guid() const { return m_product_guid; }
|
||||
|
||||
protected:
|
||||
ControllerState raw_state() override;
|
||||
|
||||
private:
|
||||
GUID m_guid;
|
||||
GUID m_product_guid{};
|
||||
|
||||
std::shared_mutex m_mutex;
|
||||
LPDIRECTINPUTDEVICE8 m_device = nullptr;
|
||||
LPDIRECTINPUTEFFECT m_effect = nullptr;
|
||||
|
||||
std::array<LONG, 6> m_min_axis{};
|
||||
std::array<LONG, 6> m_max_axis{};
|
||||
};
|
||||
65
src/input/api/DirectInput/DirectInputControllerProvider.cpp
Normal file
65
src/input/api/DirectInput/DirectInputControllerProvider.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include "input/api/DirectInput/DirectInputControllerProvider.h"
|
||||
|
||||
#include "input/api/DirectInput/DirectInputController.h"
|
||||
|
||||
DirectInputControllerProvider::DirectInputControllerProvider()
|
||||
{
|
||||
/*m_module = LoadLibraryA("dinput8.dll");
|
||||
if (!m_module)
|
||||
throw std::runtime_error("can't load any xinput dll");
|
||||
|
||||
m_DirectInput8Create = (decltype(&DirectInput8Create))GetProcAddress(m_module, "DirectInput8Create");
|
||||
m_GetdfDIJoystick = (decltype(&GetdfDIJoystick))GetProcAddress(m_module, "GetdfDIJoystick");
|
||||
|
||||
if (!m_DirectInput8Create)
|
||||
{
|
||||
FreeLibrary(m_module);
|
||||
throw std::runtime_error("can't find the DirectInput8Create export");
|
||||
}*/
|
||||
|
||||
|
||||
const auto r = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_dinput8, nullptr);
|
||||
if (FAILED(r) || !m_dinput8)
|
||||
{
|
||||
const auto error = GetLastError();
|
||||
//FreeLibrary(m_module);
|
||||
throw std::runtime_error(fmt::format("can't create direct input object (error: {:#x})", error));
|
||||
}
|
||||
}
|
||||
|
||||
DirectInputControllerProvider::~DirectInputControllerProvider()
|
||||
{
|
||||
if (m_dinput8)
|
||||
m_dinput8->Release();
|
||||
|
||||
/*if (m_module)
|
||||
FreeLibrary(m_module);
|
||||
*/
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> DirectInputControllerProvider::get_controllers()
|
||||
{
|
||||
std::vector<std::shared_ptr<ControllerBase>> result;
|
||||
|
||||
m_dinput8->EnumDevices(DI8DEVCLASS_GAMECTRL,
|
||||
[](LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) -> BOOL
|
||||
{
|
||||
auto* controllers = (decltype(&result))pvRef;
|
||||
|
||||
std::string display_name = boost::nowide::narrow(lpddi->tszProductName);
|
||||
controllers->emplace_back(std::make_shared<DirectInputController>(lpddi->guidInstance, display_name));
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}, &result, DIEDFL_ALLDEVICES);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LPCDIDATAFORMAT DirectInputControllerProvider::get_data_format() const
|
||||
{
|
||||
/*if (m_GetdfDIJoystick)
|
||||
return m_GetdfDIJoystick();*/
|
||||
|
||||
return GetdfDIJoystick();
|
||||
}
|
||||
37
src/input/api/DirectInput/DirectInputControllerProvider.h
Normal file
37
src/input/api/DirectInput/DirectInputControllerProvider.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#if BOOST_OS_WINDOWS
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
|
||||
#include <dinput.h>
|
||||
|
||||
#include "input/api/ControllerProvider.h"
|
||||
|
||||
#ifndef HAS_DIRECTINPUT
|
||||
#define HAS_DIRECTINPUT 1
|
||||
#endif
|
||||
|
||||
class DirectInputControllerProvider : public ControllerProviderBase
|
||||
{
|
||||
public:
|
||||
DirectInputControllerProvider();
|
||||
~DirectInputControllerProvider() override;
|
||||
|
||||
inline static InputAPI::Type kAPIType = InputAPI::DirectInput;
|
||||
InputAPI::Type api() const override { return kAPIType; }
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
|
||||
|
||||
IDirectInput8* get_dinput() const { return m_dinput8; }
|
||||
LPCDIDATAFORMAT get_data_format() const;
|
||||
|
||||
private:
|
||||
HMODULE m_module = nullptr;
|
||||
|
||||
decltype(&DirectInput8Create) m_DirectInput8Create;
|
||||
decltype(&GetdfDIJoystick) m_GetdfDIJoystick = nullptr;
|
||||
|
||||
IDirectInput8* m_dinput8 = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue