mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-11 07:56:15 +01:00
Enhance Wiimote support for GunCon emulation: Add configuration options and improve UI integration
This commit is contained in:
parent
f9e8fd535f
commit
6828bec394
|
|
@ -3,6 +3,7 @@
|
|||
#include "MouseHandler.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Io/guncon3_config.h"
|
||||
#include "Emu/Io/wiimote_config.h"
|
||||
#include "Emu/Cell/lv2/sys_usbd.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/RSX/Overlays/overlay_cursor.h"
|
||||
|
|
@ -212,11 +213,14 @@ extern bool is_input_allowed();
|
|||
|
||||
bool usb_device_guncon3::handle_wiimote(GunCon3_data& gc)
|
||||
{
|
||||
if (!get_wiimote_config().use_for_guncon.get())
|
||||
return false;
|
||||
|
||||
auto* wm = wiimote_handler::get_instance();
|
||||
auto states = wm->get_states();
|
||||
|
||||
// Determine which Wiimote to use based on our ordinal position among all GunCons
|
||||
int my_wiimote_index = -1;
|
||||
s64 my_wiimote_index = -1;
|
||||
{
|
||||
std::lock_guard lock(s_instances_mutex);
|
||||
auto found = std::find(s_instances.begin(), s_instances.end(), this);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,30 @@
|
|||
|
||||
LOG_CHANNEL(wiimote_log, "Wiimote");
|
||||
|
||||
template <>
|
||||
void fmt_class_string<wiimote_button>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](wiimote_button value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case wiimote_button::None: return "None";
|
||||
case wiimote_button::Left: return "Left";
|
||||
case wiimote_button::Right: return "Right";
|
||||
case wiimote_button::Down: return "Down";
|
||||
case wiimote_button::Up: return "Up";
|
||||
case wiimote_button::Plus: return "Plus";
|
||||
case wiimote_button::Two: return "Two";
|
||||
case wiimote_button::One: return "One";
|
||||
case wiimote_button::B: return "B";
|
||||
case wiimote_button::A: return "A";
|
||||
case wiimote_button::Minus: return "Minus";
|
||||
case wiimote_button::Home: return "Home";
|
||||
}
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
cfg_wiimote& get_wiimote_config()
|
||||
{
|
||||
static cfg_wiimote instance;
|
||||
|
|
@ -20,14 +44,14 @@ bool cfg_wiimote::load()
|
|||
{
|
||||
if (fs::file f{path, fs::read})
|
||||
{
|
||||
return from_string(f.to_string());
|
||||
return this->cfg::node::from_string(f.to_string());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cfg_wiimote::save() const
|
||||
{
|
||||
if (!cfg::node::save(path))
|
||||
if (!this->cfg::node::save(path))
|
||||
{
|
||||
wiimote_log.error("Failed to save wiimote config to '%s'", path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
#include "Input/wiimote_handler.h"
|
||||
|
||||
struct cfg_wiimote final : cfg::node
|
||||
struct cfg_wiimote : cfg::node
|
||||
{
|
||||
cfg_wiimote();
|
||||
bool load();
|
||||
void save() const;
|
||||
|
||||
cfg::_bool use_for_guncon{ this, "UseForGunCon", true };
|
||||
|
||||
struct node_mapping : cfg::node
|
||||
{
|
||||
node_mapping(cfg::node* _parent) : cfg::node(_parent, "Mapping") {}
|
||||
|
||||
cfg::uint<0, 0xFFFF> trigger{ this, "Trigger", 0x0400 };
|
||||
cfg::uint<0, 0xFFFF> a1{ this, "A1", 0x0800 };
|
||||
cfg::uint<0, 0xFFFF> a2{ this, "A2", 0x1000 };
|
||||
cfg::uint<0, 0xFFFF> a3{ this, "A3", 0x0001 };
|
||||
cfg::uint<0, 0xFFFF> b1{ this, "B1", 0x0200 };
|
||||
cfg::uint<0, 0xFFFF> b2{ this, "B2", 0x0100 };
|
||||
cfg::uint<0, 0xFFFF> b3{ this, "B3", 0x8000 };
|
||||
cfg::uint<0, 0xFFFF> c1{ this, "C1", 0x0010 };
|
||||
cfg::uint<0, 0xFFFF> c2{ this, "C2", 0x0002 };
|
||||
cfg::_enum<wiimote_button> trigger{ this, "Trigger", wiimote_button::B };
|
||||
cfg::_enum<wiimote_button> a1{ this, "A1", wiimote_button::A };
|
||||
cfg::_enum<wiimote_button> a2{ this, "A2", wiimote_button::Minus };
|
||||
cfg::_enum<wiimote_button> a3{ this, "A3", wiimote_button::Left };
|
||||
cfg::_enum<wiimote_button> b1{ this, "B1", wiimote_button::One };
|
||||
cfg::_enum<wiimote_button> b2{ this, "B2", wiimote_button::Two };
|
||||
cfg::_enum<wiimote_button> b3{ this, "B3", wiimote_button::Home };
|
||||
cfg::_enum<wiimote_button> c1{ this, "C1", wiimote_button::Plus };
|
||||
cfg::_enum<wiimote_button> c2{ this, "C2", wiimote_button::Right };
|
||||
|
||||
cfg::uint<0, 0xFFFF> b1_alt{ this, "B1_Alt", 0x0008 };
|
||||
cfg::uint<0, 0xFFFF> b2_alt{ this, "B2_Alt", 0x0004 };
|
||||
cfg::_enum<wiimote_button> b1_alt{ this, "B1_Alt", wiimote_button::Up };
|
||||
cfg::_enum<wiimote_button> b2_alt{ this, "B2_Alt", wiimote_button::Down };
|
||||
} mapping{ this };
|
||||
|
||||
const std::string path;
|
||||
|
|
|
|||
|
|
@ -213,17 +213,17 @@ void wiimote_handler::load_config()
|
|||
if (cfg.load())
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_mapping.trigger = static_cast<wiimote_button>(cfg.mapping.trigger.get());
|
||||
m_mapping.a1 = static_cast<wiimote_button>(cfg.mapping.a1.get());
|
||||
m_mapping.a2 = static_cast<wiimote_button>(cfg.mapping.a2.get());
|
||||
m_mapping.a3 = static_cast<wiimote_button>(cfg.mapping.a3.get());
|
||||
m_mapping.b1 = static_cast<wiimote_button>(cfg.mapping.b1.get());
|
||||
m_mapping.b2 = static_cast<wiimote_button>(cfg.mapping.b2.get());
|
||||
m_mapping.b3 = static_cast<wiimote_button>(cfg.mapping.b3.get());
|
||||
m_mapping.c1 = static_cast<wiimote_button>(cfg.mapping.c1.get());
|
||||
m_mapping.c2 = static_cast<wiimote_button>(cfg.mapping.c2.get());
|
||||
m_mapping.b1_alt = static_cast<wiimote_button>(cfg.mapping.b1_alt.get());
|
||||
m_mapping.b2_alt = static_cast<wiimote_button>(cfg.mapping.b2_alt.get());
|
||||
m_mapping.trigger = cfg.mapping.trigger.get();
|
||||
m_mapping.a1 = cfg.mapping.a1.get();
|
||||
m_mapping.a2 = cfg.mapping.a2.get();
|
||||
m_mapping.a3 = cfg.mapping.a3.get();
|
||||
m_mapping.b1 = cfg.mapping.b1.get();
|
||||
m_mapping.b2 = cfg.mapping.b2.get();
|
||||
m_mapping.b3 = cfg.mapping.b3.get();
|
||||
m_mapping.c1 = cfg.mapping.c1.get();
|
||||
m_mapping.c2 = cfg.mapping.c2.get();
|
||||
m_mapping.b1_alt = cfg.mapping.b1_alt.get();
|
||||
m_mapping.b2_alt = cfg.mapping.b2_alt.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,17 +232,17 @@ void wiimote_handler::save_config()
|
|||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
auto& cfg = get_wiimote_config();
|
||||
cfg.mapping.trigger.set(static_cast<u16>(m_mapping.trigger));
|
||||
cfg.mapping.a1.set(static_cast<u16>(m_mapping.a1));
|
||||
cfg.mapping.a2.set(static_cast<u16>(m_mapping.a2));
|
||||
cfg.mapping.a3.set(static_cast<u16>(m_mapping.a3));
|
||||
cfg.mapping.b1.set(static_cast<u16>(m_mapping.b1));
|
||||
cfg.mapping.b2.set(static_cast<u16>(m_mapping.b2));
|
||||
cfg.mapping.b3.set(static_cast<u16>(m_mapping.b3));
|
||||
cfg.mapping.c1.set(static_cast<u16>(m_mapping.c1));
|
||||
cfg.mapping.c2.set(static_cast<u16>(m_mapping.c2));
|
||||
cfg.mapping.b1_alt.set(static_cast<u16>(m_mapping.b1_alt));
|
||||
cfg.mapping.b2_alt.set(static_cast<u16>(m_mapping.b2_alt));
|
||||
cfg.mapping.trigger.set(m_mapping.trigger);
|
||||
cfg.mapping.a1.set(m_mapping.a1);
|
||||
cfg.mapping.a2.set(m_mapping.a2);
|
||||
cfg.mapping.a3.set(m_mapping.a3);
|
||||
cfg.mapping.b1.set(m_mapping.b1);
|
||||
cfg.mapping.b2.set(m_mapping.b2);
|
||||
cfg.mapping.b3.set(m_mapping.b3);
|
||||
cfg.mapping.c1.set(m_mapping.c1);
|
||||
cfg.mapping.c2.set(m_mapping.c2);
|
||||
cfg.mapping.b1_alt.set(m_mapping.b1_alt);
|
||||
cfg.mapping.b2_alt.set(m_mapping.b2_alt);
|
||||
}
|
||||
get_wiimote_config().save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ public:
|
|||
void start();
|
||||
void stop();
|
||||
|
||||
bool is_running() const { return m_running; }
|
||||
|
||||
std::vector<wiimote_state> get_states();
|
||||
usz get_device_count();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#include "stdafx.h"
|
||||
#include "wiimote_settings_dialog.h"
|
||||
#include "Input/wiimote_handler.h"
|
||||
#include "Emu/Io/wiimote_config.h"
|
||||
#include <QTimer>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
|
||||
wiimote_settings_dialog::wiimote_settings_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
|
|
@ -16,8 +19,18 @@ wiimote_settings_dialog::wiimote_settings_dialog(QWidget* parent)
|
|||
ui->cb_b1, ui->cb_b2, ui->cb_b3, ui->cb_a3, ui->cb_c2
|
||||
};
|
||||
|
||||
if (auto* use_guncon = findChild<QCheckBox*>( "useForGunCon"))
|
||||
{
|
||||
use_guncon->setChecked(get_wiimote_config().use_for_guncon.get());
|
||||
connect(use_guncon, &QCheckBox::toggled, this, [](bool checked)
|
||||
{
|
||||
get_wiimote_config().use_for_guncon.set(checked);
|
||||
get_wiimote_config().save();
|
||||
});
|
||||
}
|
||||
|
||||
update_list();
|
||||
connect(ui->restoreDefaultsButton, &QPushButton::clicked, this, &wiimote_settings_dialog::restore_defaults);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &wiimote_settings_dialog::restore_defaults);
|
||||
|
||||
// Timer updates both state AND device list (auto-refresh)
|
||||
QTimer* timer = new QTimer(this);
|
||||
|
|
@ -88,6 +101,10 @@ void wiimote_settings_dialog::restore_defaults()
|
|||
const wiimote_guncon_mapping default_map {};
|
||||
wm->set_mapping(default_map);
|
||||
|
||||
get_wiimote_config().use_for_guncon.set(true);
|
||||
if (auto* use_guncon = findChild<QCheckBox*>( "useForGunCon"))
|
||||
use_guncon->setChecked(true);
|
||||
|
||||
// Update UI
|
||||
for (auto* box : m_boxes) box->blockSignals(true);
|
||||
|
||||
|
|
@ -132,16 +149,9 @@ void wiimote_settings_dialog::update_state()
|
|||
{
|
||||
const int index = ui->wiimoteList->currentRow();
|
||||
auto* wm = wiimote_handler::get_instance();
|
||||
if (!wm || index < 0)
|
||||
{
|
||||
ui->connectionStatus->setText(tr("N/A"));
|
||||
ui->buttonState->setText(tr("N/A"));
|
||||
ui->irData->setText(tr("N/A"));
|
||||
return;
|
||||
}
|
||||
const auto states = wm && wm->is_running() ? wm->get_states() : std::vector<wiimote_state>{};
|
||||
|
||||
const auto states = wm->get_states();
|
||||
if (static_cast<usz>(index) >= states.size())
|
||||
if (!wm || !wm->is_running() || index < 0 || static_cast<usz>(index) >= states.size())
|
||||
{
|
||||
ui->connectionStatus->setText(tr("N/A"));
|
||||
ui->buttonState->setText(tr("N/A"));
|
||||
|
|
@ -153,17 +163,18 @@ void wiimote_settings_dialog::update_state()
|
|||
ui->connectionStatus->setText(state.connected ? tr("Connected") : tr("Disconnected"));
|
||||
|
||||
QStringList pressed_buttons;
|
||||
if (state.buttons & 0x0001) pressed_buttons << tr("Left");
|
||||
if (state.buttons & 0x0002) pressed_buttons << tr("Right");
|
||||
if (state.buttons & 0x0004) pressed_buttons << tr("Down");
|
||||
if (state.buttons & 0x0008) pressed_buttons << tr("Up");
|
||||
if (state.buttons & 0x0010) pressed_buttons << tr("Plus");
|
||||
if (state.buttons & 0x0100) pressed_buttons << tr("2");
|
||||
if (state.buttons & 0x0200) pressed_buttons << tr("1");
|
||||
if (state.buttons & 0x0400) pressed_buttons << tr("B");
|
||||
if (state.buttons & 0x0800) pressed_buttons << tr("A");
|
||||
if (state.buttons & 0x1000) pressed_buttons << tr("Minus");
|
||||
if (state.buttons & 0x8000) pressed_buttons << tr("Home");
|
||||
const auto is_pressed = [&](wiimote_button btn) { return (state.buttons & static_cast<u16>(btn)) != 0; };
|
||||
if (is_pressed(wiimote_button::Left)) pressed_buttons << tr("Left");
|
||||
if (is_pressed(wiimote_button::Right)) pressed_buttons << tr("Right");
|
||||
if (is_pressed(wiimote_button::Down)) pressed_buttons << tr("Down");
|
||||
if (is_pressed(wiimote_button::Up)) pressed_buttons << tr("Up");
|
||||
if (is_pressed(wiimote_button::Plus)) pressed_buttons << tr("Plus");
|
||||
if (is_pressed(wiimote_button::Two)) pressed_buttons << tr("2");
|
||||
if (is_pressed(wiimote_button::One)) pressed_buttons << tr("1");
|
||||
if (is_pressed(wiimote_button::B)) pressed_buttons << tr("B");
|
||||
if (is_pressed(wiimote_button::A)) pressed_buttons << tr("A");
|
||||
if (is_pressed(wiimote_button::Minus)) pressed_buttons << tr("Minus");
|
||||
if (is_pressed(wiimote_button::Home)) pressed_buttons << tr("Home");
|
||||
|
||||
QString button_text = QString("0x%1").arg(state.buttons, 4, 16, QChar('0')).toUpper();
|
||||
if (!pressed_buttons.isEmpty())
|
||||
|
|
@ -173,15 +184,32 @@ void wiimote_settings_dialog::update_state()
|
|||
ui->buttonState->setText(button_text);
|
||||
|
||||
QString ir_text;
|
||||
QPixmap pixmap(ui->irVisual->size());
|
||||
const int w = ui->irVisual->width();
|
||||
const int h = ui->irVisual->height();
|
||||
QPixmap pixmap(w, h);
|
||||
pixmap.fill(Qt::black);
|
||||
QPainter painter(&pixmap);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// Draw center crosshair
|
||||
// Calculate 4:3 drawing area (Wiimote IR space is 1024x768)
|
||||
int draw_w, draw_h;
|
||||
if (w * 3 > h * 4) // wider than 4:3
|
||||
{
|
||||
draw_h = h;
|
||||
draw_w = h * 4 / 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
draw_w = w;
|
||||
draw_h = w * 3 / 4;
|
||||
}
|
||||
const int offset_x = (w - draw_w) / 2;
|
||||
const int offset_y = (h - draw_h) / 2;
|
||||
|
||||
// Draw center crosshair in the 4:3 area
|
||||
painter.setPen(QPen(Qt::darkGray, 1, Qt::DashLine));
|
||||
painter.drawLine(pixmap.width() / 2, 0, pixmap.width() / 2, pixmap.height());
|
||||
painter.drawLine(0, pixmap.height() / 2, pixmap.width(), pixmap.height() / 2);
|
||||
painter.drawLine(offset_x + draw_w / 2, offset_y, offset_x + draw_w / 2, offset_y + draw_h);
|
||||
painter.drawLine(offset_x, offset_y + draw_h / 2, offset_x + draw_w, offset_y + draw_h / 2);
|
||||
|
||||
static const std::array<QColor, MAX_WIIMOTE_IR_POINTS> colors = { Qt::red, Qt::green, Qt::blue, Qt::yellow };
|
||||
|
||||
|
|
@ -191,10 +219,9 @@ void wiimote_settings_dialog::update_state()
|
|||
{
|
||||
ir_text += QString("[%1: %2, %3] ").arg(i).arg(state.ir[i].x).arg(state.ir[i].y);
|
||||
|
||||
// Map 0..1023 X and 0..767 Y to pixmap coordinates
|
||||
// Wiimote X/Y are inverted relative to pointing direction
|
||||
const float x = ((1023 - state.ir[i].x) / 1023.0f) * pixmap.width();
|
||||
const float y = (state.ir[i].y / 767.0f) * pixmap.height();
|
||||
// Map 0..1023 X and 0..767 Y to 4:3 drawing area
|
||||
const float x = offset_x + ((1023.0f - state.ir[i].x) / 1023.0f) * draw_w;
|
||||
const float y = offset_y + (state.ir[i].y / 767.0f) * draw_h;
|
||||
|
||||
painter.setPen(colors[i]);
|
||||
painter.setBrush(colors[i]);
|
||||
|
|
@ -209,7 +236,7 @@ void wiimote_settings_dialog::update_state()
|
|||
void wiimote_settings_dialog::update_list()
|
||||
{
|
||||
auto* wm = wiimote_handler::get_instance();
|
||||
if (!wm)
|
||||
if (!wm || !wm->is_running())
|
||||
{
|
||||
if (ui->wiimoteList->count() != 1 || ui->wiimoteList->item(0)->text() != tr("Wiimote Manager not initialized."))
|
||||
{
|
||||
|
|
@ -221,7 +248,17 @@ void wiimote_settings_dialog::update_list()
|
|||
|
||||
const auto states = wm->get_states();
|
||||
|
||||
// Only update if the list content actually changed (avoid flicker)
|
||||
if (states.empty())
|
||||
{
|
||||
if (ui->wiimoteList->count() != 1 || ui->wiimoteList->item(0)->text() != tr("No Wiimotes found."))
|
||||
{
|
||||
ui->wiimoteList->clear();
|
||||
ui->wiimoteList->addItem(tr("No Wiimotes found."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only update if the list count changed (avoid flicker)
|
||||
if (static_cast<usz>(ui->wiimoteList->count()) != states.size())
|
||||
{
|
||||
const int current_row = ui->wiimoteList->currentRow();
|
||||
|
|
@ -251,13 +288,9 @@ void wiimote_settings_dialog::update_list()
|
|||
QString label = tr("Wiimote #%1").arg(i + 1);
|
||||
if (!states[i].connected) label += " (" + tr("Disconnected") + ")";
|
||||
|
||||
if (static_cast<int>(i) < ui->wiimoteList->count())
|
||||
if (QListWidgetItem* item = ui->wiimoteList->item(static_cast<int>(i)); item && item->text() != label)
|
||||
{
|
||||
QListWidgetItem* item = ui->wiimoteList->item(static_cast<int>(i));
|
||||
if (item && item->text() != label)
|
||||
{
|
||||
item->setText(label);
|
||||
}
|
||||
item->setText(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,22 @@
|
|||
<string>Wiimote Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="generalGroupBox">
|
||||
<property name="title">
|
||||
<string>General Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useForGunCon">
|
||||
<property name="text">
|
||||
<string>Enable Wiimote for GunCon Emulation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
|
|
@ -205,38 +221,14 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreDefaultsButton">
|
||||
<property name="text">
|
||||
<string>Restore Defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close|QDialogButtonBox::RestoreDefaults</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
|||
Loading…
Reference in a new issue