mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-20 03:24:59 +00:00
InputCommon: Add gamepads from GCAdapter code to ControllerInterface.
This commit is contained in:
parent
4f210df86a
commit
04c5640dec
11 changed files with 321 additions and 3 deletions
|
@ -195,6 +195,8 @@ const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES{
|
|||
const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER{{System::Main, "Core", "WiimoteEnableSpeaker"}, false};
|
||||
const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE{
|
||||
{System::Main, "Core", "WiimoteControllerInterface"}, false};
|
||||
const Info<bool> MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE{
|
||||
{System::Main, "Core", "GCAdapterControllerInterface"}, false};
|
||||
const Info<bool> MAIN_MMU{{System::Main, "Core", "MMU"}, false};
|
||||
const Info<bool> MAIN_PAUSE_ON_PANIC{{System::Main, "Core", "PauseOnPanic"}, false};
|
||||
const Info<int> MAIN_BB_DUMP_PORT{{System::Main, "Core", "BBDumpPort"}, -1};
|
||||
|
|
|
@ -110,6 +110,7 @@ extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
|
|||
extern const Info<std::string> MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES;
|
||||
extern const Info<bool> MAIN_WIIMOTE_ENABLE_SPEAKER;
|
||||
extern const Info<bool> MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE;
|
||||
extern const Info<bool> MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE;
|
||||
extern const Info<bool> MAIN_MMU;
|
||||
extern const Info<bool> MAIN_PAUSE_ON_PANIC;
|
||||
extern const Info<int> MAIN_BB_DUMP_PORT;
|
||||
|
|
|
@ -534,6 +534,7 @@
|
|||
<ClInclude Include="InputCommon\ControllerInterface\MappingCommon.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\WGInput\WGInput.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\Wiimote\WiimoteController.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\GCAdapter\GCAdapterController.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\Win32\Win32.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\XInput\XInput.h" />
|
||||
<ClInclude Include="InputCommon\ControllerInterface\SDL\SDL.h" />
|
||||
|
@ -1194,6 +1195,7 @@
|
|||
<ClCompile Include="InputCommon\ControllerInterface\MappingCommon.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\WGInput\WGInput.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\Wiimote\WiimoteController.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\GCAdapter\GCAdapterController.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\Win32\Win32.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\XInput\XInput.cpp" />
|
||||
<ClCompile Include="InputCommon\ControllerInterface\SDL\SDL.cpp" />
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "DolphinQt/Config/GamecubeControllersWidget.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
|
@ -96,6 +97,10 @@ void GamecubeControllersWidget::CreateLayout()
|
|||
m_gc_layout->addWidget(gc_box, controller_row, 1);
|
||||
m_gc_layout->addWidget(gc_button, controller_row, 2);
|
||||
}
|
||||
|
||||
m_gcpad_ciface = new QCheckBox(tr("Use GameCube Controller Adapter for Emulated Controllers"));
|
||||
m_gc_layout->addWidget(m_gcpad_ciface, m_gc_layout->rowCount(), 0, 1, -1);
|
||||
|
||||
m_gc_box->setLayout(m_gc_layout);
|
||||
|
||||
auto* layout = new QVBoxLayout;
|
||||
|
@ -115,6 +120,8 @@ void GamecubeControllersWidget::ConnectWidgets()
|
|||
});
|
||||
connect(m_gc_buttons[i], &QPushButton::clicked, this, [this, i] { OnGCPadConfigure(i); });
|
||||
}
|
||||
|
||||
connect(m_gcpad_ciface, &QCheckBox::toggled, this, &GamecubeControllersWidget::SaveSettings);
|
||||
}
|
||||
|
||||
void GamecubeControllersWidget::OnGCTypeChanged(size_t index)
|
||||
|
@ -185,6 +192,9 @@ void GamecubeControllersWidget::LoadSettings(Core::State state)
|
|||
OnGCTypeChanged(i);
|
||||
}
|
||||
}
|
||||
|
||||
SignalBlocking(m_gcpad_ciface)
|
||||
->setChecked(Config::Get(Config::MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE));
|
||||
}
|
||||
|
||||
void GamecubeControllersWidget::SaveSettings()
|
||||
|
@ -204,11 +214,15 @@ void GamecubeControllersWidget::SaveSettings()
|
|||
system.GetSerialInterface().ChangeDevice(si_device, static_cast<s32>(i));
|
||||
}
|
||||
}
|
||||
|
||||
Config::SetBaseOrCurrent(Config::MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE,
|
||||
m_gcpad_ciface->isChecked());
|
||||
}
|
||||
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
|
||||
if (GCAdapter::UseAdapter())
|
||||
GCAdapter::StartScanThread();
|
||||
else
|
||||
GCAdapter::StopScanThread();
|
||||
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QHBoxLayout;
|
||||
class QGridLayout;
|
||||
|
@ -40,4 +41,5 @@ private:
|
|||
std::array<QComboBox*, 4> m_gc_controller_boxes;
|
||||
std::array<QPushButton*, 4> m_gc_buttons;
|
||||
std::array<QHBoxLayout*, 4> m_gc_groups;
|
||||
QCheckBox* m_gcpad_ciface;
|
||||
};
|
||||
|
|
|
@ -64,6 +64,8 @@ add_library(inputcommon
|
|||
ControllerInterface/MappingCommon.h
|
||||
ControllerInterface/Wiimote/WiimoteController.cpp
|
||||
ControllerInterface/Wiimote/WiimoteController.h
|
||||
ControllerInterface/GCAdapter/GCAdapterController.cpp
|
||||
ControllerInterface/GCAdapter/GCAdapterController.h
|
||||
ControlReference/ControlReference.cpp
|
||||
ControlReference/ControlReference.h
|
||||
ControlReference/ExpressionParser.cpp
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "Common/Assert.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
#include "InputCommon/ControllerInterface/GCAdapter/GCAdapterController.h"
|
||||
|
||||
#ifdef CIFACE_USE_WIN32
|
||||
#include "InputCommon/ControllerInterface/Win32/Win32.h"
|
||||
|
@ -86,6 +87,9 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
|||
m_input_backends.emplace_back(ciface::SteamDeck::CreateInputBackend(this));
|
||||
#endif
|
||||
|
||||
// TODO: obey Config::Get(Config::MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE)
|
||||
m_input_backends.emplace_back(ciface::GCAdapter::CreateInputBackend(this));
|
||||
|
||||
// Don't allow backends to add devices before the first RefreshDevices() as they will be cleaned
|
||||
// there. Or they'd end up waiting on the devices mutex if populated from another thread.
|
||||
m_is_init = true;
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "InputCommon/ControllerInterface/GCAdapter/GCAdapterController.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
|
||||
// TODO: namespace name matching the one in InputCommon/GCAdapter.h is annoying
|
||||
namespace ciface::GCAdapter
|
||||
{
|
||||
constexpr auto SOURCE_NAME = "GCAdapter";
|
||||
|
||||
class GCController;
|
||||
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
{
|
||||
public:
|
||||
InputBackend(ControllerInterface* controller_interface);
|
||||
void PopulateDevices() override;
|
||||
void UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove) override;
|
||||
|
||||
private:
|
||||
std::array<std::weak_ptr<GCController>, SerialInterface::MAX_SI_CHANNELS> m_devices;
|
||||
};
|
||||
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface)
|
||||
{
|
||||
return std::make_unique<InputBackend>(controller_interface);
|
||||
}
|
||||
|
||||
struct DigitalInputProps
|
||||
{
|
||||
u16 value;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
constexpr DigitalInputProps digital_inputs[] = {
|
||||
{PAD_BUTTON_UP, "Up"},
|
||||
{PAD_BUTTON_DOWN, "Down"},
|
||||
{PAD_BUTTON_LEFT, "Left"},
|
||||
{PAD_BUTTON_RIGHT, "Right"},
|
||||
{PAD_BUTTON_A, "A"},
|
||||
{PAD_BUTTON_B, "B"},
|
||||
{PAD_BUTTON_X, "X"},
|
||||
{PAD_BUTTON_Y, "Y"},
|
||||
{PAD_TRIGGER_Z, "Z"},
|
||||
{PAD_TRIGGER_L, "L"},
|
||||
{PAD_TRIGGER_R, "R"},
|
||||
// TODO: "START" or "Start"?
|
||||
{PAD_BUTTON_START, "Start"},
|
||||
};
|
||||
|
||||
struct AnalogInputProps
|
||||
{
|
||||
u8 GCPadStatus::*ptr;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
constexpr AnalogInputProps stick_inputs[] = {
|
||||
{&GCPadStatus::stickX, "Main X"},
|
||||
{&GCPadStatus::stickY, "Main Y"},
|
||||
{&GCPadStatus::substickX, "C X"},
|
||||
{&GCPadStatus::substickY, "C Y"},
|
||||
};
|
||||
|
||||
constexpr AnalogInputProps trigger_inputs[] = {
|
||||
{&GCPadStatus::triggerLeft, "L-Analog"},
|
||||
{&GCPadStatus::triggerRight, "R-Analog"},
|
||||
};
|
||||
|
||||
class DigitalInput : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
DigitalInput(const u16* button_state, u32 index) : m_button_state{*button_state}, m_index{index}
|
||||
{
|
||||
}
|
||||
|
||||
std::string GetName() const override { return digital_inputs[m_index].name; }
|
||||
|
||||
ControlState GetState() const override
|
||||
{
|
||||
return (digital_inputs[m_index].value & m_button_state) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const u16& m_button_state;
|
||||
const u32 m_index;
|
||||
};
|
||||
|
||||
class AnalogInput : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
AnalogInput(const GCPadStatus* pad_status, const AnalogInputProps& props, u8 neutral_value,
|
||||
s32 range)
|
||||
: m_base_name{props.name},
|
||||
m_neutral_value{neutral_value}, m_range{range}, m_state{pad_status->*props.ptr}
|
||||
{
|
||||
}
|
||||
|
||||
ControlState GetState() const override
|
||||
{
|
||||
return ControlState(m_state - m_neutral_value) / m_range;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* const m_base_name;
|
||||
const u8 m_neutral_value;
|
||||
const s32 m_range;
|
||||
|
||||
private:
|
||||
const u8& m_state;
|
||||
};
|
||||
|
||||
class StickInput : public AnalogInput
|
||||
{
|
||||
public:
|
||||
using AnalogInput::AnalogInput;
|
||||
|
||||
std::string GetName() const override
|
||||
{
|
||||
return std::string(m_base_name) + (m_range > 0 ? '+' : '-');
|
||||
}
|
||||
};
|
||||
|
||||
class TriggerInput : public AnalogInput
|
||||
{
|
||||
public:
|
||||
using AnalogInput::AnalogInput;
|
||||
|
||||
std::string GetName() const override { return m_base_name; }
|
||||
};
|
||||
|
||||
class Motor : public Core::Device::Output
|
||||
{
|
||||
public:
|
||||
Motor(u32 index) : m_index{index} {}
|
||||
|
||||
void SetState(ControlState value) override
|
||||
{
|
||||
const bool new_state = std::lround(value);
|
||||
|
||||
if (new_state == m_last_state)
|
||||
return;
|
||||
|
||||
m_last_state = new_state;
|
||||
|
||||
::GCAdapter::Output(m_index, new_state);
|
||||
}
|
||||
|
||||
std::string GetName() const override { return "Motor"; }
|
||||
|
||||
private:
|
||||
u32 m_index;
|
||||
bool m_last_state = false;
|
||||
};
|
||||
|
||||
class GCController : public Core::Device
|
||||
{
|
||||
public:
|
||||
GCController(u32 index) : m_index{index}
|
||||
{
|
||||
// Add buttons.
|
||||
for (u32 i = 0; i != std::size(digital_inputs); ++i)
|
||||
AddInput(new DigitalInput{&m_pad_status.button, i});
|
||||
|
||||
// TODO: use origin values.
|
||||
const auto origin = ::GCAdapter::GetOrigin(index);
|
||||
|
||||
// Add sticks.
|
||||
for (auto& props : stick_inputs)
|
||||
{
|
||||
// Add separate -/+ inputs.
|
||||
AddInput(new StickInput{&m_pad_status, props, GCPadStatus::MAIN_STICK_CENTER_X,
|
||||
-GCPadStatus::MAIN_STICK_RADIUS});
|
||||
AddInput(new StickInput{&m_pad_status, props, GCPadStatus::MAIN_STICK_CENTER_X,
|
||||
GCPadStatus::MAIN_STICK_RADIUS});
|
||||
}
|
||||
|
||||
// Add triggers.
|
||||
for (auto& props : trigger_inputs)
|
||||
AddInput(new TriggerInput{&m_pad_status, props, 0, std::numeric_limits<u8>::max()});
|
||||
|
||||
// Rumble.
|
||||
AddOutput(new Motor{index});
|
||||
}
|
||||
|
||||
std::optional<int> GetPreferredId() const override { return m_index; }
|
||||
|
||||
std::string GetName() const override
|
||||
{
|
||||
// TODO: name?
|
||||
return "GCPad";
|
||||
}
|
||||
|
||||
std::string GetSource() const override { return SOURCE_NAME; }
|
||||
|
||||
int GetSortPriority() const override { return -3; }
|
||||
|
||||
Core::DeviceRemoval UpdateInput() override
|
||||
{
|
||||
m_pad_status = ::GCAdapter::PeekInput(m_index);
|
||||
return Core::DeviceRemoval::Keep;
|
||||
}
|
||||
|
||||
private:
|
||||
GCPadStatus m_pad_status;
|
||||
const u32 m_index;
|
||||
};
|
||||
|
||||
InputBackend::InputBackend(ControllerInterface* controller_interface)
|
||||
: ciface::InputBackend(controller_interface)
|
||||
{
|
||||
::GCAdapter::Init();
|
||||
}
|
||||
|
||||
void InputBackend::PopulateDevices()
|
||||
{
|
||||
for (int i = 0; i != SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
if (::GCAdapter::DeviceConnected(i))
|
||||
{
|
||||
auto new_device = std::make_shared<GCController>(i);
|
||||
m_devices[i] = new_device;
|
||||
GetControllerInterface().AddDevice(std::move(new_device));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputBackend::UpdateInput(std::vector<std::weak_ptr<ciface::Core::Device>>& devices_to_remove)
|
||||
{
|
||||
// "Hotplug" is handled here.
|
||||
for (int i = 0; i != SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
const bool is_device_connected = ::GCAdapter::DeviceConnected(i);
|
||||
const auto device = m_devices[i].lock();
|
||||
|
||||
if (is_device_connected == (device != nullptr))
|
||||
continue;
|
||||
|
||||
if (is_device_connected)
|
||||
{
|
||||
auto new_device = std::make_shared<GCController>(i);
|
||||
m_devices[i] = new_device;
|
||||
GetControllerInterface().AddDevice(std::move(new_device));
|
||||
}
|
||||
else
|
||||
{
|
||||
devices_to_remove.emplace_back(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ciface::GCAdapter
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "InputCommon/ControllerInterface/InputBackend.h"
|
||||
|
||||
namespace ciface::GCAdapter
|
||||
{
|
||||
std::unique_ptr<ciface::InputBackend> CreateInputBackend(ControllerInterface* controller_interface);
|
||||
|
||||
} // namespace ciface::GCAdapter
|
|
@ -417,12 +417,14 @@ void SetAdapterCallback(std::function<void(void)> func)
|
|||
|
||||
static void RefreshConfig()
|
||||
{
|
||||
s_is_adapter_wanted = false;
|
||||
s_is_adapter_wanted = Config::Get(Config::MAIN_USE_GC_ADAPTER_FOR_CONTROLLER_INTERFACE);
|
||||
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
s_is_adapter_wanted |= Config::Get(Config::GetInfoForSIDevice(i)) ==
|
||||
SerialInterface::SIDevices::SIDEVICE_WIIU_ADAPTER;
|
||||
|
||||
// TODO: ControllerInterface shouldn't obey this setting.
|
||||
s_config_rumble_enabled[i] = Config::Get(Config::GetInfoForAdapterRumble(i));
|
||||
}
|
||||
}
|
||||
|
@ -784,6 +786,18 @@ GCPadStatus Input(int chan)
|
|||
return pad_state.status;
|
||||
}
|
||||
|
||||
GCPadStatus PeekInput(int chan)
|
||||
{
|
||||
std::lock_guard lk(s_read_mutex);
|
||||
return s_port_states[chan].status;
|
||||
}
|
||||
|
||||
GCPadStatus GetOrigin(int chan)
|
||||
{
|
||||
std::lock_guard lk(s_read_mutex);
|
||||
return s_port_states[chan].origin;
|
||||
}
|
||||
|
||||
// Get ControllerType from first byte in input payload.
|
||||
static ControllerType IdentifyControllerType(u8 data)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,11 @@ void StopScanThread();
|
|||
// Netplay and CSIDevice_GCAdapter make use of this.
|
||||
GCPadStatus Input(int chan);
|
||||
|
||||
// Retreive the latest input without changing the new connection flag.
|
||||
GCPadStatus PeekInput(int chan);
|
||||
|
||||
GCPadStatus GetOrigin(int chan);
|
||||
|
||||
void Output(int chan, u8 rumble_command);
|
||||
bool IsDetected(const char** error_message);
|
||||
bool DeviceConnected(int chan);
|
||||
|
|
Loading…
Add table
Reference in a new issue