mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 13:57:57 -07:00
28395c6302
wxQueueEvent/wxPostEvent are useful when the event is being dispatched to another separate window, but aren't really necessary when the event will be handled by the same window it's dispatched from. GetEventHandler() is unnecessary here for the same reason. It's an event intended to be handled by the dialog itself.
1248 lines
38 KiB
C++
1248 lines
38 KiB
C++
// Copyright 2011 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <wx/bitmap.h>
|
|
#include <wx/checkbox.h>
|
|
#include <wx/dcmemory.h>
|
|
#include <wx/dialog.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/slider.h>
|
|
#include <wx/statbmp.h>
|
|
#include <wx/textctrl.h>
|
|
|
|
#include "Common/Assert.h"
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/FileUtil.h"
|
|
#include "Common/Logging/Log.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/HW/Wiimote.h"
|
|
#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
|
|
#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
|
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
|
#include "Core/Movie.h"
|
|
#include "DolphinWX/DolphinSlider.h"
|
|
#include "DolphinWX/TASInputDlg.h"
|
|
#include "DolphinWX/WxUtils.h"
|
|
#include "InputCommon/GCPadStatus.h"
|
|
#include "InputCommon/InputConfig.h"
|
|
|
|
wxDEFINE_EVENT(INVALIDATE_BUTTON_EVENT, wxCommandEvent);
|
|
wxDEFINE_EVENT(INVALIDATE_CONTROL_EVENT, wxCommandEvent);
|
|
wxDEFINE_EVENT(INVALIDATE_EXTENSION_EVENT, wxThreadEvent);
|
|
|
|
struct TASWiimoteReport
|
|
{
|
|
u8* data;
|
|
WiimoteEmu::ReportFeatures rptf;
|
|
int ext;
|
|
const wiimote_key key;
|
|
};
|
|
|
|
constexpr std::array<int, 12> s_gc_pad_buttons_bitmask{{
|
|
PAD_BUTTON_DOWN, PAD_BUTTON_UP, PAD_BUTTON_LEFT, PAD_BUTTON_RIGHT, PAD_BUTTON_A, PAD_BUTTON_B,
|
|
PAD_BUTTON_X, PAD_BUTTON_Y, PAD_TRIGGER_Z, PAD_TRIGGER_L, PAD_TRIGGER_R, PAD_BUTTON_START,
|
|
}};
|
|
|
|
constexpr std::array<int, 11> s_wii_buttons_bitmask{{
|
|
WiimoteEmu::Wiimote::PAD_DOWN, WiimoteEmu::Wiimote::PAD_UP, WiimoteEmu::Wiimote::PAD_LEFT,
|
|
WiimoteEmu::Wiimote::PAD_RIGHT, WiimoteEmu::Wiimote::BUTTON_A, WiimoteEmu::Wiimote::BUTTON_B,
|
|
WiimoteEmu::Wiimote::BUTTON_ONE, WiimoteEmu::Wiimote::BUTTON_TWO,
|
|
WiimoteEmu::Wiimote::BUTTON_PLUS, WiimoteEmu::Wiimote::BUTTON_MINUS,
|
|
WiimoteEmu::Wiimote::BUTTON_HOME,
|
|
}};
|
|
|
|
constexpr std::array<int, 15> s_cc_buttons_bitmask{{
|
|
WiimoteEmu::Classic::PAD_DOWN, WiimoteEmu::Classic::PAD_UP, WiimoteEmu::Classic::PAD_LEFT,
|
|
WiimoteEmu::Classic::PAD_RIGHT, WiimoteEmu::Classic::BUTTON_A, WiimoteEmu::Classic::BUTTON_B,
|
|
WiimoteEmu::Classic::BUTTON_X, WiimoteEmu::Classic::BUTTON_Y, WiimoteEmu::Classic::BUTTON_PLUS,
|
|
WiimoteEmu::Classic::BUTTON_MINUS, WiimoteEmu::Classic::TRIGGER_L,
|
|
WiimoteEmu::Classic::TRIGGER_R, WiimoteEmu::Classic::BUTTON_ZR, WiimoteEmu::Classic::BUTTON_ZL,
|
|
WiimoteEmu::Classic::BUTTON_HOME,
|
|
}};
|
|
|
|
TASInputDlg::TASInputDlg(wxWindow* parent, wxWindowID id, const wxString& title,
|
|
const wxPoint& position, const wxSize& size, long style)
|
|
: wxDialog(parent, id, title, position, size, style)
|
|
{
|
|
}
|
|
|
|
void TASInputDlg::CreateBaseLayout()
|
|
{
|
|
m_controls = {};
|
|
m_buttons = {};
|
|
m_cc_controls = {};
|
|
|
|
m_buttons[0] = &m_dpad_down;
|
|
m_buttons[1] = &m_dpad_up;
|
|
m_buttons[2] = &m_dpad_left;
|
|
m_buttons[3] = &m_dpad_right;
|
|
m_buttons[4] = &m_a;
|
|
m_buttons[5] = &m_b;
|
|
m_controls[0] = &m_main_stick.x_cont;
|
|
m_controls[1] = &m_main_stick.y_cont;
|
|
|
|
m_a = CreateButton(_("A"));
|
|
m_a.checkbox->SetClientData(&m_a);
|
|
m_b = CreateButton(_("B"));
|
|
m_b.checkbox->SetClientData(&m_b);
|
|
m_dpad_up = CreateButton(_("Up"));
|
|
m_dpad_up.checkbox->SetClientData(&m_dpad_up);
|
|
m_dpad_right = CreateButton(_("Right"));
|
|
m_dpad_right.checkbox->SetClientData(&m_dpad_right);
|
|
m_dpad_down = CreateButton(_("Down"));
|
|
m_dpad_down.checkbox->SetClientData(&m_dpad_down);
|
|
m_dpad_left = CreateButton(_("Left"));
|
|
m_dpad_left.checkbox->SetClientData(&m_dpad_left);
|
|
|
|
m_buttons_dpad = new wxGridSizer(3);
|
|
const int space20 = FromDIP(20);
|
|
m_buttons_dpad->Add(space20, space20);
|
|
m_buttons_dpad->Add(m_dpad_up.checkbox);
|
|
m_buttons_dpad->Add(space20, space20);
|
|
m_buttons_dpad->Add(m_dpad_left.checkbox);
|
|
m_buttons_dpad->Add(space20, space20);
|
|
m_buttons_dpad->Add(m_dpad_right.checkbox);
|
|
m_buttons_dpad->Add(space20, space20);
|
|
m_buttons_dpad->Add(m_dpad_down.checkbox);
|
|
m_buttons_dpad->Add(space20, space20);
|
|
}
|
|
|
|
void TASInputDlg::CreateWiiLayout(int num)
|
|
{
|
|
if (m_has_layout)
|
|
return;
|
|
const int space5 = FromDIP(5);
|
|
|
|
CreateBaseLayout();
|
|
|
|
m_buttons[6] = &m_one;
|
|
m_buttons[7] = &m_two;
|
|
m_buttons[8] = &m_plus;
|
|
m_buttons[9] = &m_minus;
|
|
m_buttons[10] = &m_home;
|
|
|
|
m_controls[4] = &m_x_cont;
|
|
m_controls[5] = &m_y_cont;
|
|
m_controls[6] = &m_z_cont;
|
|
|
|
m_main_stick = CreateStick(ID_MAIN_STICK, 1024, 768, 512, 384, true, false);
|
|
m_main_stick_szr = CreateStickLayout(&m_main_stick, _("IR"));
|
|
|
|
m_x_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 512);
|
|
m_y_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 512);
|
|
m_z_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 616);
|
|
wxStaticBoxSizer* const axisBox =
|
|
CreateAccelLayout(&m_x_cont, &m_y_cont, &m_z_cont, _("Orientation"));
|
|
|
|
m_plus = CreateButton(_("+"));
|
|
m_plus.checkbox->SetClientData(&m_plus);
|
|
m_minus = CreateButton(_("-"));
|
|
m_minus.checkbox->SetClientData(&m_minus);
|
|
m_one = CreateButton(_("1"));
|
|
m_one.checkbox->SetClientData(&m_one);
|
|
m_two = CreateButton(_("2"));
|
|
m_two.checkbox->SetClientData(&m_two);
|
|
m_home = CreateButton(_("Home"));
|
|
m_home.checkbox->SetClientData(&m_home);
|
|
|
|
m_cc_szr = CreateCCLayout();
|
|
|
|
if (Core::IsRunning())
|
|
{
|
|
m_ext = static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(num))
|
|
->CurrentExtension();
|
|
}
|
|
else
|
|
{
|
|
IniFile ini;
|
|
ini.Load(File::GetUserPath(D_CONFIG_IDX) + "WiimoteNew.ini");
|
|
std::string extension;
|
|
ini.GetIfExists("Wiimote" + std::to_string(num + 1), "Extension", &extension);
|
|
|
|
if (extension == "Nunchuk")
|
|
m_ext = 1;
|
|
if (extension == "Classic")
|
|
m_ext = 2;
|
|
}
|
|
|
|
m_buttons[11] = &m_c;
|
|
m_buttons[12] = &m_z;
|
|
m_controls[2] = &m_c_stick.x_cont;
|
|
m_controls[3] = &m_c_stick.y_cont;
|
|
m_controls[7] = &m_nx_cont;
|
|
m_controls[8] = &m_ny_cont;
|
|
m_controls[9] = &m_nz_cont;
|
|
|
|
m_c_stick = CreateStick(ID_C_STICK, 255, 255, 128, 128, false, true);
|
|
m_c_stick_szr = CreateStickLayout(&m_c_stick, _("Nunchuk stick"));
|
|
|
|
m_nx_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 512);
|
|
m_ny_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 512);
|
|
m_nz_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 1023, 512);
|
|
wxStaticBoxSizer* const nunchukaxisBox =
|
|
CreateAccelLayout(&m_nx_cont, &m_ny_cont, &m_nz_cont, _("Nunchuk orientation"));
|
|
|
|
m_c = CreateButton(_("C"));
|
|
m_c.checkbox->SetClientData(&m_c);
|
|
m_z = CreateButton(_("Z"));
|
|
m_z.checkbox->SetClientData(&m_z);
|
|
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr)
|
|
control->slider->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnRightClickSlider, this);
|
|
}
|
|
|
|
m_ext_szr = new wxBoxSizer(wxHORIZONTAL);
|
|
m_ext_szr->Add(m_c_stick_szr, 0, wxBOTTOM, space5);
|
|
m_ext_szr->AddSpacer(space5);
|
|
m_ext_szr->Add(nunchukaxisBox, 0, wxBOTTOM, space5);
|
|
|
|
// Add non-DPad related buttons first.
|
|
auto* const buttons_grid = new wxGridSizer(4);
|
|
for (size_t i = 4; i < m_buttons.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
buttons_grid->Add(m_buttons[i]->checkbox);
|
|
}
|
|
buttons_grid->Add(space5, space5);
|
|
|
|
auto* const buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons"));
|
|
buttons_box->Add(buttons_grid);
|
|
buttons_box->Add(m_buttons_dpad, 0, wxTOP, space5);
|
|
|
|
m_wiimote_szr = new wxBoxSizer(wxHORIZONTAL);
|
|
m_wiimote_szr->AddSpacer(space5);
|
|
m_wiimote_szr->Add(m_main_stick_szr);
|
|
m_wiimote_szr->Add(axisBox, 0, wxLEFT, space5);
|
|
m_wiimote_szr->Add(buttons_box, 0, wxLEFT, space5);
|
|
m_wiimote_szr->AddSpacer(space5);
|
|
|
|
// NOTE: Not all of these are visible at the same time.
|
|
m_main_szr = new wxBoxSizer(wxVERTICAL);
|
|
m_main_szr->Add(m_wiimote_szr, 0, wxTOP | wxBOTTOM, space5);
|
|
m_main_szr->Add(m_ext_szr, 0, wxLEFT | wxRIGHT, space5);
|
|
m_main_szr->Add(m_cc_szr, 0, wxLEFT | wxRIGHT, space5);
|
|
|
|
SetSizer(m_main_szr);
|
|
HandleExtensionChange();
|
|
FinishLayout();
|
|
}
|
|
|
|
void TASInputDlg::FinishLayout()
|
|
{
|
|
Bind(wxEVT_CLOSE_WINDOW, &TASInputDlg::OnCloseWindow, this);
|
|
Bind(INVALIDATE_BUTTON_EVENT, &TASInputDlg::UpdateFromInvalidatedButton, this);
|
|
Bind(INVALIDATE_CONTROL_EVENT, &TASInputDlg::UpdateFromInvalidatedControl, this);
|
|
Bind(INVALIDATE_EXTENSION_EVENT, &TASInputDlg::UpdateFromInvalidatedExtension, this);
|
|
m_has_layout = true;
|
|
}
|
|
|
|
wxBoxSizer* TASInputDlg::CreateCCLayout()
|
|
{
|
|
const std::array<wxString, 15> button_names{{_("Down"), _("Up"), _("Left"), _("Right"), _("A"),
|
|
_("B"), _("X"), _("Y"), _("+"), _("-"), _("L"),
|
|
_("R"), _("ZR"), _("ZL"), _("Home")}};
|
|
for (size_t i = 0; i < button_names.size(); ++i)
|
|
{
|
|
m_cc_buttons[i] = CreateButton(button_names[i]);
|
|
m_cc_buttons[i].checkbox->SetClientData(&m_cc_buttons[i]);
|
|
}
|
|
|
|
m_cc_l_stick = CreateStick(ID_CC_L_STICK, 63, 63, WiimoteEmu::Classic::LEFT_STICK_CENTER_X,
|
|
WiimoteEmu::Classic::LEFT_STICK_CENTER_Y, false, true);
|
|
m_cc_r_stick = CreateStick(ID_CC_R_STICK, 31, 31, WiimoteEmu::Classic::RIGHT_STICK_CENTER_X,
|
|
WiimoteEmu::Classic::RIGHT_STICK_CENTER_Y, false, true);
|
|
|
|
m_cc_controls[CC_L_STICK_X] = &m_cc_l_stick.x_cont;
|
|
m_cc_controls[CC_L_STICK_Y] = &m_cc_l_stick.y_cont;
|
|
m_cc_controls[CC_R_STICK_X] = &m_cc_r_stick.x_cont;
|
|
m_cc_controls[CC_R_STICK_Y] = &m_cc_r_stick.y_cont;
|
|
m_cc_controls[CC_L_TRIGGER] = &m_cc_l;
|
|
m_cc_controls[CC_R_TRIGGER] = &m_cc_r;
|
|
|
|
m_cc_l_stick_szr = CreateStickLayout(&m_cc_l_stick, _("Left stick"));
|
|
m_cc_r_stick_szr = CreateStickLayout(&m_cc_r_stick, _("Right stick"));
|
|
|
|
m_cc_l = CreateControl(wxSL_VERTICAL, -1, 100, false, 31, 0);
|
|
m_cc_r = CreateControl(wxSL_VERTICAL, -1, 100, false, 31, 0);
|
|
|
|
const int space5 = FromDIP(5);
|
|
const int space20 = FromDIP(20);
|
|
|
|
auto* const shoulder_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Shoulder Buttons"));
|
|
shoulder_box->Add(m_cc_l.slider, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_cc_l.text, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_cc_r.slider, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_cc_r.text, 0, wxALIGN_CENTER_VERTICAL);
|
|
|
|
auto* const cc_buttons_dpad = new wxGridSizer(3);
|
|
cc_buttons_dpad->Add(space20, space20);
|
|
cc_buttons_dpad->Add(m_cc_buttons[1].checkbox);
|
|
cc_buttons_dpad->Add(space20, space20);
|
|
cc_buttons_dpad->Add(m_cc_buttons[2].checkbox);
|
|
cc_buttons_dpad->Add(space20, space20);
|
|
cc_buttons_dpad->Add(m_cc_buttons[3].checkbox);
|
|
cc_buttons_dpad->Add(space20, space20);
|
|
cc_buttons_dpad->Add(m_cc_buttons[0].checkbox);
|
|
cc_buttons_dpad->Add(space20, space20);
|
|
|
|
auto* const cc_buttons_grid = new wxGridSizer(4);
|
|
for (auto& button : m_cc_buttons)
|
|
if (!button.checkbox->GetContainingSizer())
|
|
cc_buttons_grid->Add(button.checkbox);
|
|
cc_buttons_grid->Add(space5, space5);
|
|
|
|
auto* const cc_buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons"));
|
|
cc_buttons_box->Add(cc_buttons_grid);
|
|
cc_buttons_box->Add(cc_buttons_dpad, 0, wxTOP, space5);
|
|
|
|
auto* const szr = new wxBoxSizer(wxHORIZONTAL);
|
|
szr->AddSpacer(space5);
|
|
szr->Add(m_cc_l_stick_szr, 0, wxTOP | wxBOTTOM, space5);
|
|
szr->AddSpacer(space5);
|
|
szr->Add(m_cc_r_stick_szr, 0, wxTOP | wxBOTTOM, space5);
|
|
szr->AddSpacer(space5);
|
|
szr->Add(shoulder_box, 0, wxTOP | wxBOTTOM, space5);
|
|
szr->AddSpacer(space5);
|
|
szr->Add(cc_buttons_box, 0, wxTOP | wxBOTTOM, space5);
|
|
szr->AddSpacer(space5);
|
|
|
|
for (Control* const control : m_cc_controls)
|
|
{
|
|
if (control != nullptr)
|
|
control->slider->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnRightClickSlider, this);
|
|
}
|
|
return szr;
|
|
}
|
|
|
|
void TASInputDlg::HandleExtensionChange()
|
|
{
|
|
if (m_ext == 1)
|
|
{
|
|
m_main_szr->Hide(m_cc_szr);
|
|
m_main_szr->Show(m_wiimote_szr);
|
|
m_main_szr->Show(m_ext_szr);
|
|
}
|
|
else if (m_ext == 2)
|
|
{
|
|
m_main_szr->Hide(m_ext_szr);
|
|
m_main_szr->Hide(m_wiimote_szr);
|
|
m_main_szr->Show(m_cc_szr);
|
|
}
|
|
else
|
|
{
|
|
m_main_szr->Hide(m_ext_szr);
|
|
m_main_szr->Hide(m_cc_szr);
|
|
m_main_szr->Show(m_wiimote_szr);
|
|
}
|
|
ResetValues();
|
|
m_main_szr->SetSizeHints(this);
|
|
Layout();
|
|
}
|
|
|
|
void TASInputDlg::CreateGCLayout()
|
|
{
|
|
if (m_has_layout)
|
|
return;
|
|
|
|
CreateBaseLayout();
|
|
|
|
m_buttons[6] = &m_x;
|
|
m_buttons[7] = &m_y;
|
|
m_buttons[8] = &m_z;
|
|
m_buttons[9] = &m_l;
|
|
m_buttons[10] = &m_r;
|
|
m_buttons[11] = &m_start;
|
|
|
|
m_controls[2] = &m_c_stick.x_cont;
|
|
m_controls[3] = &m_c_stick.y_cont;
|
|
m_controls[4] = &m_l_cont;
|
|
m_controls[5] = &m_r_cont;
|
|
|
|
m_main_stick = CreateStick(ID_MAIN_STICK, 255, 255, 128, 128, false, true);
|
|
wxStaticBoxSizer* const main_box = CreateStickLayout(&m_main_stick, _("Main Stick"));
|
|
|
|
m_c_stick = CreateStick(ID_C_STICK, 255, 255, 128, 128, false, true);
|
|
wxStaticBoxSizer* const c_box = CreateStickLayout(&m_c_stick, _("C Stick"));
|
|
|
|
auto* const shoulder_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Shoulder Buttons"));
|
|
m_l_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 255, 0);
|
|
m_r_cont = CreateControl(wxSL_VERTICAL, -1, 100, false, 255, 0);
|
|
shoulder_box->Add(m_l_cont.slider, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_l_cont.text, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_r_cont.slider, 0, wxALIGN_CENTER_VERTICAL);
|
|
shoulder_box->Add(m_r_cont.text, 0, wxALIGN_CENTER_VERTICAL);
|
|
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr)
|
|
control->slider->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnRightClickSlider, this);
|
|
}
|
|
|
|
m_x = CreateButton(_("X"));
|
|
m_x.checkbox->SetClientData(&m_x);
|
|
m_y = CreateButton(_("Y"));
|
|
m_y.checkbox->SetClientData(&m_y);
|
|
m_l = CreateButton(_("L"));
|
|
m_l.checkbox->SetClientData(&m_l);
|
|
m_r = CreateButton(_("R"));
|
|
m_r.checkbox->SetClientData(&m_r);
|
|
m_z = CreateButton(_("Z"));
|
|
m_z.checkbox->SetClientData(&m_z);
|
|
m_start = CreateButton(_("Start"));
|
|
m_start.checkbox->SetClientData(&m_start);
|
|
|
|
const int space5 = FromDIP(5);
|
|
|
|
// Add non-DPad related buttons first.
|
|
auto* const buttons_grid = new wxGridSizer(4);
|
|
for (size_t i = 4; i < m_buttons.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
buttons_grid->Add(m_buttons[i]->checkbox, false);
|
|
}
|
|
buttons_grid->Add(space5, space5);
|
|
|
|
auto* const buttons_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Buttons"));
|
|
buttons_box->Add(buttons_grid);
|
|
buttons_box->Add(m_buttons_dpad);
|
|
|
|
auto* const top_box = new wxBoxSizer(wxHORIZONTAL);
|
|
top_box->Add(main_box);
|
|
top_box->Add(c_box, 0, wxLEFT, space5);
|
|
|
|
auto* const bottom_box = new wxBoxSizer(wxHORIZONTAL);
|
|
bottom_box->Add(shoulder_box);
|
|
bottom_box->Add(buttons_box, 0, wxLEFT, space5);
|
|
|
|
auto* const main_szr = new wxBoxSizer(wxVERTICAL);
|
|
main_szr->AddSpacer(space5);
|
|
main_szr->Add(top_box, 0, wxLEFT | wxRIGHT, space5);
|
|
main_szr->AddSpacer(space5);
|
|
main_szr->Add(bottom_box, 0, wxLEFT | wxRIGHT, space5);
|
|
main_szr->AddSpacer(space5);
|
|
SetSizerAndFit(main_szr);
|
|
|
|
ResetValues();
|
|
FinishLayout();
|
|
}
|
|
|
|
TASInputDlg::Control TASInputDlg::CreateControl(long style, int width, int height, bool reverse,
|
|
u32 range, u32 default_value)
|
|
{
|
|
Control control;
|
|
control.range = range;
|
|
control.default_value = default_value;
|
|
control.slider_id = m_eleID++;
|
|
control.slider = new DolphinSlider(this, control.slider_id, default_value, 0, range,
|
|
wxDefaultPosition, FromDIP(wxSize(width, height)), style);
|
|
control.slider->Bind(wxEVT_SLIDER, &TASInputDlg::UpdateFromSliders, this);
|
|
control.text_id = m_eleID++;
|
|
control.text = new wxTextCtrl(this, control.text_id, std::to_string(default_value));
|
|
control.text->SetMaxLength(range > 999 ? 4 : 3);
|
|
control.text->SetMinSize(WxUtils::GetTextWidgetMinSize(control.text, range));
|
|
control.text->Bind(wxEVT_TEXT, &TASInputDlg::UpdateFromText, this);
|
|
control.reverse = reverse;
|
|
return control;
|
|
}
|
|
|
|
TASInputDlg::Stick TASInputDlg::CreateStick(int id_stick, int xRange, int yRange, u32 defaultX,
|
|
u32 defaultY, bool reverseX, bool reverseY)
|
|
{
|
|
Stick tempStick;
|
|
tempStick.bitmap = new wxStaticBitmap(this, id_stick, CreateStickBitmap(128, 128));
|
|
tempStick.bitmap->Bind(wxEVT_MOTION, &TASInputDlg::OnMouseDownL, this);
|
|
tempStick.bitmap->Bind(wxEVT_LEFT_DOWN, &TASInputDlg::OnMouseDownL, this);
|
|
tempStick.bitmap->Bind(wxEVT_RIGHT_UP, &TASInputDlg::OnMouseUpR, this);
|
|
tempStick.x_cont = CreateControl(wxSL_HORIZONTAL | (reverseX ? wxSL_INVERSE : 0), 120, -1,
|
|
reverseX, xRange, defaultX);
|
|
tempStick.y_cont = CreateControl(wxSL_VERTICAL | wxSL_LEFT | (reverseY ? wxSL_INVERSE : 0), -1,
|
|
120, reverseY, yRange, defaultY);
|
|
return tempStick;
|
|
}
|
|
|
|
wxStaticBoxSizer* TASInputDlg::CreateStickLayout(Stick* stick, const wxString& title)
|
|
{
|
|
const int space3 = FromDIP(3);
|
|
|
|
auto* const temp_box = new wxStaticBoxSizer(wxVERTICAL, this, title);
|
|
auto* const layout = new wxFlexGridSizer(2, space3, space3);
|
|
|
|
layout->Add(stick->x_cont.slider, 0, wxEXPAND);
|
|
layout->Add(stick->x_cont.text, 0, wxALIGN_CENTER);
|
|
layout->Add(stick->bitmap, 0, wxALIGN_RIGHT);
|
|
layout->Add(stick->y_cont.slider, 0, wxEXPAND);
|
|
layout->AddSpacer(1); // Placeholder for unused cell
|
|
layout->Add(stick->y_cont.text, 0, wxALIGN_CENTER);
|
|
|
|
temp_box->AddSpacer(space3);
|
|
temp_box->Add(layout, 0, wxLEFT | wxRIGHT, space3);
|
|
temp_box->AddSpacer(space3);
|
|
return temp_box;
|
|
}
|
|
|
|
wxStaticBoxSizer* TASInputDlg::CreateAccelLayout(Control* x, Control* y, Control* z,
|
|
const wxString& title)
|
|
{
|
|
auto* const temp_box = new wxStaticBoxSizer(wxHORIZONTAL, this, title);
|
|
auto* const xBox = new wxStaticBoxSizer(wxVERTICAL, this, _("X"));
|
|
auto* const yBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Y"));
|
|
auto* const zBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Z"));
|
|
const int space5 = FromDIP(5);
|
|
|
|
xBox->Add(x->slider, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
xBox->Add(x->text, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
yBox->Add(y->slider, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
yBox->Add(y->text, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
zBox->Add(z->slider, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
zBox->Add(z->text, 0, wxALIGN_CENTER_HORIZONTAL);
|
|
temp_box->AddSpacer(space5);
|
|
temp_box->Add(xBox, 0, wxBOTTOM, space5);
|
|
temp_box->AddSpacer(space5);
|
|
temp_box->Add(yBox, 0, wxBOTTOM, space5);
|
|
temp_box->AddSpacer(space5);
|
|
temp_box->Add(zBox, 0, wxBOTTOM, space5);
|
|
temp_box->AddSpacer(space5);
|
|
return temp_box;
|
|
}
|
|
|
|
TASInputDlg::Button TASInputDlg::CreateButton(const wxString& name)
|
|
{
|
|
Button temp;
|
|
temp.id = m_eleID++;
|
|
auto* checkbox = new wxCheckBox(this, temp.id, name);
|
|
checkbox->Bind(wxEVT_RIGHT_DOWN, &TASInputDlg::SetTurbo, this);
|
|
checkbox->Bind(wxEVT_LEFT_DOWN, &TASInputDlg::SetTurbo, this);
|
|
checkbox->Bind(wxEVT_CHECKBOX, &TASInputDlg::OnCheckboxToggle, this);
|
|
temp.checkbox = checkbox;
|
|
return temp;
|
|
}
|
|
|
|
void TASInputDlg::OnCheckboxToggle(wxCommandEvent& event)
|
|
{
|
|
auto cbox = static_cast<wxCheckBox*>(event.GetEventObject());
|
|
static_cast<Button*>(cbox->GetClientData())->is_checked = event.IsChecked();
|
|
}
|
|
|
|
void TASInputDlg::ResetValues()
|
|
{
|
|
for (Button* const button : m_buttons)
|
|
{
|
|
if (button != nullptr)
|
|
{
|
|
button->value = false;
|
|
button->is_checked = false;
|
|
button->checkbox->SetValue(false);
|
|
}
|
|
}
|
|
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr)
|
|
{
|
|
control->value = control->default_value;
|
|
control->slider->SetValue(control->default_value);
|
|
control->text->SetValue(std::to_string(control->default_value));
|
|
}
|
|
}
|
|
if (m_ext == 2)
|
|
{
|
|
for (Button& button : m_cc_buttons)
|
|
{
|
|
button.value = false;
|
|
button.is_checked = false;
|
|
button.checkbox->SetValue(false);
|
|
}
|
|
|
|
for (Control* control : m_cc_controls)
|
|
{
|
|
control->value = control->default_value;
|
|
control->slider->SetValue(control->default_value);
|
|
control->text->SetValue(std::to_string(control->default_value));
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::SetStickValue(Control* control, int CurrentValue, int center)
|
|
{
|
|
if (CurrentValue != center)
|
|
{
|
|
control->value = CurrentValue;
|
|
control->set_by_keyboard = true;
|
|
}
|
|
else if (control->set_by_keyboard)
|
|
{
|
|
control->value = center;
|
|
control->set_by_keyboard = false;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
InvalidateControl(control);
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::SetSliderValue(Control* control, int CurrentValue)
|
|
{
|
|
if (CurrentValue != (int)control->default_value)
|
|
{
|
|
control->value = CurrentValue;
|
|
control->set_by_keyboard = true;
|
|
}
|
|
else if (control->set_by_keyboard)
|
|
{
|
|
control->value = control->default_value;
|
|
control->set_by_keyboard = false;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
InvalidateControl(control);
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::SetButtonValue(Button* button, bool CurrentState)
|
|
{
|
|
if (CurrentState)
|
|
{
|
|
button->set_by_keyboard = true;
|
|
}
|
|
else if (button->set_by_keyboard)
|
|
{
|
|
button->set_by_keyboard = false;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
button->value = CurrentState;
|
|
InvalidateButton(button);
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::SetWiiButtons(u16* butt)
|
|
{
|
|
for (size_t i = 0; i < s_wii_buttons_bitmask.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
*butt |= (m_buttons[i]->is_checked) ? s_wii_buttons_bitmask[i] : 0;
|
|
}
|
|
ButtonTurbo();
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::GetKeyBoardInput(GCPadStatus* PadStatus)
|
|
{
|
|
SetStickValue(&m_main_stick.x_cont, PadStatus->stickX);
|
|
SetStickValue(&m_main_stick.y_cont, PadStatus->stickY);
|
|
|
|
SetStickValue(&m_c_stick.x_cont, PadStatus->substickX);
|
|
SetStickValue(&m_c_stick.y_cont, PadStatus->substickY);
|
|
SetSliderValue(&m_l_cont, PadStatus->triggerLeft);
|
|
SetSliderValue(&m_r_cont, PadStatus->triggerRight);
|
|
|
|
for (size_t i = 0; i < m_buttons.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
SetButtonValue(m_buttons[i], ((PadStatus->button & s_gc_pad_buttons_bitmask[i]) != 0));
|
|
}
|
|
SetButtonValue(&m_l,
|
|
((PadStatus->triggerLeft) == 255) || ((PadStatus->button & PAD_TRIGGER_L) != 0));
|
|
SetButtonValue(&m_r,
|
|
((PadStatus->triggerRight) == 255) || ((PadStatus->button & PAD_TRIGGER_R) != 0));
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
|
|
const wiimote_key key)
|
|
{
|
|
u8* const coreData = rptf.core ? (data + rptf.core) : nullptr;
|
|
u8* const accelData = rptf.accel ? (data + rptf.accel) : nullptr;
|
|
// u8* const irData = rptf.ir ? (data + rptf.ir) : nullptr;
|
|
u8* const extData = rptf.ext ? (data + rptf.ext) : nullptr;
|
|
|
|
if (coreData)
|
|
{
|
|
for (size_t i = 0; i < s_wii_buttons_bitmask.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
SetButtonValue(m_buttons[i],
|
|
(((wm_buttons*)coreData)->hex & s_wii_buttons_bitmask[i]) != 0);
|
|
}
|
|
}
|
|
if (accelData)
|
|
{
|
|
wm_accel* dt = (wm_accel*)accelData;
|
|
|
|
SetSliderValue(&m_x_cont, dt->x << 2 | ((wm_buttons*)coreData)->acc_x_lsb);
|
|
SetSliderValue(&m_y_cont, dt->y << 2 | ((wm_buttons*)coreData)->acc_y_lsb << 1);
|
|
SetSliderValue(&m_z_cont, dt->z << 2 | ((wm_buttons*)coreData)->acc_z_lsb << 1);
|
|
}
|
|
|
|
// I don't think this can be made to work in a sane manner.
|
|
// if (irData)
|
|
//{
|
|
// u16 x = 1023 - (irData[0] | ((irData[2] >> 4 & 0x3) << 8));
|
|
// u16 y = irData[1] | ((irData[2] >> 6 & 0x3) << 8);
|
|
|
|
// SetStickValue(&m_main_stick.x_cont.set_by_keyboard, &m_main_stick.x_cont.value,
|
|
// m_main_stick.x_cont.text, x, 561);
|
|
// SetStickValue(&m_main_stick.y_cont.set_by_keyboard, &m_main_stick.y_cont.value,
|
|
// m_main_stick.y_cont.text, y, 486);
|
|
//}
|
|
|
|
if (extData && ext == 1)
|
|
{
|
|
wm_nc& nunchuk = *(wm_nc*)extData;
|
|
WiimoteDecrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc));
|
|
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
|
|
SetButtonValue(m_buttons[11], nunchuk.bt.c != 0);
|
|
SetButtonValue(m_buttons[12], nunchuk.bt.z != 0);
|
|
}
|
|
|
|
if (extData && ext == 2)
|
|
{
|
|
wm_classic_extension& cc = *(wm_classic_extension*)extData;
|
|
WiimoteDecrypt(&key, (u8*)&cc, 0, sizeof(wm_classic_extension));
|
|
cc.bt.hex = cc.bt.hex ^ 0xFFFF;
|
|
for (size_t i = 0; i < m_cc_buttons.size(); ++i)
|
|
{
|
|
SetButtonValue(&m_cc_buttons[i], ((cc.bt.hex & s_cc_buttons_bitmask[i]) != 0));
|
|
}
|
|
|
|
if (m_cc_l.value == 31)
|
|
{
|
|
m_cc_buttons[10].value = true;
|
|
InvalidateButton(&m_cc_buttons[10]);
|
|
}
|
|
if (m_cc_r.value == 31)
|
|
{
|
|
m_cc_buttons[11].value = true;
|
|
InvalidateButton(&m_cc_buttons[11]);
|
|
}
|
|
|
|
SetSliderValue(&m_cc_l_stick.x_cont, cc.regular_data.lx);
|
|
SetSliderValue(&m_cc_l_stick.y_cont, cc.regular_data.ly);
|
|
|
|
SetSliderValue(&m_cc_r_stick.x_cont, cc.rx1 | (cc.rx2 << 1) | (cc.rx3 << 3));
|
|
SetSliderValue(&m_cc_r_stick.y_cont, cc.ry);
|
|
}
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
// Do not touch the GUI. Requires wxMutexGuiEnter which will deadlock against
|
|
// the GUI when pausing/stopping.
|
|
void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
|
|
const wiimote_key key)
|
|
{
|
|
if (!IsShown() || !m_has_layout)
|
|
return;
|
|
|
|
GetKeyBoardInput(data, rptf, ext, key);
|
|
|
|
u8* const coreData = rptf.core ? (data + rptf.core) : nullptr;
|
|
u8* const accelData = rptf.accel ? (data + rptf.accel) : nullptr;
|
|
u8* const irData = rptf.ir ? (data + rptf.ir) : nullptr;
|
|
u8* const extData = rptf.ext ? (data + rptf.ext) : nullptr;
|
|
if (ext != 2)
|
|
{
|
|
if (coreData)
|
|
SetWiiButtons(&((wm_buttons*)coreData)->hex);
|
|
|
|
if (accelData)
|
|
{
|
|
wm_accel& dt = *(wm_accel*)accelData;
|
|
wm_buttons& but = *(wm_buttons*)coreData;
|
|
dt.x = m_x_cont.value >> 2;
|
|
dt.y = m_y_cont.value >> 2;
|
|
dt.z = m_z_cont.value >> 2;
|
|
but.acc_x_lsb = m_x_cont.value & 0x3;
|
|
but.acc_y_lsb = m_y_cont.value >> 1 & 0x1;
|
|
but.acc_z_lsb = m_z_cont.value >> 1 & 0x1;
|
|
}
|
|
if (irData)
|
|
{
|
|
std::array<u16, 4> x;
|
|
u16 y = m_main_stick.y_cont.value;
|
|
|
|
x[0] = m_main_stick.x_cont.value;
|
|
x[1] = x[0] + 100;
|
|
x[2] = x[0] - 10;
|
|
x[3] = x[1] + 10;
|
|
|
|
u8 mode;
|
|
// Mode 5 not supported in core anyway.
|
|
if (rptf.ext)
|
|
mode = (rptf.ext - rptf.ir) == 10 ? 1 : 3;
|
|
else
|
|
mode = (rptf.size - rptf.ir) == 10 ? 1 : 3;
|
|
|
|
if (mode == 1)
|
|
{
|
|
memset(irData, 0xFF, sizeof(wm_ir_basic) * 2);
|
|
wm_ir_basic* ir_data = (wm_ir_basic*)irData;
|
|
for (unsigned int i = 0; i < 2; ++i)
|
|
{
|
|
if (x[i * 2] < 1024 && y < 768)
|
|
{
|
|
ir_data[i].x1 = static_cast<u8>(x[i * 2]);
|
|
ir_data[i].x1hi = x[i * 2] >> 8;
|
|
|
|
ir_data[i].y1 = static_cast<u8>(y);
|
|
ir_data[i].y1hi = y >> 8;
|
|
}
|
|
if (x[i * 2 + 1] < 1024 && y < 768)
|
|
{
|
|
ir_data[i].x2 = static_cast<u8>(x[i * 2 + 1]);
|
|
ir_data[i].x2hi = x[i * 2 + 1] >> 8;
|
|
|
|
ir_data[i].y2 = static_cast<u8>(y);
|
|
ir_data[i].y2hi = y >> 8;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(data, 0xFF, sizeof(wm_ir_extended) * 4);
|
|
wm_ir_extended* const ir_data = (wm_ir_extended*)irData;
|
|
for (size_t i = 0; i < x.size(); ++i)
|
|
{
|
|
if (x[i] < 1024 && y < 768)
|
|
{
|
|
ir_data[i].x = static_cast<u8>(x[i]);
|
|
ir_data[i].xhi = x[i] >> 8;
|
|
|
|
ir_data[i].y = static_cast<u8>(y);
|
|
ir_data[i].yhi = y >> 8;
|
|
|
|
ir_data[i].size = 10;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ext != m_ext)
|
|
{
|
|
m_ext = ext;
|
|
InvalidateExtension();
|
|
}
|
|
else if (extData && ext == 1)
|
|
{
|
|
wm_nc& nunchuk = *(wm_nc*)extData;
|
|
|
|
nunchuk.jx = m_c_stick.x_cont.value;
|
|
nunchuk.jy = m_c_stick.y_cont.value;
|
|
|
|
nunchuk.ax = m_nx_cont.value >> 2;
|
|
nunchuk.bt.acc_x_lsb = m_nx_cont.value & 0x3;
|
|
nunchuk.ay = m_ny_cont.value >> 2;
|
|
nunchuk.bt.acc_y_lsb = m_ny_cont.value & 0x3;
|
|
nunchuk.az = m_nz_cont.value >> 2;
|
|
nunchuk.bt.acc_z_lsb = m_nz_cont.value & 0x3;
|
|
|
|
nunchuk.bt.hex |= (m_buttons[11]->is_checked) ? WiimoteEmu::Nunchuk::BUTTON_C : 0;
|
|
nunchuk.bt.hex |= (m_buttons[12]->is_checked) ? WiimoteEmu::Nunchuk::BUTTON_Z : 0;
|
|
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
|
|
WiimoteEncrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc));
|
|
}
|
|
else if (extData && ext == 2)
|
|
{
|
|
wm_classic_extension& cc = *(wm_classic_extension*)extData;
|
|
WiimoteDecrypt(&key, (u8*)&cc, 0, sizeof(wm_classic_extension));
|
|
cc.bt.hex = 0;
|
|
|
|
for (size_t i = 0; i < m_cc_buttons.size(); ++i)
|
|
{
|
|
cc.bt.hex |= (m_cc_buttons[i].is_checked) ? s_cc_buttons_bitmask[i] : 0;
|
|
}
|
|
cc.bt.hex ^= 0xFFFF;
|
|
|
|
u16 rx = m_cc_r_stick.x_cont.value;
|
|
cc.rx1 = rx & 0x1;
|
|
cc.rx2 = (rx >> 1) & 0x3;
|
|
cc.rx3 = (rx >> 3) & 0x3;
|
|
cc.ry = m_cc_r_stick.y_cont.value;
|
|
|
|
cc.regular_data.lx = m_cc_l_stick.x_cont.value;
|
|
cc.regular_data.ly = m_cc_l_stick.y_cont.value;
|
|
|
|
cc.rt = m_cc_r.value;
|
|
cc.lt1 = m_cc_l.value & 0x7;
|
|
cc.lt2 = (m_cc_l.value >> 3) & 0x3;
|
|
|
|
WiimoteEncrypt(&key, (u8*)&cc, 0, sizeof(wm_classic_extension));
|
|
}
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::GetValues(GCPadStatus* PadStatus)
|
|
{
|
|
if (!IsShown() || !m_has_layout)
|
|
return;
|
|
|
|
// TODO:: Make this instant not when polled.
|
|
GetKeyBoardInput(PadStatus);
|
|
|
|
PadStatus->stickX = m_main_stick.x_cont.value;
|
|
PadStatus->stickY = m_main_stick.y_cont.value;
|
|
PadStatus->substickX = m_c_stick.x_cont.value;
|
|
PadStatus->substickY = m_c_stick.y_cont.value;
|
|
PadStatus->triggerLeft = m_l.is_checked ? 255 : m_l_cont.value;
|
|
PadStatus->triggerRight = m_r.is_checked ? 255 : m_r_cont.value;
|
|
|
|
for (size_t i = 0; i < m_buttons.size(); ++i)
|
|
{
|
|
if (m_buttons[i] != nullptr)
|
|
{
|
|
if (m_buttons[i]->is_checked)
|
|
PadStatus->button |= s_gc_pad_buttons_bitmask[i];
|
|
else
|
|
PadStatus->button &= ~s_gc_pad_buttons_bitmask[i];
|
|
}
|
|
}
|
|
|
|
if (m_a.is_checked)
|
|
PadStatus->analogA = 0xFF;
|
|
else
|
|
PadStatus->analogA = 0x00;
|
|
|
|
if (m_b.is_checked)
|
|
PadStatus->analogB = 0xFF;
|
|
else
|
|
PadStatus->analogB = 0x00;
|
|
|
|
ButtonTurbo();
|
|
}
|
|
|
|
void TASInputDlg::UpdateFromSliders(wxCommandEvent& event)
|
|
{
|
|
wxTextCtrl* text = nullptr;
|
|
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->slider_id)
|
|
text = control->text;
|
|
}
|
|
|
|
for (Control* const control : m_cc_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->slider_id)
|
|
text = control->text;
|
|
}
|
|
|
|
if (!text)
|
|
return;
|
|
|
|
const int slider_value = event.GetInt();
|
|
text->SetValue(std::to_string(slider_value));
|
|
}
|
|
|
|
void TASInputDlg::UpdateFromText(wxCommandEvent& event)
|
|
{
|
|
unsigned long value;
|
|
|
|
if (!event.GetString().ToULong(&value))
|
|
return;
|
|
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->text_id)
|
|
{
|
|
int v = (value > control->range) ? control->range : value;
|
|
control->slider->SetValue(v);
|
|
control->text->ChangeValue(std::to_string(v));
|
|
control->value = v;
|
|
}
|
|
}
|
|
|
|
for (Control* const control : m_cc_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->text_id)
|
|
{
|
|
int v = (value > control->range) ? control->range : value;
|
|
control->slider->SetValue(v);
|
|
control->text->ChangeValue(std::to_string(v));
|
|
control->value = v;
|
|
}
|
|
}
|
|
|
|
if (m_controls[0] != nullptr)
|
|
UpdateStickBitmap(m_main_stick);
|
|
if (m_controls[2] != nullptr)
|
|
UpdateStickBitmap(m_c_stick);
|
|
if (m_cc_controls[CC_L_STICK_X] != nullptr)
|
|
UpdateStickBitmap(m_cc_l_stick);
|
|
if (m_cc_controls[CC_R_STICK_X] != nullptr)
|
|
UpdateStickBitmap(m_cc_r_stick);
|
|
}
|
|
|
|
void TASInputDlg::UpdateStickBitmap(Stick stick)
|
|
{
|
|
int x = (u8)(
|
|
std::floor(((double)stick.x_cont.value / (double)(stick.x_cont.range + 1) * 255.0) + .5));
|
|
int y = (u8)(
|
|
std::floor(((double)stick.y_cont.value / (double)(stick.y_cont.range + 1) * 255.0) + .5));
|
|
if (stick.x_cont.reverse)
|
|
x = 256 - (u8)x;
|
|
if (stick.y_cont.reverse)
|
|
y = 256 - (u8)y;
|
|
stick.bitmap->SetBitmap(CreateStickBitmap(x, y));
|
|
}
|
|
|
|
void TASInputDlg::OnCloseWindow(wxCloseEvent& event)
|
|
{
|
|
if (event.CanVeto())
|
|
{
|
|
event.Skip(false);
|
|
Show(false);
|
|
ResetValues();
|
|
}
|
|
}
|
|
|
|
TASInputDlg::Stick* TASInputDlg::FindStickByID(int id)
|
|
{
|
|
if (id == ID_MAIN_STICK)
|
|
return &m_main_stick;
|
|
else if (id == ID_C_STICK)
|
|
return &m_c_stick;
|
|
else if (id == ID_CC_L_STICK)
|
|
return &m_cc_l_stick;
|
|
else if (id == ID_CC_R_STICK)
|
|
return &m_cc_r_stick;
|
|
else
|
|
return nullptr;
|
|
}
|
|
void TASInputDlg::OnMouseUpR(wxMouseEvent& event)
|
|
{
|
|
Stick* stick = FindStickByID(event.GetId());
|
|
if (stick == nullptr)
|
|
return;
|
|
|
|
stick->x_cont.value = stick->x_cont.default_value;
|
|
stick->y_cont.value = stick->y_cont.default_value;
|
|
stick->bitmap->SetBitmap(CreateStickBitmap(128, 128));
|
|
stick->x_cont.text->SetValue(std::to_string(stick->x_cont.default_value));
|
|
stick->y_cont.text->SetValue(std::to_string(stick->y_cont.default_value));
|
|
stick->x_cont.slider->SetValue(stick->x_cont.default_value);
|
|
stick->y_cont.slider->SetValue(stick->y_cont.default_value);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void TASInputDlg::OnRightClickSlider(wxMouseEvent& event)
|
|
{
|
|
for (Control* const control : m_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->slider_id)
|
|
{
|
|
control->value = control->default_value;
|
|
control->slider->SetValue(control->default_value);
|
|
control->text->SetValue(std::to_string(control->default_value));
|
|
return;
|
|
}
|
|
}
|
|
for (Control* const control : m_cc_controls)
|
|
{
|
|
if (control != nullptr && event.GetId() == control->slider_id)
|
|
{
|
|
control->value = control->default_value;
|
|
control->slider->SetValue(control->default_value);
|
|
control->text->SetValue(std::to_string(control->default_value));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TASInputDlg::OnMouseDownL(wxMouseEvent& event)
|
|
{
|
|
if (!event.LeftIsDown())
|
|
return;
|
|
|
|
Stick* stick = FindStickByID(event.GetId());
|
|
if (stick == nullptr)
|
|
return;
|
|
|
|
wxPoint ptM(event.GetPosition());
|
|
wxSize bitmap_size = FromDIP(wxSize(127, 127));
|
|
stick->x_cont.value = ptM.x * stick->x_cont.range / bitmap_size.GetWidth();
|
|
stick->y_cont.value = ptM.y * stick->y_cont.range / bitmap_size.GetHeight();
|
|
|
|
if ((unsigned)stick->y_cont.value > stick->y_cont.range)
|
|
stick->y_cont.value = stick->y_cont.range;
|
|
if ((unsigned)stick->x_cont.value > stick->x_cont.range)
|
|
stick->x_cont.value = stick->x_cont.range;
|
|
|
|
if (stick->y_cont.reverse)
|
|
stick->y_cont.value = stick->y_cont.range - (u16)stick->y_cont.value;
|
|
if (stick->x_cont.reverse)
|
|
stick->x_cont.value = stick->x_cont.range - (u16)stick->x_cont.value;
|
|
|
|
stick->x_cont.value = std::min<u32>(stick->x_cont.value, stick->x_cont.range);
|
|
stick->y_cont.value = std::min<u32>(stick->y_cont.value, stick->y_cont.range);
|
|
|
|
// This updates sliders and the bitmap too.
|
|
stick->x_cont.text->SetValue(std::to_string(stick->x_cont.value));
|
|
stick->y_cont.text->SetValue(std::to_string(stick->y_cont.value));
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void TASInputDlg::SetTurbo(wxMouseEvent& event)
|
|
{
|
|
auto cbox = static_cast<wxCheckBox*>(event.GetEventObject());
|
|
auto button = static_cast<Button*>(cbox->GetClientData());
|
|
|
|
if (event.LeftDown())
|
|
{
|
|
if (button)
|
|
button->turbo_on = false;
|
|
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
if (button)
|
|
{
|
|
button->checkbox->SetValue(true);
|
|
button->is_checked = true;
|
|
button->turbo_on = !button->turbo_on;
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// NOTE: Host / CPU Thread
|
|
void TASInputDlg::ButtonTurbo()
|
|
{
|
|
static u64 frame = Movie::GetCurrentFrame();
|
|
|
|
if (frame != Movie::GetCurrentFrame())
|
|
{
|
|
frame = Movie::GetCurrentFrame();
|
|
for (Button* const button : m_buttons)
|
|
{
|
|
if (button != nullptr && button->turbo_on)
|
|
{
|
|
button->value = !button->is_checked;
|
|
InvalidateButton(button);
|
|
}
|
|
}
|
|
if (m_ext == 2)
|
|
{
|
|
for (Button& button : m_cc_buttons)
|
|
{
|
|
if (button.turbo_on)
|
|
{
|
|
button.value = !button.is_checked;
|
|
InvalidateButton(&button);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TASInputDlg::InvalidateButton(Button* button)
|
|
{
|
|
if (!wxIsMainThread())
|
|
{
|
|
auto* evt = new wxCommandEvent(INVALIDATE_BUTTON_EVENT, button->id);
|
|
evt->SetClientData(button);
|
|
QueueEvent(evt);
|
|
return;
|
|
}
|
|
|
|
button->checkbox->SetValue(button->value);
|
|
button->is_checked = button->value;
|
|
}
|
|
|
|
void TASInputDlg::InvalidateControl(Control* control)
|
|
{
|
|
if (!wxIsMainThread())
|
|
{
|
|
auto* evt = new wxCommandEvent(INVALIDATE_CONTROL_EVENT, control->text_id);
|
|
evt->SetClientData(control);
|
|
QueueEvent(evt);
|
|
return;
|
|
}
|
|
|
|
control->text->SetValue(std::to_string(control->value));
|
|
}
|
|
|
|
void TASInputDlg::InvalidateExtension()
|
|
{
|
|
if (!wxIsMainThread())
|
|
{
|
|
QueueEvent(new wxThreadEvent(INVALIDATE_EXTENSION_EVENT));
|
|
return;
|
|
}
|
|
|
|
HandleExtensionChange();
|
|
}
|
|
|
|
void TASInputDlg::UpdateFromInvalidatedButton(wxCommandEvent& event)
|
|
{
|
|
auto* button = static_cast<Button*>(event.GetClientData());
|
|
_assert_msg_(PAD, button->id == button->checkbox->GetId(), "Button ids do not match: %i != %i",
|
|
button->id, button->checkbox->GetId());
|
|
button->checkbox->SetValue(button->value);
|
|
button->is_checked = button->value;
|
|
}
|
|
|
|
void TASInputDlg::UpdateFromInvalidatedControl(wxCommandEvent& event)
|
|
{
|
|
auto* control = static_cast<Control*>(event.GetClientData());
|
|
_assert_msg_(PAD, control->text_id == control->text->GetId(),
|
|
"Control ids do not match: %i != %i", control->text_id, control->text->GetId());
|
|
control->text->SetValue(std::to_string(control->value));
|
|
}
|
|
|
|
void TASInputDlg::UpdateFromInvalidatedExtension(wxThreadEvent&)
|
|
{
|
|
HandleExtensionChange();
|
|
}
|
|
|
|
wxBitmap TASInputDlg::CreateStickBitmap(int x, int y)
|
|
{
|
|
x = x / 2;
|
|
y = y / 2;
|
|
|
|
// Scale for screen DPI
|
|
static constexpr int WIDTH = 129;
|
|
static constexpr int HEIGHT = 129;
|
|
wxSize bitmap_size = FromDIP(wxSize(WIDTH, HEIGHT));
|
|
double scale_x = bitmap_size.GetWidth() / static_cast<double>(WIDTH);
|
|
double scale_y = bitmap_size.GetHeight() / static_cast<double>(HEIGHT);
|
|
|
|
wxMemoryDC memDC;
|
|
wxBitmap bitmap;
|
|
bitmap.CreateScaled(bitmap_size.GetWidth(), bitmap_size.GetHeight(), wxBITMAP_SCREEN_DEPTH,
|
|
GetContentScaleFactor());
|
|
memDC.SelectObject(bitmap);
|
|
memDC.SetUserScale(scale_x, scale_y);
|
|
memDC.SetBackground(*wxLIGHT_GREY_BRUSH);
|
|
memDC.Clear();
|
|
memDC.SetBrush(*wxWHITE_BRUSH);
|
|
memDC.DrawCircle(64, 64, 64);
|
|
memDC.SetPen(wxPen(*wxBLACK, 3, wxPENSTYLE_SOLID));
|
|
memDC.DrawLine(64, 64, x, y);
|
|
memDC.SetPen(*wxBLACK_PEN);
|
|
memDC.DrawLine(64, 0, 64, HEIGHT); // CrossHair doesn't work @96DPI on Windows for some reason
|
|
memDC.DrawLine(0, 64, WIDTH, 64);
|
|
memDC.SetBrush(*wxBLUE_BRUSH);
|
|
memDC.DrawCircle(x, y, 5);
|
|
memDC.SelectObject(wxNullBitmap);
|
|
return bitmap;
|
|
}
|