Add 'Enable Controller Input' checkbox to TAS dialogs

When disabled only inputs from TAS dialog are used.
When enabled inputs from TAS dialog are used, except when a change in
input is detected from a real controller, in this case the TAS value is
replaced with the real controller value.
This commit is contained in:
Rukai 2018-07-07 15:51:34 +10:00
parent 3d1a7737d8
commit b94262ec33
10 changed files with 403 additions and 289 deletions

View File

@ -63,7 +63,7 @@ add_executable(dolphin-emu
Config/Mapping/MappingCommon.cpp
Config/Mapping/MappingIndicator.cpp
Config/Mapping/MappingNumeric.cpp
Config/Mapping/MappingRadio.cpp
Config/Mapping/MappingRadio.cpp
Config/Mapping/MappingWidget.cpp
Config/Mapping/MappingWindow.cpp
Config/Mapping/WiimoteEmuExtension.cpp
@ -113,7 +113,7 @@ add_executable(dolphin-emu
Settings/USBDeviceAddToWhitelistDialog.cpp
TAS/GCTASInputWindow.cpp
TAS/WiiTASInputWindow.cpp
TAS/Shared.cpp
TAS/TASInputWindow.cpp
TAS/StickWidget.cpp
TAS/IRWidget.cpp
Updater.cpp

View File

@ -110,6 +110,7 @@
<QtMoc Include="FIFO\FIFOPlayerWindow.h" />
<QtMoc Include="TAS\GCTASInputWindow.h" />
<QtMoc Include="TAS\WiiTASInputWindow.h" />
<QtMoc Include="TAS\TASInputWindow.h" />
<QtMoc Include="TAS\StickWidget.h" />
<QtMoc Include="TAS\IRWidget.h" />
<QtMoc Include="Debugger\BreakpointWidget.h" />
@ -246,6 +247,7 @@
<ClCompile Include="$(QtMocOutPrefix)SettingsWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)SoftwareRendererWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)StickWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)TASInputWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ToolBar.cpp" />
<ClCompile Include="$(QtMocOutPrefix)USBDeviceAddToWhitelistDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)Updater.cpp" />
@ -318,9 +320,9 @@
<ClCompile Include="QtUtils\WinIconHelper.cpp" />
<ClCompile Include="TAS\GCTASInputWindow.cpp" />
<ClCompile Include="TAS\WiiTASInputWindow.cpp" />
<ClCompile Include="TAS\TASInputWindow.cpp" />
<ClCompile Include="TAS\StickWidget.cpp" />
<ClCompile Include="TAS\IRWidget.cpp" />
<ClCompile Include="TAS\Shared.cpp" />
<ClCompile Include="Debugger\BreakpointWidget.cpp" />
<ClCompile Include="Debugger\NewBreakpointDialog.cpp" />
<ClCompile Include="Debugger\RegisterColumn.cpp" />
@ -377,7 +379,6 @@
<ClInclude Include="QtUtils\RunOnObject.h" />
<ClInclude Include="QtUtils\WinIconHelper.h" />
<ClInclude Include="Resources.h" />
<ClInclude Include="TAS\Shared.h" />
<ClInclude Include="Translation.h" />
<ClInclude Include="WiiUpdate.h" />
</ItemGroup>

View File

@ -12,35 +12,32 @@
#include "Common/CommonTypes.h"
#include "DolphinQt/TAS/Shared.h"
#include "InputCommon/GCPadStatus.h"
GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : QDialog(parent)
GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent)
{
setWindowTitle(tr("GameCube TAS Input %1").arg(num + 1));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
auto* main_stick_box = CreateStickInputs(this, tr("Main Stick"), m_x_main_stick_value,
m_y_main_stick_value, 255, 255, Qt::Key_F, Qt::Key_G);
auto* c_stick_box = CreateStickInputs(this, tr("C Stick"), m_x_c_stick_value, m_y_c_stick_value,
255, 255, Qt::Key_H, Qt::Key_J);
m_main_stick_box = CreateStickInputs(tr("Main Stick"), m_x_main_stick_value, m_y_main_stick_value,
255, 255, Qt::Key_F, Qt::Key_G);
m_c_stick_box = CreateStickInputs(tr("C Stick"), m_x_c_stick_value, m_y_c_stick_value, 255, 255,
Qt::Key_H, Qt::Key_J);
auto* top_layout = new QHBoxLayout;
top_layout->addWidget(main_stick_box);
top_layout->addWidget(c_stick_box);
top_layout->addWidget(m_main_stick_box);
top_layout->addWidget(m_c_stick_box);
auto* triggers_box = new QGroupBox(tr("Triggers"));
m_triggers_box = new QGroupBox(tr("Triggers"));
auto* l_trigger_layout = CreateSliderValuePairLayout(this, tr("Left"), m_l_trigger_value, 255,
Qt::Key_N, triggers_box);
auto* r_trigger_layout = CreateSliderValuePairLayout(this, tr("Right"), m_r_trigger_value, 255,
Qt::Key_M, triggers_box);
auto* l_trigger_layout =
CreateSliderValuePairLayout(tr("Left"), m_l_trigger_value, 255, Qt::Key_N, m_triggers_box);
auto* r_trigger_layout =
CreateSliderValuePairLayout(tr("Right"), m_r_trigger_value, 255, Qt::Key_M, m_triggers_box);
auto* triggers_layout = new QVBoxLayout;
triggers_layout->addLayout(l_trigger_layout);
triggers_layout->addLayout(r_trigger_layout);
triggers_box->setLayout(triggers_layout);
m_triggers_box->setLayout(triggers_layout);
m_a_button = new QCheckBox(QStringLiteral("&A"));
m_b_button = new QCheckBox(QStringLiteral("&B"));
@ -76,42 +73,35 @@ GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : QDialog(parent)
buttons_layout->addLayout(buttons_layout1);
buttons_layout->addLayout(buttons_layout2);
auto* buttons_box = new QGroupBox(tr("Buttons"));
buttons_box->setLayout(buttons_layout);
m_buttons_box = new QGroupBox(tr("Buttons"));
m_buttons_box->setLayout(buttons_layout);
auto* layout = new QVBoxLayout;
layout->addLayout(top_layout);
layout->addWidget(triggers_box);
layout->addWidget(buttons_box);
layout->addWidget(m_triggers_box);
layout->addWidget(m_buttons_box);
layout->addWidget(m_use_controller);
setLayout(layout);
}
static void SetButton(QCheckBox* button, GCPadStatus* pad, u16 mask)
{
if (button->isChecked())
pad->button |= mask;
else
pad->button &= ~mask;
}
void GCTASInputWindow::GetValues(GCPadStatus* pad)
{
if (!isVisible())
return;
SetButton(m_a_button, pad, PAD_BUTTON_A);
SetButton(m_b_button, pad, PAD_BUTTON_B);
SetButton(m_x_button, pad, PAD_BUTTON_X);
SetButton(m_y_button, pad, PAD_BUTTON_Y);
SetButton(m_z_button, pad, PAD_TRIGGER_Z);
SetButton(m_l_button, pad, PAD_TRIGGER_L);
SetButton(m_r_button, pad, PAD_TRIGGER_R);
SetButton(m_left_button, pad, PAD_BUTTON_LEFT);
SetButton(m_up_button, pad, PAD_BUTTON_UP);
SetButton(m_down_button, pad, PAD_BUTTON_DOWN);
SetButton(m_right_button, pad, PAD_BUTTON_RIGHT);
SetButton(m_start_button, pad, PAD_BUTTON_START);
GetButton<u16>(m_a_button, pad->button, PAD_BUTTON_A);
GetButton<u16>(m_b_button, pad->button, PAD_BUTTON_B);
GetButton<u16>(m_x_button, pad->button, PAD_BUTTON_X);
GetButton<u16>(m_y_button, pad->button, PAD_BUTTON_Y);
GetButton<u16>(m_z_button, pad->button, PAD_TRIGGER_Z);
GetButton<u16>(m_l_button, pad->button, PAD_TRIGGER_L);
GetButton<u16>(m_r_button, pad->button, PAD_TRIGGER_R);
GetButton<u16>(m_left_button, pad->button, PAD_BUTTON_LEFT);
GetButton<u16>(m_up_button, pad->button, PAD_BUTTON_UP);
GetButton<u16>(m_down_button, pad->button, PAD_BUTTON_DOWN);
GetButton<u16>(m_right_button, pad->button, PAD_BUTTON_RIGHT);
GetButton<u16>(m_start_button, pad->button, PAD_BUTTON_START);
if (m_a_button->isChecked())
pad->analogA = 0xFF;
@ -123,11 +113,12 @@ void GCTASInputWindow::GetValues(GCPadStatus* pad)
else
pad->analogB = 0x00;
pad->triggerLeft = m_l_trigger_value->value();
pad->triggerRight = m_r_trigger_value->value();
GetSpinBoxU8(m_l_trigger_value, pad->triggerLeft);
GetSpinBoxU8(m_r_trigger_value, pad->triggerRight);
pad->stickX = m_x_main_stick_value->value();
pad->stickY = m_y_main_stick_value->value();
pad->substickX = m_x_c_stick_value->value();
pad->substickY = m_y_c_stick_value->value();
GetSpinBoxU8(m_x_main_stick_value, pad->stickX);
GetSpinBoxU8(m_y_main_stick_value, pad->stickY);
GetSpinBoxU8(m_x_c_stick_value, pad->substickX);
GetSpinBoxU8(m_y_c_stick_value, pad->substickY);
}

View File

@ -4,15 +4,14 @@
#pragma once
#include <QDialog>
#include "Common/CommonTypes.h"
#include "DolphinQt/TAS/TASInputWindow.h"
class QCheckBox;
class QGroupBox;
class QSpinBox;
struct GCPadStatus;
class GCTASInputWindow : public QDialog
class GCTASInputWindow : public TASInputWindow
{
Q_OBJECT
public:
@ -38,4 +37,8 @@ private:
QSpinBox* m_y_main_stick_value;
QSpinBox* m_x_c_stick_value;
QSpinBox* m_y_c_stick_value;
QGroupBox* m_main_stick_box;
QGroupBox* m_c_stick_box;
QGroupBox* m_triggers_box;
QGroupBox* m_buttons_box;
};

View File

@ -1,120 +0,0 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/TAS/Shared.h"
#include <cmath>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QShortcut>
#include <QSlider>
#include <QSpinBox>
#include <QVBoxLayout>
#include "Common/CommonTypes.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
#include "DolphinQt/TAS/StickWidget.h"
#include "InputCommon/GCPadStatus.h"
QGroupBox* CreateStickInputs(QDialog* window, QString name, QSpinBox*& x_value, QSpinBox*& y_value,
u16 max_x, u16 max_y, Qt::Key x_shortcut_key, Qt::Key y_shortcut_key)
{
const QKeySequence x_shortcut_key_sequence = QKeySequence(Qt::ALT + x_shortcut_key);
const QKeySequence y_shortcut_key_sequence = QKeySequence(Qt::ALT + y_shortcut_key);
auto* box =
new QGroupBox(QStringLiteral("%1 (%2/%3)")
.arg(name, x_shortcut_key_sequence.toString(QKeySequence::NativeText),
y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
auto* x_layout = new QHBoxLayout;
x_value =
CreateSliderValuePair(window, x_layout, max_x, x_shortcut_key_sequence, Qt::Horizontal, box);
auto* y_layout = new QVBoxLayout;
y_value =
CreateSliderValuePair(window, y_layout, max_y, y_shortcut_key_sequence, Qt::Vertical, box);
y_value->setMaximumWidth(60);
auto* visual = new StickWidget(window, max_x, max_y);
window->connect(x_value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), visual,
&StickWidget::SetX);
window->connect(y_value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), visual,
&StickWidget::SetY);
window->connect(visual, &StickWidget::ChangedX, x_value, &QSpinBox::setValue);
window->connect(visual, &StickWidget::ChangedY, y_value, &QSpinBox::setValue);
x_value->setValue(static_cast<int>(std::round(max_x / 2.)));
y_value->setValue(static_cast<int>(std::round(max_y / 2.)));
auto* visual_ar = new AspectRatioWidget(visual, max_x, max_y);
auto* visual_layout = new QHBoxLayout;
visual_layout->addWidget(visual_ar);
visual_layout->addLayout(y_layout);
auto* layout = new QVBoxLayout;
layout->addLayout(x_layout);
layout->addLayout(visual_layout);
box->setLayout(layout);
return box;
}
QBoxLayout* CreateSliderValuePairLayout(QDialog* window, QString name, QSpinBox*& value, u16 max,
Qt::Key shortcut_key, QWidget* shortcut_widget, bool invert)
{
const QKeySequence shortcut_key_sequence = QKeySequence(Qt::ALT + shortcut_key);
auto* label = new QLabel(QStringLiteral("%1 (%2)").arg(
name, shortcut_key_sequence.toString(QKeySequence::NativeText)));
QBoxLayout* layout = new QHBoxLayout;
layout->addWidget(label);
value = CreateSliderValuePair(window, layout, max, shortcut_key_sequence, Qt::Horizontal,
shortcut_widget, invert);
return layout;
}
// The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
// This is done to avoid ambigous shortcuts
QSpinBox* CreateSliderValuePair(QDialog* window, QBoxLayout* layout, u16 max,
QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
QWidget* shortcut_widget, bool invert)
{
auto* value = new QSpinBox();
value->setRange(0, 99999);
window->connect(value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
[value, max](int i) {
if (i > max)
value->setValue(max);
});
auto* slider = new QSlider(orientation);
slider->setRange(0, max);
slider->setFocusPolicy(Qt::ClickFocus);
slider->setInvertedAppearance(invert);
window->connect(slider, &QSlider::valueChanged, value, &QSpinBox::setValue);
window->connect(value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), slider,
&QSlider::setValue);
auto* shortcut = new QShortcut(shortcut_key_sequence, shortcut_widget);
window->connect(shortcut, &QShortcut::activated, [value] {
value->setFocus();
value->selectAll();
});
layout->addWidget(slider);
layout->addWidget(value);
if (orientation == Qt::Vertical)
layout->setAlignment(slider, Qt::AlignRight);
return value;
}

