mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-05-01 21:52:41 +02:00

SPDX standardizes how source code conveys its copyright and licensing information. See https://spdx.github.io/spdx-spec/1-rationale/ . SPDX tags are adopted in many large projects, including things like the Linux kernel.
253 lines
8.2 KiB
C++
253 lines
8.2 KiB
C++
// Copyright 2010 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "Core/HW/GCPadEmu.h"
|
|
|
|
#include <array>
|
|
|
|
#include "Common/Common.h"
|
|
#include "Common/CommonTypes.h"
|
|
|
|
#include "InputCommon/ControllerEmu/Control/Input.h"
|
|
#include "InputCommon/ControllerEmu/Control/Output.h"
|
|
#include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
|
|
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
|
|
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
|
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
|
|
#include "InputCommon/ControllerEmu/StickGate.h"
|
|
|
|
#include "InputCommon/GCPadStatus.h"
|
|
|
|
static const u16 button_bitmasks[] = {
|
|
PAD_BUTTON_A,
|
|
PAD_BUTTON_B,
|
|
PAD_BUTTON_X,
|
|
PAD_BUTTON_Y,
|
|
PAD_TRIGGER_Z,
|
|
PAD_BUTTON_START,
|
|
0 // MIC HAX
|
|
};
|
|
|
|
static const u16 trigger_bitmasks[] = {
|
|
PAD_TRIGGER_L,
|
|
PAD_TRIGGER_R,
|
|
};
|
|
|
|
static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT,
|
|
PAD_BUTTON_RIGHT};
|
|
|
|
static const char* const named_buttons[] = {"A", "B", "X", "Y", "Z", "Start"};
|
|
|
|
static const char* const named_triggers[] = {
|
|
// i18n: The left trigger button (labeled L on real controllers)
|
|
_trans("L"),
|
|
// i18n: The right trigger button (labeled R on real controllers)
|
|
_trans("R"),
|
|
// i18n: The left trigger button (labeled L on real controllers) used as an analog input
|
|
_trans("L-Analog"),
|
|
// i18n: The right trigger button (labeled R on real controllers) used as an analog input
|
|
_trans("R-Analog")};
|
|
|
|
GCPad::GCPad(const unsigned int index) : m_index(index)
|
|
{
|
|
// buttons
|
|
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
|
|
for (const char* named_button : named_buttons)
|
|
{
|
|
const bool is_start = named_button == std::string("Start");
|
|
const ControllerEmu::Translatability translate =
|
|
is_start ? ControllerEmu::Translate : ControllerEmu::DoNotTranslate;
|
|
// i18n: The START/PAUSE button on GameCube controllers
|
|
std::string ui_name = is_start ? _trans("START") : named_button;
|
|
m_buttons->AddInput(translate, named_button, std::move(ui_name));
|
|
}
|
|
|
|
// sticks
|
|
groups.emplace_back(m_main_stick = new ControllerEmu::OctagonAnalogStick(
|
|
"Main Stick", _trans("Control Stick"), MAIN_STICK_GATE_RADIUS));
|
|
groups.emplace_back(m_c_stick = new ControllerEmu::OctagonAnalogStick(
|
|
"C-Stick", _trans("C Stick"), C_STICK_GATE_RADIUS));
|
|
|
|
// triggers
|
|
groups.emplace_back(m_triggers = new ControllerEmu::MixedTriggers(_trans("Triggers")));
|
|
for (const char* named_trigger : named_triggers)
|
|
{
|
|
m_triggers->AddInput(ControllerEmu::Translate, named_trigger);
|
|
}
|
|
|
|
// rumble
|
|
groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble")));
|
|
m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor"));
|
|
|
|
// Microphone
|
|
groups.emplace_back(m_mic = new ControllerEmu::Buttons(_trans("Microphone")));
|
|
m_mic->AddInput(ControllerEmu::Translate, _trans("Button"));
|
|
|
|
// dpad
|
|
groups.emplace_back(m_dpad = new ControllerEmu::Buttons(_trans("D-Pad")));
|
|
for (const char* named_direction : named_directions)
|
|
{
|
|
m_dpad->AddInput(ControllerEmu::Translate, named_direction);
|
|
}
|
|
|
|
// options
|
|
groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options")));
|
|
m_options->AddSetting(
|
|
&m_always_connected_setting,
|
|
// i18n: Treat a controller as always being connected regardless of what
|
|
// devices the user actually has plugged in
|
|
{_trans("Always Connected"), nullptr,
|
|
_trans("If checked, the emulated controller is always connected.\n"
|
|
"If unchecked, the connection state of the emulated controller is linked\n"
|
|
"to the connection state of the real default device (if there is one).")},
|
|
false);
|
|
}
|
|
|
|
std::string GCPad::GetName() const
|
|
{
|
|
return std::string("GCPad") + char('1' + m_index);
|
|
}
|
|
|
|
ControllerEmu::ControlGroup* GCPad::GetGroup(PadGroup group)
|
|
{
|
|
switch (group)
|
|
{
|
|
case PadGroup::Buttons:
|
|
return m_buttons;
|
|
case PadGroup::MainStick:
|
|
return m_main_stick;
|
|
case PadGroup::CStick:
|
|
return m_c_stick;
|
|
case PadGroup::DPad:
|
|
return m_dpad;
|
|
case PadGroup::Triggers:
|
|
return m_triggers;
|
|
case PadGroup::Rumble:
|
|
return m_rumble;
|
|
case PadGroup::Mic:
|
|
return m_mic;
|
|
case PadGroup::Options:
|
|
return m_options;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
GCPadStatus GCPad::GetInput() const
|
|
{
|
|
const auto lock = GetStateLock();
|
|
GCPadStatus pad = {};
|
|
|
|
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected()))
|
|
{
|
|
pad.isConnected = false;
|
|
return pad;
|
|
}
|
|
|
|
// buttons
|
|
m_buttons->GetState(&pad.button, button_bitmasks);
|
|
|
|
// set analog A/B analog to full or w/e, prolly not needed
|
|
if (pad.button & PAD_BUTTON_A)
|
|
pad.analogA = 0xFF;
|
|
if (pad.button & PAD_BUTTON_B)
|
|
pad.analogB = 0xFF;
|
|
|
|
// dpad
|
|
m_dpad->GetState(&pad.button, dpad_bitmasks);
|
|
|
|
// sticks
|
|
const auto main_stick_state = m_main_stick->GetState();
|
|
pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X);
|
|
pad.stickY = MapFloat<u8>(main_stick_state.y, GCPadStatus::MAIN_STICK_CENTER_Y);
|
|
|
|
const auto c_stick_state = m_c_stick->GetState();
|
|
pad.substickX = MapFloat<u8>(c_stick_state.x, GCPadStatus::C_STICK_CENTER_X);
|
|
pad.substickY = MapFloat<u8>(c_stick_state.y, GCPadStatus::C_STICK_CENTER_Y);
|
|
|
|
// triggers
|
|
std::array<ControlState, 2> triggers;
|
|
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data());
|
|
pad.triggerLeft = MapFloat<u8>(triggers[0], 0);
|
|
pad.triggerRight = MapFloat<u8>(triggers[1], 0);
|
|
|
|
return pad;
|
|
}
|
|
|
|
void GCPad::SetOutput(const ControlState strength)
|
|
{
|
|
const auto lock = GetStateLock();
|
|
m_rumble->controls[0]->control_ref->State(strength);
|
|
}
|
|
|
|
void GCPad::LoadDefaults(const ControllerInterface& ciface)
|
|
{
|
|
EmulatedController::LoadDefaults(ciface);
|
|
|
|
// Buttons
|
|
m_buttons->SetControlExpression(0, "`X`"); // A
|
|
m_buttons->SetControlExpression(1, "`Z`"); // B
|
|
m_buttons->SetControlExpression(2, "`C`"); // X
|
|
m_buttons->SetControlExpression(3, "`S`"); // Y
|
|
m_buttons->SetControlExpression(4, "`D`"); // Z
|
|
#ifdef _WIN32
|
|
m_buttons->SetControlExpression(5, "`RETURN`"); // Start
|
|
#else
|
|
// OS X/Linux
|
|
// Start
|
|
m_buttons->SetControlExpression(5, "`Return`");
|
|
#endif
|
|
|
|
// stick modifiers to 50 %
|
|
m_main_stick->controls[4]->control_ref->range = 0.5f;
|
|
m_c_stick->controls[4]->control_ref->range = 0.5f;
|
|
|
|
// D-Pad
|
|
m_dpad->SetControlExpression(0, "`T`"); // Up
|
|
m_dpad->SetControlExpression(1, "`G`"); // Down
|
|
m_dpad->SetControlExpression(2, "`F`"); // Left
|
|
m_dpad->SetControlExpression(3, "`H`"); // Right
|
|
|
|
// C Stick
|
|
m_c_stick->SetControlExpression(0, "`I`"); // Up
|
|
m_c_stick->SetControlExpression(1, "`K`"); // Down
|
|
m_c_stick->SetControlExpression(2, "`J`"); // Left
|
|
m_c_stick->SetControlExpression(3, "`L`"); // Right
|
|
// Modifier
|
|
m_c_stick->SetControlExpression(4, "`Ctrl`");
|
|
|
|
// Control Stick
|
|
#ifdef _WIN32
|
|
m_main_stick->SetControlExpression(0, "`UP`"); // Up
|
|
m_main_stick->SetControlExpression(1, "`DOWN`"); // Down
|
|
m_main_stick->SetControlExpression(2, "`LEFT`"); // Left
|
|
m_main_stick->SetControlExpression(3, "`RIGHT`"); // Right
|
|
#elif __APPLE__
|
|
m_main_stick->SetControlExpression(0, "`Up Arrow`"); // Up
|
|
m_main_stick->SetControlExpression(1, "`Down Arrow`"); // Down
|
|
m_main_stick->SetControlExpression(2, "`Left Arrow`"); // Left
|
|
m_main_stick->SetControlExpression(3, "`Right Arrow`"); // Right
|
|
#else
|
|
m_main_stick->SetControlExpression(0, "`Up`"); // Up
|
|
m_main_stick->SetControlExpression(1, "`Down`"); // Down
|
|
m_main_stick->SetControlExpression(2, "`Left`"); // Left
|
|
m_main_stick->SetControlExpression(3, "`Right`"); // Right
|
|
#endif
|
|
// Modifier
|
|
m_main_stick->SetControlExpression(4, "`Shift`");
|
|
|
|
// Because our defaults use keyboard input, set calibration shapes to squares.
|
|
m_c_stick->SetCalibrationFromGate(ControllerEmu::SquareStickGate(1.0));
|
|
m_main_stick->SetCalibrationFromGate(ControllerEmu::SquareStickGate(1.0));
|
|
|
|
// Triggers
|
|
m_triggers->SetControlExpression(0, "`Q`"); // L
|
|
m_triggers->SetControlExpression(1, "`W`"); // R
|
|
}
|
|
|
|
bool GCPad::GetMicButton() const
|
|
{
|
|
const auto lock = GetStateLock();
|
|
return m_mic->controls.back()->GetState<bool>();
|
|
}
|