Compare commits

...

7 Commits

Author SHA1 Message Date
Jordan Woyak
2e13d3c6a3
Merge b95af2e6f0 into 2c92e5b5b3 2024-11-12 13:28:59 -06:00
OatmealDome
2c92e5b5b3
Merge pull request #13160 from cpba/flatpak-6.8-runtime
Flatpak: Upgrade kde runtime to 6.8
2024-11-12 00:30:46 -05:00
Carles Pastor
fe96bf4108 Flatpak: Upgrade kde runtime to 6.8
this version bundles SDL2-2.30.6, the temporary measure of building the
vendored version from exports is no longer necessary.
2024-11-10 12:06:06 +01:00
Jordan Woyak
b95af2e6f0 namespace fixup 2024-11-07 22:00:25 -06:00
Jordan Woyak
768f0161a8 InputCommon: Add setting to enable GCAdapter ControllerInterface backend. 2024-11-07 21:53:24 -06:00
Jordan Woyak
7418fd03bf GCAdapter: Eliminate some global variables. 2024-11-07 21:53:04 -06:00
Jordan Woyak
cbc6b15db3 InputCommon: Add gamepads from GCAdapter code to ControllerInterface. 2024-11-07 21:53:04 -06:00
13 changed files with 342 additions and 50 deletions

View File

@ -1,22 +0,0 @@
{
"name": "SDL2",
"buildsystem": "autotools",
"config-opts": ["--disable-static"],
"sources": [
{
"type": "dir",
"path": "../../Externals/SDL/SDL"
}
],
"cleanup": [ "/bin/sdl2-config",
"/include",
"/lib/libSDL2.la",
"/lib/libSDL2main.a",
"/lib/libSDL2main.la",
"/lib/libSDL2_test.a",
"/lib/libSDL2_test.la",
"/lib/cmake",
"/share/aclocal",
"/lib/pkgconfig"]
}

View File

@ -1,6 +1,6 @@
app-id: org.DolphinEmu.dolphin-emu
runtime: org.kde.Platform
runtime-version: '6.7'
runtime-version: '6.8'
sdk: org.kde.Sdk
command: dolphin-emu-wrapper
rename-desktop-file: dolphin-emu.desktop
@ -47,9 +47,6 @@ modules:
url: https://github.com/Unrud/xdg-screensaver-shim/archive/0.0.2.tar.gz
sha256: 0ed2a69fe6ee6cbffd2fe16f85116db737f17fb1e79bfb812d893cf15c728399
# build the vendored SDL2 from Externals until the runtime gets 2.30.6
- SDL2/SDL2.json
- name: dolphin-emu
buildsystem: cmake-ninja
config-opts:

View File

@ -190,6 +190,8 @@ const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING{
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};

View File

@ -108,6 +108,7 @@ extern const Info<bool> MAIN_WII_KEYBOARD;
extern const Info<bool> MAIN_WIIMOTE_CONTINUOUS_SCANNING;
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;

View File

@ -532,6 +532,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\GCAdapter.h" />
<ClInclude Include="InputCommon\ControllerInterface\Win32\Win32.h" />
<ClInclude Include="InputCommon\ControllerInterface\XInput\XInput.h" />
<ClInclude Include="InputCommon\ControllerInterface\SDL\SDL.h" />
@ -1193,6 +1194,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\GCAdapter.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\Win32\Win32.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\XInput\XInput.cpp" />
<ClCompile Include="InputCommon\ControllerInterface\SDL\SDL.cpp" />

View File

@ -3,6 +3,7 @@
#include "DolphinQt/Config/GamecubeControllersWidget.h"
#include <QCheckBox>
#include <QComboBox>
#include <QGridLayout>
#include <QGroupBox>
@ -95,6 +96,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 Wii-U 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;
@ -114,6 +119,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)
@ -184,6 +191,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()
@ -203,11 +213,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();
}

View File

@ -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;
};

View File

@ -64,6 +64,8 @@ add_library(inputcommon
ControllerInterface/MappingCommon.h
ControllerInterface/Wiimote/WiimoteController.cpp
ControllerInterface/Wiimote/WiimoteController.h
ControllerInterface/GCAdapter/GCAdapter.cpp
ControllerInterface/GCAdapter/GCAdapter.h
ControlReference/ControlReference.cpp
ControlReference/ControlReference.h
ControlReference/ExpressionParser.cpp

View File

@ -8,6 +8,7 @@
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "InputCommon/ControllerInterface/GCAdapter/GCAdapter.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;

View File

@ -0,0 +1,258 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "InputCommon/ControllerInterface/GCAdapter/GCAdapter.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

View File

@ -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

View File

@ -142,15 +142,6 @@ static Common::Event s_hotplug_event;
static std::function<void(void)> s_detect_callback;
#if defined(__FreeBSD__) && __FreeBSD__ >= 11
static bool s_libusb_hotplug_enabled = true;
#else
static bool s_libusb_hotplug_enabled = false;
#endif
#if LIBUSB_API_HAS_HOTPLUG
static libusb_hotplug_callback_handle s_hotplug_handle;
#endif
static std::unique_ptr<LibusbUtils::Context> s_libusb_context;
static u8 s_endpoint_in = 0;
@ -346,24 +337,32 @@ static void ScanThreadFunc()
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
#if LIBUSB_API_HAS_HOTPLUG
#ifndef __FreeBSD__
s_libusb_hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0;
#if defined(__FreeBSD__)
#if __FreeBSD__ >= 11
bool hotplug_enabled = true;
#else
bool hotplug_enabled = false;
#endif
if (s_libusb_hotplug_enabled)
#else
bool hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0;
#endif
libusb_hotplug_callback_handle hotplug_handle = {};
if (hotplug_enabled)
{
const int error = 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);
nullptr, &hotplug_handle);
if (error == LIBUSB_SUCCESS)
{
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Using libUSB hotplug detection");
}
else
{
s_libusb_hotplug_enabled = false;
hotplug_enabled = false;
ERROR_LOG_FMT(CONTROLLERINTERFACE, "Failed to add libUSB hotplug detection callback: {}",
LibusbUtils::ErrorWrap(error));
}
@ -378,11 +377,17 @@ static void ScanThreadFunc()
Setup();
}
if (s_libusb_hotplug_enabled)
if (hotplug_enabled)
s_hotplug_event.Wait();
else
Common::SleepCurrentThread(500);
}
#if LIBUSB_API_HAS_HOTPLUG
if (hotplug_enabled)
libusb_hotplug_deregister_callback(*s_libusb_context, hotplug_handle);
#endif
#elif GCADAPTER_USE_ANDROID_IMPLEMENTATION
JNIEnv* const env = IDCache::GetEnvForThread();
@ -410,12 +415,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));
}
}
@ -676,12 +683,6 @@ static void AddGCAdapter(libusb_device* device)
void Shutdown()
{
StopScanThread();
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
#if LIBUSB_API_HAS_HOTPLUG
if (s_libusb_context->IsValid() && s_libusb_hotplug_enabled)
libusb_hotplug_deregister_callback(*s_libusb_context, s_hotplug_handle);
#endif
#endif
Reset();
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
@ -766,6 +767,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)
{

View File

@ -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);