View File

@ -1,26 +0,0 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QWidget>
#include "Common/CommonTypes.h"
struct GCPadStatus;
class QDialog;
class QString;
class QSpinBox;
class QCheckBox;
class QBoxLayout;
class QGroupBox;
QGroupBox* CreateStickInputs(QDialog* window, QString name, QSpinBox*& x_value, QSpinBox*& y_value,
u16 max_x, u16 max_y, Qt::Key x_shortcut_key, Qt::Key y_shortcut_key);
QBoxLayout* CreateSliderValuePairLayout(QDialog* window, QString name, QSpinBox*& value, u16 max,
Qt::Key shortcut_key, QWidget* shortcut_widget,
bool invert = false);
QSpinBox* CreateSliderValuePair(QDialog* window, QBoxLayout* layout, u16 max,
QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
QWidget* shortcut_widget, bool invert = false);

View File

@ -0,0 +1,192 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cmath>
#include <QCheckBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QShortcut>
#include <QSlider>
#include <QSpinBox>
#include <QVBoxLayout>
#include "Common/CommonTypes.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/TAS/StickWidget.h"
#include "DolphinQt/TAS/TASInputWindow.h"
#include "InputCommon/GCPadStatus.h"
TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_use_controller = new QCheckBox(QStringLiteral("Enable Controller Inpu&t"));
m_use_controller->setToolTip(tr("Warning: Analog inputs may reset to controller values at "
"random. In some cases this can be fixed by adding a deadzone."));
}
QGroupBox* TASInputWindow::CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value,
u16 max_x, u16 max_y, Qt::Key x_shortcut_key,
Qt::Key y_shortcut_key)
{
const QKeySequence x_shortcut_key_sequence = QKeySequence(Qt::ALT + x_shortcut_key);
const QKeySequence y_shortcut_key_sequence = QKeySequence(Qt::ALT + y_shortcut_key);
auto* box =
new QGroupBox(QStringLiteral("%1 (%2/%3)")
.arg(name, x_shortcut_key_sequence.toString(QKeySequence::NativeText),
y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
auto* x_layout = new QHBoxLayout;
x_value = CreateSliderValuePair(x_layout, max_x, x_shortcut_key_sequence, Qt::Horizontal, box);
auto* y_layout = new QVBoxLayout;
y_value = CreateSliderValuePair(y_layout, max_y, y_shortcut_key_sequence, Qt::Vertical, box);
y_value->setMaximumWidth(60);
auto* visual = new StickWidget(this, max_x, max_y);
connect(x_value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), visual,
&StickWidget::SetX);
connect(y_value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), visual,
&StickWidget::SetY);
connect(visual, &StickWidget::ChangedX, x_value, &QSpinBox::setValue);
connect(visual, &StickWidget::ChangedY, y_value, &QSpinBox::setValue);
x_value->setValue(static_cast<int>(std::round(max_x / 2.)));
y_value->setValue(static_cast<int>(std::round(max_y / 2.)));
auto* visual_ar = new AspectRatioWidget(visual, max_x, max_y);
auto* visual_layout = new QHBoxLayout;
visual_layout->addWidget(visual_ar);
visual_layout->addLayout(y_layout);
auto* layout = new QVBoxLayout;
layout->addLayout(x_layout);
layout->addLayout(visual_layout);
box->setLayout(layout);
return box;
}
QBoxLayout* TASInputWindow::CreateSliderValuePairLayout(QString name, QSpinBox*& value, u16 max,
Qt::Key shortcut_key,
QWidget* shortcut_widget, bool invert)
{
const QKeySequence shortcut_key_sequence = QKeySequence(Qt::ALT + shortcut_key);
auto* label = new QLabel(QStringLiteral("%1 (%2)").arg(
name, shortcut_key_sequence.toString(QKeySequence::NativeText)));
QBoxLayout* layout = new QHBoxLayout;
layout->addWidget(label);
value = CreateSliderValuePair(layout, max, shortcut_key_sequence, Qt::Horizontal, shortcut_widget,
invert);
return layout;
}
// The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
// This is done to avoid ambigous shortcuts
QSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, u16 max,
QKeySequence shortcut_key_sequence,
Qt::Orientation orientation,
QWidget* shortcut_widget, bool invert)
{
auto* value = new QSpinBox();
value->setRange(0, 99999);
connect(value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
[value, max](int i) {
if (i > max)
value->setValue(max);
});
auto* slider = new QSlider(orientation);
slider->setRange(0, max);
slider->setFocusPolicy(Qt::ClickFocus);
slider->setInvertedAppearance(invert);
connect(slider, &QSlider::valueChanged, value, &QSpinBox::setValue);
connect(value, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), slider,
&QSlider::setValue);
auto* shortcut = new QShortcut(shortcut_key_sequence, shortcut_widget);
connect(shortcut, &QShortcut::activated, [value] {
value->setFocus();
value->selectAll();
});
layout->addWidget(slider);
layout->addWidget(value);
if (orientation == Qt::Vertical)
layout->setAlignment(slider, Qt::AlignRight);
return value;
}
template <typename UX>
void TASInputWindow::GetButton(QCheckBox* checkbox, UX& buttons, UX mask)
{
const bool pressed = (buttons & mask) != 0;
if (m_use_controller->isChecked())
{
if (pressed)
{
m_checkbox_set_by_controller[checkbox] = true;
QueueOnObject(checkbox, [checkbox] { checkbox->setChecked(true); });
}
else if (m_checkbox_set_by_controller.count(checkbox) && m_checkbox_set_by_controller[checkbox])
{
m_checkbox_set_by_controller[checkbox] = false;
QueueOnObject(checkbox, [checkbox] { checkbox->setChecked(false); });
}
}
if (checkbox->isChecked())
buttons |= mask;
else
buttons &= ~mask;
}
template void TASInputWindow::GetButton<u8>(QCheckBox* button, u8& pad, u8 mask);
template void TASInputWindow::GetButton<u16>(QCheckBox* button, u16& pad, u16 mask);
void TASInputWindow::GetSpinBoxU8(QSpinBox* spin, u8& controller_value)
{
if (m_use_controller->isChecked())
{
if (!m_spinbox_most_recent_values_u8.count(spin) ||
m_spinbox_most_recent_values_u8[spin] != controller_value)
QueueOnObject(spin, [spin, controller_value] { spin->setValue(controller_value); });
m_spinbox_most_recent_values_u8[spin] = controller_value;
}
else
{
m_spinbox_most_recent_values_u8.clear();
}
controller_value = spin->value();
}
void TASInputWindow::GetSpinBoxU16(QSpinBox* spin, u16& controller_value)
{
if (m_use_controller->isChecked())
{
if (!m_spinbox_most_recent_values_u16.count(spin) ||
m_spinbox_most_recent_values_u16[spin] != controller_value)
QueueOnObject(spin, [spin, controller_value] { spin->setValue(controller_value); });
m_spinbox_most_recent_values_u16[spin] = controller_value;
}
else
{
m_spinbox_most_recent_values_u16.clear();
}
controller_value = spin->value();
}

View File

@ -0,0 +1,44 @@
// Copyright 2018 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QDialog>
#include "Common/CommonTypes.h"
struct GCPadStatus;
class QBoxLayout;
class QCheckBox;
class QDialog;
class QGroupBox;
class QSpinBox;
class QString;
class TASInputWindow : public QDialog
{
Q_OBJECT
public:
explicit TASInputWindow(QWidget* parent);
protected:
QGroupBox* CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value, u16 max_x,
u16 max_y, Qt::Key x_shortcut_key, Qt::Key y_shortcut_key);
QBoxLayout* CreateSliderValuePairLayout(QString name, QSpinBox*& value, u16 max,
Qt::Key shortcut_key, QWidget* shortcut_widget,
bool invert = false);
QSpinBox* CreateSliderValuePair(QBoxLayout* layout, u16 max, QKeySequence shortcut_key_sequence,
Qt::Orientation orientation, QWidget* shortcut_widget,
bool invert = false);
template <typename UX>
void GetButton(QCheckBox* button, UX& pad, UX mask);
void GetSpinBoxU8(QSpinBox* spin, u8& controller_value);
void GetSpinBoxU16(QSpinBox* spin, u16& controller_value);
QCheckBox* m_use_controller;
private:
std::map<QCheckBox*, bool> m_checkbox_set_by_controller;
std::map<QSpinBox*, u8> m_spinbox_most_recent_values_u8;
std::map<QSpinBox*, u8> m_spinbox_most_recent_values_u16;
};

View File

@ -2,8 +2,6 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/TAS/WiiTASInputWindow.h"
#include <cmath>
#include <QCheckBox>
@ -23,15 +21,14 @@
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/TAS/IRWidget.h"
#include "DolphinQt/TAS/Shared.h"
#include "DolphinQt/TAS/WiiTASInputWindow.h"
#include "InputCommon/InputConfig.h"
WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent), m_num(num)
WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent), m_num(num)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
const QKeySequence ir_x_shortcut_key_sequence = QKeySequence(Qt::ALT + Qt::Key_F);
const QKeySequence ir_y_shortcut_key_sequence = QKeySequence(Qt::ALT + Qt::Key_G);
@ -41,12 +38,12 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
ir_y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
auto* x_layout = new QHBoxLayout;
m_ir_x_value = CreateSliderValuePair(this, x_layout, ir_max_x, ir_x_shortcut_key_sequence,
m_ir_x_value = CreateSliderValuePair(x_layout, ir_max_x, ir_x_shortcut_key_sequence,
Qt::Horizontal, m_ir_box, true);
auto* y_layout = new QVBoxLayout;
m_ir_y_value = CreateSliderValuePair(this, y_layout, ir_max_y, ir_y_shortcut_key_sequence,
Qt::Vertical, m_ir_box, true);
m_ir_y_value = CreateSliderValuePair(y_layout, ir_max_y, ir_y_shortcut_key_sequence, Qt::Vertical,
m_ir_box, true);
m_ir_y_value->setMaximumWidth(60);
auto* visual = new IRWidget(this);
@ -71,15 +68,15 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
ir_layout->addLayout(visual_layout);
m_ir_box->setLayout(ir_layout);
m_nunchuk_stick_box = CreateStickInputs(this, tr("Nunchuk Stick"), m_nunchuk_stick_x_value,
m_nunchuk_stick_box = CreateStickInputs(tr("Nunchuk Stick"), m_nunchuk_stick_x_value,
m_nunchuk_stick_y_value, 255, 255, Qt::Key_X, Qt::Key_Y);
m_classic_left_stick_box =
CreateStickInputs(this, tr("Left Stick"), m_classic_left_stick_x_value,
CreateStickInputs(tr("Left Stick"), m_classic_left_stick_x_value,
m_classic_left_stick_y_value, 63, 63, Qt::Key_F, Qt::Key_G);
m_classic_right_stick_box =
CreateStickInputs(this, tr("Right Stick"), m_classic_right_stick_x_value,
CreateStickInputs(tr("Right Stick"), m_classic_right_stick_x_value,
m_classic_right_stick_y_value, 31, 31, Qt::Key_Q, Qt::Key_W);
// Need to enforce the same minimum width because otherwise the different lengths in the labels
@ -97,17 +94,21 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
auto* remote_orientation_x_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("X"), m_remote_orientation_x_value, 1023, Qt::Key_Q,
CreateSliderValuePairLayout(tr("X"), m_remote_orientation_x_value, 1023, Qt::Key_Q,
m_remote_orientation_box);
auto* remote_orientation_y_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("Y"), m_remote_orientation_y_value, 1023, Qt::Key_W,
CreateSliderValuePairLayout(tr("Y"), m_remote_orientation_y_value, 1023, Qt::Key_W,
m_remote_orientation_box);
auto* remote_orientation_z_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("Z"), m_remote_orientation_z_value, 1023, Qt::Key_E,
CreateSliderValuePairLayout(tr("Z"), m_remote_orientation_z_value, 1023, Qt::Key_E,
m_remote_orientation_box);
m_remote_orientation_x_value->setValue(512);
m_remote_orientation_y_value->setValue(512);
m_remote_orientation_z_value->setValue(616);
auto* remote_orientation_layout = new QVBoxLayout;
remote_orientation_layout->addLayout(remote_orientation_x_layout);
remote_orientation_layout->addLayout(remote_orientation_y_layout);
@ -118,17 +119,21 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
auto* nunchuk_orientation_x_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("X"), m_nunchuk_orientation_x_value, 1023, Qt::Key_I,
CreateSliderValuePairLayout(tr("X"), m_nunchuk_orientation_x_value, 1023, Qt::Key_I,
m_nunchuk_orientation_box);
auto* nunchuk_orientation_y_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("Y"), m_nunchuk_orientation_y_value, 1023, Qt::Key_O,
CreateSliderValuePairLayout(tr("Y"), m_nunchuk_orientation_y_value, 1023, Qt::Key_O,
m_nunchuk_orientation_box);
auto* nunchuk_orientation_z_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(this, tr("Z"), m_nunchuk_orientation_z_value, 1023, Qt::Key_P,
CreateSliderValuePairLayout(tr("Z"), m_nunchuk_orientation_z_value, 1023, Qt::Key_P,
m_nunchuk_orientation_box);
m_nunchuk_orientation_x_value->setValue(512);
m_nunchuk_orientation_y_value->setValue(512);
m_nunchuk_orientation_z_value->setValue(512);
auto* nunchuk_orientation_layout = new QVBoxLayout;
nunchuk_orientation_layout->addLayout(nunchuk_orientation_x_layout);
nunchuk_orientation_layout->addLayout(nunchuk_orientation_y_layout);
@ -136,9 +141,9 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
m_nunchuk_orientation_box->setLayout(nunchuk_orientation_layout);
m_triggers_box = new QGroupBox(tr("Triggers"));
auto* l_trigger_layout = CreateSliderValuePairLayout(this, tr("Left"), m_left_trigger_value, 31,
Qt::Key_N, m_triggers_box);
auto* r_trigger_layout = CreateSliderValuePairLayout(this, tr("Right"), m_right_trigger_value, 31,
auto* l_trigger_layout =
CreateSliderValuePairLayout(tr("Left"), m_left_trigger_value, 31, Qt::Key_N, m_triggers_box);
auto* r_trigger_layout = CreateSliderValuePairLayout(tr("Right"), m_right_trigger_value, 31,
Qt::Key_M, m_triggers_box);
auto* triggers_layout = new QVBoxLayout;
@ -241,6 +246,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : QDialog(parent)
layout->addWidget(m_remote_buttons_box);
layout->addWidget(m_nunchuk_buttons_box);
layout->addWidget(m_classic_buttons_box);
layout->addWidget(m_use_controller);
layout->setAlignment(m_nunchuk_buttons_box, Qt::AlignLeft);
setLayout(layout);
@ -312,15 +318,6 @@ void WiiTASInputWindow::UpdateExt(u8 ext)
}
}
template <typename UX>
static void SetButton(QCheckBox* check_box, UX* buttons, UX mask)
{
if (check_box->isChecked())
*buttons |= mask;
else
*buttons &= ~mask;
}
void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rptf, int ext,
wiimote_key key)
{
@ -336,18 +333,18 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
if (m_remote_buttons_box->isVisible() && buttons_data)
{
u16* buttons = &(reinterpret_cast<wm_buttons*>(buttons_data))->hex;
SetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
SetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
SetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
SetButton<u16>(m_2_button, buttons, WiimoteEmu::Wiimote::BUTTON_TWO);
SetButton<u16>(m_plus_button, buttons, WiimoteEmu::Wiimote::BUTTON_PLUS);
SetButton<u16>(m_minus_button, buttons, WiimoteEmu::Wiimote::BUTTON_MINUS);
SetButton<u16>(m_home_button, buttons, WiimoteEmu::Wiimote::BUTTON_HOME);
SetButton<u16>(m_left_button, buttons, WiimoteEmu::Wiimote::PAD_LEFT);
SetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
SetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
SetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);
u16& buttons = (reinterpret_cast<wm_buttons*>(buttons_data))->hex;
GetButton<u16>(m_a_button, buttons, WiimoteEmu::Wiimote::BUTTON_A);
GetButton<u16>(m_b_button, buttons, WiimoteEmu::Wiimote::BUTTON_B);
GetButton<u16>(m_1_button, buttons, WiimoteEmu::Wiimote::BUTTON_ONE);
GetButton<u16>(m_2_button, buttons, WiimoteEmu::Wiimote::BUTTON_TWO);
GetButton<u16>(m_plus_button, buttons, WiimoteEmu::Wiimote::BUTTON_PLUS);
GetButton<u16>(m_minus_button, buttons, WiimoteEmu::Wiimote::BUTTON_MINUS);
GetButton<u16>(m_home_button, buttons, WiimoteEmu::Wiimote::BUTTON_HOME);
GetButton<u16>(m_left_button, buttons, WiimoteEmu::Wiimote::PAD_LEFT);
GetButton<u16>(m_up_button, buttons, WiimoteEmu::Wiimote::PAD_UP);
GetButton<u16>(m_down_button, buttons, WiimoteEmu::Wiimote::PAD_DOWN);
GetButton<u16>(m_right_button, buttons, WiimoteEmu::Wiimote::PAD_RIGHT);
}
if (m_remote_orientation_box->isVisible() && accel_data && buttons_data)
@ -355,16 +352,24 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
wm_accel& accel = *reinterpret_cast<wm_accel*>(accel_data);
wm_buttons& buttons = *reinterpret_cast<wm_buttons*>(buttons_data);
accel.x = m_remote_orientation_x_value->value() >> 2;
accel.y = m_remote_orientation_y_value->value() >> 2;
accel.z = m_remote_orientation_z_value->value() >> 2;
u16 accel_x = (accel.x << 2) & (buttons.acc_x_lsb & 0b11);
u16 accel_y = (accel.y << 2) & ((buttons.acc_y_lsb & 0b1) << 1);
u16 accel_z = (accel.z << 2) & ((buttons.acc_z_lsb & 0b1) << 1);
buttons.acc_x_lsb = m_remote_orientation_x_value->value() & 0x3;
buttons.acc_y_lsb = m_remote_orientation_y_value->value() >> 1 & 0x1;
buttons.acc_z_lsb = m_remote_orientation_z_value->value() >> 1 & 0x1;
GetSpinBoxU16(m_remote_orientation_x_value, accel_x);
GetSpinBoxU16(m_remote_orientation_y_value, accel_y);
GetSpinBoxU16(m_remote_orientation_z_value, accel_z);
accel.x = accel_x >> 2;
accel.y = accel_y >> 2;
accel.z = accel_z >> 2;
buttons.acc_x_lsb = accel_x & 0b11;
buttons.acc_y_lsb = (accel_y >> 1) & 0b1;
buttons.acc_z_lsb = (accel_z >> 1) & 0b1;
}
if (m_ir_box->isVisible() && ir_data)
if (m_ir_box->isVisible() && ir_data && !m_use_controller->isChecked())
{
u16 y = m_ir_y_value->value();
std::array<u16, 4> x;
@ -429,19 +434,30 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
if (ext_data && m_nunchuk_stick_box->isVisible())
{
wm_nc& nunchuk = *reinterpret_cast<wm_nc*>(ext_data);
nunchuk.jx = m_nunchuk_stick_x_value->value();
nunchuk.jy = m_nunchuk_stick_y_value->value();
nunchuk.ax = m_nunchuk_orientation_x_value->value() >> 2;
nunchuk.bt.acc_x_lsb = m_nunchuk_orientation_x_value->value() & 0x3;
nunchuk.ay = m_nunchuk_orientation_y_value->value() >> 2;
nunchuk.bt.acc_y_lsb = m_nunchuk_orientation_y_value->value() & 0x3;
nunchuk.az = m_nunchuk_orientation_z_value->value() >> 2;
nunchuk.bt.acc_z_lsb = m_nunchuk_orientation_z_value->value() & 0x3;
GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx);
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
SetButton<u8>(m_c_button, &nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_C);
SetButton<u8>(m_z_button, &nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.bt.hex ^= 0x3;
u16 accel_x = nunchuk.ax << 2 & (nunchuk.bt.acc_x_lsb & 0b11);
u16 accel_y = nunchuk.ay << 2 & (nunchuk.bt.acc_y_lsb & 0b11);
u16 accel_z = nunchuk.az << 2 & (nunchuk.bt.acc_z_lsb & 0b11);
GetSpinBoxU16(m_nunchuk_orientation_x_value, accel_x);
GetSpinBoxU16(m_nunchuk_orientation_y_value, accel_y);
GetSpinBoxU16(m_nunchuk_orientation_z_value, accel_z);
nunchuk.ax = accel_x >> 2;
nunchuk.ay = accel_y >> 2;
nunchuk.az = accel_z >> 2;
nunchuk.bt.acc_x_lsb = accel_x & 0b11;
nunchuk.bt.acc_y_lsb = accel_y & 0b11;
nunchuk.bt.acc_z_lsb = accel_z & 0b11;
nunchuk.bt.hex ^= 0b11;
GetButton<u8>(m_c_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_C);
GetButton<u8>(m_z_button, nunchuk.bt.hex, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.bt.hex ^= 0b11;
WiimoteEncrypt(&key, reinterpret_cast<u8*>(&nunchuk), 0, sizeof(wm_nc));
}
@ -450,36 +466,51 @@ void WiiTASInputWindow::GetValues(u8* report_data, WiimoteEmu::ReportFeatures rp
{
wm_classic_extension& cc = *reinterpret_cast<wm_classic_extension*>(ext_data);
WiimoteDecrypt(&key, reinterpret_cast<u8*>(&cc), 0, sizeof(wm_classic_extension));
cc.bt.hex = 0;
SetButton<u16>(m_classic_a_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
SetButton<u16>(m_classic_b_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_B);
SetButton<u16>(m_classic_x_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_X);
SetButton<u16>(m_classic_y_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_Y);
SetButton<u16>(m_classic_plus_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_PLUS);
SetButton<u16>(m_classic_minus_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_MINUS);
SetButton<u16>(m_classic_l_button, &cc.bt.hex, WiimoteEmu::Classic::TRIGGER_L);
SetButton<u16>(m_classic_r_button, &cc.bt.hex, WiimoteEmu::Classic::TRIGGER_R);
SetButton<u16>(m_classic_zl_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZL);
SetButton<u16>(m_classic_zr_button, &cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZR);
SetButton<u16>(m_classic_left_button, &cc.bt.hex, WiimoteEmu::Classic::PAD_LEFT);
SetButton<u16>(m_classic_up_button, &cc.bt.hex, WiimoteEmu::Classic::PAD_UP);
SetButton<u16>(m_classic_down_button, &cc.bt.hex, WiimoteEmu::Classic::PAD_DOWN);
SetButton<u16>(m_classic_right_button, &cc.bt.hex, WiimoteEmu::Classic::PAD_RIGHT);
cc.bt.hex ^= 0xFFFF;
GetButton<u16>(m_classic_a_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_A);
GetButton<u16>(m_classic_b_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_B);
GetButton<u16>(m_classic_x_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_X);
GetButton<u16>(m_classic_y_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_Y);
GetButton<u16>(m_classic_plus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_PLUS);
GetButton<u16>(m_classic_minus_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_MINUS);
GetButton<u16>(m_classic_l_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_L);
GetButton<u16>(m_classic_r_button, cc.bt.hex, WiimoteEmu::Classic::TRIGGER_R);
GetButton<u16>(m_classic_zl_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZL);
GetButton<u16>(m_classic_zr_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_ZR);
GetButton<u16>(m_classic_home_button, cc.bt.hex, WiimoteEmu::Classic::BUTTON_HOME);
GetButton<u16>(m_classic_left_button, cc.bt.hex, WiimoteEmu::Classic::PAD_LEFT);
GetButton<u16>(m_classic_up_button, cc.bt.hex, WiimoteEmu::Classic::PAD_UP);
GetButton<u16>(m_classic_down_button, cc.bt.hex, WiimoteEmu::Classic::PAD_DOWN);
GetButton<u16>(m_classic_right_button, cc.bt.hex, WiimoteEmu::Classic::PAD_RIGHT);
cc.bt.hex ^= 0xFFFF;
u16 rx = m_classic_right_stick_x_value->value();
cc.rx1 = rx & 0x1;
cc.rx2 = (rx >> 1) & 0x3;
cc.rx3 = (rx >> 3) & 0x3;
cc.ry = m_classic_right_stick_y_value->value();
u8 rx = (cc.rx1 & 0b1) & ((cc.rx2 & 0b11) << 1) & ((cc.rx3 & 0b11) << 3);
GetSpinBoxU8(m_classic_right_stick_x_value, rx);
cc.rx1 = rx & 0b1;
cc.rx2 = (rx >> 1) & 0b11;
cc.rx3 = (rx >> 3) & 0b11;
cc.regular_data.lx = m_classic_left_stick_x_value->value();
cc.regular_data.ly = m_classic_left_stick_y_value->value();
u8 ry = cc.ry;
GetSpinBoxU8(m_classic_right_stick_y_value, ry);
cc.ry = ry;
cc.rt = m_right_trigger_value->value();
cc.lt1 = m_left_trigger_value->value() & 0x7;
cc.lt2 = (m_left_trigger_value->value() >> 3) & 0x3;
u8 lx = cc.regular_data.lx;
GetSpinBoxU8(m_classic_left_stick_x_value, lx);
cc.regular_data.lx = lx;
u8 ly = cc.regular_data.ly;
GetSpinBoxU8(m_classic_left_stick_y_value, ly);
cc.regular_data.ly = ly;
u8 rt = cc.rt;
GetSpinBoxU8(m_right_trigger_value, rt);
cc.rt = rt;
u8 lt = (cc.lt1 & 0b111) & (cc.lt2 >> 3);
GetSpinBoxU8(m_left_trigger_value, lt);
cc.lt1 = lt & 0b111;
cc.lt2 = (lt >> 3) & 0b11;
WiimoteEncrypt(&key, reinterpret_cast<u8*>(&cc), 0, sizeof(wm_classic_extension));
}

View File

@ -4,9 +4,7 @@
#pragma once
#include <QDialog>
#include "Common/CommonTypes.h"
#include "DolphinQt/TAS/TASInputWindow.h"
namespace WiimoteEmu
{
@ -17,7 +15,7 @@ class QGroupBox;
class QSpinBox;
struct wiimote_key;
class WiiTASInputWindow : public QDialog
class WiiTASInputWindow : public TASInputWindow
{
Q_OBJECT
public: